Problem
An async render() component that reads a SIGNAL loses reactivity after it performs an ON-HYDRATION re-fetch. Subsequent signal changes do not re-render it.
Found by running the blog e2e against a Bun-served blog (#523). The <abort-demo> component (examples/blog) does async render() { const r = await getSlow(this.n.get()); ... } with n = signal(0) and an @click that bumps n.
Reproduced deterministically: WEBJS_E2E=1 WEBJS_SEED=0 node --test --test-name-pattern="superseded async render aborts" test/e2e/e2e.test.mjs fails identically to the Bun run (only getSlow?a=[0] fetches; the bumps do nothing). So it is NOT Bun-specific and NOT a test artifact: seeding merely MASKS it on Node by default.
Design / approach
The likely cause: when the FIRST client render of an async-render component awaits a promise, the SignalWatcher subscription to the signals READ during that render is not established (or is torn down) once the async render resolves, so later signal writes do not schedule an update. The seeded path resolves the first render synchronously enough that the subscription survives, hiding the bug. Investigate the async-render commit path in @webjsdev/core (render-client + SignalWatcher + the async render() await) so the signal dependency is tracked across the awaited first render.
This matters for Bun deployments specifically (seeding is always off there), where ANY async-render component that also drives state via a signal would go inert after hydration.
Acceptance criteria
Problem
An
async render()component that reads a SIGNAL loses reactivity after it performs an ON-HYDRATION re-fetch. Subsequent signal changes do not re-render it.Found by running the blog e2e against a Bun-served blog (#523). The
<abort-demo>component (examples/blog) doesasync render() { const r = await getSlow(this.n.get()); ... }withn = signal(0)and an@clickthat bumpsn.nre-renders + re-fetches. Works.module.registerHooks) OR Node withWEBJS_SEED=0: the first client render actually AWAITSgetSlow(0)(no seed). After it resolves, bumpingnproduces NO re-render and NO new fetch. Reactivity is dead.Reproduced deterministically:
WEBJS_E2E=1 WEBJS_SEED=0 node --test --test-name-pattern="superseded async render aborts" test/e2e/e2e.test.mjsfails identically to the Bun run (onlygetSlow?a=[0]fetches; the bumps do nothing). So it is NOT Bun-specific and NOT a test artifact: seeding merely MASKS it on Node by default.Design / approach
The likely cause: when the FIRST client render of an async-render component awaits a promise, the
SignalWatchersubscription to the signals READ during that render is not established (or is torn down) once the async render resolves, so later signal writes do not schedule an update. The seeded path resolves the first render synchronously enough that the subscription survives, hiding the bug. Investigate the async-render commit path in@webjsdev/core(render-client + SignalWatcher + the asyncrender()await) so the signal dependency is tracked across the awaited first render.This matters for Bun deployments specifically (seeding is always off there), where ANY async-render component that also drives state via a signal would go inert after hydration.
Acceptance criteria
#492abort e2e passes against a Bun-served blog (and on Node withWEBJS_SEED=0).