From 389ad1349c4ff245a3466a2e361c1cd1dd11d2ba Mon Sep 17 00:00:00 2001 From: "netop://ウィビ" Date: Wed, 21 Oct 2026 16:47:38 -0700 Subject: MAJOR RELEASE - inlined vendored dependencies from npm - makes this module work in Ende --- source/common.ts | 2 +- source/gql.ts | 3 +- source/graphiql/assets.ts | 28 ++++++ source/graphiql/favicon.svg | 15 +++ source/graphiql/render.ts | 56 +++++------ source/graphiql/standalone.css | 2 + source/graphiql/standalone.js | 207 +++++++++++++++++++++++++++++++++++++++++ source/graphiql/version.ts | 1 + source/http.ts | 10 +- source/import.ts | 2 +- source/utility/pubsub.ts | 64 +++++++++++++ source/utility/types.ts | 2 +- source/ws.ts | 4 +- 13 files changed, 362 insertions(+), 34 deletions(-) create mode 100644 source/graphiql/assets.ts create mode 100644 source/graphiql/favicon.svg create mode 100644 source/graphiql/standalone.css create mode 100644 source/graphiql/standalone.js create mode 100644 source/graphiql/version.ts create mode 100644 source/utility/pubsub.ts (limited to 'source') diff --git a/source/common.ts b/source/common.ts index 902bdd0..777bb98 100755 --- a/source/common.ts +++ b/source/common.ts @@ -3,7 +3,7 @@ /*** IMPORT ------------------------------------------- ***/ -import { graphql, type ExecutionResult } from "graphql"; +import { graphql, type ExecutionResult } from "#deps"; /*** UTILITY ------------------------------------------ ***/ diff --git a/source/gql.ts b/source/gql.ts index f88d8ff..823f73a 100644 --- a/source/gql.ts +++ b/source/gql.ts @@ -8,7 +8,7 @@ import { type DefinitionNode, type DocumentNode, type Location -} from "graphql"; +} from "#deps"; /*** UTILITY ------------------------------------------ ***/ @@ -143,6 +143,7 @@ function parseDocument(source: string) { const cacheKey = normalize(source); if (!docCache.has(cacheKey)) { + // @ts-ignore: TS2353 const parsed = parse(source, { allowLegacyFragmentVariables }); if (!parsed || parsed.kind !== "Document") diff --git a/source/graphiql/assets.ts b/source/graphiql/assets.ts new file mode 100644 index 0000000..d0848e7 --- /dev/null +++ b/source/graphiql/assets.ts @@ -0,0 +1,28 @@ +// source/graphiql/assets.ts +import css from "./standalone.css" with { type: "text" }; +import favicon from "./favicon.svg" with { type: "text" }; +import js from "./standalone.js" with { type: "text" }; +import { GRAPHIQL_VERSION } from "./version.ts"; + +const base = `/_graphiql/${GRAPHIQL_VERSION}`; + +export const assets: Record = { + [`${base}/favicon.svg`]: { body: favicon, type: "image/svg+xml" }, + [`${base}/standalone.css`]: { body: css, type: "text/css; charset=utf-8" }, + [`${base}/standalone.js`]: { body: js, type: "text/javascript; charset=utf-8" } +}; + +export function serveAsset(request: { url: string }): Response | undefined { + const { pathname } = new URL(request.url); + const key = Object.keys(assets).find((path) => pathname.endsWith(path)); + + if (!key) + return; + + return new Response(assets[key].body, { + headers: { + "cache-control": "public, max-age=31536000, immutable", + "content-type": assets[key].type + } + }); +} diff --git a/source/graphiql/favicon.svg b/source/graphiql/favicon.svg new file mode 100644 index 0000000..80a7a21 --- /dev/null +++ b/source/graphiql/favicon.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + diff --git a/source/graphiql/render.ts b/source/graphiql/render.ts index 7adec57..c846d34 100755 --- a/source/graphiql/render.ts +++ b/source/graphiql/render.ts @@ -4,26 +4,31 @@ /*** IMPORT ------------------------------------------- ***/ import { default as dedent } from "@netopwibby/dedent"; -import { filterXSS } from "xss"; +import { GRAPHIQL_VERSION } from "./version.ts"; /*** UTILITY ------------------------------------------ ***/ -const filter = (val: string) => filterXSS(val, { - stripIgnoreTag: true, - stripIgnoreTagBody: ["script"], - whiteList: {} -}); +const escapeHtml = (value: string) => value + .replaceAll("&", "&") + .replaceAll("<", "<") + .replaceAll(">", ">") + .replaceAll("\"", """) + .replaceAll("'", "'"); + +const toScriptString = (value: string) => JSON.stringify(value).replaceAll("<", "\\u003c"); const buildAssetUrl = ({ cdnUrl, suffix, version }: { - cdnUrl: string - suffix: string - version?: string -}) => filter(`${cdnUrl}/@eeeooolll/graphiql${version ? `@${version}` : ""}/dist/${suffix}`); + cdnUrl?: string; + suffix: string; + version: string; +}) => cdnUrl ? + `${cdnUrl}/@eeeooolll/graphiql@${version}/dist/${suffix}` : + `./_graphiql/${GRAPHIQL_VERSION}/${suffix}`; /*** EXPORT ------------------------------------------- ***/ export interface RenderPageOptions { - /** Base CDN. Defaults to jsDelivr. */ + /** Load the bundle from this CDN base (e.g. `//cdn.jsdelivr.net/npm`) instead of the copy embedded in gq. */ cdnUrl?: string; /** GraphQL endpoint the embedded fetcher posts to. */ endpoint?: string; @@ -31,7 +36,7 @@ export interface RenderPageOptions { faviconUrl?: string | null; /** Document ``. */ title?: string; - /** Pin `@eeeooolll/graphiql` to a specific version on the CDN. */ + /** `@eeeooolll/graphiql` version to load from `cdnUrl`. Defaults to the embedded version; ignored when self-hosting. */ version?: string; } @@ -39,31 +44,28 @@ export interface RenderPageOptions { * Renders the GraphiQL HTML shell. * * Loads the prebuilt IIFE bundle from `@eeeooolll/graphiql` (registered as - * `window.EolGraphiQL`) and mounts it against `endpoint`. + * `window.EolGraphiQL`) and mounts it against `endpoint`. The bundle is + * embedded in gq and served by `GraphQLHTTP`; set `cdnUrl` to load it + * from a CDN instead. * * Usually called indirectly via `GraphQLHTTP({ graphiql: true })`; invoke it * directly if you need to embed GraphiQL in a custom route. */ export function renderPlaygroundPage(options: RenderPageOptions): string { const { - cdnUrl = "//cdn.jsdelivr.net/npm", + cdnUrl, endpoint = "/graphql", faviconUrl, title = "GraphiQL", - version + version = GRAPHIQL_VERSION } = options; - const safeEndpoint = filter(endpoint); - const safeTitle = filter(title); - const scriptUrl = buildAssetUrl({ cdnUrl, suffix: "standalone.js", version }); - const styleUrl = buildAssetUrl({ cdnUrl, suffix: "standalone.css", version }); + const scriptUrl = escapeHtml(buildAssetUrl({ cdnUrl, suffix: "standalone.js", version })); + const styleUrl = escapeHtml(buildAssetUrl({ cdnUrl, suffix: "standalone.css", version })); - const faviconLink = - faviconUrl === null ? - "" : - typeof faviconUrl === "string" ? - `<link rel="shortcut icon" href="${filter(faviconUrl)}"/>` : - `<link rel="shortcut icon" href="${buildAssetUrl({ cdnUrl, suffix: "favicon.svg", version })}"/>`; + const faviconLink = faviconUrl === null ? + "" : + `<link rel="shortcut icon" href="${escapeHtml(faviconUrl ?? buildAssetUrl({ cdnUrl, suffix: "favicon.svg", version }))}"/>`; return dedent` <!DOCTYPE html> @@ -71,7 +73,7 @@ export function renderPlaygroundPage(options: RenderPageOptions): string { <head> <meta charset="utf-8"/> <meta name="viewport" content="width=device-width, initial-scale=1"/> - <title>${safeTitle} + ${escapeHtml(title)} ${faviconLink} @@ -106,7 +108,7 @@ export function renderPlaygroundPage(options: RenderPageOptions): string {