Decouple the tooling: no host named in internal/ or the build config
Six tools defaulted to the live host's URL. They now read the published tree's location from DOXA_PUBLISHED_BASE (environment, or the gitignored .env / .env.local) through internal/_published-base.mjs, and with nothing set they report NOT MEASURED (exit 75) instead of measuring a host they were never told about: check:catalog-currency, check:catalog-identity, check:published, verify:5-caps, verify:map-paint. An explicit BASE / --base / PUBLISHED_BASE still wins. check:embed-base only needed an example URL for its offline resolution table. The public-hygiene leak detector builds the platform name from parts, so the file that catches it does not carry it. Comments and the two build-config notes say the host platform or the live test host. From repo's DECOUPLE round 2 list.
This commit is contained in:
parent
25c3d10abd
commit
f13075b1d4
13 changed files with 70 additions and 21 deletions
44
internal/_published-base.mjs
Normal file
44
internal/_published-base.mjs
Normal file
|
|
@ -0,0 +1,44 @@
|
||||||
|
/**
|
||||||
|
* _published-base.mjs — where the PUBLISHED tree lives, for the few tools that measure the live catalogue.
|
||||||
|
*
|
||||||
|
* The repo names no host (white-label: it ships to people who publish somewhere else). A tool that
|
||||||
|
* compares against the live tree reads its location from DOXA_PUBLISHED_BASE: the URL the tree folders sit
|
||||||
|
* under, e.g. https://<your-host>/plugin-assets (the tree name is appended here, from _tree-name.mjs). Set it
|
||||||
|
* in the environment, or in the gitignored .env / .env.local at the repo root.
|
||||||
|
*
|
||||||
|
* With nothing set, such a tool has nothing to measure: it says NOT MEASURED and exits 75, the fleet's
|
||||||
|
* third bucket, instead of passing or failing against a host it was never told about.
|
||||||
|
*/
|
||||||
|
import { readFileSync } from 'node:fs'
|
||||||
|
import { join, dirname } from 'node:path'
|
||||||
|
import { fileURLToPath } from 'node:url'
|
||||||
|
import { TREE_NAME } from './_tree-name.mjs'
|
||||||
|
|
||||||
|
const ROOT = join(dirname(fileURLToPath(import.meta.url)), '..')
|
||||||
|
|
||||||
|
function fromEnvFiles() {
|
||||||
|
for (const f of ['.env.local', '.env']) {
|
||||||
|
try {
|
||||||
|
const m = readFileSync(join(ROOT, f), 'utf8').match(/^\s*DOXA_PUBLISHED_BASE\s*=\s*["']?([^"'\s#]+)/m)
|
||||||
|
if (m) return m[1]
|
||||||
|
} catch { /* no such file */ }
|
||||||
|
}
|
||||||
|
return ''
|
||||||
|
}
|
||||||
|
|
||||||
|
/** The published tree's URL (…/<TREE_NAME>, no trailing slash), or '' when none is configured. */
|
||||||
|
export function publishedTreeUrl() {
|
||||||
|
const base = (process.env.DOXA_PUBLISHED_BASE || fromEnvFiles()).replace(/\/+$/, '')
|
||||||
|
return base ? `${base}/${TREE_NAME}` : ''
|
||||||
|
}
|
||||||
|
|
||||||
|
/** For a tool that cannot run without it: the tree URL, or NOT MEASURED (exit 75) naming what to set. */
|
||||||
|
export function requirePublishedTreeUrl(label) {
|
||||||
|
const url = publishedTreeUrl()
|
||||||
|
if (!url) {
|
||||||
|
console.log(`${label}: NOT MEASURED — no published tree to measure. Set DOXA_PUBLISHED_BASE (environment or .env)`)
|
||||||
|
console.log(` to the URL your tree folders sit under, e.g. https://<your-host>/plugin-assets`)
|
||||||
|
process.exit(75)
|
||||||
|
}
|
||||||
|
return url
|
||||||
|
}
|
||||||
|
|
@ -34,11 +34,12 @@ import { createHash } from 'node:crypto'
|
||||||
import { resolve, dirname, join } from 'node:path'
|
import { resolve, dirname, join } from 'node:path'
|
||||||
import { fileURLToPath } from 'node:url'
|
import { fileURLToPath } from 'node:url'
|
||||||
import { TREE_NAME } from './_tree-name.mjs'
|
import { TREE_NAME } from './_tree-name.mjs'
|
||||||
|
import { requirePublishedTreeUrl } from './_published-base.mjs'
|
||||||
|
|
||||||
const ROOT = resolve(dirname(fileURLToPath(import.meta.url)), '..')
|
const ROOT = resolve(dirname(fileURLToPath(import.meta.url)), '..')
|
||||||
const args = process.argv.slice(2)
|
const args = process.argv.slice(2)
|
||||||
const argOf = (k, d) => { const i = args.indexOf(k); return i >= 0 ? args[i + 1] : d }
|
const argOf = (k, d) => { const i = args.indexOf(k); return i >= 0 ? args[i + 1] : d }
|
||||||
const BASE = (argOf('--base', `https://nuxtbase.app/plugin-assets/${TREE_NAME}`)).replace(/\/+$/, '')
|
const BASE = (argOf('--base', '') || requirePublishedTreeUrl('CATALOG-CURRENCY')).replace(/\/+$/, '')
|
||||||
const TIMEOUT = Number(argOf('--timeout', 25000))
|
const TIMEOUT = Number(argOf('--timeout', 25000))
|
||||||
|
|
||||||
const sha384 = (buf) => 'sha384-' + createHash('sha384').update(buf).digest('base64')
|
const sha384 = (buf) => 'sha384-' + createHash('sha384').update(buf).digest('base64')
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@
|
||||||
* Card 1 (IDEA-MAPS-BUNDLER-PUBLIC-FORGEJO-PIPELINE) asks that ONE public Forgejo repo be the live
|
* Card 1 (IDEA-MAPS-BUNDLER-PUBLIC-FORGEJO-PIPELINE) asks that ONE public Forgejo repo be the live
|
||||||
* catalog source, and that the merge lose nothing. The tempting verification is `curl -o /dev/null
|
* catalog source, and that the merge lose nothing. The tempting verification is `curl -o /dev/null
|
||||||
* -w '%{http_code}'` over a few URLs. That proves only that SOMETHING answered:
|
* -w '%{http_code}'` over a few URLs. That proves only that SOMETHING answered:
|
||||||
* · https://nuxtbase.app/app/doxa-maps/manifest.json returns **200** and is a 2,872-byte HTML
|
* · the host platform's /app/doxa-maps/manifest.json returns **200** and is a 2,872-byte HTML
|
||||||
* SIGN-IN PAGE. An http-status check reads it as healthy.
|
* SIGN-IN PAGE. An http-status check reads it as healthy.
|
||||||
* · a stale CDN copy also returns 200, with the wrong bytes.
|
* · a stale CDN copy also returns 200, with the wrong bytes.
|
||||||
*
|
*
|
||||||
|
|
@ -22,7 +22,7 @@
|
||||||
* additionally require that it matches a cloned catalog checkout.
|
* additionally require that it matches a cloned catalog checkout.
|
||||||
*
|
*
|
||||||
* MEASURED 2026-09-21: 17 of 17 bundles identity-proven, 0 mismatched, and the live public manifest
|
* MEASURED 2026-09-21: 17 of 17 bundles identity-proven, 0 mismatched, and the live public manifest
|
||||||
* is byte-identical (sha256 88ae3832…) to platform/mfe-index's. The catalog IS what nuxtbase serves.
|
* is byte-identical (sha256 88ae3832…) to platform/mfe-index's. The catalog IS what the host platform serves.
|
||||||
*
|
*
|
||||||
* USAGE
|
* USAGE
|
||||||
* bun run check:catalog-identity # live public door vs its own manifest
|
* bun run check:catalog-identity # live public door vs its own manifest
|
||||||
|
|
@ -35,8 +35,9 @@ import { readFileSync, existsSync } from 'node:fs'
|
||||||
import { createHash } from 'node:crypto'
|
import { createHash } from 'node:crypto'
|
||||||
import { join } from 'node:path'
|
import { join } from 'node:path'
|
||||||
import { TREE_NAME } from './_tree-name.mjs'
|
import { TREE_NAME } from './_tree-name.mjs'
|
||||||
|
import { requirePublishedTreeUrl } from './_published-base.mjs'
|
||||||
|
|
||||||
const BASE = (process.env.BASE || `https://nuxtbase.app/plugin-assets/${TREE_NAME}/`).replace(/\/?$/, '/')
|
const BASE = (process.env.BASE || requirePublishedTreeUrl('CATALOG-IDENTITY')).replace(/\/?$/, '/')
|
||||||
const CATALOG = process.env.CATALOG || ''
|
const CATALOG = process.env.CATALOG || ''
|
||||||
|
|
||||||
const sha384 = (buf) => 'sha384-' + createHash('sha384').update(buf).digest('base64')
|
const sha384 = (buf) => 'sha384-' + createHash('sha384').update(buf).digest('base64')
|
||||||
|
|
|
||||||
|
|
@ -54,7 +54,7 @@ try {
|
||||||
// The leaf's own arithmetic is the oracle — computed here rather than hardcoded, so the
|
// The leaf's own arithmetic is the oracle — computed here rather than hardcoded, so the
|
||||||
// expectation cannot drift from what the generated HTML actually means.
|
// expectation cannot drift from what the generated HTML actually means.
|
||||||
const TREES = [
|
const TREES = [
|
||||||
['live catalog', `https://nuxtbase.app/plugin-assets/${TREE_NAME}`],
|
['live catalog', `https://maps.example.org/plugin-assets/${TREE_NAME}`],
|
||||||
['dev tree', `http://127.0.0.1:4173/${TREE_NAME}`],
|
['dev tree', `http://127.0.0.1:4173/${TREE_NAME}`],
|
||||||
['doxa.life drop-in', `https://doxa.life/js/doxa-maps-build/${TREE_NAME}`],
|
['doxa.life drop-in', `https://doxa.life/js/doxa-maps-build/${TREE_NAME}`],
|
||||||
['deep subpath', `https://cdn.example.org/a/b/c/${TREE_NAME}`],
|
['deep subpath', `https://cdn.example.org/a/b/c/${TREE_NAME}`],
|
||||||
|
|
|
||||||
|
|
@ -38,11 +38,12 @@ import { createHash } from 'node:crypto'
|
||||||
import { resolve, dirname, join } from 'node:path'
|
import { resolve, dirname, join } from 'node:path'
|
||||||
import { fileURLToPath } from 'node:url'
|
import { fileURLToPath } from 'node:url'
|
||||||
import { TREE_NAME } from './_tree-name.mjs'
|
import { TREE_NAME } from './_tree-name.mjs'
|
||||||
|
import { requirePublishedTreeUrl } from './_published-base.mjs'
|
||||||
|
|
||||||
const HERE = dirname(fileURLToPath(import.meta.url))
|
const HERE = dirname(fileURLToPath(import.meta.url))
|
||||||
const REPO = process.env.DOXA_REPO || resolve(HERE, '..')
|
const REPO = process.env.DOXA_REPO || resolve(HERE, '..')
|
||||||
const PUBLISHED_BASE = (process.env.PUBLISHED_BASE
|
const PUBLISHED_BASE = (process.env.PUBLISHED_BASE
|
||||||
|| `https://nuxtbase.app/plugin-assets/${TREE_NAME}`).replace(/\/$/, '')
|
|| requirePublishedTreeUrl('PUBLISHED-MATCHES-BUILD')).replace(/\/$/, '')
|
||||||
const TREE = resolve(REPO, `public/${TREE_NAME}`)
|
const TREE = resolve(REPO, `public/${TREE_NAME}`)
|
||||||
const MANIFEST = join(TREE, 'manifest.json')
|
const MANIFEST = join(TREE, 'manifest.json')
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -86,14 +86,14 @@ function readBuildConfig() {
|
||||||
// The data API every generated page points at, baked as window.MAP_APP_API_URL before the
|
// The data API every generated page points at, baked as window.MAP_APP_API_URL before the
|
||||||
// bundle loads — the HOT-SWAPPABLE backend seam. Empty = same-origin. Set it to the live API
|
// bundle loads — the HOT-SWAPPABLE backend seam. Empty = same-origin. Set it to the live API
|
||||||
// now (https://pray.doxa.life) and flip this ONE field to the self-hosted campaigns-server URL
|
// now (https://pray.doxa.life) and flip this ONE field to the self-hosted campaigns-server URL
|
||||||
// once it runs on go-base; every map repoints with no code change. A per-instance apiBaseUrl
|
// once it runs on the host platform; every map repoints with no code change. A per-instance apiBaseUrl
|
||||||
// in a *.instances.json still overrides this for that instance.
|
// in a *.instances.json still overrides this for that instance.
|
||||||
const apiBaseUrl = (typeof cfg.apiBaseUrl === 'string' ? cfg.apiBaseUrl.trim() : '').replace(/\/+$/, '')
|
const apiBaseUrl = (typeof cfg.apiBaseUrl === 'string' ? cfg.apiBaseUrl.trim() : '').replace(/\/+$/, '')
|
||||||
// GATED profiles: bundle folder names withheld from the generated tree, staging pages and
|
// GATED profiles: bundle folder names withheld from the generated tree, staging pages and
|
||||||
// manifest (governance — e.g. key-cities). Their .js still BUILDS via vite; they simply never
|
// manifest (governance — e.g. key-cities). Their .js still BUILDS via vite; they simply never
|
||||||
// appear on any public surface the generator emits. convert.py gates the plugin index too.
|
// appear on any public surface the generator emits. convert.py gates the plugin index too.
|
||||||
const gatedProfiles = Array.isArray(cfg.gatedProfiles) ? cfg.gatedProfiles.filter((x) => typeof x === 'string' && x.trim()).map((x) => x.trim()) : []
|
const gatedProfiles = Array.isArray(cfg.gatedProfiles) ? cfg.gatedProfiles.filter((x) => typeof x === 'string' && x.trim()).map((x) => x.trim()) : []
|
||||||
// QA corner widget baked into generated embed pages (the go-base plugin surface). Default true.
|
// QA corner widget baked into generated embed pages (the host platform's plugin surface). Default true.
|
||||||
// Partner embeds use the copy-embed SNIPPET with their own config, so they never get this.
|
// Partner embeds use the copy-embed SNIPPET with their own config, so they never get this.
|
||||||
const qaWidget = cfg.qaWidget !== false
|
const qaWidget = cfg.qaWidget !== false
|
||||||
// Ship the 42 MB print-resolution posters/ inside the tree? DEFAULT FALSE. The wall renders
|
// Ship the 42 MB print-resolution posters/ inside the tree? DEFAULT FALSE. The wall renders
|
||||||
|
|
@ -758,7 +758,7 @@ function mapPageHtml(opts) {
|
||||||
const apiScript = apiBaseUrl
|
const apiScript = apiBaseUrl
|
||||||
? `\n <script>window.MAP_APP_API_URL = ${JSON.stringify(apiBaseUrl)};</script>`
|
? `\n <script>window.MAP_APP_API_URL = ${JSON.stringify(apiBaseUrl)};</script>`
|
||||||
: ''
|
: ''
|
||||||
// Bake the QA corner widget onto the go-base embed surface (opt-out via build-config qaWidget:false).
|
// Bake the QA corner widget onto the host platform's embed surface (opt-out via build-config qaWidget:false).
|
||||||
const pageConfig = DEST_CONFIG.qaWidget ? { ...config, qa: true } : config
|
const pageConfig = DEST_CONFIG.qaWidget ? { ...config, qa: true } : config
|
||||||
// PROVIDER-AWARE PAGE (see library/composables/useMapProvider.js): a profile or
|
// PROVIDER-AWARE PAGE (see library/composables/useMapProvider.js): a profile or
|
||||||
// instance whose config says provider:'maplibre' gets the MapLibre peer (token-
|
// instance whose config says provider:'maplibre' gets the MapLibre peer (token-
|
||||||
|
|
|
||||||
|
|
@ -85,7 +85,7 @@ for (const layout of new Set(nested)) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// SUBPATH-PORTABLE imports: `../<layout>/` resolves only at the web-root (../ cannot escape
|
// SUBPATH-PORTABLE imports: `../<layout>/` resolves only at the web-root (../ cannot escape
|
||||||
// root); served under a subpath (e.g. as a go-base plugin at /plugin-assets/doxa-maps-spa/)
|
// root); served under a subpath (e.g. as a host-platform plugin at /plugin-assets/doxa-maps-spa/)
|
||||||
// it 404s. The layouts are copied as SIBLINGS of index.html, so `./<layout>/` is correct at
|
// it 404s. The layouts are copied as SIBLINGS of index.html, so `./<layout>/` is correct at
|
||||||
// EVERY depth. Rewrite the copied entry so the shell works both at root and under any subpath.
|
// EVERY depth. Rewrite the copied entry so the shell works both at root and under any subpath.
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -81,7 +81,7 @@ const LEAK_STRICT = [
|
||||||
[/doxa-life\/doxa-maps/, 'private repo name'],
|
[/doxa-life\/doxa-maps/, 'private repo name'],
|
||||||
[/\bdoxa-life\b/, 'private org name'],
|
[/\bdoxa-life\b/, 'private org name'],
|
||||||
[/\bforgejo\b/i, 'self-hosted forge name'],
|
[/\bforgejo\b/i, 'self-hosted forge name'],
|
||||||
[/\bnuxtbase\b/, 'internal platform name'],
|
[new RegExp('\\b' + ['nuxt', 'base'].join('') + '\\b'), 'internal platform name'], // built from parts so this file does not itself carry the name
|
||||||
[/\bpersecution\b/, 'operational-context vocabulary (allowed only inside data CSVs)'],
|
[/\bpersecution\b/, 'operational-context vocabulary (allowed only inside data CSVs)'],
|
||||||
[/\.forgejo\/workflows/, 'CI workflow path — process, never ships in a public tree'],
|
[/\.forgejo\/workflows/, 'CI workflow path — process, never ships in a public tree'],
|
||||||
]
|
]
|
||||||
|
|
@ -97,7 +97,7 @@ const LEAK_STRICT = [
|
||||||
// ★ pk. IS DELIBERATELY NOT HERE (Driver ruling 2026-09-21): "all maps can use the pk from the
|
// ★ pk. IS DELIBERATELY NOT HERE (Driver ruling 2026-09-21): "all maps can use the pk from the
|
||||||
// api, no problem — if they want to set a mapbox sk they can". A Mapbox `pk.` is a PUBLIC key: it
|
// api, no problem — if they want to set a mapbox sk they can". A Mapbox `pk.` is a PUBLIC key: it
|
||||||
// is designed to ship in client code and is protected by URL RESTRICTIONS, not by secrecy
|
// is designed to ship in client code and is protected by URL RESTRICTIONS, not by secrecy
|
||||||
// (measured: the same pk. serves tiles 200 with a doxa.life Referer and 403s with nuxtbase.app).
|
// (measured: the same pk. serves tiles 200 with a doxa.life Referer and 403s with the live test host's Referer).
|
||||||
// Flagging it made the token ladder's rung-3 baked token UNBUILDABLE — this gate runs inside
|
// Flagging it made the token ladder's rung-3 baked token UNBUILDABLE — this gate runs inside
|
||||||
// build:unlocked, so `pk.` in public/ failed the build that is supposed to produce it.
|
// build:unlocked, so `pk.` in public/ failed the build that is supposed to produce it.
|
||||||
// `sk.` (SECRET key) and `tk.` (temporary) stay flagged: those are real credentials.
|
// `sk.` (SECRET key) and `tk.` (temporary) stay flagged: those are real credentials.
|
||||||
|
|
|
||||||
|
|
@ -148,7 +148,7 @@ async function shot(name, path, {
|
||||||
// Verified in-browser before wiring: 8/8 tile responses 200 with the header, 403 without.
|
// Verified in-browser before wiring: 8/8 tile responses 200 with the header, 403 without.
|
||||||
// This unblocks BUG-README-SCREENSHOTS-SHOW-BLANK-BASEMAPS and
|
// This unblocks BUG-README-SCREENSHOTS-SHOW-BLANK-BASEMAPS and
|
||||||
// BUG-FOUR-README-PROOF-IMAGES-SHIP-A-BLANK-BASEMAP without needing the Driver to change the
|
// BUG-FOUR-README-PROOF-IMAGES-SHIP-A-BLANK-BASEMAP without needing the Driver to change the
|
||||||
// token's allow-list. That change is still worth making for LIVE nuxtbase.app rendering, which
|
// token's allow-list. That change is still worth making for rendering on the LIVE test host, which
|
||||||
// this does not address — a browser sets its own Referer and cannot be overridden this way.
|
// this does not address — a browser sets its own Referer and cannot be overridden this way.
|
||||||
//
|
//
|
||||||
// ★ WHY A CONTEXT-WIDE HEADER AND NOT ROUTE INTERCEPTION, measured both ways 2026-09-22:
|
// ★ WHY A CONTEXT-WIDE HEADER AND NOT ROUTE INTERCEPTION, measured both ways 2026-09-22:
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@
|
||||||
*
|
*
|
||||||
* ★ THE DEFECT THIS EXISTS FOR, hit live 2026-09-23. `bun run serve` was
|
* ★ THE DEFECT THIS EXISTS FOR, hit live 2026-09-23. `bun run serve` was
|
||||||
* `python3 -m http.server`, which resolves a DIRECTORY request to its index.html. The live
|
* `python3 -m http.server`, which resolves a DIRECTORY request to its index.html. The live
|
||||||
* nuxtbase.app static layer does NOT:
|
* test host's static layer does NOT:
|
||||||
*
|
*
|
||||||
* /plugin-assets/doxa-maps/dashboard-plus-grid/dashboard-plus-grid/ -> 404 live, 200 local
|
* /plugin-assets/doxa-maps/dashboard-plus-grid/dashboard-plus-grid/ -> 404 live, 200 local
|
||||||
* /plugin-assets/doxa-maps/dashboard-plus-grid/dashboard-plus-grid/index.html -> 200 both
|
* /plugin-assets/doxa-maps/dashboard-plus-grid/dashboard-plus-grid/index.html -> 200 both
|
||||||
|
|
@ -122,10 +122,10 @@ createServer((req, res) => {
|
||||||
// composed surfaces (cap-4/cap-5) then FETCH manifest.json + assets/ -- and fetch()
|
// composed surfaces (cap-4/cap-5) then FETCH manifest.json + assets/ -- and fetch()
|
||||||
// enforces CORS, so on any third-party host the shell mounted and every manifest-composed
|
// enforces CORS, so on any third-party host the shell mounted and every manifest-composed
|
||||||
// tab died with 'Could not load ...' (measured 2026-09-24 from a second local origin; the
|
// tab died with 'Could not load ...' (measured 2026-09-24 from a second local origin; the
|
||||||
// LIVE nuxtbase static layer has the same gap -- carded with the Traefik ready-fix).
|
// LIVE test host's static layer has the same gap -- carded with the Traefik ready-fix).
|
||||||
// ACAO:* is the standard for credential-less public assets; production parity is the
|
// ACAO:* is the standard for credential-less public assets; production parity is the
|
||||||
// whole point of this server, so it must teach the same requirement.
|
// whole point of this server, so it must teach the same requirement.
|
||||||
// LIVE_CORS=1 reproduces the LIVE static layer's rule instead (measured on nuxtbase 2026-09-25):
|
// LIVE_CORS=1 reproduces the LIVE static layer's rule instead (measured on the live test host 2026-09-25):
|
||||||
// ACAO on every path INSIDE a folder of the tree, NOT on the tree-root manifest.json. Off by
|
// ACAO on every path INSIDE a folder of the tree, NOT on the tree-root manifest.json. Off by
|
||||||
// default; check:gallery-embed --live-cors uses it to prove the gallery still boots cross-origin
|
// default; check:gallery-embed --live-cors uses it to prove the gallery still boots cross-origin
|
||||||
// under the rule partners actually get.
|
// under the rule partners actually get.
|
||||||
|
|
|
||||||
|
|
@ -37,9 +37,10 @@ import { requireThisTree } from './_serves-this-tree.mjs'
|
||||||
import { resolve as _resolvePath, dirname as _dirnamePath } from 'node:path'
|
import { resolve as _resolvePath, dirname as _dirnamePath } from 'node:path'
|
||||||
import { fileURLToPath as _fileURLToPath } from 'node:url'
|
import { fileURLToPath as _fileURLToPath } from 'node:url'
|
||||||
import { TREE_NAME } from './_tree-name.mjs'
|
import { TREE_NAME } from './_tree-name.mjs'
|
||||||
|
import { requirePublishedTreeUrl } from './_published-base.mjs'
|
||||||
|
|
||||||
const GL=['--use-gl=swiftshader','--enable-unsafe-swiftshader','--ignore-gpu-blocklist','--enable-webgl','--no-sandbox']
|
const GL=['--use-gl=swiftshader','--enable-unsafe-swiftshader','--ignore-gpu-blocklist','--enable-webgl','--no-sandbox']
|
||||||
const B=(process.env.BASE || `https://nuxtbase.app/plugin-assets/${TREE_NAME}/`).replace(/\/?$/,'/')
|
const B=(process.env.BASE || requirePublishedTreeUrl('VERIFY-5-CAPABILITIES')).replace(/\/?$/,'/')
|
||||||
const ROOT = _resolvePath(_dirnamePath(_fileURLToPath(import.meta.url)), '..')
|
const ROOT = _resolvePath(_dirnamePath(_fileURLToPath(import.meta.url)), '..')
|
||||||
|
|
||||||
// ★ MEASURE THIS BUILD, OR MEASURE NOTHING. Several worktrees run on one machine and a default port
|
// ★ MEASURE THIS BUILD, OR MEASURE NOTHING. Several worktrees run on one machine and a default port
|
||||||
|
|
|
||||||
|
|
@ -34,6 +34,7 @@ import { requireThisTree } from './_serves-this-tree.mjs'
|
||||||
import { resolve as _resolvePath, dirname as _dirnamePath } from 'node:path'
|
import { resolve as _resolvePath, dirname as _dirnamePath } from 'node:path'
|
||||||
import { fileURLToPath as _fileURLToPath } from 'node:url'
|
import { fileURLToPath as _fileURLToPath } from 'node:url'
|
||||||
import { TREE_NAME } from './_tree-name.mjs'
|
import { TREE_NAME } from './_tree-name.mjs'
|
||||||
|
import { requirePublishedTreeUrl } from './_published-base.mjs'
|
||||||
|
|
||||||
// The SwiftShader launch. Without these a headless Chromium here has NO GL context at all.
|
// The SwiftShader launch. Without these a headless Chromium here has NO GL context at all.
|
||||||
const GL_ARGS = [
|
const GL_ARGS = [
|
||||||
|
|
@ -44,7 +45,7 @@ const GL_ARGS = [
|
||||||
'--no-sandbox',
|
'--no-sandbox',
|
||||||
]
|
]
|
||||||
|
|
||||||
const BASE = process.env.BASE || `https://nuxtbase.app/plugin-assets/${TREE_NAME}`
|
const BASE = process.env.BASE || requirePublishedTreeUrl('VERIFY-MAP-PAINT')
|
||||||
const ROOT = _resolvePath(_dirnamePath(_fileURLToPath(import.meta.url)), '..')
|
const ROOT = _resolvePath(_dirnamePath(_fileURLToPath(import.meta.url)), '..')
|
||||||
|
|
||||||
// ★ MEASURE THIS BUILD, OR MEASURE NOTHING. Several worktrees run on one machine and a default port
|
// ★ MEASURE THIS BUILD, OR MEASURE NOTHING. Several worktrees run on one machine and a default port
|
||||||
|
|
@ -121,7 +122,7 @@ console.log('MAP-PAINT: GL ok —', gl.version, '/', gl.renderer)
|
||||||
* ★ SAY WHICH TREE WAS MEASURED (2026-09-25) — this gate defaults to the DEPLOYED site, and a
|
* ★ SAY WHICH TREE WAS MEASURED (2026-09-25) — this gate defaults to the DEPLOYED site, and a
|
||||||
* failure line that does not say so reads as "your code is broken" when it means "your code is
|
* failure line that does not say so reads as "your code is broken" when it means "your code is
|
||||||
* not deployed yet". MEASURED TWICE IN ONE DAY, both times wrongly: resources-map and
|
* not deployed yet". MEASURED TWICE IN ONE DAY, both times wrongly: resources-map and
|
||||||
* connectivity-map both reported FAIL ("the stylesheet is ABSENT here") against nuxtbase.app
|
* connectivity-map both reported FAIL ("the stylesheet is ABSENT here") against the live test host
|
||||||
* while the fix for exactly that defect was sitting, verified, on disk. Served locally, the
|
* while the fix for exactly that defect was sitting, verified, on disk. Served locally, the
|
||||||
* same gate passed all 13 leaves.
|
* same gate passed all 13 leaves.
|
||||||
*
|
*
|
||||||
|
|
|
||||||
|
|
@ -28,11 +28,11 @@
|
||||||
"from the api, no problem \u2014 if they want to set a mapbox sk they can'. This is token-ladder",
|
"from the api, no problem \u2014 if they want to set a mapbox sk they can'. This is token-ladder",
|
||||||
"RUNG 3 (see internal/generate-tree.mjs:595): rungs 1-3 are used DIRECTLY with no network call,",
|
"RUNG 3 (see internal/generate-tree.mjs:595): rungs 1-3 are used DIRECTLY with no network call,",
|
||||||
"so baking it makes every map work out of the box on ANY host \u2014 including one with no",
|
"so baking it makes every map work out of the box on ANY host \u2014 including one with no",
|
||||||
"/api/maps/token endpoint and no CORS on the fallback, which is exactly why nuxtbase.app maps",
|
"/api/maps/token endpoint and no CORS on the fallback, which is exactly why maps on the live test host",
|
||||||
"were blank. SOURCE OF TRUTH: https://doxa.life/api/maps/token (see",
|
"were blank. SOURCE OF TRUTH: https://doxa.life/api/maps/token (see",
|
||||||
"_qa-cycle/01-ideation-research/REFERENCE-mapbox-token-endpoint.md). Re-fetch from there rather",
|
"_qa-cycle/01-ideation-research/REFERENCE-mapbox-token-endpoint.md). Re-fetch from there rather",
|
||||||
"than copying this literal around. A pk. is a PUBLIC key protected by URL restrictions, not by",
|
"than copying this literal around. A pk. is a PUBLIC key protected by URL restrictions, not by",
|
||||||
"secrecy \u2014 never put an sk. here; check:public-hygiene still fails the build on sk./tk."
|
"secrecy \u2014 never put an sk. here; check:public-hygiene still fails the build on sk./tk."
|
||||||
],
|
],
|
||||||
"//tokenUrl-2026-09-22": "EMPTIED 2026-09-22. It pointed at https://doxa.life/api/maps/token, which serves a URL-RESTRICTED token \u2014 tiles 403 from every origin except doxa.life, so the basemap rendered as an empty void while pins drew fine (the Driver's exact report: 'it's loading the pins, but it's not loading the actual underlying map'). That endpoint sits ABOVE the baked token in the ladder, so leaving it set would fetch the broken token and overwrite the good one. Measured: the baked token = 200 from no-Referer, nuxtbase.app AND localhost; the domain-restricted token = 403 from all three."
|
"//tokenUrl-2026-09-22": "EMPTIED 2026-09-22. It pointed at https://doxa.life/api/maps/token, which serves a URL-RESTRICTED token \u2014 tiles 403 from every origin except doxa.life, so the basemap rendered as an empty void while pins drew fine (the Driver's exact report: 'it's loading the pins, but it's not loading the actual underlying map'). That endpoint sits ABOVE the baked token in the ladder, so leaving it set would fetch the broken token and overwrite the good one. Measured: the baked token = 200 from no-Referer, the live test host AND localhost; the domain-restricted token = 403 from all three."
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue