Merge main: regenerate the gallery index and manifests (no bundle source came in, so no rebuild)
This commit is contained in:
commit
fe76c18046
8 changed files with 130 additions and 117 deletions
|
|
@ -7,127 +7,100 @@
|
|||
* live in a fresh context, and published. All true — and insufficient. `big-picture` (cap-4,
|
||||
* tab-1 of the cap-5 SPA) and `dashboard-plus-grid` NEST that grid and COMPILE IT IN, so both
|
||||
* kept shipping the PRE-FIX code. The surface that was proved is not the surface anyone opens.
|
||||
*
|
||||
* ★ A COMPOSITE IS A COPY TAKEN AT ITS OWN BUILD TIME, NOT A POINTER TO ITS CHILD.
|
||||
* So a child's source newer than a parent's bundle means the parent is serving stale code.
|
||||
*
|
||||
* WHY NOT GREP FOR A MARKER CLASS
|
||||
* That is how the bug was originally found, but it does not generalise: it needs a human to
|
||||
* remember a string unique to each fix. Worse, the OBVIOUS choice misleads — grepping the
|
||||
* pre-existing `filters__toggle` reported "present" on all three bundles and HID the staleness;
|
||||
* only the fix-introduced `filters__toggle-count` exposed it. A gate that depends on choosing
|
||||
* the right marker will eventually be handed the wrong one. MTIME needs no such choice.
|
||||
* ★ WHY IT NOW REBUILDS AND COMPARES BYTES, NOT MTIMES (2026-09-25). The first version compared
|
||||
* the newest child SOURCE mtime with the parent BUNDLE mtime. File times are not build facts:
|
||||
* a git merge or checkout rewrites sources with checkout-time mtimes, so rega's publish of
|
||||
* beb13da3 stopped on "doxa-maps-spa / plugin-studio / stack-demo STALE vs gospel-resources /
|
||||
* research-dashboard" although a rebuild reproduced the committed bytes exactly — and two of
|
||||
* those parents mount their children BY TAG at runtime, so they compile none of the child in
|
||||
* and CANNOT be stale with respect to it. It was also gameable the other way: checking out the
|
||||
* parents makes their mtimes new and the gate passes on stale code.
|
||||
* The question is "does the committed bundle equal what its source builds TODAY?", so that is
|
||||
* what is asked: each composite is rebuilt into a temp dir (vite.config.js DOXA_OUT_ROOT — never
|
||||
* public/) and its sha256 compared with the committed bundle. Builds are deterministic (two runs
|
||||
* → identical bytes, verified), so a difference is real: the committed parent predates a source
|
||||
* change it compiles in — its own, or a child's. A child it only mounts by tag cannot move it.
|
||||
*
|
||||
* HOW IT WORKS
|
||||
* 1. read every plugin-designs/<b>/<b>.meta.json `composes: []` — the AUTHORITATIVE declaration
|
||||
* of nesting (the manifest has no `composes` field).
|
||||
* 2. resolve each entry to a bundle: it is either a bundle DIRECTORY or an ELEMENT TAG owned by
|
||||
* one (manifest `bundles[].elements`). A tag that resolves to nothing is REPORTED, never
|
||||
* silently skipped — an unresolved edge is an unchecked edge.
|
||||
* 3. for each parent→child edge compare the NEWEST child source mtime against the parent's
|
||||
* built bundle mtime. Child newer ⇒ FAIL.
|
||||
* 1. every plugin-designs/<b>/<b>.meta.json with `composes: [...]` is a composite parent (entries
|
||||
* are still resolved and an unresolvable one is REPORTED — an unresolved edge is an unchecked
|
||||
* edge);
|
||||
* 2. BUNDLE=<parent> DOXA_OUT_ROOT=<tmp> vite build, then sha256(tmp) vs sha256(public/…/<b>.js);
|
||||
* 3. differ ⇒ STALE (FAIL); no committed bundle ⇒ reported, not built ⇒ FAIL.
|
||||
* Usage: node internal/check-composite-freshness.mjs [--only a,b] (~5-15 s per composite)
|
||||
*
|
||||
* SELF-EDGES ARE SKIPPED: big-picture composes `bp-research-header`, which resolves back to
|
||||
* big-picture. A bundle cannot be stale with respect to itself.
|
||||
*
|
||||
* NEGATIVE-TESTED: `touch plugin-designs/people-groups-grid/people-groups-grid.vue` makes this
|
||||
* FAIL naming big-picture + dashboard-plus-grid + doxa-maps-spa + plugin-studio + stack-demo;
|
||||
* a full `bun run build` returns it to PASS. A gate without a negative test is not a gate.
|
||||
* NEGATIVE-TESTED 2026-09-25: an edit to people-groups-grid.vue with no rebuild fails the
|
||||
* composites that compile it in; `touch` on every child source (no content change) still PASSES on
|
||||
* a fresh tree — the mtime false alarm is gone.
|
||||
*/
|
||||
import { readFileSync, existsSync, statSync, readdirSync } from 'node:fs'
|
||||
import { join, dirname, resolve } from 'node:path'
|
||||
import { readFileSync, existsSync, readdirSync, mkdtempSync, rmSync } from 'node:fs'
|
||||
import { createHash } from 'node:crypto'
|
||||
import { spawnSync } from 'node:child_process'
|
||||
import { tmpdir } from 'node:os'
|
||||
import { resolve, dirname, join } from 'node:path'
|
||||
import { fileURLToPath } from 'node:url'
|
||||
|
||||
const ROOT = resolve(dirname(fileURLToPath(import.meta.url)), '..')
|
||||
const DESIGNS = join(ROOT, 'plugin-designs')
|
||||
const BUILT = join(ROOT, 'public', 'doxa-maps')
|
||||
const MANIFEST = join(BUILT, 'manifest.json')
|
||||
const TREE = join(ROOT, 'public', 'doxa-maps')
|
||||
const argv = process.argv.slice(2)
|
||||
const oi = argv.indexOf('--only')
|
||||
const ONLY = oi >= 0 && argv[oi + 1] ? new Set(argv[oi + 1].split(',')) : null
|
||||
|
||||
if (!existsSync(MANIFEST)) {
|
||||
console.log('COMPOSITE-FRESHNESS: SKIP — no manifest yet (run `bun run build` first).')
|
||||
if (!existsSync(join(TREE, 'manifest.json'))) {
|
||||
console.log('COMPOSITE-FRESHNESS: SKIP — no built tree (public/doxa-maps/manifest.json).')
|
||||
process.exit(0)
|
||||
}
|
||||
|
||||
const manifest = JSON.parse(readFileSync(MANIFEST, 'utf8'))
|
||||
|
||||
const manifest = JSON.parse(readFileSync(join(TREE, 'manifest.json'), 'utf8'))
|
||||
// tag -> owning bundle, so a composes entry naming an ELEMENT still resolves.
|
||||
const tagToBundle = new Map()
|
||||
for (const b of manifest.bundles || []) {
|
||||
for (const e of b.elements || []) {
|
||||
const tag = typeof e === 'string' ? e : e?.tag
|
||||
if (tag) tagToBundle.set(tag, b.bundle)
|
||||
}
|
||||
}
|
||||
const byTag = new Map()
|
||||
for (const b of manifest.bundles || []) for (const t of b.elements || []) byTag.set(t, b.bundle)
|
||||
const bundles = new Set(readdirSync(DESIGNS).filter((d) => existsSync(join(DESIGNS, d, 'index.js'))))
|
||||
// A bare PROFILE name of the same bundle (gospel-resources composes "resources-map", "data-tables" —
|
||||
// its own sibling profiles) resolves to that bundle, as the studio resolves it.
|
||||
const profilesOf = new Map((manifest.bundles || []).map((b) => [b.bundle, new Set((b.profiles || []).map((p) => p.profile))]))
|
||||
const resolveEntry = (e, parent) => { const s = String(e); const head = s.split('/')[0]
|
||||
if (bundles.has(head)) return head
|
||||
if (parent && profilesOf.get(parent) && profilesOf.get(parent).has(s)) return parent
|
||||
return byTag.get(s) || null }
|
||||
|
||||
/** Newest mtime under a bundle's SOURCE dir (its .vue/.js/.json — not node_modules). */
|
||||
function newestSource(bundle) {
|
||||
const dir = join(DESIGNS, bundle)
|
||||
if (!existsSync(dir)) return null
|
||||
let newest = 0, file = null
|
||||
const walk = (d) => {
|
||||
for (const ent of readdirSync(d, { withFileTypes: true })) {
|
||||
if (ent.name === 'node_modules' || ent.name.startsWith('.')) continue
|
||||
const p = join(d, ent.name)
|
||||
if (ent.isDirectory()) { walk(p); continue }
|
||||
if (!/\.(vue|js|mjs|json|css)$/.test(ent.name)) continue
|
||||
const m = statSync(p).mtimeMs
|
||||
if (m > newest) { newest = m; file = p.slice(ROOT.length + 1) }
|
||||
const sha = (p) => createHash('sha256').update(readFileSync(p)).digest('hex')
|
||||
const tmp = mkdtempSync(join(tmpdir(), 'composite-freshness-'))
|
||||
const failures = [], unresolved = [], fresh = []
|
||||
let composites = 0
|
||||
try {
|
||||
for (const bundle of [...bundles].sort()) {
|
||||
if (ONLY && !ONLY.has(bundle)) continue
|
||||
let composes = null
|
||||
for (const f of readdirSync(join(DESIGNS, bundle)).filter((f) => f.endsWith('.meta.json'))) {
|
||||
try { const c = JSON.parse(readFileSync(join(DESIGNS, bundle, f), 'utf8')).composes; if (Array.isArray(c) && c.length) composes = (composes || []).concat(c) } catch { /* not JSON: not a declaration */ }
|
||||
}
|
||||
}
|
||||
walk(dir)
|
||||
return newest ? { mtime: newest, file } : null
|
||||
}
|
||||
|
||||
function builtBundle(bundle) {
|
||||
const p = join(BUILT, bundle, `${bundle}.js`)
|
||||
return existsSync(p) ? { mtime: statSync(p).mtimeMs, file: p.slice(ROOT.length + 1) } : null
|
||||
}
|
||||
|
||||
const failures = []
|
||||
const unresolved = []
|
||||
let edges = 0, composites = 0
|
||||
|
||||
for (const ent of readdirSync(DESIGNS, { withFileTypes: true })) {
|
||||
if (!ent.isDirectory()) continue
|
||||
const bundle = ent.name
|
||||
const meta = join(DESIGNS, bundle, `${bundle}.meta.json`)
|
||||
if (!existsSync(meta)) continue
|
||||
let composes
|
||||
try { composes = JSON.parse(readFileSync(meta, 'utf8')).composes } catch { continue }
|
||||
if (!Array.isArray(composes) || !composes.length) continue
|
||||
composites++
|
||||
|
||||
const parentBuilt = builtBundle(bundle)
|
||||
if (!parentBuilt) continue // never built — not this gate's concern
|
||||
|
||||
for (const rawEntry of composes) {
|
||||
// a composes entry may be "bundle", "bundle/profile", or an element tag
|
||||
const head = String(rawEntry).split('/')[0]
|
||||
const child = existsSync(join(DESIGNS, head)) ? head : tagToBundle.get(head)
|
||||
if (!child) { unresolved.push(`${bundle} composes "${rawEntry}" — resolves to no bundle`); continue }
|
||||
if (child === bundle) continue // self-edge (own element); cannot be stale vs itself
|
||||
edges++
|
||||
const src = newestSource(child)
|
||||
if (!src) continue
|
||||
if (src.mtime > parentBuilt.mtime) {
|
||||
const lagS = Math.round((src.mtime - parentBuilt.mtime) / 1000)
|
||||
failures.push(
|
||||
`${bundle} is STALE vs its child ${child}\n` +
|
||||
` child source : ${src.file}\n` +
|
||||
` parent bundle: ${parentBuilt.file}\n` +
|
||||
` child is ${lagS}s newer — the parent compiled an OLD copy. Run a FULL \`bun run build\`.`
|
||||
)
|
||||
if (!composes) continue
|
||||
for (const e of composes) if (!resolveEntry(e, bundle)) unresolved.push(`${bundle} composes "${e}" — resolves to no bundle`)
|
||||
composites++
|
||||
const committed = join(TREE, bundle, `${bundle}.js`)
|
||||
if (!existsSync(committed)) { failures.push(`${bundle}: no committed bundle at public/doxa-maps/${bundle}/${bundle}.js`); continue }
|
||||
const out = join(tmp, bundle)
|
||||
const r = spawnSync('bun', ['x', 'vite', 'build'], { cwd: ROOT, env: { ...process.env, BUNDLE: bundle, DOXA_OUT_ROOT: out }, encoding: 'utf8' })
|
||||
const built = join(out, bundle, `${bundle}.js`)
|
||||
if (r.status !== 0 || !existsSync(built)) {
|
||||
failures.push(`${bundle}: the rebuild FAILED (exit ${r.status}) — ${String(r.stderr || r.stdout).trim().split('\n').slice(-2).join(' / ').slice(0, 200)}`)
|
||||
continue
|
||||
}
|
||||
if (sha(built) !== sha(committed)) {
|
||||
failures.push(`${bundle} is STALE — the committed bundle is not what its source builds today (committed ${sha(committed).slice(0, 12)}, rebuilt ${sha(built).slice(0, 12)}). Rebuild it: BUNDLE=${bundle} bun x vite build, then bun run fix:sri.`)
|
||||
} else fresh.push(bundle)
|
||||
}
|
||||
}
|
||||
} finally { rmSync(tmp, { recursive: true, force: true }) }
|
||||
|
||||
console.log(`COMPOSITE-FRESHNESS: ${composites} composite(s), ${edges} parent->child edge(s) checked.`)
|
||||
console.log(`COMPOSITE-FRESHNESS: ${composites} composite(s) rebuilt into a temp dir and compared byte for byte with the committed bundle.`)
|
||||
if (fresh.length) console.log(` ✓ ${fresh.join(', ')}`)
|
||||
for (const u of unresolved) console.log(` ! UNRESOLVED ${u}`)
|
||||
|
||||
if (failures.length) {
|
||||
console.log(`\nCOMPOSITE-FRESHNESS: FAIL — ${failures.length} composite(s) carry a stale child:`)
|
||||
console.log(`\nCOMPOSITE-FRESHNESS: FAIL — ${failures.length} composite(s):`)
|
||||
for (const f of failures) console.log(` ✗ ${f}`)
|
||||
console.log('\n A composite is a COPY taken at its own build time, not a pointer to its child.')
|
||||
process.exit(1)
|
||||
}
|
||||
console.log('COMPOSITE-FRESHNESS: PASS — every composite was built after the children it nests.')
|
||||
console.log('COMPOSITE-FRESHNESS: PASS — every committed composite is exactly what its source builds today.')
|
||||
|
|
|
|||
|
|
@ -271,6 +271,47 @@ function ownsAMapDeeply(profileDir) {
|
|||
return false
|
||||
}
|
||||
|
||||
/**
|
||||
* Does THIS PROFILE reach a map through its own import graph?
|
||||
*
|
||||
* ★ PER PROFILE, NOT PER BUNDLE (BUG-FALSE-ISMAP-BAKES-403KB-MAPBOX-INTO-MAPLESS-LEAVES, fixed
|
||||
* 2026-09-25). ownsAMapDeeply() answers "is there a map ANYWHERE under this bundle's src/", and the
|
||||
* single-leaf branch used it for every profile — so a map-less sibling inherited map-ness from the
|
||||
* bundle: regions-dashboard/regions-list shows a list and no map, but src/RegionCountryMap.vue made
|
||||
* it isMap=true. Its leaf then baked the ~400 KB Mapbox engine it never uses, and since 4a6886d2 its
|
||||
* embed snippet also preloaded it. This follows the RELATIVE imports reachable from the profile's
|
||||
* own .vue (bounded to the bundle folder, .vue/.js/.mjs), so research-dashboard — whose map sits
|
||||
* several imports deep in src/components/dashboards/DashboardMapPanel.vue — still resolves true.
|
||||
* An explicit boolean `isMap` in <profile>.meta.json wins over the scan.
|
||||
*/
|
||||
const RELATIVE_IMPORT_RE = /(?:import[^'"`;]*?from\s*|import\s*\(\s*|import\s+)['"`](\.{1,2}\/[^'"`]+)['"`]/g
|
||||
function profileReachesAMap(dir, file) {
|
||||
const seen = new Set()
|
||||
const stack = [join(dir, file)]
|
||||
while (stack.length) {
|
||||
const f = stack.pop()
|
||||
if (seen.has(f)) continue
|
||||
seen.add(f)
|
||||
let src
|
||||
try { src = readFileSync(f, 'utf8') } catch { continue }
|
||||
if (MAP_ENGINE_RE.test(src)) return true
|
||||
for (const m of src.matchAll(RELATIVE_IMPORT_RE)) {
|
||||
const base = resolve(dirname(f), m[1])
|
||||
if (!base.startsWith(dir)) continue // stay inside this bundle
|
||||
for (const cand of [base, base + '.vue', base + '.js', base + '.mjs', join(base, 'index.js')]) {
|
||||
if (NESTED_SCAN_EXT.test(cand) && existsSync(cand) && statSync(cand).isFile()) { stack.push(cand); break }
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
function metaIsMap(dir, profileName) {
|
||||
try {
|
||||
const meta = JSON.parse(readFileSync(join(dir, `${profileName}.meta.json`), 'utf8'))
|
||||
return typeof meta?.isMap === 'boolean' ? meta.isMap : null
|
||||
} catch { return null }
|
||||
}
|
||||
|
||||
/**
|
||||
* Does a cap-4 composite (`nested-elements-mfe`) compose a child that paints a map?
|
||||
*
|
||||
|
|
@ -1206,7 +1247,7 @@ function scanBundles() {
|
|||
? false
|
||||
: profileType === 'nested-elements-mfe'
|
||||
? composesAMapLeaf(dir, profileName, file)
|
||||
: (isMapLeaf(join(dir, file)) || ownsAMapDeeply(dir))
|
||||
: (metaIsMap(dir, profileName) ?? profileReachesAMap(dir, file))
|
||||
|
||||
return {
|
||||
profile: profileName,
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
4
public/doxa-maps/doxa-maps-spa/manifest.json
generated
4
public/doxa-maps/doxa-maps-spa/manifest.json
generated
|
|
@ -917,12 +917,12 @@
|
|||
"description": "A refactor of DX-life's /regions index page (app/pages/regions/index.vue) into",
|
||||
"element": "regions-dashboard",
|
||||
"type": "single",
|
||||
"isMap": true,
|
||||
"isMap": false,
|
||||
"iframeUrl": "./regions-dashboard/regions-list/index.html",
|
||||
"scriptUrl": "./regions-dashboard/regions-dashboard.js",
|
||||
"embed": {
|
||||
"iframe": "<iframe src=\"./regions-dashboard/regions-list/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen></iframe>",
|
||||
"script": "<!-- Mapbox engine, fetched early; remove this line if you set \"provider\":\"maplibre\" -->\n<link rel=\"preload\" as=\"script\" href=\"https://api.mapbox.com/mapbox-gl-js/v3.24.0/mapbox-gl.js\">\n<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./regions-dashboard/regions-dashboard.js\" integrity=\"sha384-zE42DnXbiIsJpnPZ2ZLVClbwODYgF0j6agMZoJTuhR/AW56nzpl7TwT8X8R9FPH4\" crossorigin=\"anonymous\"></script>\n<regions-dashboard profile-config='{\"profile\":\"regions-list\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></regions-dashboard>"
|
||||
"script": "<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./regions-dashboard/regions-dashboard.js\" integrity=\"sha384-zE42DnXbiIsJpnPZ2ZLVClbwODYgF0j6agMZoJTuhR/AW56nzpl7TwT8X8R9FPH4\" crossorigin=\"anonymous\"></script>\n<regions-dashboard profile-config='{\"profile\":\"regions-list\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></regions-dashboard>"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
|
|
|||
2
public/doxa-maps/index.html
generated
2
public/doxa-maps/index.html
generated
File diff suppressed because one or more lines are too long
4
public/doxa-maps/manifest.json
generated
4
public/doxa-maps/manifest.json
generated
|
|
@ -917,12 +917,12 @@
|
|||
"description": "A refactor of DX-life's /regions index page (app/pages/regions/index.vue) into",
|
||||
"element": "regions-dashboard",
|
||||
"type": "single",
|
||||
"isMap": true,
|
||||
"isMap": false,
|
||||
"iframeUrl": "./regions-dashboard/regions-list/index.html",
|
||||
"scriptUrl": "./regions-dashboard/regions-dashboard.js",
|
||||
"embed": {
|
||||
"iframe": "<iframe src=\"./regions-dashboard/regions-list/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen></iframe>",
|
||||
"script": "<!-- Mapbox engine, fetched early; remove this line if you set \"provider\":\"maplibre\" -->\n<link rel=\"preload\" as=\"script\" href=\"https://api.mapbox.com/mapbox-gl-js/v3.24.0/mapbox-gl.js\">\n<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./regions-dashboard/regions-dashboard.js\" integrity=\"sha384-zE42DnXbiIsJpnPZ2ZLVClbwODYgF0j6agMZoJTuhR/AW56nzpl7TwT8X8R9FPH4\" crossorigin=\"anonymous\"></script>\n<regions-dashboard profile-config='{\"profile\":\"regions-list\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></regions-dashboard>"
|
||||
"script": "<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./regions-dashboard/regions-dashboard.js\" integrity=\"sha384-zE42DnXbiIsJpnPZ2ZLVClbwODYgF0j6agMZoJTuhR/AW56nzpl7TwT8X8R9FPH4\" crossorigin=\"anonymous\"></script>\n<regions-dashboard profile-config='{\"profile\":\"regions-list\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></regions-dashboard>"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
|
|
|||
|
|
@ -5,13 +5,10 @@
|
|||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>regions-list · regions-dashboard</title>
|
||||
<link rel="icon" href="data:,">
|
||||
<!-- Mapbox peer loaded ONCE here; the map custom element mounts in this page and
|
||||
uses window.mapboxgl directly (same realm → no cross-realm container errors). -->
|
||||
<link rel="preconnect" href="https://api.mapbox.com" crossorigin />
|
||||
<link href="https://api.mapbox.com/mapbox-gl-js/v3.24.0/mapbox-gl.css" rel="stylesheet" />
|
||||
<link href="https://api.mapbox.com/mapbox-gl-js/plugins/mapbox-gl-geocoder/v5.0.0/mapbox-gl-geocoder.css" rel="stylesheet" />
|
||||
<script src="https://api.mapbox.com/mapbox-gl-js/v3.24.0/mapbox-gl.js"></script>
|
||||
<script src="https://api.mapbox.com/mapbox-gl-js/plugins/mapbox-gl-geocoder/v5.0.0/mapbox-gl-geocoder.min.js"></script>
|
||||
<!-- NON-MAP leaf (app-shell / composite / grid / card / chart-table): NO map peer.
|
||||
This element draws no map tiles, so it needs no Mapbox/MapLibre engine and MUST NOT
|
||||
gate on a token. Any nested MAP (in a composite/shell) renders in its own iframe leaf,
|
||||
which loads its own map peer + token. A peer here would be dead weight + imply a gate. -->
|
||||
<style>
|
||||
/* BARE full-page map — no chrome. The custom element IS the page, so the
|
||||
browser URL shares the map itself. */
|
||||
|
|
@ -25,7 +22,7 @@
|
|||
</head>
|
||||
<body>
|
||||
<div id="slot"></div>
|
||||
<div id="status">loading token…</div>
|
||||
<div id="status">loading…</div>
|
||||
|
||||
<script>window.MAP_APP_API_URL = "https://pray.doxa.life";</script>
|
||||
<!-- Bundle IIFE — registers the <regions-dashboard> custom element. -->
|
||||
|
|
@ -48,7 +45,7 @@
|
|||
var BASE_CONFIG = {"pinHint":false,"profile":"regions-list","dataSource":"pray-tools","instanceId":"regions-dashboard-regions-list","assetsBase":"../../assets"};
|
||||
// MapLibre map pages AND shell pages (app-shell / nested-mfe-spa) need NO token:
|
||||
// the ladder below is skipped entirely and the element mounts immediately.
|
||||
var TOKEN_FREE = false;
|
||||
var TOKEN_FREE = true;
|
||||
// A token-free COMPOSITE still nests plugins, and a nested Mapbox map reads window.MAP_TOKEN
|
||||
// rather than this page's profile-config. With no baked token (the public copy), it must get
|
||||
// one from ?tk= or a pasted token, or be asked for one — see the rungs below.
|
||||
|
|
|
|||
|
|
@ -44,7 +44,9 @@ function readMfeName() {
|
|||
const MFE_NAME = readMfeName()
|
||||
|
||||
// Default output tree. generate-tree.mjs reads the same name + copies it onward.
|
||||
const OUT_ROOT = resolve(__dirname, 'public', MFE_NAME)
|
||||
// DOXA_OUT_ROOT redirects a build's output WITHOUT touching public/: check:composite rebuilds each
|
||||
// composite into a temp dir and compares bytes with the committed bundle. Unset = unchanged behaviour.
|
||||
const OUT_ROOT = process.env.DOXA_OUT_ROOT ? resolve(process.env.DOXA_OUT_ROOT) : resolve(__dirname, 'public', MFE_NAME)
|
||||
|
||||
function discoverBundles() {
|
||||
if (!existsSync(profilesDir)) return {}
|
||||
|
|
|
|||
Loading…
Reference in a new issue