diff --git a/internal/check-auto-engine.mjs b/internal/check-auto-engine.mjs index 40b76ab90..cdfbbf21c 100644 --- a/internal/check-auto-engine.mjs +++ b/internal/check-auto-engine.mjs @@ -1,21 +1,28 @@ #!/usr/bin/env node /** - * check-auto-engine — the default engine is 'auto': Mapbox WHEN a token is available, MapLibre otherwise. + * check-auto-engine — the default engine ('auto') is MapLibre, token or not; Mapbox only when asked for. * - * WHY (2026-09-25, by project decision). The public repo carries no Mapbox token. With a default of - * 'mapbox', a keyless visitor got a map that could not draw and a paste-a-token prompt, and the only - * way out was a token in the repo. 'auto' asks the token ladder instead (useMapProvider.js), and the - * generated leaf asks the same ladder before it mounts (generate-tree.mjs, PROVIDER). + * WHY. The public repo carries no Mapbox token, so a default of 'mapbox' gave a keyless visitor a map + * that could not draw. From 2026-09-25 'auto' asked the token ladder (a token meant Mapbox); since + * 2026-09-28 it is MapLibre whether or not a token is present (useMapProvider.js, resolveMapProvider): + * holding a token is a capability, choosing Mapbox is a decision. A token still authenticates a map + * that asked for Mapbox, and place search uses one on either engine. + * + * ⚠ THIS GATE ASSERTED THE OLD RULE FOR A DAY AND NOBODY SAW: it sat in no chain, so its case 2 + * (a host token draws Mapbox) never ran against the change that made it false. It is now in + * gates:post, where it is NOT MEASURED outside the browser slot and runs in it. * * THREE CASES, on one map leaf of THIS tree (or --dir), each in a fresh browser context: - * 1 NO TOKEN the map draws with MapLibre, makes ZERO requests to any *.mapbox.com host, and - * PAINTS (distinct colours on the canvas, not a blank rectangle). - * 2 HOST TOKEN the host sets window.MAP_TOKEN before the page runs: the map draws with Mapbox, - * and THAT token is the access_token on its requests to Mapbox. - * 3 EXPLICIT WINS the same host token, with ?provider=maplibre: MapLibre, zero Mapbox requests. - * The token in case 2 is a well-formed FAKE, built at run time; Mapbox requests that carry it are - * recorded and aborted, never sent. The engine is read from the canvas that drew - * (mapboxgl-canvas / maplibregl-canvas), not from which globals happen to exist. + * 1 NO TOKEN the map draws with MapLibre, makes ZERO requests to any *.mapbox.com host, and + * PAINTS (distinct colours on the canvas, not a blank rectangle). + * 2 HOST TOKEN ALONE the host sets window.MAP_TOKEN before the page runs and names no provider: the + * map STILL draws with MapLibre, paints, and makes ZERO Mapbox requests. This is + * the case the 09-28 rule exists for. + * 3 ASKED FOR MAPBOX the same host token with ?provider=mapbox: the map draws with Mapbox, and THAT + * token is the access_token on its requests to Mapbox. + * The token is a well-formed FAKE, built at run time; Mapbox requests that carry it are recorded and + * aborted, never sent. The engine is read from the canvas that drew (mapboxgl-canvas / + * maplibregl-canvas), not from which globals happen to exist. * * USAGE node internal/check-auto-engine.mjs [--dir ] [--leaf /] * Run through internal/browser-slot.sh. @@ -108,8 +115,8 @@ async function measure({ hostToken = '', query = '' }) { const results = [] try { results.push({ name: '1 no token', r: await measure({}) }) - results.push({ name: '2 host token', r: await measure({ hostToken: FAKE_TOKEN }) }) - results.push({ name: '3 explicit maplibre + token', r: await measure({ hostToken: FAKE_TOKEN, query: '?provider=maplibre' }) }) + results.push({ name: '2 host token alone', r: await measure({ hostToken: FAKE_TOKEN }) }) + results.push({ name: '3 provider mapbox + token', r: await measure({ hostToken: FAKE_TOKEN, query: '?provider=mapbox' }) }) } finally { await browser.close(); stop() } let bad = 0, unmeasured = 0 @@ -118,17 +125,16 @@ for (const { name, r } of results) { if (r.err) { console.log(` ?? ${name.padEnd(30)} NOT MEASURED — ${r.err}`); unmeasured++; continue } const withToken = r.mapbox.filter((u) => u.searchParams.has('access_token')) const summary = `engine ${r.engine}, ${r.mapbox.length} Mapbox request(s)${r.prompt ? ', TOKEN PROMPT shown' : ''}` - if (name.startsWith('1')) { + if (name.startsWith('1') || name.startsWith('2')) { + // The same verdict for both: a token that is merely PRESENT must change nothing about the engine. say(r.engine === 'maplibre' && r.mapbox.length === 0 && r.colours >= MIN_COLOURS && !r.prompt, name, `${summary}, ${r.colours} colours (floor ${MIN_COLOURS}) — want maplibre, 0 Mapbox requests, painted, no prompt`) - } else if (name.startsWith('2')) { + } else { const carries = withToken.length > 0 && withToken.every((u) => u.searchParams.get('access_token') === FAKE_TOKEN) say(r.engine === 'mapbox' && carries, name, `${summary}, ${withToken.length} carry a token${withToken.length && !carries ? ' — NOT the host token' : ''} — want mapbox, the host token on its requests`) - } else { - say(r.engine === 'maplibre' && r.mapbox.length === 0, name, `${summary} — want maplibre, 0 Mapbox requests`) } } if (bad) { console.log(`AUTO-ENGINE: FAIL — ${bad} of 3 case(s) do not hold.`); process.exit(1) } if (unmeasured) { console.log(`AUTO-ENGINE: NOT MEASURED — ${unmeasured} case(s) could not be judged.`); process.exit(75) } -console.log('AUTO-ENGINE: PASS — no token draws MapLibre keyless; a host token draws Mapbox with that token; an explicit provider wins.') +console.log('AUTO-ENGINE: PASS — the default draws MapLibre keyless with or without a host token; provider mapbox draws Mapbox with that token.') diff --git a/package.json b/package.json index 584709198..4d3da4d53 100644 --- a/package.json +++ b/package.json @@ -327,7 +327,7 @@ "dev": "bun x vite", "fix:sri": "node internal/fix-sri.mjs", "gallery": "node internal/generate-gallery.mjs", - "gates:post": "node internal/run-parallel.mjs --label=post -- check:capability-derived check:drop check:parse verify check:sri check:public-hygiene check:label-joins check:facet-cascade check:facet-one-field check:heartbeat check:kpi-unit check:composite-reg check:descriptions check:no-abs-paths check:no-codename check:dep-names check:composite-srcs check:embed-targets check:nested-token check:i18n-parity check:tabs-localized check:packets-all check:packet-strings-parity check:zone-census check:zoning-declared check:share-policy check:library-index check:csv-shape check:capability-citations check:catalog-identity check:no-orphan-gates check:quoted-scripts check:done-evidence check:script-flags check:backlog-status check:capability-exemplars check:packet-fixture check:packet-browser check:provider-switch check:locale-reachable check:url-resolver check:manifest-covers check:one-palette check:pin-dim-slider check:legend-source check:backlog-refs check:clustering-modes check:composite-embed-knobs check:composite-locale-leaks check:linked-vocabulary check:wp-embed check:dashboard-geocoder-provider check:leaf-engine-per-provider check:map-props-unit check:composites-mount check:composite-interop check:no-free-identifiers check:map-fly-reduced-motion check:mst-clustering check:list-prop-unit check:profiles-mount check:cdn-pins check:no-eval check:popup-escaping check:i18n-treeshake check:pinned-umd check:one-palette:self-test check:no-cross-plugin-imports check:no-cross-plugin-imports:self-test check:map-attribution check:studio-rail check:approx-pin-note check:embedder-keys check:provider-for check:legend-logical-insets check:legend-logical-insets:self-test check:optional-t-equivalence check:text-highlight check:religion-label-not-value check:i18n-stopped-scope check:no-silent-engine check:regions-uses-library check:shadow-css-parses check:square-map-controls check:preset-config-merge check:instance-config-keys check:frozen-labels check:plural-forms check:region-fill-repaint check:poster-print-fallback check:posters-exportable check:country-names check:reduced-motion check:no-dead-library check:universal-props check:readme-size check:tree-relative check:snippet-engine check:snippet-engine:self-test", + "gates:post": "node internal/run-parallel.mjs --label=post -- check:capability-derived check:drop check:parse verify check:sri check:public-hygiene check:label-joins check:facet-cascade check:facet-one-field check:heartbeat check:kpi-unit check:composite-reg check:descriptions check:no-abs-paths check:no-codename check:dep-names check:composite-srcs check:embed-targets check:nested-token check:i18n-parity check:tabs-localized check:packets-all check:packet-strings-parity check:zone-census check:zoning-declared check:share-policy check:library-index check:csv-shape check:capability-citations check:catalog-identity check:no-orphan-gates check:quoted-scripts check:done-evidence check:script-flags check:backlog-status check:capability-exemplars check:packet-fixture check:packet-browser check:provider-switch check:locale-reachable check:url-resolver check:manifest-covers check:one-palette check:pin-dim-slider check:legend-source check:backlog-refs check:clustering-modes check:composite-embed-knobs check:composite-locale-leaks check:linked-vocabulary check:wp-embed check:dashboard-geocoder-provider check:leaf-engine-per-provider check:auto-engine check:engine-contract check:map-props-unit check:composites-mount check:composite-interop check:no-free-identifiers check:map-fly-reduced-motion check:mst-clustering check:list-prop-unit check:profiles-mount check:cdn-pins check:no-eval check:popup-escaping check:i18n-treeshake check:pinned-umd check:one-palette:self-test check:no-cross-plugin-imports check:no-cross-plugin-imports:self-test check:map-attribution check:studio-rail check:approx-pin-note check:embedder-keys check:provider-for check:legend-logical-insets check:legend-logical-insets:self-test check:optional-t-equivalence check:text-highlight check:religion-label-not-value check:i18n-stopped-scope check:no-silent-engine check:regions-uses-library check:shadow-css-parses check:square-map-controls check:preset-config-merge check:instance-config-keys check:frozen-labels check:plural-forms check:region-fill-repaint check:poster-print-fallback check:posters-exportable check:country-names check:reduced-motion check:no-dead-library check:universal-props check:readme-size check:tree-relative check:snippet-engine check:snippet-engine:self-test", "gates:pre": "node internal/run-parallel.mjs --label=pre -- check:template-literal check:mjs-parse check:sfc-parse check:tab-radius check:synced-css check:doc-scripts check:setup-order check:package-json-unique-keys check:no-repo-pb-hooks check:no-credentials-omit check:no-credentials-omit:self-test check:shadow-reduced-motion check:poster-action-print check:targets check:targets:self-test check:browser-gates-guarded check:pin-selection check:facet-shape check:hardcoded-english:self-test check:list-selection check:selector-fields check:upg-zone-joins check:gospel-set-charts check:gospel-set-fit check:describe-group check:one-scroll-container check:packet-charts check:composite-token-url check:language-tree-layout check:i18n-keys-exist check:packet-variants check:profile-table check:element-bootstrap check:embed-url-absolute check:frame-target check:language-tree-model check:map-menu-anchor check:selection-kinds check:tiered-points check:whiteboard-projection check:mercator-slice check:ring-vocabulary check:map-props-unit check:mobile-gating check:provide-shadowing check:legend-top-offset check:cdn-cache check:plural-forms check:select-zone-race check:script-registry check:map-control-overlap check:poster-attribution check:gospel-whiteboard check:packet-map-missing-loud check:poster-layout-optin check:ported-globals-identical check:ring-bands check:packet-a1-structure check:zone-title-tier-a check:evangelical-share check:map-labels check:packet-offline check:help-reachable check:gated-not-in-tree check:window-globals-released check:legend-data-contract check:no-new-driver-references check:poster-fetch-guard check:people-group-records check:sentence-slots check:popup-href-escaped check:label-key-coverage check:poster-browser-equiv check:poster-render-pinned check:host-locale-fetch check:print-frame-reclaim", "gen:iso3-to-alpha2": "node library/scripts/generate-iso3-to-alpha2.mjs", "internet:adm1": "node plugin-designs/cities-internet/scripts/build-adm1-internet.mjs", @@ -387,7 +387,7 @@ "check:a11y-button-names": "every VISIBLE button must have an accessible name (aria-label / aria-labelledby / visible text — a `title` is a TOOLTIP and does NOT count). Measures the RENDERED DOM through shadow roots: a source grep cannot answer this (132 `