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.
44 lines
2.4 KiB
JavaScript
44 lines
2.4 KiB
JavaScript
/**
|
|
* useHostTokenUrl.js — a composite honours `tokenUrl` the way ProfileLoader does.
|
|
*
|
|
* THE DEFECT (PM-tok, PRODUCTION-MATRIX.md:285). `tokenUrl` is a token SERVICE a host names in its
|
|
* profile-config 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 — the whole point of a composite, one tag on a partner's
|
|
* page — dropped a tokenUrl-only config on the floor. With an outer ProfileLoader on the page it worked,
|
|
* because THAT fetched and published the token; the composite was passing an exam somebody else sat.
|
|
*
|
|
* ★ THE FAILURE IS TOKENLESS MAPBOX, WHICH LOOKS LIKE A DIFFERENT BUG. No token means "No Mapbox access
|
|
* token provided", 0 tiles and no canvas — the same symptom as a missing `tk`, which IS threaded, so the
|
|
* obvious reading is that the host mis-typed their config rather than that a knob is unread.
|
|
*
|
|
* ★ AND IT LIVES HERE RATHER THAN IN EACH COMPOSITE because there are two composites today and the rule
|
|
* has already been copied once (ProfileLoader.vue:132). A third and fourth copy of "ask only when no
|
|
* token is at hand" is how one of them later learns to ask always, and a token service billed per call
|
|
* notices that before we do.
|
|
*/
|
|
import { computed, watch } from 'vue'
|
|
import { useSharedMapToken, fetchMapToken } from './useSharedMapToken.js'
|
|
import { shouldFetchTokenUrl, readHostTokenUrl } from './host-token-url.js'
|
|
|
|
export { shouldFetchTokenUrl, readHostTokenUrl }
|
|
|
|
/**
|
|
* Wire a composite's host config to the shared token service.
|
|
* @param {import('vue').Ref} hostConfig parsed profile-config of the COMPOSITE
|
|
* @param {import('vue').Ref} hostToken the host's own `tk`, which always wins
|
|
* @returns {import('vue').ComputedRef<string>} the tokenUrl, to forward into children's profile-config
|
|
*/
|
|
export function useHostTokenUrl(hostConfig, hostToken) {
|
|
const sharedToken = useSharedMapToken()
|
|
const tokenUrl = computed(() => readHostTokenUrl(hostConfig?.value))
|
|
// immediate, because a host that set tokenUrl before mount is the ordinary case, not the edge one.
|
|
watch(tokenUrl, (url) => {
|
|
if (shouldFetchTokenUrl({ tokenUrl: url, token: hostToken?.value || '', sharedToken: sharedToken.value })) {
|
|
fetchMapToken(url)
|
|
}
|
|
}, { immediate: true })
|
|
return tokenUrl
|
|
}
|
|
|
|
export default useHostTokenUrl
|