From bcc4b30f1b2d82670071388e127d9e69ab27480b Mon Sep 17 00:00:00 2001 From: Mark Larah Date: Tue, 9 Sep 2025 11:52:21 +0200 Subject: [PATCH 1/7] Update index.html to include integrity hashes --- examples/graphiql-cdn/index.html | 34 ++++++++++++++++++++++---------- 1 file changed, 24 insertions(+), 10 deletions(-) diff --git a/examples/graphiql-cdn/index.html b/examples/graphiql-cdn/index.html index 1fb5b7cef3..42325f7171 100644 --- a/examples/graphiql-cdn/index.html +++ b/examples/graphiql-cdn/index.html @@ -28,10 +28,17 @@ font-size: 4rem; } - + + + + + + + GraphiQL 5 with React 19 and GraphiQL Explorer + + + + + + + + +
+
Loading…
+
+ + From bc4c6ee94cef32af9e213881675f07978ed2f828 Mon Sep 17 00:00:00 2001 From: Mark Larah Date: Mon, 27 Oct 2025 08:40:22 -0500 Subject: [PATCH 3/7] Trim update-cdn-example.yml workflow --- .github/workflows/update-cdn-example.yml | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/.github/workflows/update-cdn-example.yml b/.github/workflows/update-cdn-example.yml index e3b5991401..fb30a4189f 100644 --- a/.github/workflows/update-cdn-example.yml +++ b/.github/workflows/update-cdn-example.yml @@ -5,8 +5,6 @@ on: # Run every Monday at 10:00 UTC - cron: '0 10 * * 1' workflow_dispatch: # Allow manual triggering - repository_dispatch: - types: [update-cdn-dependencies] permissions: contents: write @@ -26,11 +24,8 @@ jobs: with: node-version: '22' - - name: Install Dependencies - run: npm install -g npm-check-updates semver - - - name: Check for Updates and Update index.html - id: update + - name: Generate index.html + id: generate run: | node .github/scripts/update-cdn-versions.mjs > examples/graphiql-cdn/index.html @@ -54,7 +49,7 @@ jobs: This PR automatically updates the CDN example dependencies to their latest versions. 🤖 This PR was automatically generated automatically, beep boop - branch: automated/update-cdn-dependencies + branch: automated/update-cdn-example-dependencies delete-branch: true labels: | dependencies From 5ce84f31241a607a6fa2dcc4f92cdee7b31a1c1c Mon Sep 17 00:00:00 2001 From: Mark Larah Date: Mon, 27 Oct 2025 08:43:44 -0500 Subject: [PATCH 4/7] trigger for releases --- .github/workflows/update-cdn-example.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/update-cdn-example.yml b/.github/workflows/update-cdn-example.yml index fb30a4189f..59b762b58f 100644 --- a/.github/workflows/update-cdn-example.yml +++ b/.github/workflows/update-cdn-example.yml @@ -5,6 +5,8 @@ on: # Run every Monday at 10:00 UTC - cron: '0 10 * * 1' workflow_dispatch: # Allow manual triggering + release: + types: [released] permissions: contents: write From e3f35f7dee1d8c091cef0f0611c9b8f67f80312c Mon Sep 17 00:00:00 2001 From: Mark Larah Date: Mon, 27 Oct 2025 09:00:55 -0500 Subject: [PATCH 5/7] tidy up .github/scripts/update-cdn-versions.mjs --- .github/scripts/update-cdn-versions.mjs | 48 +++++++++++++++++-------- 1 file changed, 34 insertions(+), 14 deletions(-) diff --git a/.github/scripts/update-cdn-versions.mjs b/.github/scripts/update-cdn-versions.mjs index b81e045b90..060b5ff582 100644 --- a/.github/scripts/update-cdn-versions.mjs +++ b/.github/scripts/update-cdn-versions.mjs @@ -18,16 +18,14 @@ const PACKAGES = [ 'graphql', ]; -async function fetchIntegrityHash(url) { - const response = await fetch(url, { redirect: 'follow' }); - if (!response.ok) { - throw new Error(`Failed to fetch ${url}: ${response.statusText}`); - } - const content = await response.text(); - const hash = crypto.createHash('sha384').update(content).digest('base64'); - return [url, `sha384-${hash}`]; -} - +/** + * Given the name of an npm package, return a tuple of: [name, latest version] + * + * @example + * + * fetchLatestVersion('left-pad') + * => ['left-pad', '1.0.1'] + */ async function fetchLatestVersion(packageName) { const url = `https://registry.npmjs.org/${packageName}/latest`; const response = await fetch(url); @@ -38,6 +36,24 @@ async function fetchLatestVersion(packageName) { return [packageName, version] } +/** + * Given the url of a file, return a tuple of: [url, sha384] + * + * @example + * + * fetchLatestVersion('https://esm.sh/left-pad/lib/index.js') + * => ['https://esm.sh/left-pad/lib/index.js', 'sha-384-deadbeef123'] + */ +async function fetchIntegrityHash(url) { + const response = await fetch(url, { redirect: 'follow' }); + if (!response.ok) { + throw new Error(`Failed to fetch ${url}: ${response.statusText}`); + } + const content = await response.text(); + const hash = crypto.createHash('sha384').update(content).digest('base64'); + return [url, `sha384-${hash}`]; +} + async function main () { const versions = Object.fromEntries(await Promise.all(PACKAGES.map(fetchLatestVersion))); const cdnUrl = packageName => `https://esm.sh/${packageName}@${versions[packageName]}`; @@ -68,24 +84,28 @@ async function main () { cdnUrl('graphql'), ].map(fetchIntegrityHash))); - const importMap = { imports, integrity }; + let importMap = JSON.stringify({ imports, integrity }, null, 2); // CSS const graphiqlCss = `${cdnUrl('graphiql')}/dist/style.css`; + const graphiqlCssHash = (await fetchIntegrityHash(graphiqlCss))[1]; const graphiqlPluginExplorer = `${cdnUrl('@graphiql/plugin-explorer')}/dist/style.css`; + const graphiqlPluginExplorerHash = (await fetchIntegrityHash(graphiqlPluginExplorer))[1]; // Generate index.html const templatePath = path.join(import.meta.dirname, '../../resources/index.html.template'); const template = fs.readFileSync(templatePath, 'utf8'); + // Indent import map to be correctly formatted in index.html const indent = lines => lines.split('\n').map(line => ` ${line}`).join('\n'); + importMap = indent(importMap); const output = template - .replace('{{IMPORTMAP}}', indent(JSON.stringify(importMap, null, 2))) + .replace('{{IMPORTMAP}}', importMap) .replace('{{GRAPHIQL_CSS_URL}}', graphiqlCss) - .replace('{{GRAPHIQL_CSS_INTEGRITY}}', (await fetchIntegrityHash(graphiqlCss))[1]) + .replace('{{GRAPHIQL_CSS_INTEGRITY}}', graphiqlCssHash) .replace('{{PLUGIN_EXPLORER_CSS_URL}}', graphiqlPluginExplorer) - .replace('{{PLUGIN_EXPLORER_CSS_INTEGRITY}}', (await fetchIntegrityHash(graphiqlPluginExplorer))[1]); + .replace('{{PLUGIN_EXPLORER_CSS_INTEGRITY}}', graphiqlPluginExplorerHash); console.log(output); } From 5cd5ae532429b2c17cb9669e024c5ed5b1336e5a Mon Sep 17 00:00:00 2001 From: Mark Larah Date: Mon, 27 Oct 2025 09:01:58 -0500 Subject: [PATCH 6/7] Update update-cdn-versions.mjs --- .github/scripts/update-cdn-versions.mjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/scripts/update-cdn-versions.mjs b/.github/scripts/update-cdn-versions.mjs index 060b5ff582..38aeaf3560 100644 --- a/.github/scripts/update-cdn-versions.mjs +++ b/.github/scripts/update-cdn-versions.mjs @@ -42,7 +42,7 @@ async function fetchLatestVersion(packageName) { * @example * * fetchLatestVersion('https://esm.sh/left-pad/lib/index.js') - * => ['https://esm.sh/left-pad/lib/index.js', 'sha-384-deadbeef123'] + * => ['https://esm.sh/left-pad/lib/index.js', 'sha384-deadbeef123'] */ async function fetchIntegrityHash(url) { const response = await fetch(url, { redirect: 'follow' }); From d3f1bb6fa841e2e7fd4fe86cb622d109f682fc04 Mon Sep 17 00:00:00 2001 From: Mark Larah Date: Mon, 27 Oct 2025 09:04:40 -0500 Subject: [PATCH 7/7] update workflow deps --- .github/workflows/update-cdn-example.yml | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/.github/workflows/update-cdn-example.yml b/.github/workflows/update-cdn-example.yml index 59b762b58f..fff13b5079 100644 --- a/.github/workflows/update-cdn-example.yml +++ b/.github/workflows/update-cdn-example.yml @@ -18,13 +18,10 @@ jobs: runs-on: ubuntu-latest steps: - - name: Checkout Code - uses: actions/checkout@v4 - - - name: Setup Node.js - uses: actions/setup-node@v4 + - uses: actions/checkout@v5 + - uses: actions/setup-node@v6 with: - node-version: '22' + node-version: 22 - name: Generate index.html id: generate