doxa-maps/library/composables/host-token-url.js
Doxa Maps 1aa9a0ac99 A composite embedded bare dropped tokenUrl, and passed because a ProfileLoader sat above it
PM-tok, PRODUCTION-MATRIX.md:285. `tokenUrl` names a token SERVICE a host points at
instead of pasting a key: the element asks that URL when it was handed no token. Only
ProfileLoader ever read it. A pane composite bakes its children in rather than mounting
them through ProfileLoader, so a composite embedded BARE -- one tag on a partner's page,
which is the entire point of a composite -- dropped a tokenUrl-only config on the floor.

It worked in every place anyone looked, because a ProfileLoader elsewhere on the page
fetched and published the token: the composite was passing an exam somebody else sat.
And the failure wears the wrong face. No token is "No Mapbox access token provided",
0 tiles, no canvas -- identical to a missing `tk`, which IS threaded -- so the natural
reading is that the host mistyped their config rather than that a knob is unread.

The rule lives in library/composables, not in each composite, because it has already
been copied once (ProfileLoader.vue:132) and a third and fourth copy is how one of them
later learns to ask ALWAYS -- which a token service billed per call notices before we do.
The pure decision is split into host-token-url.js with NO framework import, because a
fresh worktree has no node_modules and a module importing vue is unloadable by the gate
that proves it right. Same shape as describeGroup keeping countryName injectable.

check-composite-token-url takes its expectation from ProfileLoader -- the contract a
composite REPLACES -- not from the composites it audits, so it cannot pass by
construction; it exercises the decision rather than grepping for it; and it finds its
subjects by a PROPERTY, a production bundle already forwarding `tk: hostToken`, so a
third composite is covered the day it is written. stack-demo has the same shape and is
excluded by its STAGE, not by name, so promoting it pulls it in automatically.

Negative-tested 5 ways. The fifth found a bug in the gate itself: /tokenUrl/ also matches
`tokenUrlX`, so the check meant to notice the contract being renamed could not fail. It
is \btokenUrl\b now. A guard that cannot fail is the one nobody ever finds by reading.
2026-09-28 15:52:47 +00:00

23 lines
1.1 KiB
JavaScript

/**
* host-token-url.js — the `tokenUrl` rule, with NO framework import, so a gate can run it.
*
* Split out of useHostTokenUrl.js deliberately. The composable needs vue's computed/watch; a headless
* gate has no node_modules in a fresh worktree, so a module that imports vue is unloadable by the test
* that proves it right. Same shape as library/packets/describeGroup.js keeping countryName injectable:
* the DECISION lives where it can be executed, the WIRING lives where it needs a framework.
*/
/**
* Ask the token service ONLY when nothing else supplied a token.
* Mirrors ProfileLoader.vue:133 — `url && !tk && !shared` — because a composite must behave the way the
* loader it replaces behaves, and because a service billed per call should be asked once, not always.
*/
export function shouldFetchTokenUrl({ tokenUrl = '', token = '', sharedToken = '' } = {}) {
return Boolean(tokenUrl) && !token && !sharedToken
}
/** The `tokenUrl` a host actually wrote, or ''. A non-string is not a URL and is not passed on. */
export function readHostTokenUrl(cfg) {
const u = cfg && cfg.tokenUrl
return typeof u === 'string' && u ? u : ''
}