Token gates follow the auto engine: no token ships, and an explicit engine beats an available token
The build config no longer bakes a Mapbox token, and the default engine is 'auto' (Mapbox only when a token is available). Two gates still assumed the old contract. - check:nested-token: with the build config's token empty (the shipped default), no leaf and no bundle may carry a pk. token, and every leaf must carry the runtime engine ladder (EXPLICIT / HAS_TOKEN), so with no token it draws MapLibre, keyless. A builder who bakes their own token gets the original check. Fails on the pre-rebuild tree, which still has baked tokens. - check:provider-switch: the Mapbox baseline is now ?provider=mapbox with a fake host token (its requests are aborted, never sent); the switch run adds ?provider=maplibre to the same token (an explicit engine must win); a third run requires the no-token default to make no Mapbox requests.
This commit is contained in:
parent
0b9b068b44
commit
be0a9efecf
2 changed files with 42 additions and 7 deletions
|
|
@ -97,6 +97,16 @@ if (leaves.length < MIN_LEAVES) {
|
||||||
process.exit(1)
|
process.exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ★ THE AUTO CONTRACT (2026-09-26, by project decision: no Mapbox token in the public repo). With the
|
||||||
|
// build config's "token" EMPTY — the shipped default — a leaf must carry NO token, and must instead carry
|
||||||
|
// the runtime engine ladder (EXPLICIT / HAS_TOKEN): with no token it draws MapLibre, keyless; with one
|
||||||
|
// from the host, ?tk=, a paste or a token service, Mapbox. A builder who opts into baking a token
|
||||||
|
// ("token": "pk.…" in their own config) gets the original check: every leaf carries it.
|
||||||
|
let bakedToken = ''
|
||||||
|
try { bakedToken = String(JSON.parse(readFileSync(join(ROOT, 'vite.doxa-maps-build-config.json'), 'utf8')).token || '') } catch { /* none */ }
|
||||||
|
const BAKED = TOKEN_RE.test(bakedToken)
|
||||||
|
const AUTO_RE = /var EXPLICIT = [^;]*;[\s\S]*?var HAS_TOKEN = /
|
||||||
|
|
||||||
const missing = []
|
const missing = []
|
||||||
let checked = 0
|
let checked = 0
|
||||||
for (const leaf of leaves) {
|
for (const leaf of leaves) {
|
||||||
|
|
@ -108,9 +118,15 @@ for (const leaf of leaves) {
|
||||||
}
|
}
|
||||||
checked++
|
checked++
|
||||||
const html = readFileSync(abs, 'utf8')
|
const html = readFileSync(abs, 'utf8')
|
||||||
if (!TOKEN_RE.test(html)) {
|
if (BAKED && !TOKEN_RE.test(html)) {
|
||||||
missing.push({ ...leaf, why: 'no pk. token baked into the leaf — a nested map here would get NOTHING' })
|
missing.push({ ...leaf, why: 'no pk. token baked into the leaf — a nested map here would get NOTHING' })
|
||||||
}
|
}
|
||||||
|
if (!BAKED && TOKEN_RE.test(html)) {
|
||||||
|
missing.push({ ...leaf, why: 'a pk. token is in the leaf, but the build config bakes none — a token leaked into public output' })
|
||||||
|
}
|
||||||
|
if (!BAKED && !AUTO_RE.test(html)) {
|
||||||
|
missing.push({ ...leaf, why: 'no runtime engine ladder (EXPLICIT/HAS_TOKEN) — with no token this leaf cannot fall back to MapLibre' })
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// The other direction: a token must never be compiled into a .js bundle.
|
// The other direction: a token must never be compiled into a .js bundle.
|
||||||
|
|
@ -127,7 +143,7 @@ console.log(`NESTED-TOKEN: ${checked} shipped leaf page(s) checked, ${(manifest.
|
||||||
|
|
||||||
if (missing.length || inBundles.length) {
|
if (missing.length || inBundles.length) {
|
||||||
if (missing.length) {
|
if (missing.length) {
|
||||||
console.error(`\nNESTED-TOKEN: FAIL — ${missing.length} leaf page(s) ship without a token:`)
|
console.error(`\nNESTED-TOKEN: FAIL — ${missing.length} leaf page(s) break the token contract (${BAKED ? 'baked token' : 'auto, no token'}):`)
|
||||||
for (const m of missing.slice(0, 20)) console.error(` ✗ ${m.bundle}/${m.profile}\n ${m.url}\n ${m.why}`)
|
for (const m of missing.slice(0, 20)) console.error(` ✗ ${m.bundle}/${m.profile}\n ${m.url}\n ${m.why}`)
|
||||||
if (missing.length > 20) console.error(` … +${missing.length - 20} more`)
|
if (missing.length > 20) console.error(` … +${missing.length - 20} more`)
|
||||||
console.error(
|
console.error(
|
||||||
|
|
@ -151,6 +167,7 @@ if (missing.length || inBundles.length) {
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log(`NESTED-TOKEN: tree public/${treeName}/ (name from ${treeNameSource}).`)
|
console.log(`NESTED-TOKEN: tree public/${treeName}/ (name from ${treeNameSource}).`)
|
||||||
console.log('NESTED-TOKEN: PASS — every shipped leaf carries its own token, and no bundle does.')
|
console.log(BAKED ? 'NESTED-TOKEN: PASS — every shipped leaf carries its own token, and no bundle does.'
|
||||||
|
: 'NESTED-TOKEN: PASS (auto) — no leaf or bundle carries a token, and every leaf picks its engine at runtime (MapLibre keyless without one).')
|
||||||
console.log(' This is DELIVERY only. It does not prove a basemap paints: that needs a real GPU and')
|
console.log(' This is DELIVERY only. It does not prove a basemap paints: that needs a real GPU and')
|
||||||
console.log(' a token whose URL restrictions allow the serving host (see verify:map-paint).')
|
console.log(' a token whose URL restrictions allow the serving host (see verify:map-paint).')
|
||||||
|
|
|
||||||
|
|
@ -78,9 +78,17 @@ let OWN_SERVER = null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function run(provider) {
|
// A well-formed FAKE public token (never a real one): enough for the Mapbox engine to start and make its
|
||||||
|
// requests, which is all the comparison run needs. Requests that carry it are aborted, never sent.
|
||||||
|
const FAKE_TK = ['pk', Buffer.from(JSON.stringify({ u: 'provider-switch-check' })).toString('base64url'), 'c2ln'].join('.')
|
||||||
|
|
||||||
|
async function run(provider, token = '') {
|
||||||
const b = await chromium.launch({ args: ['--use-gl=swiftshader', '--enable-unsafe-swiftshader'] })
|
const b = await chromium.launch({ args: ['--use-gl=swiftshader', '--enable-unsafe-swiftshader'] })
|
||||||
const pg = await b.newPage({ viewport: { width: 1280, height: 900 } })
|
const pg = await b.newPage({ viewport: { width: 1280, height: 900 } })
|
||||||
|
if (token) {
|
||||||
|
await pg.addInitScript((tk) => { window.MAP_TOKEN = tk }, token)
|
||||||
|
await pg.route((u) => /(^|\.)mapbox\.com$/i.test(u.hostname) && u.searchParams.has('access_token'), (r) => r.abort())
|
||||||
|
}
|
||||||
const hosts = new Map()
|
const hosts = new Map()
|
||||||
let unparsed = 0
|
let unparsed = 0
|
||||||
pg.on('request', (r) => {
|
pg.on('request', (r) => {
|
||||||
|
|
@ -107,8 +115,13 @@ async function run(provider) {
|
||||||
return { mapboxReq: count('mapbox.com'), globals, errs, unparsed, total: [...hosts.values()].reduce((a, n) => a + n, 0) }
|
return { mapboxReq: count('mapbox.com'), globals, errs, unparsed, total: [...hosts.values()].reduce((a, n) => a + n, 0) }
|
||||||
}
|
}
|
||||||
|
|
||||||
const def = await run(null)
|
// ★ THE AUTO CONTRACT (2026-09-26). The default engine is 'auto': Mapbox only when a token is available.
|
||||||
const ml = await run('maplibre')
|
// So the Mapbox baseline is ?provider=mapbox WITH a (fake) host token, the switch run adds the same token
|
||||||
|
// and ?provider=maplibre (an explicit engine must beat an available token), and a third run checks the
|
||||||
|
// no-token default is already keyless.
|
||||||
|
const def = await run('mapbox', FAKE_TK)
|
||||||
|
const ml = await run('maplibre', FAKE_TK)
|
||||||
|
const auto = await run(null)
|
||||||
|
|
||||||
// DERIVED from the leaf's own HTML, not a hardcoded number: count the distinct
|
// DERIVED from the leaf's own HTML, not a hardcoded number: count the distinct
|
||||||
// api.mapbox.com references the generated page carries, then allow a little slack
|
// api.mapbox.com references the generated page carries, then allow a little slack
|
||||||
|
|
@ -120,8 +133,10 @@ const declared = new Set((pageHtml.match(/https:\/\/api\.mapbox\.com[^"')]+/g) |
|
||||||
const PRELOAD_ALLOWANCE = Number(process.env.PROVIDER_PRELOADS ?? (declared.size + 1))
|
const PRELOAD_ALLOWANCE = Number(process.env.PROVIDER_PRELOADS ?? (declared.size + 1))
|
||||||
|
|
||||||
console.log(` page preloads : ${declared.size} distinct api.mapbox.com url(s) in the leaf HTML`)
|
console.log(` page preloads : ${declared.size} distinct api.mapbox.com url(s) in the leaf HTML`)
|
||||||
console.log(` default : ${def.mapboxReq} mapbox / ${def.total} total request(s) `
|
console.log(` ?provider=mapbox : ${def.mapboxReq} mapbox / ${def.total} total request(s) `
|
||||||
+ `[mapboxgl=${def.globals.mapbox} maplibregl=${def.globals.maplibre}]`)
|
+ `[mapboxgl=${def.globals.mapbox} maplibregl=${def.globals.maplibre}]`)
|
||||||
|
console.log(` no token (auto) : ${auto.mapboxReq} mapbox / ${auto.total} total request(s) `
|
||||||
|
+ `[mapboxgl=${auto.globals.mapbox} maplibregl=${auto.globals.maplibre}]`)
|
||||||
console.log(` ?provider=maplibre: ${ml.mapboxReq} mapbox / ${ml.total} total request(s) `
|
console.log(` ?provider=maplibre: ${ml.mapboxReq} mapbox / ${ml.total} total request(s) `
|
||||||
+ `[mapboxgl=${ml.globals.mapbox} maplibregl=${ml.globals.maplibre}]`)
|
+ `[mapboxgl=${ml.globals.mapbox} maplibregl=${ml.globals.maplibre}]`)
|
||||||
|
|
||||||
|
|
@ -144,6 +159,9 @@ if (def.mapboxReq <= PRELOAD_ALLOWANCE) {
|
||||||
fails.push(`the default run made only ${def.mapboxReq} mapbox request(s) — the map is probably `
|
fails.push(`the default run made only ${def.mapboxReq} mapbox request(s) — the map is probably `
|
||||||
+ 'not rendering, so this comparison proves nothing')
|
+ 'not rendering, so this comparison proves nothing')
|
||||||
}
|
}
|
||||||
|
if (auto.mapboxReq > PRELOAD_ALLOWANCE) {
|
||||||
|
fails.push(`the no-token default made ${auto.mapboxReq} mapbox request(s) — 'auto' must draw MapLibre, keyless, with no token`)
|
||||||
|
}
|
||||||
if (!def.total) fails.push('no requests were recorded at all — the tally is broken, not the page')
|
if (!def.total) fails.push('no requests were recorded at all — the tally is broken, not the page')
|
||||||
if (ml.errs.length) fails.push(`${ml.errs.length} page error(s) under maplibre: ${ml.errs[0]}`)
|
if (ml.errs.length) fails.push(`${ml.errs.length} page error(s) under maplibre: ${ml.errs[0]}`)
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue