Merge branch 'main' of https://git.nuxtbase.app/doxa-life/doxa-maps into bundler-3/work
Some checks are pending
build-gate / build (push) Waiting to run

This commit is contained in:
doxa-maps-88655c70 2026-09-24 23:01:56 +00:00
commit 31b934bdf5
15 changed files with 464 additions and 194 deletions

View file

@ -58,9 +58,34 @@ for (const f of files) {
}
}
// ★ SAY HOW MANY ZONES THIS ACTUALLY COVERED. "every generated packet renders" is true and reads
// like "every packet is fine" — but only the zones someone generated are ever here. MEASURED
// 2026-09-24: 11 packets, ALL of them the SAME zone (laos) in 11 locales, against 28 poster zones
// in out/posters. So the suite proved one zone's layout 11 times and had never rendered the other
// 27, any of which could carry a different data shape (fewer groups, a missing photo, an
// unresolvable map). A silent cap reads as full coverage; naming it is the difference between
// "verified" and "verified what happened to be lying around".
// NOT a failure — generating 28 zones x 11 locales means a network fetch per zone and ~100MB of
// artefacts, which is a deliberate trade, not an oversight. The gate's job here is to stop the
// PASS line from overclaiming.
const zoneOf = (f) => f.replace(/\.html$/, '').replace(/\.[a-z]{2}$/, '')
const zones = [...new Set(files.map(zoneOf))].sort()
let totalZones = null
try {
const postersDir = resolve(REPO, 'library/scripts/posters/out/posters')
if (existsSync(postersDir)) totalZones = readdirSync(postersDir).filter((f) => f.endsWith('.html')).length
} catch { /* the denominator is a nicety; its absence must not fail the gate */ }
console.log(`\nPACKETS-ALL: ${files.length} packet(s) checked, ${files.length - failed.length} passed`)
console.log(` zones covered : ${zones.length}${totalZones ? ` of ${totalZones}` : ''} — ${zones.join(', ')}`)
if (totalZones && zones.length < totalZones) {
console.log(` ⚠ ${totalZones - zones.length} zone(s) have NO packet here and were not rendered by`)
console.log(' anything. Generate another with: bun run packet:ci (one zone, committed fixture,')
console.log(' no network), or ONLY=<zone> node library/scripts/posters/packet.mjs.')
}
if (failed.length) {
console.error(`PACKETS-ALL: FAIL — ${failed.length} packet(s): ${failed.join(', ')}`)
process.exit(1)
}
console.log('PACKETS-ALL: PASS — every generated packet renders, in every locale present.')
console.log(`PACKETS-ALL: PASS — every generated packet renders, across ${zones.length} zone(s) `
+ 'and every locale present. This does NOT cover zones no one generated.')

View file

@ -0,0 +1,140 @@
#!/usr/bin/env node
/**
* check-published-matches-build — what readers are served must be what we built.
*
* THE DEFECT THIS CATCHES, and it has cost more agent-hours than any bug in the tree.
* Every gate in this repo measures the LOCAL build. Nothing measured the PUBLISHED one. So the
* whole board can be green for weeks while readers are served a months-old bundle, and the only
* detector is a human noticing something looks wrong.
*
* MEASURED 2026-09-24: live my-upg-100-list.js was 485,665 bytes against 678,837 built — 193KB and
* many merges behind. Live LACKED the legend tab labels, LACKED the a11y keys, LACKED
* v3.24.0/mapbox-gl.css and still HAD v3.3.0. Every other bundle was stale too (country-posters
* -194KB, research-dashboard -98KB, gospel-resources larger than local, i.e. not even the same
* lineage).
*
* ★ THE EXPENSIVE PART IS NOT THE STALENESS, IT IS THE PHANTOM BUGS. A stale publish manufactures
* bug reports that cannot be reproduced. Three separate reports this week — print buttons
* truncated to "Pr...", the legend showing "Generation 1/2/3" instead of the real tab names, the
* pin hint saying "red dot" instead of "coloured dot" — were ALL fixed in main and ALL still
* visible live. Each read as a regression against a card marked done. Two of them consumed most of
* a tick to diagnose, and the diagnosis was the same sentence every time.
*
* ⚠ IT COMPARES THE SRI HASH, NOT THE BYTE SIZE. manifest.json already carries a sha384 per
* bundle, which is the identity the browser itself enforces; two different builds can coincide in
* length, and a size check would call that a match. Size is reported only to make a mismatch
* legible ("193KB behind" reads better than two hashes).
*
* ⚠ IT IS A NETWORK GATE, so it SKIPs rather than fails when the published host is unreachable.
* A gate that goes red on someone's flaky wifi teaches people to ignore it. Absence of evidence is
* reported as a SKIP with the reason.
*
* Usage: node internal/check-published-matches-build.mjs [bundle ...]
* PUBLISHED_BASE=<url> where the published tree lives
* Defaults to every bundle in the local manifest.
*/
import { readFileSync, existsSync, statSync } from 'node:fs'
import { createHash } from 'node:crypto'
import { resolve, dirname, join } from 'node:path'
import { fileURLToPath } from 'node:url'
const HERE = dirname(fileURLToPath(import.meta.url))
const REPO = process.env.DOXA_REPO || resolve(HERE, '..')
const PUBLISHED_BASE = (process.env.PUBLISHED_BASE
|| 'https://nuxtbase.app/plugin-assets/doxa-maps').replace(/\/$/, '')
const TREE = resolve(REPO, 'public/doxa-maps')
const MANIFEST = join(TREE, 'manifest.json')
if (!existsSync(MANIFEST)) {
console.log(`PUBLISHED-MATCHES-BUILD: SKIP — no local manifest at ${MANIFEST}.`)
console.log(' Build the tree first (bun run tree). Absence is not a pass.')
process.exit(0)
}
const manifest = JSON.parse(readFileSync(MANIFEST, 'utf8'))
const all = (manifest.bundles || []).filter((b) => b.bundle && b.output)
const picked = process.argv.slice(2).length
? all.filter((b) => process.argv.slice(2).includes(b.bundle))
: all
if (!picked.length) {
console.log('PUBLISHED-MATCHES-BUILD: SKIP — no bundles selected from the manifest.')
process.exit(0)
}
const sri = (buf) => 'sha384-' + createHash('sha384').update(buf).digest('base64')
const kb = (n) => (n / 1024).toFixed(0) + 'KB'
// One reachability probe before the sweep. If the host is down we SKIP once, rather than
// reporting every bundle as a separate failure of the same cause.
let hostUp = false
try {
const r = await fetch(PUBLISHED_BASE + '/manifest.json', { signal: AbortSignal.timeout(15000) })
hostUp = r.ok || r.status === 404 // 404 still proves the host answers
} catch { /* handled below */ }
if (!hostUp) {
console.log(`PUBLISHED-MATCHES-BUILD: SKIP — ${PUBLISHED_BASE} is not answering.`)
console.log(' This gate needs the network. Unreachable is NOT a pass: it means the published')
console.log(' tree was never compared, so a stale publish would go unnoticed right now.')
process.exit(0)
}
console.log('PUBLISHED-MATCHES-BUILD')
console.log(` published : ${PUBLISHED_BASE}`)
console.log(` built : ${TREE}`)
const stale = []
const unreachable = []
let matched = 0
for (const b of picked) {
const localPath = resolve(REPO, 'public', b.output)
if (!existsSync(localPath)) { unreachable.push(`${b.bundle}: no local build at ${b.output}`); continue }
const localBuf = readFileSync(localPath)
const localHash = sri(localBuf)
const url = `${PUBLISHED_BASE}/${b.bundle}/${b.bundle}.js`
let liveBuf = null
try {
const r = await fetch(url, { signal: AbortSignal.timeout(30000) })
if (!r.ok) { unreachable.push(`${b.bundle}: published copy returned HTTP ${r.status}`); continue }
liveBuf = Buffer.from(await r.arrayBuffer())
} catch (e) {
unreachable.push(`${b.bundle}: fetch failed — ${String(e.message).slice(0, 60)}`)
continue
}
const liveHash = sri(liveBuf)
if (liveHash === localHash) {
matched++
console.log(` ✓ ${b.bundle} — published copy is the built copy (${kb(localBuf.length)})`)
} else {
const delta = liveBuf.length - localBuf.length
console.log(` ✗ ${b.bundle} — published ${kb(liveBuf.length)} vs built ${kb(localBuf.length)}`
+ ` (${delta > 0 ? '+' : ''}${kb(delta)})`)
stale.push({ bundle: b.bundle, live: liveBuf.length, local: localBuf.length, delta })
}
}
console.log(`\n ${matched} of ${picked.length} bundle(s) match; ${stale.length} stale; `
+ `${unreachable.length} could not be compared`)
for (const u of unreachable) console.log(` · ${u}`)
if (stale.length) {
console.error(`\nPUBLISHED-MATCHES-BUILD: FAIL — ${stale.length} published bundle(s) are NOT what we built.`)
for (const s of stale) {
console.error(` ✗ ${s.bundle}: readers get ${kb(s.live)}, we built ${kb(s.local)}`
+ ` (${s.delta > 0 ? '+' : ''}${kb(s.delta)})`)
}
console.error('\n Every other gate in this repo measures the LOCAL tree, so they stay green while')
console.error(' readers are served something else. The expensive part is not the staleness: it is')
console.error(' that a stale publish MANUFACTURES BUG REPORTS THAT CANNOT BE REPRODUCED. Three')
console.error(' reports this week (truncated print buttons, "Generation 1/2/3" legend tabs, the')
console.error(' "red dot" pin hint) were all fixed in main and all still visible live, and each')
console.error(' one read as a regression against a card marked done.')
console.error('\n FIX: publish. That is a COORDINATOR action — deploy:dev runs a full build whose')
console.error(' first step deletes public/doxa-maps/ and tears the tree out from under every')
console.error(' other agent, which is why the build guard refuses it without BUILD_ALLOW=1.')
process.exit(1)
}
console.log('\nPUBLISHED-MATCHES-BUILD: PASS — every compared bundle is byte-identical to the build.')

View file

@ -291,6 +291,21 @@ export function useMapInstance({
provider = undefined,
center = [20, 10],
zoom = 2,
// ★ bounds / fitBoundsOptions: frame a BOX at construction instead of a centre+zoom.
// Mapbox honours these only in the constructor, and that is the whole point of exposing them
// rather than letting a caller do `fitBounds` after style load. MEASURED 2026-09-24 on one page,
// one box, one engine (v3.24.0), via _probe-fit-at-construction-vs-after-load.mjs:
// constructor bounds -> zoom 3.210 (getProjection() reports 'mercator' at t=0)
// fitBounds post-load -> zoom 2.777 (getProjection() reports 'globe')
// Same box, same padding, same maxZoom, same 1200x780 frame: 0.433 zoom apart, which is 1.35x
// linear scale. Mapbox v3 resolves a constructor `bounds` while the camera is still mercator and
// applies the style's globe afterwards, so the camera keeps the tighter mercator fit. A post-load
// fitBounds is resolved with the globe already active and pulls BACK, because a sphere shows more
// planet at a given zoom. Neither is a bug of ours; they are two different framings, and a clone
// has to take the one the reference page takes. Ignored by Mapbox when absent, so this is inert
// for every existing caller.
bounds = undefined,
fitBoundsOptions = undefined,
// minZoom: a profile may PIN a value (e.g. 0.5). It is treated as a LOWER
// bound only — the composable raises it to the aspect-aware floor whenever the
// viewport is wide enough that the pinned value would reveal a duplicate world.
@ -454,6 +469,8 @@ export function useMapInstance({
bearing,
dragRotate: false,
renderWorldCopies,
// bounds OVERRIDE center/zoom in Mapbox's own option handling, so both can be passed.
...(bounds ? { bounds, ...(fitBoundsOptions ? { fitBoundsOptions } : {}) } : {}),
...(maxBounds ? { maxBounds } : {}),
attributionControl: true, // Mapbox's own attribution on every map
// Phones: logo and attribution both bottom-right, so the bottom-left corner stays free
@ -487,6 +504,8 @@ export function useMapInstance({
// The single-finger page-scroll escape is now the gray side-edge affordance
// wired below (showEdgeBorders): the .map-scroll-edge gutters flash gray while
// touching the map and are scrollable to move the page past the map.
// bounds OVERRIDE center/zoom in Mapbox's own option handling, so both can be passed.
...(bounds ? { bounds, ...(fitBoundsOptions ? { fitBoundsOptions } : {}) } : {}),
...(maxBounds ? { maxBounds } : {}), // only if a profile explicitly supplied bounds
attributionControl: true, // Mapbox's own attribution on every map
// Phones: logo and attribution both bottom-right, so the bottom-left corner stays free

View file

@ -177,7 +177,8 @@
"check:poster-print-fallback": "node internal/check-poster-print-fallback.mjs # 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:packet-strings-parity": "node internal/check-packet-strings-parity.mjs # the PACKET catalog (packet-strings.json) must be whole in every language: all string keys, all 12 prayer points with every field, all 12 wheel rhythms. check:i18n-parity covers library/i18n/locales only — six gates READ this file and none checked it. Asserts PRESENCE, not translatedness, with point4_passage declared-absent on record.",
"check:poster-degrades": "node internal/check-poster-degrades-honestly.mjs # 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:upg-map-data": "node internal/check-upg-map-data-painted.mjs # the UPG-100 map must carry OUR polygons/zones/pins, not just a basemap — reads the counter the profile itself writes. Pass live URLs to audit what is published."
"check:upg-map-data": "node internal/check-upg-map-data-painted.mjs # the UPG-100 map must carry OUR polygons/zones/pins, not just a basemap — reads the counter the profile itself writes. Pass live URLs to audit what is published.",
"check:published": "node internal/check-published-matches-build.mjs # 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)."
},
"devDependencies": {
"@capacitor/android": "^7.6.9",

View file

@ -118,6 +118,9 @@ const geojson = computed(() => {
}
})
// Their fitBoundsOptions, one constant so the constructor path and fitTheirWay cannot drift apart.
const FIT_OPTS = { padding: 40, maxZoom: 6 } // CountryMap.vue:179, verbatim
const { map, isMapReady, initializeMap, destroy } = useMapInstance({
containerRef: mapContainer,
// A STRING, not the ref -- useMapInstance calls accessToken.startsWith('pk.') directly, so a
@ -130,6 +133,12 @@ const { map, isMapReady, initializeMap, destroy } = useMapInstance({
center: props.center,
zoom: props.zoom,
maxZoom: 12,
// THEIR CONSTRUCTOR FORM (CountryMap.vue:179), for the case where the box is already known at
// mount. Mapbox honours `bounds` only in the constructor, and the dashboard usually mounts its
// one map BEFORE the fetch resolves, so this covers the deep-link case and fitTheirWay() below
// covers the normal one. Both land the same camera on purpose.
bounds: props.bounds || undefined,
fitBoundsOptions: FIT_OPTS,
// THE SHARED EDGE-SCROLL STRIPS, on by Driver decision (2026-09-24, via shepherd card
// BUILD-REGIONS-MAP-RESTORE-EDGE-SCROLL-STRIPS). This was deliberately false, and the reason is
// kept here rather than deleted because it is the thing to re-examine if the result looks wrong:
@ -381,6 +390,48 @@ function chooserZoom(m) {
return Math.max(0, Math.min(2, Math.log2(w / 512)))
}
/**
* Frame `bounds` the way THEIR CONSTRUCTOR frames it, not the way a post-load fitBounds does.
*
* ★ MEASURED 2026-09-24, one page, one box, one engine (mapbox-gl 3.24.0 on both sides), by
* _qa-cycle/02-builder-cycle/_probe-fit-at-construction-vs-after-load.mjs:
*
* new mapboxgl.Map({ bounds, fitBoundsOptions }) -> zoom 3.210, getProjection() 'mercator' at t=0
* map.fitBounds(bounds, sameOptions) -> zoom 2.777, getProjection() 'globe'
*
* Same box [[-16.683,-33.976],[57.497,16.769]], same padding 40, same maxZoom 6, same 1200x780
* frame. 0.433 zoom apart == 1.35x linear scale, which IS the difference the Africa crops showed
* and the reason their frame cuts the Sahel while ours had room for Madagascar and Sri Lanka.
*
* The cause is not the box and not the options: Mapbox v3 resolves a constructor `bounds` while
* the camera is still mercator and applies the style's globe afterwards, so that camera keeps the
* tighter mercator fit forever. A post-load fitBounds is resolved with the globe already active,
* and a sphere shows more planet at the same zoom, so it pulls back.
*
* So the fix is to ask the engine the SAME question their constructor asks -- compute the camera
* with the projection set to mercator, then put the globe back and jump there. Asking the engine
* rather than reimplementing a Web Mercator fit keeps Mapbox's own padding and worldSize
* semantics, which a hand-rolled version would drift from at the next upgrade.
*
* Both setProjection calls land in ONE synchronous tick, so no frame renders in mercator; the
* probe asserts the projection is still 'globe' afterwards rather than trusting that.
*/
function fitTheirWay(m, bounds) {
let cam = null
const proj = (m.getProjection && m.getProjection().name) || null
try {
if (proj && proj !== 'mercator') m.setProjection('mercator')
cam = m.cameraForBounds(bounds, FIT_OPTS)
} catch (e) {
console.warn('[RegionCountryMap] mercator fit unavailable, falling back to fitBounds:', e && e.message)
} finally {
// Restore in `finally` so a throw inside cameraForBounds can never leave the map flat.
try { if (proj && proj !== 'mercator') m.setProjection(proj) } catch (_) {}
}
if (cam && cam.center && typeof cam.zoom === 'number') m.easeTo({ center: cam.center, zoom: cam.zoom, duration: 600 })
else m.fitBounds(bounds, { ...FIT_OPTS, duration: 600 })
}
function applySubject() {
const m = map.value
// Deliberately NOT gated on isMapReady: that flag tracks useMapInstance's own view of the
@ -415,7 +466,7 @@ function applySubject() {
// Expose the box we asked to fit, so a probe can ask the engine what IT would have chosen for
// the same box and tell 'the fit is wrong' apart from 'something overrode the fit'.
if (props.bounds && typeof window !== 'undefined') window.__rcmBounds = props.bounds
if (props.bounds) m.fitBounds(props.bounds, { padding: 40, maxZoom: 6, duration: 600 })
if (props.bounds) fitTheirWay(m, props.bounds)
else m.flyTo({ center: props.center, zoom: chooserZoom(m), duration: 600 })
}
@ -511,21 +562,55 @@ useShadowStyles(
'touch-action:pan-y;pointer-events:auto;background:transparent}' +
'.map-scroll-edge--left{left:0}' +
'.map-scroll-edge--right{right:0}' +
// ★ THEIR GLOBAL BOX-SIZING RESET, which no scoped style could ever have shown me.
// app/assets/styles/base/_reset.scss:3-8 is `*, *::before, *::after { box-sizing: border-box }`
// (a piccalil.li reset) and it applies to every element on their page, the legend swatch
// included. Our leaf document already carries the same rule -- regions-dashboard/index.html:9 --
// but this component renders inside a SHADOW ROOT, and a document-level `*` selector does not
// cross a shadow boundary. So every element in here was content-box while theirs was border-box.
// MEASURED: their legend swatch is `width:10px; border:1px solid #fff`, which is 10px under
// border-box and 12px under content-box. Two swatch columns of 1px each are exactly the 2px by
// which our legend panel measured wider than theirs (167px vs 165px) after the rest of the
// legend was aligned to the pixel -- and the same string measures 129px in both, in the same
// Poppins, so the text was never the cause.
// This is the SEVENTH global rule of theirs found missing, and the reason the codebase had
// accumulated seven separate per-element `box-sizing:border-box` patches: each one was a local
// workaround for the reset that never arrived. Scoped to `.rcm` rather than the whole shadow
// root so it is verified where it is measured; porting it bundle-wide and DELETING those seven
// patches is carded (BUILD-PORT-THEIR-BOX-SIZING-RESET-TO-THE-SHADOW-ROOT) because it changes
// geometry in components whose match against live has to be re-measured, not assumed.
'.rcm, .rcm *, .rcm *::before, .rcm *::after{ box-sizing:border-box; }' +
'.rcm{ display:block; position:relative; width:100%; min-height:780px; aspect-ratio:16/9;' +
' overflow:hidden; border-radius:inherit; }' +
'@media (max-width:768px){ .rcm{ min-height:0; aspect-ratio:1/2; } }' +
'.rcm__canvas{ position:absolute; inset:0; }' +
'.rcm__note{ position:absolute; inset:0; display:flex; align-items:center; justify-content:center;' +
' text-align:center; padding:24px; color:#6d766e; font-size:15.625px; background:#f3f3f1; }' +
// Their key, overlaid bottom-left above the canvas (CountryMap.vue's .country-map__legend).
// Logical, so the chooser legend moves to the reading-start corner in Arabic. This is one
// of the sanctioned additions rather than a clone of theirs, but a legend pinned to the
// THEIR key, overlaid bottom-left above the canvas -- CountryMap.vue's .country-map__legend,
// now copied property-for-property. An earlier comment here called this "one of the sanctioned
// additions"; that was WRONG and is why it drifted. Their CountryMap.vue carries this legend with
// these same four entries, so it is a CLONE and every property is theirs to dictate. Caught by
// looking at the side-by-side Africa crops after the framing fix: our rows sat a few px off and
// the box read differently. Diffing our block against theirs found six differences, none of which
// any computed-CSS probe was covering -- font-size 12.5px vs their 12px (a rounded-token artifact
// of mine, not their scale), colour #2a332a vs their #1f1f1f, and four properties simply missing:
// the panel's box-shadow, pointer-events:none, and the swatch's display, border and ring.
// The swatch border is the one with layout consequences: a 10px dot inside a 1px border is 12px
// tall, so dropping it shortened every row and moved the whole stack -- which is exactly the
// drift the crop showed.
// THE ONE DEVIATION, deliberate and documented: inset-inline-start instead of their physical
// `left`, so the legend sits in the reading-start corner in Arabic. A legend pinned to the
// physical left in an RTL layout reads as broken regardless of whose design it is.
'.rcm__legend{ position:absolute; inset-inline-start:10px; bottom:28px; z-index:2; display:flex;' +
' flex-direction:column; gap:4px; padding:8px 10px; border-radius:8px;' +
' background:rgba(255,255,255,0.92); font-size:12.5px; font-weight:600; color:#2a332a; }' +
' background:rgba(255,255,255,0.92); box-shadow:0 2px 8px rgba(0,0,0,0.15);' +
' font-size:12px; font-weight:600; color:#1f1f1f; pointer-events:none; }' +
'.rcm__legend span{ display:inline-flex; align-items:center; gap:6px; }' +
'.rcm__legend i{ width:10px; height:10px; border-radius:100px; flex:0 0 auto; }' +
// `flex:0 0 auto` used to guard the swatch against shrinking. Their border/ring pair makes the
// box 12px and theirs does not shrink, so the guard is deleted rather than kept alongside a
// faithful copy -- deleting a workaround beats adding one. The re-cropped legend is the check.
'.rcm__legend i{ display:inline-block; width:10px; height:10px; border-radius:100px;' +
' border:1px solid #ffffff; box-shadow:0 0 0 1px rgba(0,0,0,0.15); }' +
// ── THEIR PIN POPUP'S CSS, ported verbatim from CountryMap.vue's scoped block ──────────────
// Mapbox builds the popup inside the map container, which is inside this shadow root, so these

View file

@ -29,7 +29,7 @@
<script>window.MAP_APP_API_URL = "https://pray.doxa.life";</script>
<!-- Bundle IIFE — registers the <country-posters-map> custom element. -->
<script src="../country-posters.js" integrity="sha384-8fQR1xHoPidgeI7VU/Z82bxA303lsM/UZXfsgRu8MZG/Z5ASzgiel/am7dHiO2kH" crossorigin="anonymous"></script>
<script src="../country-posters.js" integrity="sha384-+miFg4jJ9ffz3j/bWOVAozmvoj7APOdJxIl0WmOWrSMDbutivwv8gRBy43qL6Q5b" crossorigin="anonymous"></script>
<script>
// Mapbox token resolution — the A3 ladder, in precedence order:

View file

@ -26,7 +26,7 @@
<script>window.MAP_APP_API_URL = "https://pray.doxa.life";</script>
<!-- Bundle IIFE — registers the <country-posters-map> custom element. -->
<script src="../country-posters.js" integrity="sha384-8fQR1xHoPidgeI7VU/Z82bxA303lsM/UZXfsgRu8MZG/Z5ASzgiel/am7dHiO2kH" crossorigin="anonymous"></script>
<script src="../country-posters.js" integrity="sha384-+miFg4jJ9ffz3j/bWOVAozmvoj7APOdJxIl0WmOWrSMDbutivwv8gRBy43qL6Q5b" crossorigin="anonymous"></script>
<script>
// Mapbox token resolution — the A3 ladder, in precedence order:

View file

@ -40,7 +40,7 @@
<script>window.MAP_APP_API_URL = "https://pray.doxa.life";</script>
<!-- Bundle IIFE — registers the <country-posters-map> custom element. -->
<script src="../country-posters.js" integrity="sha384-8fQR1xHoPidgeI7VU/Z82bxA303lsM/UZXfsgRu8MZG/Z5ASzgiel/am7dHiO2kH" crossorigin="anonymous"></script>
<script src="../country-posters.js" integrity="sha384-+miFg4jJ9ffz3j/bWOVAozmvoj7APOdJxIl0WmOWrSMDbutivwv8gRBy43qL6Q5b" crossorigin="anonymous"></script>
<script>
// Mapbox token resolution — the A3 ladder, in precedence order:

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -158,7 +158,7 @@
"bundle": "country-posters",
"scriptUrl": "./country-posters/country-posters.js",
"output": "doxa-maps/country-posters/country-posters.js",
"integrity": "sha384-8fQR1xHoPidgeI7VU/Z82bxA303lsM/UZXfsgRu8MZG/Z5ASzgiel/am7dHiO2kH",
"integrity": "sha384-+miFg4jJ9ffz3j/bWOVAozmvoj7APOdJxIl0WmOWrSMDbutivwv8gRBy43qL6Q5b",
"elements": [
"country-posters-map"
],
@ -178,7 +178,7 @@
"scriptUrl": "./country-posters/country-posters.js",
"embed": {
"iframe": "<iframe src=\"./country-posters/countries-map/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen></iframe>",
"script": "<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./country-posters/country-posters.js\" integrity=\"sha384-8fQR1xHoPidgeI7VU/Z82bxA303lsM/UZXfsgRu8MZG/Z5ASzgiel/am7dHiO2kH\" crossorigin=\"anonymous\"></script>\n<country-posters-map profile-config='{\"profile\":\"countries-map\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></country-posters-map>"
"script": "<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./country-posters/country-posters.js\" integrity=\"sha384-+miFg4jJ9ffz3j/bWOVAozmvoj7APOdJxIl0WmOWrSMDbutivwv8gRBy43qL6Q5b\" crossorigin=\"anonymous\"></script>\n<country-posters-map profile-config='{\"profile\":\"countries-map\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></country-posters-map>"
}
},
{
@ -192,7 +192,7 @@
"scriptUrl": "./country-posters/country-posters.js",
"embed": {
"iframe": "<iframe src=\"./country-posters/country-poster-wall/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen></iframe>",
"script": "<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./country-posters/country-posters.js\" integrity=\"sha384-8fQR1xHoPidgeI7VU/Z82bxA303lsM/UZXfsgRu8MZG/Z5ASzgiel/am7dHiO2kH\" crossorigin=\"anonymous\"></script>\n<country-posters-map profile-config='{\"profile\":\"country-poster-wall\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></country-posters-map>"
"script": "<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./country-posters/country-posters.js\" integrity=\"sha384-+miFg4jJ9ffz3j/bWOVAozmvoj7APOdJxIl0WmOWrSMDbutivwv8gRBy43qL6Q5b\" crossorigin=\"anonymous\"></script>\n<country-posters-map profile-config='{\"profile\":\"country-poster-wall\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></country-posters-map>"
}
},
{
@ -206,7 +206,7 @@
"scriptUrl": "./country-posters/country-posters.js",
"embed": {
"iframe": "<iframe src=\"./country-posters/country-posters-mfe/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen></iframe>",
"script": "<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./country-posters/country-posters.js\" integrity=\"sha384-8fQR1xHoPidgeI7VU/Z82bxA303lsM/UZXfsgRu8MZG/Z5ASzgiel/am7dHiO2kH\" crossorigin=\"anonymous\"></script>\n<country-posters-map profile-config='{\"profile\":\"country-posters-mfe\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></country-posters-map>"
"script": "<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./country-posters/country-posters.js\" integrity=\"sha384-+miFg4jJ9ffz3j/bWOVAozmvoj7APOdJxIl0WmOWrSMDbutivwv8gRBy43qL6Q5b\" crossorigin=\"anonymous\"></script>\n<country-posters-map profile-config='{\"profile\":\"country-posters-mfe\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></country-posters-map>"
}
}
]
@ -879,7 +879,7 @@
"bundle": "regions-dashboard",
"scriptUrl": "./regions-dashboard/regions-dashboard.js",
"output": "doxa-maps/regions-dashboard/regions-dashboard.js",
"integrity": "sha384-Jvx1V3YrtL3L0p6eqByA/1hw4IWz6NpLp7Uza6fbl3HZCVa7JiAR5WMFvYsN6tPP",
"integrity": "sha384-yxWIwZyBb6+xve6pVbhxlLEJn4diVUhzV0fZReHWRYgdIb8U0OZUrAi7SEu9DnpI",
"elements": [
"regions-dashboard"
],
@ -899,7 +899,7 @@
"scriptUrl": "./regions-dashboard/regions-dashboard.js",
"embed": {
"iframe": "<iframe src=\"./regions-dashboard/region-dashboards/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen></iframe>",
"script": "<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./regions-dashboard/regions-dashboard.js\" integrity=\"sha384-Jvx1V3YrtL3L0p6eqByA/1hw4IWz6NpLp7Uza6fbl3HZCVa7JiAR5WMFvYsN6tPP\" crossorigin=\"anonymous\"></script>\n<regions-dashboard profile-config='{\"profile\":\"region-dashboards\",\"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-yxWIwZyBb6+xve6pVbhxlLEJn4diVUhzV0fZReHWRYgdIb8U0OZUrAi7SEu9DnpI\" crossorigin=\"anonymous\"></script>\n<regions-dashboard profile-config='{\"profile\":\"region-dashboards\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></regions-dashboard>"
}
},
{
@ -913,7 +913,7 @@
"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": "<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./regions-dashboard/regions-dashboard.js\" integrity=\"sha384-Jvx1V3YrtL3L0p6eqByA/1hw4IWz6NpLp7Uza6fbl3HZCVa7JiAR5WMFvYsN6tPP\" 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-yxWIwZyBb6+xve6pVbhxlLEJn4diVUhzV0fZReHWRYgdIb8U0OZUrAi7SEu9DnpI\" 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>"
}
},
{
@ -927,7 +927,7 @@
"scriptUrl": "./regions-dashboard/regions-dashboard.js",
"embed": {
"iframe": "<iframe src=\"./regions-dashboard/regions-mfe/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen></iframe>",
"script": "<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./regions-dashboard/regions-dashboard.js\" integrity=\"sha384-Jvx1V3YrtL3L0p6eqByA/1hw4IWz6NpLp7Uza6fbl3HZCVa7JiAR5WMFvYsN6tPP\" crossorigin=\"anonymous\"></script>\n<regions-dashboard profile-config='{\"profile\":\"regions-mfe\",\"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-yxWIwZyBb6+xve6pVbhxlLEJn4diVUhzV0fZReHWRYgdIb8U0OZUrAi7SEu9DnpI\" crossorigin=\"anonymous\"></script>\n<regions-dashboard profile-config='{\"profile\":\"regions-mfe\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></regions-dashboard>"
}
}
]

View file

@ -29,7 +29,7 @@
<script>window.MAP_APP_API_URL = "https://pray.doxa.life";</script>
<!-- Bundle IIFE — registers the <regions-dashboard> custom element. -->
<script src="../regions-dashboard.js" integrity="sha384-Jvx1V3YrtL3L0p6eqByA/1hw4IWz6NpLp7Uza6fbl3HZCVa7JiAR5WMFvYsN6tPP" crossorigin="anonymous"></script>
<script src="../regions-dashboard.js" integrity="sha384-yxWIwZyBb6+xve6pVbhxlLEJn4diVUhzV0fZReHWRYgdIb8U0OZUrAi7SEu9DnpI" crossorigin="anonymous"></script>
<script>
// Mapbox token resolution — the A3 ladder, in precedence order:

File diff suppressed because one or more lines are too long

View file

@ -29,7 +29,7 @@
<script>window.MAP_APP_API_URL = "https://pray.doxa.life";</script>
<!-- Bundle IIFE — registers the <regions-dashboard> custom element. -->
<script src="../regions-dashboard.js" integrity="sha384-Jvx1V3YrtL3L0p6eqByA/1hw4IWz6NpLp7Uza6fbl3HZCVa7JiAR5WMFvYsN6tPP" crossorigin="anonymous"></script>
<script src="../regions-dashboard.js" integrity="sha384-yxWIwZyBb6+xve6pVbhxlLEJn4diVUhzV0fZReHWRYgdIb8U0OZUrAi7SEu9DnpI" crossorigin="anonymous"></script>
<script>
// Mapbox token resolution — the A3 ladder, in precedence order:

View file

@ -40,7 +40,7 @@
<script>window.MAP_APP_API_URL = "https://pray.doxa.life";</script>
<!-- Bundle IIFE — registers the <regions-dashboard> custom element. -->
<script src="../regions-dashboard.js" integrity="sha384-Jvx1V3YrtL3L0p6eqByA/1hw4IWz6NpLp7Uza6fbl3HZCVa7JiAR5WMFvYsN6tPP" crossorigin="anonymous"></script>
<script src="../regions-dashboard.js" integrity="sha384-yxWIwZyBb6+xve6pVbhxlLEJn4diVUhzV0fZReHWRYgdIb8U0OZUrAi7SEu9DnpI" crossorigin="anonymous"></script>
<script>
// Mapbox token resolution — the A3 ladder, in precedence order: