Found while implementing #1847. Independent of that feature — it affects every server block.
Repro
import { extractVariables } from './packages/stx/src/variable-extractor'
const ctx = {}
await extractVariables('export async function g({ form }) { return 1 }', ctx, '/tmp/t.stx')
// ctx === {} — `g` is goneMatrix
Same file, same block, only the signature changes:
| declaration | reaches the render context |
|---|---|
export async function noParam() {} | ✅ |
export async function plainParam(x) {} | ✅ |
export async function objReturn() { return { a: 1 } } | ✅ |
export async function destructured({ form }) {} | ❌ dropped |
export async function both({ form }) { return { a: 1 } } | ❌ dropped |
Confirmed through a real render too, not just the unit call — a page with
<script server>
export async function destructured({ form }) { return 1 }
</script>
<div>{{ typeof destructured }}</div>renders undefined.
Why it matters
It fails silently. No warning, no render error — the function simply is not there, and undefined is undefined. Destructured parameters are the normal way to write a handler in this ecosystem ({ form }, { request, params }), so this quietly removes a shape people reach for by default. It is also the blocker for the action API proposed in #1847, which is specified as action({ request, form }).
Where I got to
packages/stx/src/variable-extractor.ts. The dispatcher at :1205 matches export async function correctly and calls parseFunctionDeclaration, whose own name regex (:1744) handles the input fine — so the drop happens after that.
I tried needsMultilineFunctionReading (:1866) first, on the theory that functionLine.indexOf('{') finds the destructuring brace rather than the body brace, so a multi-line function gets read as a one-liner and keeps only its signature. Making it search for the body brace after the parameter list is arguably more correct on its own terms, but it did not change the behaviour — including for the single-line case above, which never takes that path. So that is not it, or not only it, and I reverted the change rather than leave an unverified edit in the tree.
The comment at :1189 mentions parse failures "silently strand the whole script in the fallback extractor", which sounds like the right neighbourhood — something upstream is likely throwing and falling back to a path that misses the declaration.
Not currently working on this; writing it down so the repro is not lost.