check:profiles-mount: every profile and preset instance mounts in en and ar, not only composites

check:composites-mount gains --all-profiles: every .vue profile of every bundle, and every preset instance
with its props (62 x en/ar, 124 mounted clean, ~38 s), in gates:post. Its entry defines are now recorded
once per bundle per locale (a bundle's second profile read an empty record). Scope stated honestly: this
is a SOURCE mount; an error that exists only in the built bundle (a minified name hoisted out of order)
needs a mount of the build and is not covered here.
This commit is contained in:
Doxa Maps 2026-09-26 04:51:09 +00:00
parent e3d25ff6db
commit 4f47486105
2 changed files with 45 additions and 9 deletions

View file

@ -69,6 +69,33 @@ function installPage(lang) {
if (!globalThis.navigator?.language) globalThis.navigator = { language: lang, languages: [lang], userAgent: 'gate' }
}
// --all-profiles (check:profiles-mount): EVERY profile .vue of every bundle, and every preset instance
// with its props, not only composites. 0a2a7b09 was a setup-time TDZ throw in a NON-composite lens
// (upg-neighbor lang-cluster, reached through an instance's `view` prop), which the composite scope
// could not see and check:no-free-identifiers cannot see either (the name IS bound, only late).
const ALL = process.argv.includes('--all-profiles')
function allProfiles() {
const out = []
const base = join(ROOT, 'plugin-designs')
for (const bundle of readdirSync(base)) {
if (bundle.startsWith('_') || bundle.startsWith('.')) continue
const dir = join(base, bundle)
let files = []
try { files = readdirSync(dir) } catch { continue }
if (!files.includes('index.js')) continue
for (const f of files.filter((f) => f.endsWith('.vue'))) {
const profile = f.replace(/\.vue$/, '')
out.push({ bundle, profile, vue: join(dir, f), missing: false })
let inst = null
for (const cand of [`${profile}.instances.json`, ...files.filter((x) => x.endsWith('.instances.json'))]) {
try { const j = JSON.parse(readFileSync(join(dir, cand), 'utf8')); if (!j.profile || j.profile === profile) { inst = j; break } } catch { /* none */ }
}
for (const i of (inst && inst.instances) || []) out.push({ bundle, profile, vue: join(dir, f), missing: false, instance: i.id, props: i.props || {} })
}
}
return out
}
function composites() {
const out = []
const base = join(ROOT, 'plugin-designs')
@ -87,7 +114,7 @@ function composites() {
return out
}
const list = composites()
const list = ALL ? allProfiles() : composites()
if (!list.length) { console.error('COMPOSITES-MOUNT: FAIL, found no composites (vacuous run)'); process.exit(1) }
// ── mount each composite the way its bundle entry does ─────────────────────────────────────────
@ -150,15 +177,21 @@ try {
server.moduleGraph.invalidateAll()
const stores = await Promise.all(['uiStore', 'mapStore', 'dataStore'].map((n) => server.ssrLoadModule(`/library/stores/${n}.js`)))
const [useUIStore, useMapStore, useDataStore] = [stores[0].useUIStore, stores[1].useMapStore, stores[2].useDataStore]
const entryDefs = {}
for (const c of list) {
const tag = `${c.bundle}/${c.profile} [${lang}]`
const tag = `${c.bundle}/${c.profile}${c.instance ? '/' + c.instance : ''} [${lang}]`
if (c.missing) { failures.push(`${tag}: no ${c.profile}.vue beside its meta.json`); continue }
let comp, defs
try {
comp = (await server.ssrLoadModule(c.vue)).default
globalThis.__gateDefs = []
await server.ssrLoadModule(`/plugin-designs/${c.bundle}/index.js`)
defs = globalThis.__gateDefs
// The entry runs ONCE per locale (the module is cached after), so its recorded defines are kept
// per bundle: a second profile of the same bundle reads the same record.
entryDefs[c.bundle] ||= await (async () => {
globalThis.__gateDefs = []
await server.ssrLoadModule(`/plugin-designs/${c.bundle}/index.js`)
return globalThis.__gateDefs
})()
defs = entryDefs[c.bundle]
} catch (e) { failures.push(`${tag}: import threw: ${e?.message || e}`); continue }
const own = defs.find((d) => d.comp === comp)
@ -166,7 +199,7 @@ try {
const def = own || viaLoader
if (!def) { failures.push(`${tag}: its bundle entry defines neither this component nor ProfileLoader`); continue }
const cfg = { profile: c.profile, lang, locale: lang }
const cfg = { ...(c.props || {}), profile: c.profile, lang, locale: lang }
let root = comp
if (!own) {
// Under ProfileLoader the profile is a child: give it what ProfileLoader provides.
@ -201,7 +234,8 @@ try {
await server.close()
}
console.log(`COMPOSITES-MOUNT: ${list.length} composite(s) x ${LOCALES.length} locale(s): ${mounted} mounted clean`)
console.log(`${ALL ? 'PROFILES-MOUNT' : 'COMPOSITES-MOUNT'}: ${list.length} ${ALL ? 'profile/instance(s)' : 'composite(s)'} x ${LOCALES.length} locale(s): ${mounted} mounted clean`)
for (const f of failures) console.log(` FAIL ${f}`)
console.log(failures.length ? `COMPOSITES-MOUNT: FAIL (${failures.length})` : 'COMPOSITES-MOUNT: PASS')
const NAME = ALL ? 'PROFILES-MOUNT' : 'COMPOSITES-MOUNT'
console.log(failures.length ? `${NAME}: FAIL (${failures.length})` : `${NAME}: PASS`)
process.exit(failures.length ? 1 : 0)

View file

@ -144,6 +144,7 @@
"check:prayer-walk-planner": "node internal/check-prayer-walk-planner.mjs",
"check:print-geometry": "node internal/check-print-geometry.mjs",
"check:pin-dim-slider": "node plugin-designs/doxa-research-map/scripts/check-pin-dim-slider.mjs",
"check:profiles-mount": "node internal/check-composites-mount.mjs --all-profiles",
"check:provider-switch": "node internal/check-provider-switch.mjs",
"check:public-hygiene": "node internal/public-hygiene-check.mjs",
"check:published": "node internal/check-published-matches-build.mjs",
@ -193,7 +194,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: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: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",
"gates:post": "node internal/run-parallel.mjs --label=post -- 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: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",
"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",
"library-index": "node internal/generate-library-index.mjs",
"lists": "node library/scripts/people-groups/build-lists.mjs",
@ -324,6 +325,7 @@
"check:poster-degrades": "with NEITHER poster nor source — the DEFAULT drop — nothing must print and the reader must be told why. check:poster-print-fallback SKIPs that case, so the behaviour a reader most often meets had no gate. MUST be served from the TREE ROOT, not public/, or assetsBase resolves outside the tree and the poster loads when it should 404.",
"check:poster-print-fallback": "clicks the REAL poster print control with every posters/*.webp aborted, and asserts the preview RASTERISES from posters/<id>.html instead of opening on \"Poster not available yet\". Needs a served tree built with posterSources:true; SKIPs otherwise.",
"check:prayer-walk-planner": "the Prayer Walk Planner is a full-page tool that prints a real walking map: the map canvas fills the page below the toolbar in its own leaf AND inside the Research Gallery at 1280x800 and 390x844 (the gallery once collapsed it to 76px / 0px); a real zone (Balat, Istanbul) renders a >=2000px print map that is not blank, saved to OUT=<dir> so a human can read it; ?lang=es gives a Spanish toolbar; 0 page errors. Run inside the shared browser locks. Exit 2 = SKIP.",
"check:profiles-mount": "check:composites-mount over EVERY profile of every bundle and every preset instance (with its props), in en and ar: any setup, watcher or mount error fails it. Headless, ~40 s. A source mount: a build-only error (a minified name hoisted out of order) needs the built bundle and is not covered.",
"check:provider-switch": "the provider switch must actually SWITCH: counts requests BY HOST (not window globals, which stay defined after a swap) and asserts the maplibre run stays within the leaf HTML's own declared api.mapbox.com preloads. Defaults to the upg-100 leaf; pass a URL or set BASE_URL for another. CONDITIONAL: SKIPs when nothing is served.",
"check:published": "what readers are SERVED must be what we BUILT. Every other gate measures the local tree, so all of them stay green while a stale publish serves something else — and manufactures bug reports that cannot be reproduced. Compares the sha384 per bundle. SKIPs when the published host is unreachable (a network gate that goes red on flaky wifi gets ignored).",
"check:quoted-scripts": "documentation/README-SECTION-3-cli-commands.md QUOTES the scripts block verbatim; it had silently drifted TWICE (a stale bunx cap:sync, and build:unlocked missing check:public-hygiene + check:label-joins). package.json is what RUNS — update the DOC.",