/// import
import { filterXSS } from "npm:xss@1.0.14";
/// util
import { getLoadingMarkup } from "./markup.ts";
const CONFIG_ID = "playground-config";
const loading = getLoadingMarkup();
const filter = (val: string) => {
return filterXSS(val, {
stripIgnoreTag: true,
stripIgnoreTagBody: ["script"],
whiteList: {}
});
}
const getCdnMarkup = ({ cdnUrl = "//cdn.jsdelivr.net/npm", faviconUrl, version }: {
cdnUrl?: string
faviconUrl?: string | null
version?: string
}) => {
const buildCDNUrl = (packageName: string, suffix: string) =>
filter(`${cdnUrl}/${packageName}${version ? `@${version}` : ""}/${suffix}` || "");
return `
${typeof faviconUrl === "string" ? `` : ""}
${faviconUrl === undefined ? `` : ""}
`;
}
const renderConfig = (config: unknown) => {
return filterXSS(`
${JSON.stringify(config)}
`, {
whiteList: { div: ["id"] }
});
};
/// export
export interface MiddlewareOptions {
codeTheme?: EditorColours;
config?: any;
endpoint?: string;
env?: any;
schema?: IntrospectionResult;
settings?: ISettings;
subscriptionEndpoint?: string;
tabs?: Tab[];
workspaceName?: string;
}
export type CursorShape = "line" | "block" | "underline";
export type Theme = "dark" | "light";
export interface ISettings {
"editor.cursorShape": CursorShape;
"editor.fontFamily": string;
"editor.fontSize": number;
"editor.reuseHeaders": boolean;
"editor.theme": Theme;
"general.betaUpdates": boolean;
"request.credentials": string;
"request.globalHeaders": { [key: string]: string };
"schema.polling.enable": boolean;
"schema.polling.endpointFilter": string;
"schema.polling.interval": number;
"tracing.hideTracingResponse": boolean;
"tracing.tracingSupported": boolean;
}
export interface EditorColours {
atom: string;
attribute: string;
builtin: string;
comment: string;
cursorColor: string;
def: string;
editorBackground: string;
keyword: string;
leftDrawerBackground: string;
meta: string;
number: string;
property: string;
punctuation: string;
qualifier: string;
resultBackground: string;
rightDrawerBackground: string;
selection: string;
string: string;
string2: string;
variable: string;
ws: string;
}
export interface IntrospectionResult {
__schema: any;
}
export interface RenderPageOptions extends MiddlewareOptions {
cdnUrl?: string;
env?: any;
faviconUrl?: string | null;
title?: string;
version?: string;
}
export interface Tab {
endpoint: string;
headers?: { [key: string]: string };
name?: string;
query: string;
responses?: string[];
variables?: string;
}
export function renderPlaygroundPage(options: RenderPageOptions) {
const extendedOptions:
& Partial<{
canSaveConfig: boolean
configString: string
}>
& RenderPageOptions = {
...options,
canSaveConfig: false
};
if (options.config)
extendedOptions.configString = JSON.stringify(options.config, null, 2);
if (!extendedOptions.endpoint && !extendedOptions.configString)
console.warn("WARNING: You did not provide an endpoint and do not have a .graphqlconfig. Make sure you have at least one of them.");
else if (extendedOptions.endpoint)
extendedOptions.endpoint = filter(extendedOptions.endpoint || "");
return `
${extendedOptions.title || "GraphQL Playground"}
${extendedOptions.env === "react" || extendedOptions.env === "electron" ? "" : getCdnMarkup(extendedOptions)}
${loading.container}
${renderConfig(extendedOptions)}
`;
}