import type { ApolloServerPluginEmbeddedLandingPageLocalDefaultOptions, ApolloServerPluginEmbeddedLandingPageProductionDefaultOptions, } from './types'; // This function turns an object into a string and replaces // <, >, &, ' with their unicode chars to avoid adding html tags to // the landing page html that might be passed from the config. // The only place these characters can appear in the output of // JSON.stringify is within string literals, where they can equally // well appear \u-escaped. This specifically means that // `` won't terminate the script block early. // (Perhaps we should have done this instead of the triple-encoding // of encodeConfig for the main landing page.) function getConfigStringForHtml(config: object) { return JSON.stringify(config) .replace('<', '\\u003c') .replace('>', '\\u003e') .replace('&', '\\u0026') .replace("'", '\\u0027'); } export const getEmbeddedExplorerHTML = ( explorerCdnVersion: string, config: ApolloServerPluginEmbeddedLandingPageProductionDefaultOptions, apolloServerVersion: string, nonce: string, ) => { interface EmbeddableExplorerOptions { graphRef: string; target: string; initialState?: { document?: string; variables?: Record; headers?: Record; collectionId?: string; operationId?: string; displayOptions: { docsPanelState?: 'open' | 'closed'; // default to 'open', showHeadersAndEnvVars?: boolean; // default to `false` theme?: 'dark' | 'light'; }; }; persistExplorerState?: boolean; // defaults to 'false' endpointUrl: string; includeCookies?: boolean; // defaults to 'false' runTelemetry?: boolean; allowDynamicStyles?: boolean; // defaults to 'true' } const productionLandingPageEmbedConfigOrDefault = { displayOptions: {}, persistExplorerState: false, runTelemetry: true, ...(typeof config.embed === 'boolean' ? {} : config.embed), }; const embeddedExplorerParams: Omit< EmbeddableExplorerOptions, 'endpointUrl' > & { runtime: string } = { graphRef: config.graphRef, target: '#embeddableExplorer', initialState: { ...('document' in config || 'headers' in config || 'variables' in config ? { document: config.document, headers: config.headers, variables: config.variables, } : {}), ...('collectionId' in config ? { collectionId: config.collectionId, operationId: config.operationId, } : {}), displayOptions: { ...productionLandingPageEmbedConfigOrDefault.displayOptions, }, }, persistExplorerState: productionLandingPageEmbedConfigOrDefault.persistExplorerState, includeCookies: config.includeCookies, runtime: apolloServerVersion, runTelemetry: productionLandingPageEmbedConfigOrDefault.runTelemetry, allowDynamicStyles: false, // disabled for CSP - we add the iframe styles ourselves instead }; return `

Welcome to Apollo Server

Apollo Explorer cannot be loaded; it appears that you might be offline.

`; }; export const getEmbeddedSandboxHTML = ( sandboxCdnVersion: string, config: ApolloServerPluginEmbeddedLandingPageLocalDefaultOptions, apolloServerVersion: string, nonce: string, ) => { const localDevelopmentEmbedConfigOrDefault = { runTelemetry: true, endpointIsEditable: false, initialState: {}, ...(typeof config.embed === 'boolean' ? {} : (config.embed ?? {})), }; const embeddedSandboxConfig = { target: '#embeddableSandbox', initialState: { ...('document' in config || 'headers' in config || 'variables' in config ? { document: config.document, variables: config.variables, headers: config.headers, } : {}), ...('collectionId' in config ? { collectionId: config.collectionId, operationId: config.operationId, } : {}), includeCookies: config.includeCookies, ...localDevelopmentEmbedConfigOrDefault.initialState, }, hideCookieToggle: false, endpointIsEditable: localDevelopmentEmbedConfigOrDefault.endpointIsEditable, runtime: apolloServerVersion, runTelemetry: localDevelopmentEmbedConfigOrDefault.runTelemetry, allowDynamicStyles: false, // disabled for CSP - we add the iframe styles ourselves instead }; return `

Welcome to Apollo Server

Apollo Sandbox cannot be loaded; it appears that you might be offline.

`; };