From cafee0d4322e5868f66399951d6d5c8e51fc2c74 Mon Sep 17 00:00:00 2001 From: Doxa Maps Date: Tue, 29 Sep 2026 09:18:39 +0000 Subject: [PATCH] The default embed snippet preloads MapLibre, the engine it draws with; Mapbox gets its own snippet Every map profile's copy-paste snippet began with a preload of api.mapbox.com's mapbox-gl.js, while its own profile-config said provider "maplibre", and since 2026-09-28 only an explicit provider "mapbox" selects Mapbox. So an unedited paste downloaded the Mapbox engine on every view, never used it, and made a Mapbox request from a keyless page. Its only guard was a stale comment asking the partner to delete the line. - embed.script now preloads MapLibre's script and stylesheet, pinned the way useMapInstance loads them so the preloads are reused, and its note points a host who wants Mapbox at the variant. - embed.scriptMapbox is that variant: the Mapbox preloads and the same element with "provider":"mapbox". - check:snippet-engine (gates:post, with a self-test) reads the built manifest: no default snippet names api.mapbox.com, every map snippet preloads MapLibre, and every Mapbox variant preloads Mapbox and sets its provider. Planted: the old snippet and a provider-less variant both fail. --- internal/check-snippet-engine.mjs | 77 ++++++++++++++++++++++ internal/generate-tree.mjs | 30 +++++++-- package.json | 5 +- public/dx-maps/doxa-maps-spa/manifest.json | 57 ++++++++++------ public/dx-maps/manifest.json | 57 ++++++++++------ 5 files changed, 181 insertions(+), 45 deletions(-) create mode 100644 internal/check-snippet-engine.mjs diff --git a/internal/check-snippet-engine.mjs b/internal/check-snippet-engine.mjs new file mode 100644 index 000000000..5556d0b68 --- /dev/null +++ b/internal/check-snippet-engine.mjs @@ -0,0 +1,77 @@ +#!/usr/bin/env node +/** + * check-snippet-engine.mjs — the copy-paste snippet preloads the engine it will actually use. + * + * THE DEFECT (FIX-DEFAULT-EMBED-SNIPPET-PRELOADS-THE-MAPBOX-ENGINE). Every map profile's default snippet + * began with a preload of api.mapbox.com's mapbox-gl.js, while the same snippet's profile-config said + * provider "maplibre" — and since 2026-09-28 only an explicit provider "mapbox" selects Mapbox. So an + * unedited paste downloaded the Mapbox engine on every view, never used it, and made a Mapbox request + * from a keyless page. Its only guard was a comment asking the partner to delete the line. + * + * Reads the BUILT manifest (public//manifest.json) and asserts, for every profile: + * a map profile embed.script names no api.mapbox.com URL and preloads MapLibre (its script pin); + * embed.scriptMapbox preloads the Mapbox engine AND sets "provider":"mapbox" + * any other profile embed.script names no api.mapbox.com URL, and there is no scriptMapbox + * `--self-test` plants the old snippet and a variant that forgot its provider; both must fail. + * Exit 0 pass · 1 fail · 2 no manifest. + */ +import { readFileSync, existsSync } from 'node:fs' +import { resolve, dirname, join } from 'node:path' +import { fileURLToPath } from 'node:url' +import { TREE_NAME } from './_tree-name.mjs' +import { CDN } from '../library/constants/cdn.js' + +const ROOT = resolve(dirname(fileURLToPath(import.meta.url)), '..') +const MANIFEST = join(ROOT, 'public', TREE_NAME, 'manifest.json') +const MAPBOX_HOST = 'api.mapbox.com' + +export function checkSnippets(manifest) { + const errs = [] + let maps = 0 + for (const b of manifest.bundles || []) { + for (const p of b.profiles || []) { + const id = `${b.bundle}/${p.profile}` + const script = String(p.embed?.script || '') + if (script.includes(MAPBOX_HOST)) errs.push(`${id}: the default snippet names ${MAPBOX_HOST} — an unedited paste would fetch an engine it never uses`) + if (p.isMap) { + maps++ + if (!script.includes(CDN.maplibreGlJs.url)) errs.push(`${id}: the default snippet does not preload MapLibre, the engine it draws with`) + const mb = String(p.embed?.scriptMapbox || '') + if (!mb) errs.push(`${id}: no embed.scriptMapbox, so a host that chooses Mapbox has no snippet for it`) + else { + if (!mb.includes(CDN.mapboxGlJs.url)) errs.push(`${id}: scriptMapbox does not preload the Mapbox engine`) + if (!mb.includes('"provider":"mapbox"')) errs.push(`${id}: scriptMapbox does not set "provider":"mapbox", so it would still draw with MapLibre`) + } + } else if (p.embed?.scriptMapbox) errs.push(`${id}: a non-map profile carries a Mapbox snippet`) + } + } + return { errs, maps } +} + +if (!existsSync(MANIFEST)) { console.error(`SNIPPET-ENGINE: no ${TREE_NAME}/manifest.json — build the tree first`); process.exit(2) } +const manifest = JSON.parse(readFileSync(MANIFEST, 'utf8')) + +if (process.argv.includes('--self-test')) { + const clone = () => JSON.parse(JSON.stringify(manifest)) + const firstMap = (m) => m.bundles.flatMap((b) => b.profiles).find((p) => p.isMap) + const oldShape = clone(); { const p = firstMap(oldShape); p.embed.script = `\n` + p.embed.script.replace(/= want + if (!pass) ok = false + console.log(` ${pass ? '✓' : '✗'} ${name} -> ${n} error(s)`) + } + console.log(ok ? 'SNIPPET-ENGINE SELF-TEST: PASS' : 'SNIPPET-ENGINE SELF-TEST: FAIL') + process.exit(ok ? 0 : 1) +} + +const { errs, maps } = checkSnippets(manifest) +if (errs.length) { + console.error(`SNIPPET-ENGINE: FAIL — ${errs.length} problem(s)`) + for (const e of errs.slice(0, 20)) console.error(' ✗ ' + e) + process.exit(1) +} +console.log(`SNIPPET-ENGINE: PASS — ${maps} map snippet(s) preload MapLibre and name no ${MAPBOX_HOST}; each has a Mapbox variant that sets its provider`) diff --git a/internal/generate-tree.mjs b/internal/generate-tree.mjs index 935eba149..98f78f789 100644 --- a/internal/generate-tree.mjs +++ b/internal/generate-tree.mjs @@ -179,6 +179,12 @@ const SCAFFOLD_BUNDLES = ['template-bundle'] // so the embed snippet's preload can never point at a different version than the one the bundle // requests — a mismatched preload is not used, and the engine would download twice. const MAPBOX_GL_JS = CDN.mapboxGlJs.url // the ONE registry the bundle's loader reads too (library/constants/cdn.js) +// An engine's script + stylesheet preloads, pinned as useMapInstance.js loads them (a preload is reused only +// when its URL and request mode match the later request). +const pinnedPreload = (e, as) => `\n` +const enginePreload = (engine) => engine === 'mapbox' + ? pinnedPreload(CDN.mapboxGlJs, 'script') + pinnedPreload(CDN.mapboxGlCss, 'style') + : pinnedPreload(CDN.maplibreGlJs, 'script') + pinnedPreload(CDN.maplibreGlCss, 'style') const isBundleDir = (name) => !name.startsWith('_') && !name.startsWith('.') && !DEST_CONFIG.gatedProfiles.includes(name) && !SCAFFOLD_BUNDLES.includes(name) @@ -1352,11 +1358,16 @@ function scanBundles() { assetsBase: DEST_CONFIG.assetsBaseUrl || 'YOUR_TREE_URL/assets', } const apiBase = DEST_CONFIG.apiBaseUrl || 'YOUR_API_BASE_URL' - const scriptEmbed = + const scriptEmbedFor = (cfg, note) => `\n\n\n" + "script": "\n\n\n\n\n\n", + "scriptMapbox": "\n\n\n\n\n\n" }, "bundle": "all-people-groups" } @@ -144,7 +145,8 @@ "scriptUrl": "./cities-internet/cities-internet.js", "embed": { "iframe": "", - "script": "\n\n\n\n\n" + "script": "\n\n\n\n\n\n", + "scriptMapbox": "\n\n\n\n\n\n" } }, { @@ -188,7 +190,8 @@ "scriptUrl": "./country-map/country-map.js", "embed": { "iframe": "", - "script": "\n\n\n\n\n" + "script": "\n\n\n\n\n\n", + "scriptMapbox": "\n\n\n\n\n\n" } } ] @@ -220,7 +223,8 @@ "scriptUrl": "./country-posters/country-posters.js", "embed": { "iframe": "", - "script": "\n\n\n\n\n" + "script": "\n\n\n\n\n\n", + "scriptMapbox": "\n\n\n\n\n\n" } }, { @@ -252,7 +256,8 @@ "scriptUrl": "./country-posters/country-posters.js", "embed": { "iframe": "", - "script": "\n\n\n\n\n" + "script": "\n\n\n\n\n\n", + "scriptMapbox": "\n\n\n\n\n\n" } } ] @@ -285,7 +290,8 @@ "scriptUrl": "./dashboard-plus-grid/dashboard-plus-grid.js", "embed": { "iframe": "", - "script": "\n\n\n\n\n" + "script": "\n\n\n\n\n\n", + "scriptMapbox": "\n\n\n\n\n\n" }, "instances": [ { @@ -327,7 +333,8 @@ "scriptUrl": "./demo-map/demo-map.js", "embed": { "iframe": "", - "script": "\n\n\n\n\n" + "script": "\n\n\n\n\n\n", + "scriptMapbox": "\n\n\n\n\n\n" }, "instances": [ { @@ -428,7 +435,8 @@ "scriptUrl": "./doxa-research-map/doxa-research-map.js", "embed": { "iframe": "", - "script": "\n\n\n\n\n" + "script": "\n\n\n\n\n\n", + "scriptMapbox": "\n\n\n\n\n\n" }, "instances": [ { @@ -474,7 +482,8 @@ "scriptUrl": "./doxa-simple-map/doxa-simple-map.js", "embed": { "iframe": "", - "script": "\n\n\n\n\n" + "script": "\n\n\n\n\n\n", + "scriptMapbox": "\n\n\n\n\n\n" }, "instances": [ { @@ -665,7 +674,8 @@ "scriptUrl": "./gospel-internet/gospel-internet.js", "embed": { "iframe": "", - "script": "\n\n\n\n\n" + "script": "\n\n\n\n\n\n", + "scriptMapbox": "\n\n\n\n\n\n" }, "instances": [ { @@ -827,7 +837,8 @@ "scriptUrl": "./gospel-resources/gospel-resources.js", "embed": { "iframe": "", - "script": "\n\n\n\n\n" + "script": "\n\n\n\n\n\n", + "scriptMapbox": "\n\n\n\n\n\n" } }, { @@ -859,7 +870,8 @@ "scriptUrl": "./gospel-resources/gospel-resources.js", "embed": { "iframe": "", - "script": "\n\n\n\n\n" + "script": "\n\n\n\n\n\n", + "scriptMapbox": "\n\n\n\n\n\n" } } ] @@ -907,7 +919,8 @@ "scriptUrl": "./my-upg-100-list/my-upg-100-list.js", "embed": { "iframe": "", - "script": "\n\n\n\n\n" + "script": "\n\n\n\n\n\n", + "scriptMapbox": "\n\n\n\n\n\n" } }, { @@ -923,7 +936,8 @@ "scriptUrl": "./my-upg-100-list/my-upg-100-list.js", "embed": { "iframe": "", - "script": "\n\n\n\n\n" + "script": "\n\n\n\n\n\n", + "scriptMapbox": "\n\n\n\n\n\n" } } ] @@ -1073,7 +1087,8 @@ "scriptUrl": "./pplr-data-maps/pplr-data-maps.js", "embed": { "iframe": "", - "script": "\n\n\n\n\n" + "script": "\n\n\n\n\n\n", + "scriptMapbox": "\n\n\n\n\n\n" }, "bundle": "pplr-data-maps" } @@ -1101,7 +1116,8 @@ "scriptUrl": "./prayer-walk-planner/prayer-walk-planner.js", "embed": { "iframe": "", - "script": "\n\n\n\n\n" + "script": "\n\n\n\n\n\n", + "scriptMapbox": "\n\n\n\n\n\n" } } ] @@ -1133,7 +1149,8 @@ "scriptUrl": "./regions-dashboard/regions-dashboard.js", "embed": { "iframe": "", - "script": "\n\n\n\n\n" + "script": "\n\n\n\n\n\n", + "scriptMapbox": "\n\n\n\n\n\n" } }, { @@ -1193,7 +1210,8 @@ "scriptUrl": "./research-dashboard/research-dashboard.js", "embed": { "iframe": "", - "script": "\n\n\n\n\n" + "script": "\n\n\n\n\n\n", + "scriptMapbox": "\n\n\n\n\n\n" }, "bundle": "research-dashboard" } @@ -1316,7 +1334,8 @@ "scriptUrl": "./upg-neighbor/upg-neighbor.js", "embed": { "iframe": "", - "script": "\n\n\n\n\n" + "script": "\n\n\n\n\n\n", + "scriptMapbox": "\n\n\n\n\n\n" }, "instances": [ { diff --git a/public/dx-maps/manifest.json b/public/dx-maps/manifest.json index ef57d2ab0..6a76a061f 100644 --- a/public/dx-maps/manifest.json +++ b/public/dx-maps/manifest.json @@ -30,7 +30,8 @@ "scriptUrl": "./all-people-groups/all-people-groups.js", "embed": { "iframe": "", - "script": "\n\n\n\n\n" + "script": "\n\n\n\n\n\n", + "scriptMapbox": "\n\n\n\n\n\n" }, "bundle": "all-people-groups" } @@ -144,7 +145,8 @@ "scriptUrl": "./cities-internet/cities-internet.js", "embed": { "iframe": "", - "script": "\n\n\n\n\n" + "script": "\n\n\n\n\n\n", + "scriptMapbox": "\n\n\n\n\n\n" } }, { @@ -188,7 +190,8 @@ "scriptUrl": "./country-map/country-map.js", "embed": { "iframe": "", - "script": "\n\n\n\n\n" + "script": "\n\n\n\n\n\n", + "scriptMapbox": "\n\n\n\n\n\n" } } ] @@ -220,7 +223,8 @@ "scriptUrl": "./country-posters/country-posters.js", "embed": { "iframe": "", - "script": "\n\n\n\n\n" + "script": "\n\n\n\n\n\n", + "scriptMapbox": "\n\n\n\n\n\n" } }, { @@ -252,7 +256,8 @@ "scriptUrl": "./country-posters/country-posters.js", "embed": { "iframe": "", - "script": "\n\n\n\n\n" + "script": "\n\n\n\n\n\n", + "scriptMapbox": "\n\n\n\n\n\n" } } ] @@ -285,7 +290,8 @@ "scriptUrl": "./dashboard-plus-grid/dashboard-plus-grid.js", "embed": { "iframe": "", - "script": "\n\n\n\n\n" + "script": "\n\n\n\n\n\n", + "scriptMapbox": "\n\n\n\n\n\n" }, "instances": [ { @@ -327,7 +333,8 @@ "scriptUrl": "./demo-map/demo-map.js", "embed": { "iframe": "", - "script": "\n\n\n\n\n" + "script": "\n\n\n\n\n\n", + "scriptMapbox": "\n\n\n\n\n\n" }, "instances": [ { @@ -428,7 +435,8 @@ "scriptUrl": "./doxa-research-map/doxa-research-map.js", "embed": { "iframe": "", - "script": "\n\n\n\n\n" + "script": "\n\n\n\n\n\n", + "scriptMapbox": "\n\n\n\n\n\n" }, "instances": [ { @@ -474,7 +482,8 @@ "scriptUrl": "./doxa-simple-map/doxa-simple-map.js", "embed": { "iframe": "", - "script": "\n\n\n\n\n" + "script": "\n\n\n\n\n\n", + "scriptMapbox": "\n\n\n\n\n\n" }, "instances": [ { @@ -665,7 +674,8 @@ "scriptUrl": "./gospel-internet/gospel-internet.js", "embed": { "iframe": "", - "script": "\n\n\n\n\n" + "script": "\n\n\n\n\n\n", + "scriptMapbox": "\n\n\n\n\n\n" }, "instances": [ { @@ -827,7 +837,8 @@ "scriptUrl": "./gospel-resources/gospel-resources.js", "embed": { "iframe": "", - "script": "\n\n\n\n\n" + "script": "\n\n\n\n\n\n", + "scriptMapbox": "\n\n\n\n\n\n" } }, { @@ -859,7 +870,8 @@ "scriptUrl": "./gospel-resources/gospel-resources.js", "embed": { "iframe": "", - "script": "\n\n\n\n\n" + "script": "\n\n\n\n\n\n", + "scriptMapbox": "\n\n\n\n\n\n" } } ] @@ -907,7 +919,8 @@ "scriptUrl": "./my-upg-100-list/my-upg-100-list.js", "embed": { "iframe": "", - "script": "\n\n\n\n\n" + "script": "\n\n\n\n\n\n", + "scriptMapbox": "\n\n\n\n\n\n" } }, { @@ -923,7 +936,8 @@ "scriptUrl": "./my-upg-100-list/my-upg-100-list.js", "embed": { "iframe": "", - "script": "\n\n\n\n\n" + "script": "\n\n\n\n\n\n", + "scriptMapbox": "\n\n\n\n\n\n" } } ] @@ -1073,7 +1087,8 @@ "scriptUrl": "./pplr-data-maps/pplr-data-maps.js", "embed": { "iframe": "", - "script": "\n\n\n\n\n" + "script": "\n\n\n\n\n\n", + "scriptMapbox": "\n\n\n\n\n\n" }, "bundle": "pplr-data-maps" } @@ -1101,7 +1116,8 @@ "scriptUrl": "./prayer-walk-planner/prayer-walk-planner.js", "embed": { "iframe": "", - "script": "\n\n\n\n\n" + "script": "\n\n\n\n\n\n", + "scriptMapbox": "\n\n\n\n\n\n" } } ] @@ -1133,7 +1149,8 @@ "scriptUrl": "./regions-dashboard/regions-dashboard.js", "embed": { "iframe": "", - "script": "\n\n\n\n\n" + "script": "\n\n\n\n\n\n", + "scriptMapbox": "\n\n\n\n\n\n" } }, { @@ -1193,7 +1210,8 @@ "scriptUrl": "./research-dashboard/research-dashboard.js", "embed": { "iframe": "", - "script": "\n\n\n\n\n" + "script": "\n\n\n\n\n\n", + "scriptMapbox": "\n\n\n\n\n\n" }, "bundle": "research-dashboard" } @@ -1316,7 +1334,8 @@ "scriptUrl": "./upg-neighbor/upg-neighbor.js", "embed": { "iframe": "", - "script": "\n\n\n\n\n" + "script": "\n\n\n\n\n\n", + "scriptMapbox": "\n\n\n\n\n\n" }, "instances": [ {