Repository navigation
v0.1.47
Give each render its own id sequence, not the process one
renderPage reset the counter and then awaited — resolving the page module,
gathering shared props, calling the component. Two requests overlapping across
any of those awaits shared one counter: the second reset it under the first,
and that response shipped trigger-2 while its own browser, hydrating from
zero, looked for trigger-1. Every id-based lookup then answered null, which
is silent — a tooltip that never opens on a page where everything else works.
Found by an external audit. The sequential tests could not see it, and now
there is one that does: two renders interleaved inside resolve(), both
expected to start at 1. It fails against the old counter.
The counter moved into the per-request AsyncLocalStorage renderPage already
runs — the same seam the cookie store and the route manifest use. id.ts
imports nothing from node:: it reads its cell through a reader the server
installs, and falls back to a module-level one in the browser, where a page is
rendered once and there is nothing to race.
release: aurora 0.1.47
Keep both lifecycles, and reset the id sequence per pass
Two bugs, one symptom: a component whose whole job happens on mount doing
nothing at all, silently, on a page where every binding works. A tooltip that
never opens while its trigger's aria-describedby toggles correctly.
FIRST — the lifecycle rides on the returned TemplateResult, under a symbol. A
component is free to return another one's result rather than a template of its
own:
const TextField = component((props) => Field({ ... }))
const Passthrough = component((props) => props.children)
and the outer wrap assigned straight over the inner one. The inner component's
setup ran, its bindings worked, its onMount was dropped. Merged now, outer
first — the order the renderer drains the queue in everywhere else.
SECOND — ids. Templates have no ref directive, so a component that must anchor
or measure a node finds it by id, and ARIA needs one regardless. The counter is
monotonic, which makes the sequence survive hydration ONLY if both passes start
from the same place. A server process is long-lived: with no reset its counter
climbs across requests, so the second page it serves ships trigger-14 while the
browser, starting fresh, looks up trigger-1. Every lookup then answers null —
and answers it silently.
uid / resetIds / byId now live here, and renderPage and hydrate each
reset before they build. nebula had its own counter, which saw none of those
resets; it re-exports these instead.
Changes since v0.1.46.