Patch Changes
-
8bf04ea: New dev diagnostic
ASYNC_STORE_SETTER: a store setter callback that returns a Promise now throws in dev (through the diagnostics channel) instead of being silently ignored. A store setter is a synchronous transaction — the draft closes when the callback returns — sosetStore(async d => …)committed only the writes before its firstawaitand lost the rest. Covers every store family with a user setter (createStore,createOptimisticStore, the derived store's manual setter); a derived store's own async compute is not affected. Store-specific: a signal may legitimately hold a promise, sosetSignalhas no such rule. Production is unchanged.Docs: the
OBSERVE.excludeguidance in RFC 08 no longer suggestsrunWithOwner(panelRoot, …)for panel writes (that is a write in an owned scope, see #3512); the store's own nodes carry the excluded owner. -
d826cd3: The client error hook —
configureClientErrors({ onError })fromsolid-js, andrender/hydrate'sonErroroption in@solidjs/web: the prod-tier seam through which an app, or an error monitor, hears the one failure nothing else can see — an<Errored>/createErrorBoundarycollected it and renders its fallback. The twin of the server'sconfigureServerErrors. An uncaught error is not this hook's: the halt (REACTIVITY_HALTED) hands its cause to the platform'sreportError, the channel every monitor already listens on.Once per error object (a
reset()re-collecting the same failure says nothing new; a primitive is reported per sight);ownerPathcarries the component labels where the runtime keeps owner names; no return — the client has no wire to map for; a throwing hook is reported and ignored. A root's own hook wins over the ambient one for failures under it.Pay-for-use: the hook machinery (
core/error-hooks.ts) is retained bycreateErrorBoundaryor the app's ownconfigureClientErrorsimport; a root's hook is parked on the root owner under the registeredROOT_ERROR_HOOKsymbol (defined in the scheduler), sorenderretains nothing for an app that passes none. Core floor unchanged; apps with a boundary +~200 B.The server surface consolidates on the same name.
onErroronrenderToStream/renderToStringand onhandleServerFunctionRequestis the server error hook ((error, context) => wire | void);onServerErroris removed. A one-argumentonErrorwritten for the old shape keeps working and now hears every handled failure — filter oncontext.handling === "failed"for the request-failing ones alone. Newhandling: "serialize"for a hydration value that would not serialize (what seroval'sonErrorreported, for a render that passed one). With no hook anywhere, a failure that fails the request still reachesconsole.error. -
cc0396b:
_parentjoins_nameas a field signals' property mangling reserves — the two cross-package owner fields.Signals' prod and observe artifacts rename every
_-prefixed property except a reserved list; the dev artifact (which the test suites run against) is unmangled. Two things read_parentacross the package boundary and only worked in dev:solid-js's client hydration walksowner._parentto the root to mark the hydration snapshot scope. In the built prod and observe artifacts the walk found nothing and marked the current owner instead, so computations created outside that owner's subtree during hydration read live values rather than the server snapshot.- The core's owner walks —
ownerPathandOBSERVE.exclude/isExcluded— oversolid-js's server owners.ownerPathhad a server-side shim (located(), now removed);OBSERVE.excludewas a silent no-op for a server owner outside dev.
Cost: ~40 B brotli on the prod app scenarios; the observe scenarios did not grow. Pinned from both ends:
packages/solid/test/cross-package-fields.spec.tschecks the reserved fields survive in the mangled artifacts and scans the built client artifacts ofsolid-js,@solidjs/weband@solidjs/universalfor any signals_field that is not reserved;packages/web/test/server/server-owner-walks.spec.tsxrunsownerPathandOBSERVE.excludeover server owners against the built observe and development artifacts.Also: RFC 08 gains "Values in records — the PII surface", the complete list of record and finding fields that carry user data (value previews, interaction target text, navigation paths/params,
data.erroron the server error findings) for exporters that leave the process. -
53280e7: The error hooks and
SSR_RENDER_ERROR_CONTAINEDtell where an error was thrown apart from where it was met.ownerPathonClientErrorContextandServerErrorContextis now where the error was thrown: the labels root-first up the owner chain of the computation that threw — the component that broke — falling back to the boundary's chain when the throw crossed nothing the runtime could name. A newboundaryPathis where it was met: the same labels up the chain of the<Errored>that rendered its fallback (client and server) or the<Loading>that shipped the rejection (server,handling: "client"). Before,ownerPathwas the boundary's on both sides, so every component under one boundary grouped into one path. On the client the engine's status wrapper already named the thrower (StatusError.source); on the server the owner scopes stamp it as the error escapes. TheSSR_RENDER_ERROR_CONTAINEDfinding follows:ownerPathlocates the throw,data.boundary/data.boundaryPaththe boundary. -
3154ed6: A signal written from
onSettledorcreateEffectduring hydration no longer strands the DOM it reveals (#3504). Two halves: a root created while hydrating marks the hydration snapshot scope itself, so a write during the root pass is held until the pass completes and then replays — previously the scope was marked lazily by the first hydration-aware primitive, so a<Show>condition created before that sat outside it and the write cascaded mid-claim. And a streamed boundary's resume window now claims only the subtree under that boundary: a write from the resumed content that reaches a signal above it (a route'sonSettledadding a toast to a provider) re-renders that already-hydrated region as a client render — fresh nodes, live inserts — instead of claiming against the server registry, missing with a "Hydration key miss" warning, and rendering detached. -
b298154: Move the runtimes' seams off the
solid-jssurface and behindsolid-js/internalmerge()/omit()returning lazy views (#3454) gavespread()and
ssrElement()a protocol for reading props leaf by leaf instead of trapping
through the proxy per key. Because@solidjs/weband@solidjs/universal
depend onsolid-jsalone — never on@solidjs/signalsdirectly, so an app
holds exactly one reactive engine — every piece of that protocol went out
throughsolid-js's main export: eleven names,mergeSourcesbefore them in
#3325, on the public surface with no marking. None of it is API.The protocol (
viewOf,mergeView/omitView,MergeView/OmitView,
sourceKeys/sourceHas/sourceGet,hasStaticKeys,resolvedTable, the
SOURCE_*kinds,SourceKind) now lives on asolid-js/internalsubpath,
along with the server-scope seams that were already@internalin JSDoc and
consumed only by@solidjs/web(ssrHandleError,ssrScope,
runInServerComponentScope,inServerComponentScope,creationStamp,
getProjectionTrace,materializeContainerTrace). The names stay exported
from the main entries at runtime, so the subpath shares one module state and
the single-engine guarantee is untouched;stripInternalkeeps them out of
the generated declarations, so TypeScript no longer offers or types them.
internal-surface.spec.tspins the boundary in both directions.Also dropped from the entries:
storeIsShallow,storeHasFamily,
storeHasOptimisticFamily(leftovers of the gutted patch channel),
storePathand$REFRESH(referenced only by@solidjs/signals's own
internals), andNoHydrateContext(@internal, used only bysolid-js's
server code). Nothing in the repo consumed them.No runtime behavior change.
merge,omit, and the rest of the reactive
surface are unaffected. -
899c2c4:
merge()andomit()are always lazy views, and props consumers read their leavesomit(props, ...keys)returns a live view ofpropsfor every input — a plain object included — instead of copying it with agetOwnPropertyDescriptor+definePropertyper prop. A predicate form hides keys by rule without enumerating first:omit(props, k => k[0] === "$").merge()no longer builds an eager copy when its sources are plain objects: underProxyit always returns an O(1) view over the flattened sources (a single non-function source is returned as is).The two compose flat. An
omit()over amerge()carries one filtered view per flattened merge source, amerge()over anomit()takes the view record as a leaf, and nested omits fold their filters into one record. A component chain ofmerge(defaults) → omit(consumed) → merge(statics) → omit("as")— the shape headless-UI libraries render every element through — collapses to leaf views over the original objects, each with its accumulated filter, with no proxy layer left between the outermost spread and the author's props.merge()keeps the omitted keys hidden by construction (#3014) rather than by treating the omit as opaque. Construction cost drops 3–7× at depth 1–7; the SSR polymorphic-chain bench (#3448) runs ~2.4× faster.Reads stay cheap: a view over plain objects resolves a key → owning-leaf table once, on first read, and every
get/has/descriptor is one lookup after that.spread()(DOM and universal) andssrElement()read the leaves directly — never through the proxies' traps — and walk that table when there is one, so an effect rerun costs one read per key, as it did over the copy. Both proxies use a class target and one shared handler (no per-instance closures).A view over a store asks the store nothing but the read. Each source's kind (plain object, omit record, proxy, memo) is decided once, when the view is built, and carried beside it — every brand check on a Proxy is a trap (
instanceofis agetPrototypeOftrap, as expensive as a store read), and store detection goes through$TARGET, a symbol the store'sgettrap answers on its fast path, never its generic tracked-read path.merge(defaults, store)constructs ~30% faster than the copy did and reads ~15% faster;omit(store)reads at parity.The views tell the truth:
Object.getOwnPropertyDescriptor(view, key)reports a data descriptor only when the key is a data property of a plain leaf (the compiler's encoding of a static prop) and an accessor for a getter, a store key, or a memo source. Together with the new internalhasStaticKeys(),spread()now skips the children effect for static children behindomit/mergelayers (#3388 through views).Behavior changes:
- Writes to a
merge()oromit()result are no-ops (they already were for the proxy forms). A caller that needs its own object copies it ({ ...merged }), and the copy carries no sources (#3384).@solidjs/htmlnow collects its own props and spreads into onemerge()at the end instead of assigning onto the result. - A data property on a source is read live through the view rather than snapshotted at
merge()/omit()time. - Key order of a merged view is the merged order — every key at the position of the last source that carries it — matching
ssrElement's array form. - Sources are treated as own-keyed; a key added to a plain source after merging is not seen (the copy did not see it either).
- Enumerating a view through its traps (
for…in,Object.keys,{ ...view }) costs a trap per key, as any proxy does; the internal consumers avoid it. Environments withoutProxykeep the copy paths.
Internal helpers for consumers, exported from
solid-js:viewOf(o),mergeView(o),omitView(o),sourceKeys(entry, kind),sourceHas(entry, kind, key),sourceGet(entry, kind, key),hasStaticKeys(o),resolvedTable(o), theSOURCE_*kinds. - Writes to a
-
3ae9e92:
OBSERVE.records— one records channel on both platforms (observe/dev tiers); frame records from both ends; the client"call"record;observeServerFunctionCallsremoved@solidjs/signals:OBSERVE.records—subscribe(type, listener),observed(type),emit(type, event, live)— the channel every runtime record rides, created once per process and registered onglobalThisunderSymbol.for("@solidjs/signals/observe/records")so a second copy of the core (a bundled server build instrumented through--import) and wire layers bundled without a framework import reach the same listener sets. Listeners are snapshotted per emit; a throwing listener is reported and the rest run. Types:Records,RecordTypes(extendsHostRecordTypes; both declared empty, for the runtimes to augment — one augmenter per interface),RecordType,RecordEvent,RecordLive,RecordListener. Folds out of prod. NewOBSERVE.attribution.currentOrigin()(and thecurrentOriginhook onAttributionHooks): the provenance a root write performed now would be stamped with — the interaction whose handler is running, the navigation/effect/action frame open, or inside a recompute the origin of the change that caused it — as the engine's ownChangeOriginobject,undefinedwhen external or with no engine; for a runtime stamping a record of its own. The installed hooks are also registered onglobalThisunderSymbol.for("@solidjs/signals/observe/attribution"), the same reach-without-an-import the channel has.solid-js: the"boundary"record moves fromOBSERVE.server.recordstoOBSERVE.records(augmenting the core'sRecordTypes).OBSERVE.serverkeeps only thetraceslot;ServerRecordsis gone.@solidjs/web: the"invocation"and"frame"records move toOBSERVE.records(augmentingHostRecordTypesthroughsolid-js). New"call"record (CallEvent,CallLive,CallListener): one per server-function call made from the browser, at the caller's settle —{ id, at, durationMs, method: "GET" | "POST", outcome, status?, origin?, deferred? }with{ args, response?, result? | error? }beside it; joins the server's"invocation"byid, and — throughorigin, the engine's own interaction/navigation object read at dispatch viacurrentOrigin()— the attribution engine'sInteractionEvent/NavigationEvent/HoldEventby identity, so an observer files the call under the click that made it without a time join. The"frame"record now has a client half:FrameEventisFrameProducedEvent | FrameAppliedEvent, discriminated byside, same census on both; the client half (applyFrameResponse, one per stream in a response) addsaddress(theasremap) andoutcome: "truncated"for a body that ended beforecomplete, withlive.response. Server census fix:regionscountshtmlchunks addressed to a child frame id (the former count read a chunk type that does not exist), andshellMsis set by the stream's own shell only. The emitters and their wrappers fold out of the prod client artifacts behind the observe literal (prodapplyFrameResponseand the server-function dispatch are the pre-existing functions, no extra frame or promise hop). The server-functions and frames client entries gainobserveanddevelopmentbuilds and export conditions (server-functions/dist/client.{observe,dev}.js,frames/dist/client.observe.js); the server-functions client is now built with its flags replaced in every tier (before,_SOLID_DEV_there was an unreplaced truthy string).- Removed:
observeServerFunctionCallsand theServerFunctionCall/ServerFunctionRequestCall/ServerFunctionResponseCalltypes, from both server-function entries. Subscribe toOBSERVE.records"call"(client) or"invocation"(server) instead. @solidjs/diagnostics(format v6):artifact.serveris replaced byartifact.records: { boundary, invocation, frame, call }— always present, captured on both platforms including the browser bridge; typesBoundaryRecord,InvocationRecord,FrameRecord(FrameProducedRecord | FrameAppliedRecord),CallRecord(withorigin?: ChangeOrigin),ArtifactRecordsreplace theServer*Record/ArtifactServernames. JSONL: one line per record withtypenaming its table; the meta line'sboundaryCount/invocationCount/frameCountbecomerecordCounts: { boundary, invocation, frame, call }.
-
328580f:
omit()over amerge()is one record that holds the merge's record, not one leaf view per merge sourceAn omit over a merge used to flatten at construction: one
OmitViewplus one combined hidden-key list per flattened leaf, and the nextmerge()copied those entries into its own arrays. On a component chain of defaults + omit + spread (Kobalte-shaped:merge(omit(merge(omit(props)))), four layers) that was ~19 records and as many list copies per element — the largest allocation of the render. The omit now holds theMergeViewrecord itself (a new source kind,SOURCE_MERGE) and is one record however many leaves the merge has; a latermerge()carries it as one entry, and a lateromit()folds into it. Nothing is read through a proxy trap along the way: the entry helpers (sourceKeys/sourceHas/sourceGet,hasStaticKeys, descriptors, the resolved table) recurse into the record by function call.- Reads through the nesting are one walk per read (a nested entry answers presence and value together), and a nested record counts no reads of its own toward the table threshold — the view that was asked decides for the whole tree, and its table is collected in one pass over the leaves rather than one table per layer.
- New
@internalsourceOwners(source, keys, owners): every key of a props source — a plain object, a store, or a merge/omit view — appended in merged order with the object that owns it, in one pass, later sources moving a key to the end.ssrElementcollects any spread that is not plain objects only (a view, a store, the array form with one among them) this way, so each attribute is one direct read of its owner — noinwalk per key through the layers, no key list per leaf, no table, and no per-entry classification (pushEntryis gone). - An omit's
$SOURCESnever answers anything now (previously its filtered leaf views); consumers reach the record throughviewOfand walk it as one filtered entry.
Measured against
next(interleaved, min of N, quiet machine): the tier-1 polymorphic-chain SSR harness allocates 12% less per row (14.6 → 12.8 KB) and is 2–6% faster across the interpreter, Sparkplug, Maglev and TurboFan tiers; the props-chain microbench builds 6–65% faster and builds+consumes 7–31% faster by depth and tier; the omit/merge micro-suite is flat or better in every shape. On the yak-bench SSR lanes with every yak piece on Solid primitives: +7% geomean, +20–36% on the component-composition cases (polymorphic-chain,tabs,multifile-composition), which brings those to parity with yak's hand-rolled runtime. -
61a114c: Server boundary records on
OBSERVE.server.records(observe/dev tiers)- New
"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? }, with the thrown error beside it.idpairs it withSSR_RENDER_ERROR_CONTAINED;passescounts render passes (a sequential chain reads as3+); under a<Reveal>group the record waits for the group's swap soheldMsmeasures how long finished content was held for its siblings. No clock is read without a listener, and the emitter folds out of prod. - The server records channel is
OBSERVE.server.records.subscribe(type, listener)— the server twin ofOBSERVE.attribution.subscribe(type, …).OBSERVE.server.invocations(unreleased) is renamed onto it:subscribe("invocation", …). TheInvocationChanneltype is gone;ServerRecordsis the channel's interface. - Types now layer one augmenter per interface:
@solidjs/signalsdeclaresServerObserveempty;solid-jsaugments it withrecords: ServerRecordsandtrace: ServerTrace, declaring both;@solidjs/webaugments those two through"solid-js".TraceSlotin@solidjs/webis now an alias ofsolid-js'sServerTrace. (Two augmentations of one re-exported interface through different module aliases merge order-dependently in TypeScript — one set was silently lost.) @solidjs/signalsexportsownerPath(subject)— the root-first component-label walk its diagnostics already make — so a record'sownerPathand the finding it pairs with come from the one walk.
- New
-
0d8347a: Server records reach the diagnostics artifact and the dev checks (server-dev-build-plan P4)
@solidjs/diagnosticsartifact format v5:artifact.server: { boundaries, invocations } | nullfoldsOBSERVE.server.recordswhen the scenario runs under the server runtime —captureArtifact(() => renderToStream(…))— one row per<Loading>boundary that waited and per server-function execution;nullfor client captures and the browser bridge. New exported typesArtifactServer,ServerBoundaryRecord,ServerInvocationRecord(mirrors of the runtime'sBoundaryEvent/InvocationEvent; the package still depends on@solidjs/signalsalone). JSONL egress addsboundaryandinvocationlines and the header counts.InvocationEvent.boundary: a direct server-function call made during a<Loading>boundary's render pass carries that boundary's hydration id, the"boundary"record'sid— the join between a boundary's wait and the calls under it.- Two dev checks derived from the boundary facts in
ssrLoadingBoundary:ASYNC_WATERFALLwithdata.side: "server"(passes - 1sequential flights; 2 →info, structured only; 3+ → consolewarn) and a new codeSSR_CLIENT_CONTENT_MASKED(warn,ssr) for client-only content that surfaced only after a real server wait — the server's work discarded, the fallback shown for the wait. Dev tier only; the boundary clock now runs in dev without a listener. solid-js's serveremitFindingkeepsinfofindings off the console (structured channel only), matching the core.
-
7623ce1: Server diagnostics on
OBSERVE.diagnostics;OBSERVE.serverowned bysolid-js's server entryThe server runtime now reports on the same structured channel as the client. Findings — facts about a render, present in observe and dev builds —
SSR_RENDER_ERROR_CONTAINED(a render error a boundary routed;data.handlingisfallback,client, orfailed— the structured face of whatrenderToStream'sonErrorreceives, on the process-wide channel),SSR_SUBTREE_ABANDONED(a failed fragment's pending descendants discarded),SSR_STREAM_ABANDONED(consumer cancelled or sink failed mid-render),LATE_HEADER_WRITE(recorded beside the existing dev throw / prod log),SERVER_FN_ERROR_SANITIZED(the original error the production wire replaced), andFRAME_MARKER_CORRUPTEDfrom the frames client. Checks — dev-only guidance — convert every serverconsole.warnto a code:SERVER_WRITE,REVEAL_IN_RENDER_TO_STRING,LAZY_ASSET_UNMAPPED,PRELOAD_DESCRIPTOR_INVALID,HEAD_TAG_INVALID,BEHAVIOR_CLAIM_DROPPED, andUNRECOGNIZED_INSERT_VALUE(now one code and arenderkind on both platforms);ASYNC_OUTSIDE_LOADING_BOUNDARYon the server records withdata.side: "server"before it throws. Server components are labelled forownerPath(createComponentruns the body under a transparent<Name>owner in observe/dev — no hydration id consumed), soin <App> › <Page>reads the same on both sides, and the server entry installs the same repair-guide console footer as the client. Prod artifacts carry none of it;DiagnosticKindgainsssr,head,render.OBSERVE.server's objects (the invocation listener set, the trace-provider slot) are now created bysolid-js's server entry, once per process underSymbol.for("solid-js/observe/server")onglobalThis, rather than by@solidjs/web's module init: an observer'sinit()that imports onlysolid-jscan subscribe andprovidebefore the web runtime has loaded, and a host that bundles the runtime into its server build and instruments through a--imported module finds one listener set and one provider across both copies. The core keepsserver: {}; the client pays nothing. -
56858e7: The server error hook —
configureServerErrors({ onError })from@solidjs/web,onServerErroronrenderToStream/renderToStringand onhandleServerFunctionRequest: the prod-tier seam through which a server integration hears every failure the runtime handles and may say what the client receives instead (sentry-integration-plan C6, #3468 part 3).Until now a production build reported only the failure that fails a request (
renderToStream'sonError); an<Errored>fallback rendered, a<Loading>fragment rejected and re-rendered by the client, and a server-function throw sanitized onto the wire were observe-tier findings only. The hook is called once per error object, at first sight, with where the failure was met —kind: "render"(fallback/client/failed, with the boundary's hydration id and the component labels when compiled in) orkind: "server-function"(thrown/channel, withfunctionIdanddirectfor an in-process call during SSR) — and the request event. Its return is the wire value: rendered into the fallback, serialized for hydration, sent as the RPC error;undefinedkeeps the default policy (generic outside dev, fidelity in dev); ignored forfailed. A direct server-function call that throws during SSR is reported once as the function's failure, and the boundary that contains it reuses the verdict. Two tiers aswrapInvocationhas: ambient (registered onglobalThisunderSymbol.for("solid-js/server/errors"), shared by a bundled build and an--imported module) and per request, the latter winning. A throwing hook is reported and treated as silent. The hook fires in every tier;onErrorkeeps its meaning.Internals:
solid-js's server entry owns the verdict engine (reportServerError/ssrSanitizeErrorthroughsolid-js/internal, one cache per error object); the SSR context gainserrorPolicyandflushed; the_frrejection reads its verdict at delivery. A rejected async source's own serialized rejection is encoded in the rejection's microtask, ahead of the boundary, and carries the default policy's value — the mapping reaches the boundary's record, which the hydrating client renders from; no tick is added to the error path.SSR_ERROR_SANITIZEDcarriesdata.wire. -
af94f67: Server observe surface:
OBSERVE.serverand the invocation channelOBSERVEgains aserverslot — an augmentableServerObserveinterface declared empty in@solidjs/signals(re-exported bysolid-js), typed and emitted into by@solidjs/web's server runtime, so server-side observability consumers subscribe on the oneOBSERVEobject they already know from the client. The first channel isOBSERVE.server.invocations:subscribe("invocation", (event, live) => …)delivers one{ id, direct, at, durationMs, outcome, deferred? }record per server-function execution — HTTP dispatch and direct SSR calls alike — when it settles, with the request event,request,args, and the result or the error as thrown beside it. Observers, not policy: any number of listeners, none able to alter the call;wrapInvocationremains the single policy hook.@solidjs/webnow publishes observe-tier server artifacts (dist/server.observe.js,server-functions/dist/server.observe.js,frames/dist/server.observe.js) under theobserveexport condition, alongside the existing dev/prod pairs. The surface and every emit site fold out of the prod artifacts. -
e87d694:
{ shallow: true }computed stores keep their leaves raw on every path (#3498). The projection draft no longer wraps a nested value in a draft proxy — leaf identity holds and a frozen leaf can no longer trip a Proxy invariant — and the loading shadow, its commit copy, the SSR draft and snapshots, and the hydration replay shadow copy only the root container instead of JSON-cloning the tree, which turnedDateinto a string,NaNintonull, droppedundefinedproperties, and could not represent BigInt or cycles. Deep stores are unchanged. -
5426ffb: Fix a server-rendered
<Errored>fallback hydrating dead (#3414). When the boundary's children threw synchronously on the server and the fallback was a zero-arg thunk (fallback={() => <Fallback />}), the thunk was handed back unresolved and unwrapped by the enclosing boundary — the client does that inside the boundary's flatten computed, the server did it inline under the boundary owner — so the fallback's hydration keys disagreed, its root element failed to claim the server node, and its event handlers and effects never attached. The serverErroredandLoadingboundaries now resolve their children's result in a virtual id scope mirroring the client's second computed; the error record stays at the boundary id. -
63560a1:
componentNamesnow applies to SSR output. Under the option both compilers keep thecreateComponentcall they otherwise inline toComp(props)and pass the source tag name —createComponent(Comp, props, "Comp")— so the server runtime's observe/devcreateComponentlabels the owner and a server finding'sownerPathreads<App> › <Page>like the client's. Without the option (prod builds) SSR output is unchanged.@solidjs/vite-pluginalready passes the option for its dev and observe postures, so app server builds pick this up with no config change.Fixes
ssrScopeunder transparent owners: the virtual hole scope swapped the current owner's id counter, but content inside a hole resolves ids by walking past transparent owners, so with one in between (the server-component scope owner; now the labelled component owner) the hole's content took ids from the enclosing counter and disagreed with the client. The scope now swaps the nearest id-bearing owner. -
34287d8: SSR render failures reach the client sanitized (#3468): the wire policy the server-function handler has applied since #3113/#3116 now covers every SSR road a failure takes — the error an
<Errored>serializes for hydration, a rejected async source serialized into the stream, a<Loading>fragment's_frrejection, a frame stream's error chunks (the fragment's, a live hole's, the root's). Outside the dev build a plain thrown value is replaced with a genericError("Internal Server Error");message,causeand own properties stay on the server. A"use server"function called in-process during SSR never touches the RPC wire, so before this the production page load shipped what that wire withholds.solid-js(server):<Errored>sanitizes before rendering its fallback and serializes the same replacement, so fallback markup and the hydration record agree. A fallback printingerr().messageshows the generic message in production, as for a server-function failure.markSafeError(Symbol.for("solid.SafeError")) passes through with own properties; an Error reached as a value is data and passes as written (#3113's ruling). One replacement per original, however many roads it takes. New findingSSR_ERROR_SANITIZED(info, observe + dev;data.errorthe original), besideSSR_RENDER_ERROR_CONTAINEDwhich carries the failure itself.ssrSanitizeErroris exposed to the runtimes throughsolid-js/internal. Server findings now carry theirownerPathin the observe artifact too — the core's walk reads_parentunder its own build's property mangling and found nothing on a server owner there, so the server entry locates its findings itself.@solidjs/web(server): the hydration serialize funnel guards every channel it writes (a promise's rejection, an async iterable's thrown step); a fragment's terminal error reaches the_frrejection and a transport sink's error chunk sanitized while the abandonment ledger keeps the original; the frame sink's root error chunk and the live-hole/live-attribute error chunks carry the replacement's message.@solidjs/signals: theSSR_ERROR_SANITIZEDcode.
The dev/prod line is the build variant: the
developmentserver artifacts keep full fidelity; production and observe sanitize. -
Updated dependencies [8ff4803]
-
Updated dependencies [e9c464b]
-
Updated dependencies [0da94f9]
-
Updated dependencies [8bf04ea]
-
Updated dependencies [eaa7e33]
-
Updated dependencies [ebedb44]
-
Updated dependencies [a8a8949]
-
Updated dependencies [d80cd1f]
-
Updated dependencies [d826cd3]
-
Updated dependencies [cc0396b]
-
Updated dependencies [d2a36f5]
-
Updated dependencies [50323b4]
-
Updated dependencies [53280e7]
-
Updated dependencies [d7cb456]
-
Updated dependencies [a0d6dd2]
-
Updated dependencies [c3ae310]
-
Updated dependencies [6095955]
-
Updated dependencies [e80f241]
-
Updated dependencies [84adf0b]
-
Updated dependencies [5c1f01f]
-
Updated dependencies [a5d8eae]
-
Updated dependencies [14ded24]
-
Updated dependencies [25c5064]
-
Updated dependencies [1ce0f85]
-
Updated dependencies [05c7e21]
-
Updated dependencies [9da7f0a]
-
Updated dependencies [62b0a22]
-
Updated dependencies [27b24aa]
-
Updated dependencies [549f482]
-
Updated dependencies [d80cd1f]
-
Updated dependencies [76230f9]
-
Updated dependencies [347a5ca]
-
Updated dependencies [a8a8949]
-
Updated dependencies [632e45c]
-
Updated dependencies [75c5113]
-
Updated dependencies [899c2c4]
-
Updated dependencies [ca05917]
-
Updated dependencies [3ae9e92]
-
Updated dependencies [328580f]
-
Updated dependencies [0bffee2]
-
Updated dependencies [f329a26]
-
Updated dependencies [c827758]
-
Updated dependencies [6e9243c]
-
Updated dependencies [7a09cd9]
-
Updated dependencies [61a114c]
-
Updated dependencies [0d8347a]
-
Updated dependencies [7623ce1]
-
Updated dependencies [af94f67]
-
Updated dependencies [e87d694]
-
Updated dependencies [dd19e9e]
-
Updated dependencies [c245532]
-
Updated dependencies [34287d8]
-
Updated dependencies [64f9266]
-
Updated dependencies [0148d58]
-
Updated dependencies [765a656]
-
Updated dependencies [9db33cf]
-
Updated dependencies [bfd6f6c]
-
Updated dependencies [2054045]
-
Updated dependencies [c410709]
-
Updated dependencies [f555ec2]
-
Updated dependencies [d8e35a3]
-
Updated dependencies [5f7da9d]
-
Updated dependencies [7f5f902]
-
Updated dependencies [31adfce]
- @solidjs/signals@2.0.0-rc.9