From 17dc7bad87678eea7a1f77a4a33f43ad8ece7204 Mon Sep 17 00:00:00 2001 From: Doxa Maps Date: Fri, 25 Sep 2026 23:22:27 +0000 Subject: [PATCH] check:dev-assets: every leaf of the seven studio maps, with bytes that parse and a bogus-path control Each leaf's dev page must hand its element assetsBase "/assets"; a boundary file and a table under that base must answer 200 with bytes that JSON.parse (never an HTML fallback), and a bogus file under it must be a real 404. --- internal/check-dev-assets.mjs | 36 +++++++++++++++++++++++++++++------ 1 file changed, 30 insertions(+), 6 deletions(-) diff --git a/internal/check-dev-assets.mjs b/internal/check-dev-assets.mjs index c959beb3d..226eef918 100644 --- a/internal/check-dev-assets.mjs +++ b/internal/check-dev-assets.mjs @@ -13,8 +13,10 @@ * HTTP boundaries/, previews/, fonts/, images/ and tables/ each resolve (200, not HTML) at every * prefix a dev page can produce: /assets/, /__plugin-gallery/assets/, //assets/, * /__leaf//assets/ and /__leaf///assets/. - * LEAF a dev leaf page (/__leaf//) hands its element assetsBase "/assets", as a - * built leaf hands "../../assets". + * LEAVES every leaf of the seven studio maps (Big Picture, Simple Research, Regions, UPG-100, Prayer + * Progress, Cities, Gospel): its dev page hands the element assetsBase "/assets" (as a built + * leaf hands "../../assets"); a boundary file and a table under THAT base answer 200 with bytes + * that JSON.parse, and a bogus file under it is a real 404, not HTML. * HEADLESS by project rule (browsers are one agent's, on this machine): whether each map then DRAWS * its polygons is a visual check, handed over as "needs eyes". * @@ -31,6 +33,8 @@ import { TREE_NAME } from './_tree-name.mjs' const ROOT = resolve(dirname(fileURLToPath(import.meta.url)), '..') const argv = process.argv.slice(2) const arg = (k, d) => { const i = argv.indexOf(k); return i >= 0 && argv[i + 1] ? argv[i + 1] : d } +const MAP_BUNDLES = ['dashboard-plus-grid', 'doxa-research-map', 'regions-dashboard', 'my-upg-100-list', 'doxa-simple-map', 'cities-internet', 'gospel-resources'] +const JSON_SAMPLES = ['boundaries/world-adm0.geojson', 'tables/country-connectivity.json'] const SAMPLES = ['boundaries/world-adm0.geojson', 'previews/andean-caribbean.webp', 'fonts/bebas-neue/bebas-neue-latin.woff2', 'images/worldmap.svg', 'tables/arclight-index.json'] let BASE = arg('--url', ''), dev = null @@ -65,12 +69,32 @@ try { console.log(` ${res.length ? 'FAIL' : 'ok '} ${p.padEnd(44)} ${res.length ? res.join(', ') : SAMPLES.map((s) => s.split('/')[0]).join(' ') + ' all 200'}`) } - // ── THE DEV LEAF PAGE hands its element the pool, as the built leaf's BASE_CONFIG does ── - for (const leaf of ['/__leaf/regions-dashboard/region-dashboards', '/__leaf/my-upg-100-list/upg-100-list']) { + // ── EVERY LEAF OF THE SEVEN MAPS: the base its page hands the element serves REAL, parseable data ── + // (bytes + JSON.parse, never an HTML fallback), and a bogus path under the same base is a real 404. + const reg = await fetch(BASE + '/__plugin-gallery/registry.json').then((r) => r.json()).catch(() => []) + const leaves = [] + for (const b of reg) if (MAP_BUNDLES.includes(b.bundle)) for (const it of b.items || []) leaves.push(`/__leaf/${b.bundle}/${it.profile}`) + if (leaves.length < MAP_BUNDLES.length) { bad++; console.log(` FAIL registry lists ${leaves.length} leaves for the ${MAP_BUNDLES.length} map bundles`) } + for (const leaf of leaves) { const html = await fetch(BASE + leaf).then((r) => r.text()).catch(() => '') - const ok = /profile-config='[^']*"assetsBase":"\/assets"/.test(html) + const m = /profile-config='([^']*)'/.exec(html) + let base = '' + try { base = JSON.parse(m[1]).assetsBase || '' } catch { /* no config */ } + const out = [] + let ok = base === '/assets' + if (!ok) out.push(`assetsBase ${JSON.stringify(base)}`) + const at = (f) => base ? BASE + base + '/' + f : new URL('assets/' + f, BASE + leaf).href + for (const f of JSON_SAMPLES) { + const r = await fetch(at(f)).catch(() => null) + const body = r ? await r.text() : '' + let parsed = false + try { JSON.parse(body); parsed = true } catch { /* not JSON */ } + if (!r || r.status !== 200 || !parsed) { ok = false; out.push(`${f} ${r ? r.status : 'ERR'}${parsed ? '' : ' NOT JSON'}`) } else out.push(`${f.split('/').pop()} ${body.length} B`) + } + const ctl = await fetch(at('boundaries/no-such-file.geojson')).catch(() => null) + if (!ctl || ctl.status !== 404) { ok = false; out.push(`bogus path ${ctl ? ctl.status : 'ERR'} (want 404)`) } if (!ok) bad++ - console.log(` ${ok ? 'ok ' : 'FAIL'} ${leaf.padEnd(44)} ${ok ? 'profile-config carries assetsBase "/assets"' : 'profile-config has NO root-absolute assetsBase'}`) + console.log(` ${ok ? 'ok ' : 'FAIL'} ${leaf.padEnd(52)} assetsBase ${base || '(none)'}: ${out.join(', ')}${ok ? ', bogus 404' : ''}`) } } finally { stop() }