Skip to content

Commit ad644ce

Browse files
committed
fix(server): wire up requestContext/query-string globals in buddy serve
storage/framework/core/actions/src/dev/views.ts sets globalThis.requestContext (cookie access) and __stxServeSearch (query string) for `<script server>` blocks in .stx pages, but only dev/views.ts (buddy dev) did this — buddy serve (this file, the actual production entrypoint) never did. Found via an end-to-end test in a downstream app: a real login cookie was sent, but server-rendered pages never saw it and rendered as fully signed-out for every request. Uses plain globals rather than AsyncLocalStorage (which dev/views.ts uses) — confirmed via the same e2e test that bun-plugin-stx's internal request handling doesn't preserve AsyncLocalStorage context between onRequest returning and the page actually rendering, so the store was always empty by read time. __stxServeSearch already relies on a plain global for the same reason; __stxServeCookies follows that precedent.
1 parent 1e6f92c commit ad644ce

1 file changed

Lines changed: 60 additions & 0 deletions

File tree

  • storage/framework/core/buddy/src/commands

storage/framework/core/buddy/src/commands/serve.ts

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,60 @@ import { join } from 'node:path'
55
import process from 'node:process'
66
import { log } from '@stacksjs/cli'
77

8+
/**
9+
* Request-scoped context (query string + parsed cookies) for `<script
10+
* server>` blocks in `.stx` pages — mirrors `dev/views.ts`'s dev-only
11+
* setup of the same globals. Without this, `globalThis.requestContext`
12+
* and `__stxServeSearch` are simply undefined in production: every
13+
* cookie-aware or query-param-aware page (auth+team resolution on the
14+
* dashboard, filter params on monitors/incidents, etc.) silently reads
15+
* nothing and falls back to its unauthenticated/no-filter state, even
16+
* for a legitimately signed-in request. `dev/views.ts` sets these up
17+
* for `buddy dev`, but `buddy serve` (this file, the actual Hetzner
18+
* entrypoint) never did — this was found by an end-to-end login +
19+
* dashboard smoke test, not by inspection.
20+
*
21+
* Plain globals, not `AsyncLocalStorage` — tried that first (mirroring
22+
* dev/views.ts's own approach) and confirmed via the same e2e test that
23+
* the store is empty by the time a `<script server>` block reads it:
24+
* bun-plugin-stx's internal request handling doesn't preserve the async
25+
* context across whatever it does between `onRequest` returning and the
26+
* page actually rendering. `__stxServeSearch` already uses a plain
27+
* global for the exact same reason (and already accepts the same
28+
* concurrent-request race this shares) — `__stxServeCookies` follows
29+
* that precedent instead of a mechanism that demonstrably doesn't work
30+
* in this server.
31+
*/
32+
;(globalThis as any).requestContext = {
33+
cookie(name: string): string | null {
34+
const cookies = (globalThis as { __stxServeCookies?: Record<string, string> }).__stxServeCookies
35+
return cookies?.[name] ?? null
36+
},
37+
url(): string {
38+
return (globalThis as { __stxServeSearch?: string }).__stxServeSearch ?? ''
39+
},
40+
}
41+
42+
function parseCookies(req: Request): Record<string, string> {
43+
const out: Record<string, string> = {}
44+
const header = req.headers.get('cookie') || ''
45+
if (!header)
46+
return out
47+
for (const part of header.split(';')) {
48+
const trimmed = part.trim()
49+
const eq = trimmed.indexOf('=')
50+
if (eq === -1)
51+
continue
52+
const k = trimmed.slice(0, eq).trim()
53+
const v = trimmed.slice(eq + 1).trim()
54+
if (!k)
55+
continue
56+
try { out[k] = decodeURIComponent(v) }
57+
catch { out[k] = v }
58+
}
59+
return out
60+
}
61+
862
/**
963
* `buddy serve` — boot the production HTTP server.
1064
*
@@ -121,6 +175,12 @@ export function serve(buddy: CLI): void {
121175
}
122176
}
123177

178+
// Stash cookies + query string so server-script blocks rendering
179+
// this request can pull them via globalThis.requestContext /
180+
// __stxServeSearch — see the doc comment above this function.
181+
;(globalThis as { __stxServeSearch?: string }).__stxServeSearch = url.search
182+
;(globalThis as { __stxServeCookies?: Record<string, string> }).__stxServeCookies = parseCookies(req)
183+
124184
return undefined
125185
},
126186
})

0 commit comments

Comments
 (0)