Revert "The map engine loads from the tree it was served from" for the handoff (unverified, now conflicting)

The same-origin engine copy collided with the CDN preloads that landed meanwhile: map
leaves now preload https://cdn.jsdelivr.net/.../maplibre-gl.js (leaf hints), and composite
snippets preload the same URL. With the engine loaded from <tree>/vendor/ instead, those
preloads would never be reused and every map page would download the engine twice.
Re-pointing the preloads is design work, which the handoff does not start. The change
stays in history (fc7259e06, studio/vendor-maplibre 275cdc807), and it belongs to the
HANDOFF-BRANCHES-RECORDED-AS-MERGED-WITHOUT-THEIR-CONTENT card.
This commit is contained in:
Doxa Maps 2026-09-29 15:38:28 +00:00
parent 8eccda7752
commit 7f08a74784
3 changed files with 20 additions and 69 deletions

View file

@ -30,7 +30,7 @@
* Run standalone: bun generate-tree.mjs (expects vite build already ran)
* Wired into: bun run build (runs after the per-bundle vite builds)
*/
import { CDN, VENDORED } from '../library/constants/cdn.js'
import { CDN } from '../library/constants/cdn.js'
import { isWebBuilt } from './plugin-targets.mjs'
import {
readFileSync, writeFileSync, readdirSync, existsSync, mkdirSync, statSync,
@ -1703,21 +1703,6 @@ function writeManifestAndHtml(root, bundles, tokenUrlExpr, bakedToken) {
}
mkdirSync(root, { recursive: true })
writeFileSync(join(root, 'manifest.json'), JSON.stringify(manifest, null, 2) + '\n')
// ★ THE MAP ENGINE, SAME-ORIGIN (2026-09-28: maps must load fast). Each pinned file in
// library/constants/cdn.js VENDORED is copied from node_modules into the tree, and a copy whose bytes do
// not hash to that entry's integrity pin stops the build: the page loads it with the same pin.
for (const v of VENDORED) {
const src = resolve(__dirname, '..', v.from)
const pin = Object.values(CDN).find((e) => e.url === v.cdn)?.integrity || ''
const bytes = readFileSync(src)
const got = 'sha384-' + createHash('sha384').update(bytes).digest('base64')
if (got !== pin) {
console.error(`generate-tree: ${v.from} does not match its pin (${pin.slice(0, 20)}…, got ${got.slice(0, 20)}…). Reinstall node_modules at the pinned version, or re-pin library/constants/cdn.js.`)
process.exit(1)
}
mkdirSync(dirname(join(root, v.path)), { recursive: true })
writeFileSync(join(root, v.path), bytes)
}
// ★ A CORS-READABLE COPY for the Research Gallery, byte-identical, written in the SAME step so the
// two can never diverge. The live static layer sends Access-Control-Allow-Origin on paths inside a
// folder of the tree but not on the root manifest (measured 2026-09-25), so a partner page's

View file

@ -99,8 +99,7 @@ const OPENFREEMAP_DARK_STYLE = 'https://tiles.openfreemap.org/styles/fiord'
// 'midnight-glow' / … resolves per engine here; raw URLs pass through.
import { resolveNamedStyle, applyStylePaint } from '../styles/registry.js'
import { resolveMapProvider, explicitMapProvider } from './useMapProvider.js'
import { CDN, pinElement, vendoredUrl } from '../constants/cdn.js'
import { bundleTreeRoot } from '../utils/embedBase.js'
import { CDN, pinElement } from '../constants/cdn.js'
import { installRtlTextPlugin } from '../maps/rtl-text.js'
import { localizeBasemapLabels } from '../maps/localize-labels.js'
import { tStatic } from '../i18n/index.js'
@ -139,26 +138,7 @@ export function engineGlobal(provider) {
* inject undefined.
*/
export function engineStylesheetUrl(provider) {
const css = (ENGINES[provider] || ENGINES.mapbox).css || null
return (css && vendoredUrl(css, bundleTreeRoot())) || css
}
// Load one engine file from its same-origin copy when this bundle was served from a built tree, falling
// back to the pinned CDN url if that copy is missing (a tree copied without its vendor/ folder).
function appendPinned(tag, url, fallbackUrl, attrs) {
return new Promise((resolve, reject) => {
const el = document.createElement(tag)
Object.assign(el, attrs)
if (tag === 'script') el.src = url; else el.href = url
pinElement(el, url)
el.addEventListener('load', () => resolve(el), { once: true })
el.addEventListener('error', () => {
el.remove()
if (fallbackUrl && fallbackUrl !== url) appendPinned(tag, fallbackUrl, null, attrs).then(resolve, reject)
else reject(new Error(`[useMapInstance] Failed to load ${url}`))
}, { once: true })
document.head.appendChild(el)
})
return (ENGINES[provider] || ENGINES.mapbox).css || null
}
/**
@ -171,14 +151,15 @@ export async function ensureEngineScript(provider) {
if (engineGlobal(provider)) return // already loaded — no-op
if (typeof document === 'undefined') return
const root = bundleTreeRoot()
const cssUrl = vendoredUrl(cfg.css, root) || cfg.css
const jsUrl = vendoredUrl(cfg.js, root) || cfg.js
if (!document.querySelector(`link[href="${cssUrl}"]`) && !document.querySelector(`link[href="${cfg.css}"]`)) {
appendPinned('link', cssUrl, cfg.css, { rel: 'stylesheet' }).catch(() => {})
if (!document.querySelector(`link[href="${cfg.css}"]`)) {
const link = document.createElement('link')
link.rel = 'stylesheet'
link.href = cfg.css
pinElement(link, cfg.css)
document.head.appendChild(link)
}
const existing = document.querySelector(`script[src="${jsUrl}"]`) || document.querySelector(`script[src="${cfg.js}"]`)
const existing = document.querySelector(`script[src="${cfg.js}"]`)
if (existing) {
if (engineGlobal(provider)) return
await new Promise(resolve => {
@ -188,8 +169,14 @@ export async function ensureEngineScript(provider) {
return
}
await appendPinned('script', jsUrl, cfg.js, { async: false }).catch(() => {
throw new Error(`[useMapInstance] Failed to load ${provider} engine script`)
await new Promise((resolve, reject) => {
const s = document.createElement('script')
s.src = cfg.js
pinElement(s, cfg.js)
s.async = false
s.addEventListener('load', resolve, { once: true })
s.addEventListener('error', () => reject(new Error(`[useMapInstance] Failed to load ${provider} engine script`)), { once: true })
document.head.appendChild(s)
})
}

View file

@ -34,30 +34,9 @@ export const CDN = {
d3: { url: 'https://cdn.jsdelivr.net/npm/d3@7.9.0/dist/d3.min.js', integrity: 'sha384-CjloA8y00+1SDAUkjs099PVfnY2KmDC2BZnws9kh8D/lX1s46w6EPhpXdqMfjK6i' },
}
/**
* SAME-ORIGIN COPIES (2026-09-28: maps must load fast). generate-tree copies each file below
* from node_modules into <tree>/<path> and REFUSES a copy whose bytes do not match that entry's pin, so the
* one integrity hash guards both copies. A built page then loads the engine from the tree it already came
* from — no second origin to connect to, one cached copy for every bundle — and jsdelivr is the fallback.
* MapLibre only: Mapbox GL's licence ties it to Mapbox, whose servers a Mapbox map talks to anyway.
*/
export const VENDORED = [
{ cdn: CDN.maplibreGlJs.url, path: 'vendor/maplibre-gl@5.24.0/maplibre-gl.js', from: 'node_modules/maplibre-gl/dist/maplibre-gl.js' },
{ cdn: CDN.maplibreGlCss.url, path: 'vendor/maplibre-gl@5.24.0/maplibre-gl.css', from: 'node_modules/maplibre-gl/dist/maplibre-gl.css' },
]
/** The same-origin copy of a pinned CDN url under a built tree's root, or null (dev, or not vendored). */
export function vendoredUrl(cdnUrl, treeRoot) {
const v = VENDORED.find((x) => x.cdn === cdnUrl)
return v && treeRoot ? String(treeRoot).replace(/\/+$/, '') + '/' + v.path : null
}
/** Set integrity + crossorigin on a <script>/<link> for a registry entry (no-op for an unknown url). A
* same-origin copy carries its CDN entry's pin. */
/** Set integrity + crossorigin on a <script>/<link> for a registry entry (no-op for an unknown url). */
export function pinElement(el, url) {
const v = VENDORED.find((x) => String(url).endsWith('/' + x.path))
const pinUrl = v ? v.cdn : url
const e = Object.values(CDN).find((x) => x.url === pinUrl)
const e = Object.values(CDN).find((x) => x.url === url)
if (!e) return el
el.integrity = e.integrity
el.crossOrigin = 'anonymous'