From 71f36b50792008163fe70170587de4d40cc495f9 Mon Sep 17 00:00:00 2001 From: doxa-maps Date: Tue, 29 Sep 2026 08:53:48 +0000 Subject: [PATCH] A9 leaf hints: key on the bundle's engine, not the leaf's isMap flag MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit rega's A9NET run found two production composite tabs — regions-dashboard/ regions-mfe and research-map-plus-grid — getting NO hints, because my first cut gated on the leaf's isMap flag and both are isMap:false composites that mount a map child and load the engine at runtime. Same trap the check:leaf-engine-per-provider isMap gate fell into. The gate now keys on whether the leaf's OWN page loads a map engine: a real map leaf (isMap), OR a cap-4 nested-elements-mfe composite whose BUILT bundle references an engine (builtBundleLoadsEngine). It excludes a cap-5 nested-mfe-spa shell (maps are iframe children), a genuinely map-less composite (big-picture, stack-demo: 0 engine refs), and a plain non-map leaf sharing a bundle with a map (connectivity-list). regions-mfe (33 engine refs) is now hinted; research-map-plus-grid is not — its own bundle has 0 refs, the engine loads in a lazily-fetched child (recorded gap; the deeper cause is the upstream isMap classifier mislabelling these composites, which composes-by-tag where the child .vue filename differs). check:leaf-hints rebuilt to the same rule, read from the manifest (profile type + isMap) and the built bundle, so gate and generator cannot drift. PASS 37 hinted / 27 bare; both new poisons bite (composite stripped of hints; non-map leaf in a map bundle given a hint). --- internal/check-leaf-hints.mjs | 62 ++++++++++++++----- internal/generate-tree.mjs | 34 +++++++++- .../regions-dashboard/regions-mfe/index.html | 5 ++ 3 files changed, 82 insertions(+), 19 deletions(-) diff --git a/internal/check-leaf-hints.mjs b/internal/check-leaf-hints.mjs index 8a9085e51..7a49e9656 100644 --- a/internal/check-leaf-hints.mjs +++ b/internal/check-leaf-hints.mjs @@ -36,13 +36,37 @@ const ENGINE_ORIGINS = [ORIGIN(CDN.mapboxGlJs.url), ORIGIN(CDN.maplibreGlJs.url) const STYLE_ORIGINS = ['https://tiles.openfreemap.org', ORIGIN(CDN.mapboxGlJs.url)] const MAP_ORIGINS = new Set([...ENGINE_ORIGINS, ...STYLE_ORIGINS].filter(Boolean)) -// Markers generate-tree writes into a leaf's (mapPageHtml peerBlock): -const NON_MAP = 'NON-MAP leaf' // grid/card/chart-table/app-shell/composite → no peer -const MAPLIBRE_PEER = 'MapLibre peer' // provider:'maplibre' static engine -const AUTO_NOTE = 'NO STATIC ENGINE TAGS' // the 'auto' leaf: engine chosen at runtime const HINTS = 'A9 hints' +const ENGINE_RE = /useMapInstance|useMapProvider|mapbox-gl|maplibre-gl|window\.(?:mapboxgl|maplibregl)/ -// Every leaf index.html under the tree (a profile or instance page). +// The rule the generator applies (mapPageHtml → drawsMapInPage): a leaf gets A9 hints iff its OWN page +// loads a map engine — a real map leaf (`isMap`), OR a cap-4 `nested-elements-mfe` composite whose bundle +// mounts a map inline (`bundleLoadsEngine`) — and it is not a cap-5 `nested-mfe-spa` shell (its maps are +// iframe children). A plain non-map leaf that merely SHARES a bundle with a map leaf gets none. The gate +// reads the same authoritative facts: profile `type`/`isMap` from the manifest, and the built bundle. So +// the gate and the generator cannot drift. +const manifest = JSON.parse(readFileSync(join(TREE, 'manifest.json'), 'utf8')) +const engineBundles = new Map() +function bundleLoadsEngine(bundle) { + if (!engineBundles.has(bundle)) { + let hit = false + try { hit = ENGINE_RE.test(readFileSync(join(TREE, bundle, `${bundle}.js`), 'utf8')) } catch { hit = false } + engineBundles.set(bundle, hit) + } + return engineBundles.get(bundle) +} +// Expected hint state per profile, and the leaf dirs it renders to (profile page + any instance pages). +const expect = new Map() // "/" -> boolean shouldHint +for (const b of manifest.bundles || []) { + for (const p of b.profiles || []) { + const shell = p.type === 'nested-mfe-spa' + const composite = p.type === 'nested-elements-mfe' + const shouldHint = (!!p.isMap || (composite && bundleLoadsEngine(b.bundle))) && !shell + expect.set(`${b.bundle}/${p.profile}`, shouldHint) + } +} + +// Every leaf index.html under the tree, mapped back to its "/". function leaves(dir, out = []) { for (const e of readdirSync(dir)) { const p = join(dir, e) @@ -51,32 +75,36 @@ function leaves(dir, out = []) { } return out } +// rel is "/[/]"; the profile key is the first two segments. +const keyOf = (rel) => rel.split('/').slice(0, 2).join('/') const fails = [] -let maps = 0 -let nonMaps = 0 +let hinted = 0 +let bare = 0 +let unclassified = 0 for (const file of leaves(TREE)) { const html = readFileSync(file, 'utf8') const rel = file.slice(TREE.length + 1) - const isMapLeaf = html.includes(MAPLIBRE_PEER) || html.includes(AUTO_NOTE) - const isNonMap = html.includes(NON_MAP) - // Engine/style-origin preconnects and engine preloads present in this page. + const key = keyOf(rel) + if (!expect.has(key)) { unclassified += 1; continue } // a non-profile page (e.g. tree root) — not our subject + const shouldHint = expect.get(key) + const hasHints = html.includes(HINTS) const preconnects = [...html.matchAll(/ m[1]) const enginePreload = / MAP_ORIGINS.has(o)) || enginePreload - if (isMapLeaf) { - maps += 1 - if (!html.includes(HINTS)) fails.push(`${rel}: MAP leaf with no A9 hints (expected preconnect + preload)`) - // Every preconnect on a map leaf must name an origin the code uses (no stale host). + if (shouldHint) { + if (!hasHints) fails.push(`${rel}: loads a map engine in its own page but has NO A9 hints`) + else hinted += 1 for (const o of preconnects) { if (!MAP_ORIGINS.has(o) && !/^https:\/\/[a-z0-9.-]+$/i.test(o)) fails.push(`${rel}: malformed preconnect origin ${o}`) } - } else if (isNonMap) { - nonMaps += 1 - if (mapHint) fails.push(`${rel}: NON-MAP leaf carries a map-engine hint (a non-map leaf loads no peer)`) + } else { + if (mapHint || hasHints) fails.push(`${rel}: does not load a map engine in its own page, yet carries a map-engine hint`) + else bare += 1 } } +const maps = hinted, nonMaps = bare if (maps === 0) fails.push('no map leaves found in the tree: the classifier or the tree is wrong') diff --git a/internal/generate-tree.mjs b/internal/generate-tree.mjs index dea0c586d..13ed3c5f3 100644 --- a/internal/generate-tree.mjs +++ b/internal/generate-tree.mjs @@ -881,8 +881,17 @@ function resourceHints({ isMapLibre, config, apiBaseUrl, tokenUrlExpr, bakedToke return lines.length ? ` \n${lines.join('\n')}\n` : '' } +// Does a bundle's BUILT .js reference a map engine? The directly verifiable signal for A9 leaf hints +// (below), read from public///.js with the same regex the source classifier uses. +// This catches a composite whose own bundle mounts a map inline even when its profile is isMap:false; +// it does not catch one whose engine loads only in a lazily-fetched child bundle (see the hint comment). +function builtBundleLoadsEngine(bundleName) { + try { return MAP_ENGINE_RE.test(readFileSync(join(DEFAULT_ROOT, bundleName, `${bundleName}.js`), 'utf8')) } + catch { return false } +} + function mapPageHtml(opts) { - const { title, tag, config, jsSrc, apiBaseUrl, tokenUrlExpr, bakedToken, integrity, shellTokenFree, isMap, scrollPage, isComposite } = opts + const { title, tag, config, jsSrc, apiBaseUrl, tokenUrlExpr, bakedToken, integrity, shellTokenFree, isMap, scrollPage, isComposite, bundleLoadsEngine } = opts const apiScript = apiBaseUrl ? `\n ` : '' @@ -924,7 +933,24 @@ function mapPageHtml(opts) { // hardcoded: preconnect the token-implied engine + the style host + the data origin, and — only // where the leaf emits no engine