The two composites that draw a map get the map snippets: a MapLibre preload and a Mapbox variant

research-map-plus-grid and regions-mfe draw a map through a child element, so their isMap is false,
and their published snippets had no engine preload and no scriptMapbox. A pasted composite still
draws on Mapbox given provider "mapbox" and a token (measured), so this is the snippet's
convenience, not a broken map.

generate-tree now decides after every profile's isMap is known: a composite (nested-elements-mfe)
whose OWN composes reach a map profile gets the map form of the default snippet and the Mapbox
variant. It reads the profile's own composes, never the bundle's hoisted list, which the bundle's
plain leaves (charts, data-tables, the poster wall) share while drawing nothing. The gallery shell
is left out until its passing of provider to its tabs is measured. Regenerated: exactly those two
profiles' embed changes, in both manifests; nothing internal leaks into the output.

check:snippet-engine asserts the same rule on the built manifest (21 map snippets, 19 maps + 2
composites). Its self-test adds a composite stripped of its variant, which fails. Against the
manifest as it stood before this change, the widened gate fails on exactly these two composites;
on the regenerated one it passes.
This commit is contained in:
Doxa Maps 2026-09-29 12:31:08 +00:00
parent b899e6887a
commit 9061065558
4 changed files with 52 additions and 9 deletions

View file

@ -9,10 +9,13 @@
* from a keyless page. Its only guard was a comment asking the partner to delete the line.
*
* Reads the BUILT manifest (public/<tree>/manifest.json) and asserts, for every profile:
* a map profile embed.script names no api.mapbox.com URL and preloads MapLibre (its script pin);
* a profile that DRAWS A MAP (isMap, or a composite whose bundle composes a map profile: the map is
* its child, as in research-map-plus-grid and regions-mfe)
* embed.script names no api.mapbox.com URL and preloads MapLibre (its script pin);
* embed.scriptMapbox preloads the Mapbox engine AND sets "provider":"mapbox"
* any other profile embed.script names no api.mapbox.com URL, and there is no scriptMapbox
* `--self-test` plants the old snippet and a variant that forgot its provider; both must fail.
* `--self-test` plants the old snippet, a variant that forgot its provider, and a map-drawing composite
* stripped of its variant; each must fail.
* Exit 0 pass · 1 fail · 2 no manifest.
*/
import { readFileSync, existsSync } from 'node:fs'
@ -25,15 +28,27 @@ const ROOT = resolve(dirname(fileURLToPath(import.meta.url)), '..')
const MANIFEST = join(ROOT, 'public', TREE_NAME, 'manifest.json')
const MAPBOX_HOST = 'api.mapbox.com'
// A profile DRAWS A MAP if it is one, or if it is a composite (nested-elements-mfe) whose bundle composes a
// map profile: research-map-plus-grid and regions-mfe have isMap false because their map is a child, and
// they draw on Mapbox when pasted with provider "mapbox". A bundle's plain leaves (type single) share its
// composes list but draw nothing, so the type is part of the rule. Same rule as generate-tree's composite pass.
export function drawsMapOf(manifest) {
const isMap = new Map()
for (const b of manifest.bundles || []) for (const p of b.profiles || []) isMap.set(`${b.bundle}/${p.profile}`, !!p.isMap)
const reaches = (ref) => isMap.get(ref) || [...isMap].some(([k, v]) => v && k.startsWith(`${ref}/`))
return (b, p) => !!p.isMap || (p.type === 'nested-elements-mfe' && (b.composes || []).some((r) => reaches(String(r))))
}
export function checkSnippets(manifest) {
const errs = []
let maps = 0
const drawsMap = drawsMapOf(manifest)
for (const b of manifest.bundles || []) {
for (const p of b.profiles || []) {
const id = `${b.bundle}/${p.profile}`
const script = String(p.embed?.script || '')
if (script.includes(MAPBOX_HOST)) errs.push(`${id}: the default snippet names ${MAPBOX_HOST} — an unedited paste would fetch an engine it never uses`)
if (p.isMap) {
if (drawsMap(b, p)) {
maps++
if (!script.includes(CDN.maplibreGlJs.url)) errs.push(`${id}: the default snippet does not preload MapLibre, the engine it draws with`)
const mb = String(p.embed?.scriptMapbox || '')
@ -56,7 +71,10 @@ if (process.argv.includes('--self-test')) {
const firstMap = (m) => m.bundles.flatMap((b) => b.profiles).find((p) => p.isMap)
const oldShape = clone(); { const p = firstMap(oldShape); p.embed.script = `<link rel="preload" as="script" href="${CDN.mapboxGlJs.url}">\n` + p.embed.script.replace(/<link rel="preload"[^\n]*\n/g, '') }
const noProvider = clone(); { const p = firstMap(noProvider); p.embed.scriptMapbox = p.embed.scriptMapbox.replace('"provider":"mapbox"', '"provider":"maplibre"') }
const cases = [['the built tree passes', manifest, 0], ['the old Mapbox-preloading default fails', oldShape, 1], ['a Mapbox variant without its provider fails', noProvider, 1]]
// A composite that draws its map through a child must carry the variant too: take it away from one.
const composite = clone(); { const draws = drawsMapOf(composite); const hit = composite.bundles.flatMap((b) => b.profiles.map((p) => [b, p])).find(([b, p]) => !p.isMap && draws(b, p)); if (hit) delete hit[1].embed.scriptMapbox }
const cases = [['the built tree passes', manifest, 0], ['the old Mapbox-preloading default fails', oldShape, 1], ['a Mapbox variant without its provider fails', noProvider, 1],
['a map-drawing composite without its Mapbox variant fails', composite, 1]]
let ok = true
for (const [name, m, want] of cases) {
const n = checkSnippets(m).errs.length

View file

@ -1469,6 +1469,9 @@ function scanBundles() {
// consumers but useless for deriving a tier — a bundle's composite and its plain leaves would
// all inherit the same edges. The tier is a property of the profile that declares them.
composesOwn: readComposes(dir, profileName),
// Generator-internal, stripped with the two above: what the snippets are built from, so the
// composite pass below can rebuild them once every profile's isMap is known.
_snippet: { cfg: embedCfg, build: scriptEmbedFor, mapNote: MAP_NOTE, mapboxNote: MAPBOX_NOTE },
...rest,
}
})
@ -1552,6 +1555,24 @@ function writeManifestAndHtml(root, bundles, tokenUrlExpr, bakedToken) {
}
}
// ★ A COMPOSITE THAT DRAWS ITS MAP THROUGH A CHILD GETS THE MAP SNIPPETS TOO. isMap is false on
// research-map-plus-grid and regions-mfe, because their map is a child element, so they published no
// engine preload and no Mapbox variant, though both draw a map and pass provider to it (pasted with
// provider "mapbox" and a token, both draw on Mapbox: measured). Decided here, once every profile's
// isMap exists, and from each profile's OWN composes, never the bundle's hoisted list: a bundle's
// plain leaves (charts, data-tables, a poster wall) share that list and must stay plain. The gallery
// shell (nested-mfe-spa) is left out until its passing of provider to its tabs is measured.
// check:snippet-engine asserts the same rule on the written manifest.
const isMapById = new Map()
for (const b of bundles) for (const p of b.profiles) isMapById.set(`${b.bundle}/${p.profile}`, !!p.isMap)
const reachesMap = (ref) => isMapById.get(ref) || [...isMapById].some(([k, v]) => v && k.startsWith(`${ref}/`))
for (const b of bundles) for (const p of b.profiles) {
if (p.isMap || p.type !== 'nested-elements-mfe' || !(p.composesOwn || []).some((r) => reachesMap(String(r)))) continue
const s = p._snippet
p.embed.script = `<!-- MapLibre engine (the default), fetched early -->\n${enginePreload('maplibre')}` + s.build(s.cfg, s.mapNote)
p.embed.scriptMapbox = `<!-- Mapbox engine, fetched early -->\n${enginePreload('mapbox')}` + s.build({ ...s.cfg, provider: 'mapbox' }, s.mapboxNote)
}
const manifest = {
schema: 'doxa-maps-manifest@1',
generatedFrom: 'plugin-designs/*/*.vue',
@ -1576,7 +1597,7 @@ function writeManifestAndHtml(root, bundles, tokenUrlExpr, bakedToken) {
// Consumers read the manifest as a contract; a new key would be a silent schema change.
bundles: bundles.map((b) => ({
...b,
profiles: b.profiles.map(({ extraConfig, composesOwn, ...p }) => p),
profiles: b.profiles.map(({ extraConfig, composesOwn, _snippet, ...p }) => p),
})),
}
mkdirSync(root, { recursive: true })

View file

@ -1182,7 +1182,8 @@
"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-G/ip3BKDre+u59aTOCgbJTMnG9Alb+dbrkt6YIw2TqbNG6PGreXCMqjFLfhrBesZ\" crossorigin=\"anonymous\"></script>\n<!-- provider: \"maplibre\" is the default; change it to \"mapbox\" and fill tk to use Mapbox. -->\n<regions-dashboard profile-config='{\"profile\":\"regions-mfe\",\"provider\":\"maplibre\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></regions-dashboard>"
"script": "<!-- MapLibre engine (the default), fetched early -->\n<link rel=\"preload\" as=\"script\" href=\"https://cdn.jsdelivr.net/npm/maplibre-gl@5.24.0/dist/maplibre-gl.js\" integrity=\"sha384-5+cfbwT0iiub6VsQAdn6yz16nr6sDiQoHx6tm4O8OVYXHYOxcffFmCJBL0dgdvGp\" crossorigin=\"anonymous\">\n<link rel=\"preload\" as=\"style\" href=\"https://cdn.jsdelivr.net/npm/maplibre-gl@5.24.0/dist/maplibre-gl.css\" integrity=\"sha384-uTttxo/aOKbdE5RlD/SPzSDoDmNvGlUYPjONi2MN/b7c9HPSvW07OIuyP7uL6jxK\" crossorigin=\"anonymous\">\n<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./regions-dashboard/regions-dashboard.js\" integrity=\"sha384-G/ip3BKDre+u59aTOCgbJTMnG9Alb+dbrkt6YIw2TqbNG6PGreXCMqjFLfhrBesZ\" crossorigin=\"anonymous\"></script>\n<!-- MapLibre draws this map and needs no token. For Mapbox, paste this map's embed.scriptMapbox from manifest.json instead: it sets \"provider\":\"mapbox\" and preloads the Mapbox engine; fill \"tk\" with your token. -->\n<regions-dashboard profile-config='{\"profile\":\"regions-mfe\",\"provider\":\"maplibre\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></regions-dashboard>",
"scriptMapbox": "<!-- Mapbox engine, fetched early -->\n<link rel=\"preload\" as=\"script\" href=\"https://api.mapbox.com/mapbox-gl-js/v3.24.0/mapbox-gl.js\" integrity=\"sha384-tO0clamXONKwbiz7O6xJpUeyJbTSPugC+xM9clvfFvKTH3i8SmCawHGdf5quMWmZ\" crossorigin=\"anonymous\">\n<link rel=\"preload\" as=\"style\" href=\"https://api.mapbox.com/mapbox-gl-js/v3.24.0/mapbox-gl.css\" integrity=\"sha384-rp1X/jJ9MpWPef9OimfXhOaOc4Gzs87yQvF+ajwY6X2H4PTeHNycNlLWiWvj7ehV\" crossorigin=\"anonymous\">\n<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./regions-dashboard/regions-dashboard.js\" integrity=\"sha384-G/ip3BKDre+u59aTOCgbJTMnG9Alb+dbrkt6YIw2TqbNG6PGreXCMqjFLfhrBesZ\" crossorigin=\"anonymous\"></script>\n<!-- Mapbox draws this map: fill \"tk\" with your Mapbox token. -->\n<regions-dashboard profile-config='{\"profile\":\"regions-mfe\",\"provider\":\"mapbox\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></regions-dashboard>"
}
}
]
@ -1244,7 +1245,8 @@
"scriptUrl": "./research-map-plus-grid/research-map-plus-grid.js",
"embed": {
"iframe": "<iframe src=\"./research-map-plus-grid/research-map-plus-grid/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=\"./research-map-plus-grid/research-map-plus-grid.js\" integrity=\"sha384-WhDZ/YlrGBo8tKC4FIehWyVHE6YPgsC/CNgSQp6NA2/Ntm6BTD/DlbHwaGNRBbI3\" crossorigin=\"anonymous\"></script>\n<!-- provider: \"maplibre\" is the default; change it to \"mapbox\" and fill tk to use Mapbox. -->\n<research-map-plus-grid profile-config='{\"profile\":\"research-map-plus-grid\",\"provider\":\"maplibre\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></research-map-plus-grid>"
"script": "<!-- MapLibre engine (the default), fetched early -->\n<link rel=\"preload\" as=\"script\" href=\"https://cdn.jsdelivr.net/npm/maplibre-gl@5.24.0/dist/maplibre-gl.js\" integrity=\"sha384-5+cfbwT0iiub6VsQAdn6yz16nr6sDiQoHx6tm4O8OVYXHYOxcffFmCJBL0dgdvGp\" crossorigin=\"anonymous\">\n<link rel=\"preload\" as=\"style\" href=\"https://cdn.jsdelivr.net/npm/maplibre-gl@5.24.0/dist/maplibre-gl.css\" integrity=\"sha384-uTttxo/aOKbdE5RlD/SPzSDoDmNvGlUYPjONi2MN/b7c9HPSvW07OIuyP7uL6jxK\" crossorigin=\"anonymous\">\n<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./research-map-plus-grid/research-map-plus-grid.js\" integrity=\"sha384-WhDZ/YlrGBo8tKC4FIehWyVHE6YPgsC/CNgSQp6NA2/Ntm6BTD/DlbHwaGNRBbI3\" crossorigin=\"anonymous\"></script>\n<!-- MapLibre draws this map and needs no token. For Mapbox, paste this map's embed.scriptMapbox from manifest.json instead: it sets \"provider\":\"mapbox\" and preloads the Mapbox engine; fill \"tk\" with your token. -->\n<research-map-plus-grid profile-config='{\"profile\":\"research-map-plus-grid\",\"provider\":\"maplibre\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></research-map-plus-grid>",
"scriptMapbox": "<!-- Mapbox engine, fetched early -->\n<link rel=\"preload\" as=\"script\" href=\"https://api.mapbox.com/mapbox-gl-js/v3.24.0/mapbox-gl.js\" integrity=\"sha384-tO0clamXONKwbiz7O6xJpUeyJbTSPugC+xM9clvfFvKTH3i8SmCawHGdf5quMWmZ\" crossorigin=\"anonymous\">\n<link rel=\"preload\" as=\"style\" href=\"https://api.mapbox.com/mapbox-gl-js/v3.24.0/mapbox-gl.css\" integrity=\"sha384-rp1X/jJ9MpWPef9OimfXhOaOc4Gzs87yQvF+ajwY6X2H4PTeHNycNlLWiWvj7ehV\" crossorigin=\"anonymous\">\n<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./research-map-plus-grid/research-map-plus-grid.js\" integrity=\"sha384-WhDZ/YlrGBo8tKC4FIehWyVHE6YPgsC/CNgSQp6NA2/Ntm6BTD/DlbHwaGNRBbI3\" crossorigin=\"anonymous\"></script>\n<!-- Mapbox draws this map: fill \"tk\" with your Mapbox token. -->\n<research-map-plus-grid profile-config='{\"profile\":\"research-map-plus-grid\",\"provider\":\"mapbox\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></research-map-plus-grid>"
},
"instances": [
{

View file

@ -1182,7 +1182,8 @@
"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-G/ip3BKDre+u59aTOCgbJTMnG9Alb+dbrkt6YIw2TqbNG6PGreXCMqjFLfhrBesZ\" crossorigin=\"anonymous\"></script>\n<!-- provider: \"maplibre\" is the default; change it to \"mapbox\" and fill tk to use Mapbox. -->\n<regions-dashboard profile-config='{\"profile\":\"regions-mfe\",\"provider\":\"maplibre\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></regions-dashboard>"
"script": "<!-- MapLibre engine (the default), fetched early -->\n<link rel=\"preload\" as=\"script\" href=\"https://cdn.jsdelivr.net/npm/maplibre-gl@5.24.0/dist/maplibre-gl.js\" integrity=\"sha384-5+cfbwT0iiub6VsQAdn6yz16nr6sDiQoHx6tm4O8OVYXHYOxcffFmCJBL0dgdvGp\" crossorigin=\"anonymous\">\n<link rel=\"preload\" as=\"style\" href=\"https://cdn.jsdelivr.net/npm/maplibre-gl@5.24.0/dist/maplibre-gl.css\" integrity=\"sha384-uTttxo/aOKbdE5RlD/SPzSDoDmNvGlUYPjONi2MN/b7c9HPSvW07OIuyP7uL6jxK\" crossorigin=\"anonymous\">\n<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./regions-dashboard/regions-dashboard.js\" integrity=\"sha384-G/ip3BKDre+u59aTOCgbJTMnG9Alb+dbrkt6YIw2TqbNG6PGreXCMqjFLfhrBesZ\" crossorigin=\"anonymous\"></script>\n<!-- MapLibre draws this map and needs no token. For Mapbox, paste this map's embed.scriptMapbox from manifest.json instead: it sets \"provider\":\"mapbox\" and preloads the Mapbox engine; fill \"tk\" with your token. -->\n<regions-dashboard profile-config='{\"profile\":\"regions-mfe\",\"provider\":\"maplibre\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></regions-dashboard>",
"scriptMapbox": "<!-- Mapbox engine, fetched early -->\n<link rel=\"preload\" as=\"script\" href=\"https://api.mapbox.com/mapbox-gl-js/v3.24.0/mapbox-gl.js\" integrity=\"sha384-tO0clamXONKwbiz7O6xJpUeyJbTSPugC+xM9clvfFvKTH3i8SmCawHGdf5quMWmZ\" crossorigin=\"anonymous\">\n<link rel=\"preload\" as=\"style\" href=\"https://api.mapbox.com/mapbox-gl-js/v3.24.0/mapbox-gl.css\" integrity=\"sha384-rp1X/jJ9MpWPef9OimfXhOaOc4Gzs87yQvF+ajwY6X2H4PTeHNycNlLWiWvj7ehV\" crossorigin=\"anonymous\">\n<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./regions-dashboard/regions-dashboard.js\" integrity=\"sha384-G/ip3BKDre+u59aTOCgbJTMnG9Alb+dbrkt6YIw2TqbNG6PGreXCMqjFLfhrBesZ\" crossorigin=\"anonymous\"></script>\n<!-- Mapbox draws this map: fill \"tk\" with your Mapbox token. -->\n<regions-dashboard profile-config='{\"profile\":\"regions-mfe\",\"provider\":\"mapbox\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></regions-dashboard>"
}
}
]
@ -1244,7 +1245,8 @@
"scriptUrl": "./research-map-plus-grid/research-map-plus-grid.js",
"embed": {
"iframe": "<iframe src=\"./research-map-plus-grid/research-map-plus-grid/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=\"./research-map-plus-grid/research-map-plus-grid.js\" integrity=\"sha384-WhDZ/YlrGBo8tKC4FIehWyVHE6YPgsC/CNgSQp6NA2/Ntm6BTD/DlbHwaGNRBbI3\" crossorigin=\"anonymous\"></script>\n<!-- provider: \"maplibre\" is the default; change it to \"mapbox\" and fill tk to use Mapbox. -->\n<research-map-plus-grid profile-config='{\"profile\":\"research-map-plus-grid\",\"provider\":\"maplibre\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></research-map-plus-grid>"
"script": "<!-- MapLibre engine (the default), fetched early -->\n<link rel=\"preload\" as=\"script\" href=\"https://cdn.jsdelivr.net/npm/maplibre-gl@5.24.0/dist/maplibre-gl.js\" integrity=\"sha384-5+cfbwT0iiub6VsQAdn6yz16nr6sDiQoHx6tm4O8OVYXHYOxcffFmCJBL0dgdvGp\" crossorigin=\"anonymous\">\n<link rel=\"preload\" as=\"style\" href=\"https://cdn.jsdelivr.net/npm/maplibre-gl@5.24.0/dist/maplibre-gl.css\" integrity=\"sha384-uTttxo/aOKbdE5RlD/SPzSDoDmNvGlUYPjONi2MN/b7c9HPSvW07OIuyP7uL6jxK\" crossorigin=\"anonymous\">\n<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./research-map-plus-grid/research-map-plus-grid.js\" integrity=\"sha384-WhDZ/YlrGBo8tKC4FIehWyVHE6YPgsC/CNgSQp6NA2/Ntm6BTD/DlbHwaGNRBbI3\" crossorigin=\"anonymous\"></script>\n<!-- MapLibre draws this map and needs no token. For Mapbox, paste this map's embed.scriptMapbox from manifest.json instead: it sets \"provider\":\"mapbox\" and preloads the Mapbox engine; fill \"tk\" with your token. -->\n<research-map-plus-grid profile-config='{\"profile\":\"research-map-plus-grid\",\"provider\":\"maplibre\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></research-map-plus-grid>",
"scriptMapbox": "<!-- Mapbox engine, fetched early -->\n<link rel=\"preload\" as=\"script\" href=\"https://api.mapbox.com/mapbox-gl-js/v3.24.0/mapbox-gl.js\" integrity=\"sha384-tO0clamXONKwbiz7O6xJpUeyJbTSPugC+xM9clvfFvKTH3i8SmCawHGdf5quMWmZ\" crossorigin=\"anonymous\">\n<link rel=\"preload\" as=\"style\" href=\"https://api.mapbox.com/mapbox-gl-js/v3.24.0/mapbox-gl.css\" integrity=\"sha384-rp1X/jJ9MpWPef9OimfXhOaOc4Gzs87yQvF+ajwY6X2H4PTeHNycNlLWiWvj7ehV\" crossorigin=\"anonymous\">\n<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./research-map-plus-grid/research-map-plus-grid.js\" integrity=\"sha384-WhDZ/YlrGBo8tKC4FIehWyVHE6YPgsC/CNgSQp6NA2/Ntm6BTD/DlbHwaGNRBbI3\" crossorigin=\"anonymous\"></script>\n<!-- Mapbox draws this map: fill \"tk\" with your Mapbox token. -->\n<research-map-plus-grid profile-config='{\"profile\":\"research-map-plus-grid\",\"provider\":\"mapbox\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></research-map-plus-grid>"
},
"instances": [
{