Summary
The list of runtime globals destructured from window.stx at the top of every generated client script is hardcoded in three separate places. They have drifted, so which stx APIs you can call depends on which kind of script you are writing — and the failure is a bare ReferenceError at runtime with nothing at build time.
The three copies
| Site | File | Names |
|---|---|---|
wrapClientScript — @included component scripts | packages/stx/src/includes.ts:268 | 51 |
processScriptSetup — page setup function | packages/stx/src/signal-processing.ts:1264 | 54 |
renderComponentWithSlot — <Component /> client scripts | packages/stx/src/utils.ts:1152 | 53 |
Union: 56 distinct names. Eight are missing from at least one site:
defineStore missing from: renderComponentWithSlot
inject missing from: wrapClientScript, processScriptSetup
nextTick missing from: wrapClientScript, processScriptSetup
onBeforeUnmount missing from: wrapClientScript
onDestroy missing from: renderComponentWithSlot
onMount missing from: renderComponentWithSlot
onMounted missing from: wrapClientScript
onUnmounted missing from: wrapClientScriptWhich gaps are real
Three are masked because the runtime also assigns them as bare globals, so the identifier still resolves:
onMount,onDestroy(window.onMount/window.onDestroyare assigned)defineStore(window.defineStoreis assigned)
Five are not masked — no global fallback exists, so they throw:
injectnextTickonMountedonUnmountedonBeforeUnmount
Verified
Rendered through processDirectives on current main:
PAGE setup: nextTick in the window.stx destructure? NO -> bare nextTick() = ReferenceError
COMPONENT script: onMounted in its destructure? NO -> bare onMounted() = ReferenceErrorSo nextTick() works in a <Component /> client script but throws in a page's <script client>, and onMounted() works in a page but throws in an @included component. Same API, same syntax, different context, silent inconsistency.
Why it keeps happening
Adding a runtime global means remembering three unrelated files. Nothing links them, nothing tests that they agree, and the drift above is the natural result. A fourth copy would rot the same way — which is why composable-loader.ts deliberately detects built-in collisions at runtime against window.stx rather than hardcoding the list again (#1780).
Suggested fix
- Export the list once (e.g.
STX_RUNTIME_GLOBALSin a small module) and build all three destructures from it, so adding a global is a one-line change. - Add a test asserting the three generated destructures are identical — that is what actually prevents the drift from returning.
- Decide the intended surface for the currently-inconsistent eight (particularly whether the Vue-style aliases
onMounted/onUnmounted/onBeforeUnmountshould be available everywhere, or only where they are now).
Happy to do this, but it touches all three files, so it wants a quiet moment rather than landing mid-burst.