Bake the API base into the dev pages so dashboards are not silently empty
Some checks are pending
build-gate / build (push) Waiting to run
Some checks are pending
build-gate / build (push) Waiting to run
The built leaf pages set window.MAP_APP_API_URL; the dev pages set only the Mapbox token. Every dashboard in dev therefore fell back to a relative /api/people-groups/list, which Vite answers with SPA-fallback HTML at HTTP 200 — so the fetch "succeeds", the parse yields nothing, and the facet rail renders zero rows with no console error and no error card. One studio sweep produced eight such requests. It reads as a broken component and is really a missing global. Both dev routes now read the value from the same build config the built leaves use, so the two surfaces agree by construction rather than through a second hard-coded copy, and each route resolves the token and the API base from a single config read so they cannot disagree about their source. Measured after: the research dashboard renders 66 facet rows and 216 table cells with zero 4xx.
This commit is contained in:
parent
afb998f17b
commit
371119a3f8
1 changed files with 26 additions and 4 deletions
|
|
@ -174,8 +174,12 @@ export function autoSpaDevPlugin(token = '') {
|
|||
const entry = reg.find(b => b.bundle === bundle)
|
||||
if (!entry) return json(res, { error: 'unknown bundle', bundle }, 404)
|
||||
const tag = entry.tag
|
||||
let leafTok = token
|
||||
if (!leafTok) { try { leafTok = (readBuildConfig() || {}).token || '' } catch { leafTok = '' } }
|
||||
// One config read for BOTH globals, so the token and the API base can never disagree about
|
||||
// which config they came from.
|
||||
let leafCfg = {}
|
||||
try { leafCfg = readBuildConfig() || {} } catch { leafCfg = {} }
|
||||
const leafTok = token || leafCfg.token || ''
|
||||
const apiBase = leafCfg.apiBaseUrl || ''
|
||||
// profile-config carries the same shape the built leaves pass, so a profile/instance
|
||||
// renders the SAME view here as in production rather than the bundle's default.
|
||||
const pc = JSON.stringify(instance ? { profile, instanceId: instance } : { profile })
|
||||
|
|
@ -188,6 +192,16 @@ export function autoSpaDevPlugin(token = '') {
|
|||
'<meta name="viewport" content="width=device-width, initial-scale=1.0" />',
|
||||
'<link rel="icon" href="data:,">',
|
||||
leafTok ? `<script>window.MAP_TOKEN=${JSON.stringify(leafTok)};</script>` : '',
|
||||
// ★ MAP_APP_API_URL IS NOT OPTIONAL — without it every dashboard renders EMPTY in dev.
|
||||
// The BUILT leaves bake `window.MAP_APP_API_URL = "https://pray.doxa.life"`; these dev
|
||||
// pages baked only the Mapbox token, so the dashboards fell back to a RELATIVE
|
||||
// `/api/people-groups/list?...`. Vite answers an unknown path with SPA-fallback HTML at
|
||||
// HTTP 200, so the fetch "succeeds", JSON.parse fails or yields nothing, and the facet
|
||||
// rail renders ZERO rows with no console error and no error card. Measured 2026-09-22:
|
||||
// 8 such 404/fallback requests during one studio sweep. That reads as a broken component
|
||||
// and is really a missing global — the worst kind of dev-only divergence, and exactly the
|
||||
// four-surface class (see REFERENCE-the-four-surface-contract.md).
|
||||
apiBase ? `<script>window.MAP_APP_API_URL=${JSON.stringify(apiBase)};</script>` : '',
|
||||
'<style>html,body{margin:0;padding:0;height:100%;background:#f3f3f1;}',
|
||||
`${tag}{display:block;width:100%;height:100vh;}</style>`,
|
||||
'</head><body>',
|
||||
|
|
@ -230,8 +244,11 @@ export function autoSpaDevPlugin(token = '') {
|
|||
// called HERE rather than reusing page()'s `cfg` — that const lives inside the page()
|
||||
// arrow function, so it is not in scope in this middleware (an earlier version referenced
|
||||
// it and the dev server 500'd with "cfg is not defined").
|
||||
let devTok = token
|
||||
if (!devTok) { try { devTok = (readBuildConfig() || {}).token || '' } catch { devTok = '' } }
|
||||
// One config read for BOTH globals (see the leaf route above for why the API base matters).
|
||||
let devCfg = {}
|
||||
try { devCfg = readBuildConfig() || {} } catch { devCfg = {} }
|
||||
const devTok = token || devCfg.token || ''
|
||||
const devApiBase = devCfg.apiBaseUrl || ''
|
||||
res.statusCode = 200
|
||||
res.setHeader('Content-Type', 'text/html; charset=utf-8')
|
||||
res.setHeader('Cache-Control', 'no-cache')
|
||||
|
|
@ -244,6 +261,11 @@ export function autoSpaDevPlugin(token = '') {
|
|||
// The token ladder's baked rung, exactly as the built leaves receive it, so maps paint
|
||||
// in dev without anyone appending ?tk=.
|
||||
devTok ? `<script>window.MAP_TOKEN=${JSON.stringify(devTok)};</script>` : "",
|
||||
// Same reason as the leaf route above: without MAP_APP_API_URL the dashboards nested in
|
||||
// the studio fetch a relative /api/... that Vite answers with HTML at 200, so they mount
|
||||
// and then show nothing. The value comes from the SAME build config the built leaves use,
|
||||
// so dev and built agree by construction rather than by a second hard-coded copy.
|
||||
devApiBase ? `<script>window.MAP_APP_API_URL=${JSON.stringify(devApiBase)};</script>` : "",
|
||||
// No chrome, no prose: the element fills the viewport. "Without a lot of text garb
|
||||
// that takes up so much space" is a layout requirement, so the host page contributes
|
||||
// ZERO text — every pixel belongs to the studio element.
|
||||
|
|
|
|||
Loading…
Reference in a new issue