feat(solid,web,signals): server boundary records on OBSERVE.server.records - #3452
Merged
Merged
Conversation
…cords
The `"boundary"` record — one per `<Loading>` boundary that WAITED during
a server render, delivered when it settles: `{ id, at, durationMs, heldMs,
passes, outcome: "settled" | "fallback" | "client" | "error", streamed,
revealGroup?, ownerPath? }`, the thrown error beside it. `id` is the
boundary's hydration id, the one `SSR_RENDER_ERROR_CONTAINED` names in
`data.boundary`, so a record and a finding pair by it. `passes` counts
render passes over the content (discovery plus one per wait — `2` is one
round of async, more is a sequential chain) and doubles as the convergence
budget's counter. `streamed` is `registerFragment`'s answer — whether the
outcome reached the client after the shell flushed. Under a `<Reveal>`
group the record waits for the group's swap, via a per-leaf `onReveal`
hook every `revealFragments` site in `Reveal` now fires, so `heldMs` is
how long finished content sat behind its siblings. A boundary that
renders on its first pass emits nothing (the client `hold` rule); no clock
is read without a listener; the slots and emitter fold out of prod.
The channel is `OBSERVE.server.records.subscribe(type, listener)` — the
server twin of `OBSERVE.attribution.subscribe(type, …)`. C0's
`OBSERVE.server.invocations` (unreleased) is renamed onto it as the
`"invocation"` type; `InvocationChannel` is gone.
The types now layer one augmenter per interface, matching the package
chain. First cut had signals declare `ServerRecords`, solid-js augment it
through `"@solidjs/signals"` and web through `"solid-js"`; each side then
saw only its own overload. Reproduced in isolation: TypeScript merges two
augmentations of one re-exported interface through DIFFERENT alias paths
order-dependently, dropping one set with no error. So: signals declares
`ServerObserve` empty; solid-js augments it once with `records:
ServerRecords` and `trace: ServerTrace`, declaring both (the `"boundary"`
overload inline); web augments those two through `"solid-js"`
(`"invocation"`, `provide`). `TraceSlot` is an alias of `ServerTrace`.
The rule is documented on `ServerObserve`; the web type tests require
both overloads. (`core/index.ts` had also never re-exported the new name.)
Specs: web/test/server/server-boundary-records.spec.tsx against the real
renderers and Reveal — settled/streamed/inlined, a 3-pass chain,
renderToString fallback, client holes at discovery and after a wait, error
paired with the finding, together/natural holds, grouped error, listener
resilience, prod fold-out. Docs: RFC 08 (`OBSERVE.server`, the record),
RFC 10, the plans (C3 landed).
Co-authored-by: Cursor <cursoragent@cursor.com>
Coverage Report for CI Build 34942153511Coverage remained the same at 71.842%Details
Uncovered ChangesNo uncovered changes found. Coverage RegressionsNo coverage regressions found. Coverage Stats
💛 - Coveralls |
…eveal onReveal plumbing folds out of prod - `@solidjs/signals` exports `ownerPath`; the server boundary record uses it instead of a verbatim copy (`ownerLabels`) in solid-js's server observe module. - The Reveal group's `onReveal` hook registration and dispatch are gated on `IS_OBSERVE` so the prod artifact carries neither; a second tier pin in server-boundary-records.spec asserts `onReveal` is absent from server.js and present in server.observe.js / server.dev.js. - Merged origin/next. Co-authored-by: Claude via Cursor <noreply@cursor.com> Co-authored-by: Cursor <cursoragent@cursor.com>
🦋 Changeset detectedLatest commit: f519d15 The changes in this PR will be included in the next version bump. This PR includes changesets to release 11 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Sentry integration plan C3 — server boundary timing, as records on the server observe surface. Follows #3433 (C2) and #3441 (SSR component labels).
The
"boundary"recordOne per
<Loading>boundary that waited during a server render, delivered when it settles:idis the boundary's hydration id — the samedata.boundaryonSSR_RENDER_ERROR_CONTAINED, so record and finding pair.passes= discovery pass + one per wait.2is one round of async; more is a sequential chain (the waterfall substrate). It also replaces the convergence budget's private counter.streamedisregisterFragment's answer: did the outcome reach the client after the shell flushed.heldMsis real: under a<Reveal>group the record waits for the group's swap (a per-leafonRevealhook everyrevealFragmentssite inRevealnow fires) and measures how long finished content sat behind its siblings. A group that never reveals (stream abandoned) loses the record;SSR_STREAM_ABANDONEDis that request's account.holdrule). No clock is read without a listener. Slots and emitter fold out ofdist/server.js(pinned).Channel rename
OBSERVE.server.records.subscribe(type, listener)— the server twin ofOBSERVE.attribution.subscribe(type, …). C0'sOBSERVE.server.invocations(unreleased) is renamed onto it as the"invocation"type;InvocationChannelis gone. Frame records (C4) join by name.Type layering — a TypeScript trap worth knowing about
The first cut had signals declare
ServerRecords, solid-js augment it via"@solidjs/signals"and web via"solid-js". Each side then saw only its own overload, with no error. Reproduced in isolation: TS (6.0.3) merges two augmentations of one re-exported interface through different alias paths order-dependently and silently drops one set.ServerObservehad dodged this so far only because web was its sole augmenter.Now one augmenter per interface, matching the package chain:
@solidjs/signalsdeclaresServerObserveempty.solid-jsaugments it once (via"@solidjs/signals") withrecords: ServerRecordsandtrace: ServerTrace, declaring both interfaces itself,"boundary"overload inline.@solidjs/webaugments those two via"solid-js"("invocation",provide).TraceSlotis now an alias ofServerTrace.The rule is documented on
ServerObserve;web/test/server-observe.type-tests.tsrequires both overloads. This also caught thatcore/index.tshad never re-exported the new name.Tests
web/test/server/server-boundary-records.spec.tsx(14) against the real renderers and<Reveal>: settled / streamed / inlined viadeferStream, a 3-pass chain,renderToStringfallback, client holes at discovery and after a real wait, error paired with the finding byid,togetherhold (heldMs ≥ 30early sibling,≈0late one),natural, grouped error, throwing listener, unsubscribe, prod fold-out. Type tests updated. Signals 1891 / solid 618 / web 783 + 909 green;tscandtest-typesclean on all three.Docs
RFC 08 (
OBSERVE.server— the layering, the"boundary"record), RFC 10,sentry-integration-plan.md(C3 landed, with the deviations noted),server-dev-build-plan.md. Changeset included.