You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
RFC 10 follow-up: expose declaration metadata to invocation hooks #3058
The server-function metadata channel is available to client transport policy through prepareRequest(init, { id, meta }). The server-side wrapInvocation context currently exposes id, args, event, request, and direct, but not declaration metadata.
This leaves external middleware and policy packages without a stable way to select per-function behavior. They must either repeat policy inside every function body or key global policy by generated function ids. The registered HTTP implementation is the raw function, so getServerFunctionMetadata(getServerFunction(id)) does not recover metadata attached to the server reference.
Proposed scope
Make declaration metadata available during server invocation, for both HTTP dispatch and direct SSR calls. For example:
wrapInvocation(run,{
id,
meta,
args,
event,
request,
direct
});
This may require server-side metadata bookkeeping keyed by function id. The metadata should remain local declaration data and should not be serialized over the wire.
Why core
This does not ask core to implement middleware chains, authentication, or validation. It completes the mechanism that lets external packages build those policies while preserving direct-call and HTTP-dispatch parity.
Implemented by #3059 — wrapInvocation now receives declaration meta on both HTTP dispatch and direct SSR calls, without serializing it over the wire. On next for the next rc.
Reopened: #3059 was merged without proper design review and has been reverted (2c90ae6). See the discussion there — the direction we'd take is registration-scoped metadata (declared where dispatch structurally finds it) rather than harvesting call-path reference decorations into an id-keyed map.
HTTP dispatch and direct SSR would then execute the same wrapped function. The client side can target Solid's createServerReference while retaining TanStack's ergonomic API.
That keeps the boundary clear:
Solid owns function identity, registration, transport and dispatch.
TanStack owns middleware, validation and typed context.
Solid does not recognize createServerFn or middleware declarations.
withMeta remains call-path transport metadata.
Keeping a small semantic compiler is not the same as keeping a separate protocol. TanStack can compile its higher-level API to Solid's runtime ABI without requiring Solid to understand those semantics.
This narrows the remaining question for #3058: is there an independent consumer that needs declarative registration metadata to be inspected centrally through wrapInvocation, rather than composing policy into the raw registered implementation? The TanStack procedure example alone does not establish that requirement.
The useful next step is therefore an adapter prototype targeting Solid's registration ABI. If it exposes something that cannot be expressed by registering the composed implementation, that concrete failure can define the smaller core mechanism.
Agreed with your narrowing, and closing on it — with the reopen condition explicit.
The standing rule ("mechanism waits for a concrete consumer") is doing the work here, and it has a good track record this cycle: invoke (#3057) landed cleanly because it waited for the data-layer cancellation case to concretize, and just this week we cut speculative invocation-channel forwarding from the router primitives for the mirror-image reason — semantics with no consumer are load-bearing the day they ship and revisable never. Registration-scoped metadata is in the same spot: the #3059 revert already sketched the shape that survives the design objection (attached at registerServerReference time, where dispatch structurally can't miss it — making getServerFunctionMetadata(getServerFunction(id)) just work, with wrapInvocation's context unchanged), but the shape without a consumer is exactly what we don't ship.
Your TanStack lowering is the sanctioned answer, and it's worth stating that it isn't a workaround: registering the composed implementation IS the model. Export-value registration exists precisely so policy composes into the thing dispatch executes, identically on HTTP and in-process SSR. A layer with its own compiler targeting registerServerReference(id, applyMiddleware(handler, mw)) is using the ABI as designed — Solid owns identity/transport/dispatch, the layer owns its semantics, and core recognizes nothing.
For the adapter prototype, here's what a genuine composition-inexpressible would look like — the two places a composed implementation is structurally too late or too opaque:
Pre-decode policy. The composed function runs after argument deserialization. Per-function body-size caps, content-type gates, or rate limits that must reject before parsing the payload need dispatch-visible metadata — the handler has to read policy off the id before it touches the body.
Central introspection. Enumerating registered functions with their policy (manifests, generated docs, route tables). Composition hides policy inside closures; only registration-scoped data can be walked.
If the prototype (or any real consumer) hits either of those — or a third we haven't named — that concrete failure reopens this and defines the mechanism, and most of its design is already written down in the #3059 revert comment. Until then the registered implementation is the composition point, and withMeta stays what it is: call-path transport metadata for the paths that actually go through the reference.
Activity
ryansolid commentedon Aug 25, 2026
Implemented by #3059 —
wrapInvocationnow receives declarationmetaon both HTTP dispatch and direct SSR calls, without serializing it over the wire. Onnextfor the next rc.ryansolid commentedon Aug 25, 2026
Reopened: #3059 was merged without proper design review and has been reverted (2c90ae6). See the discussion there — the direction we'd take is registration-scoped metadata (declared where dispatch structurally finds it) rather than harvesting call-path reference decorations into an id-keyed map.
birkskyum commentedon Aug 26, 2026
Following up with a concrete consumer after the #3059 revert: TanStack Start does not necessarily require a new registration-metadata API.
A TanStack-style layer can keep its fluent semantics:
Its compiler can lower the server side conceptually to a composed implementation registered with Solid:
HTTP dispatch and direct SSR would then execute the same wrapped function. The client side can target Solid's
createServerReferencewhile retaining TanStack's ergonomic API.That keeps the boundary clear:
createServerFnor middleware declarations.withMetaremains call-path transport metadata.Keeping a small semantic compiler is not the same as keeping a separate protocol. TanStack can compile its higher-level API to Solid's runtime ABI without requiring Solid to understand those semantics.
This narrows the remaining question for #3058: is there an independent consumer that needs declarative registration metadata to be inspected centrally through
wrapInvocation, rather than composing policy into the raw registered implementation? The TanStack procedure example alone does not establish that requirement.The useful next step is therefore an adapter prototype targeting Solid's registration ABI. If it exposes something that cannot be expressed by registering the composed implementation, that concrete failure can define the smaller core mechanism.
ryansolid commentedon Aug 28, 2026
Agreed with your narrowing, and closing on it — with the reopen condition explicit.
The standing rule ("mechanism waits for a concrete consumer") is doing the work here, and it has a good track record this cycle:
invoke(#3057) landed cleanly because it waited for the data-layer cancellation case to concretize, and just this week we cut speculative invocation-channel forwarding from the router primitives for the mirror-image reason — semantics with no consumer are load-bearing the day they ship and revisable never. Registration-scoped metadata is in the same spot: the #3059 revert already sketched the shape that survives the design objection (attached atregisterServerReferencetime, where dispatch structurally can't miss it — makinggetServerFunctionMetadata(getServerFunction(id))just work, withwrapInvocation's context unchanged), but the shape without a consumer is exactly what we don't ship.Your TanStack lowering is the sanctioned answer, and it's worth stating that it isn't a workaround: registering the composed implementation IS the model. Export-value registration exists precisely so policy composes into the thing dispatch executes, identically on HTTP and in-process SSR. A layer with its own compiler targeting
registerServerReference(id, applyMiddleware(handler, mw))is using the ABI as designed — Solid owns identity/transport/dispatch, the layer owns its semantics, and core recognizes nothing.For the adapter prototype, here's what a genuine composition-inexpressible would look like — the two places a composed implementation is structurally too late or too opaque:
If the prototype (or any real consumer) hits either of those — or a third we haven't named — that concrete failure reopens this and defines the mechanism, and most of its design is already written down in the #3059 revert comment. Until then the registered implementation is the composition point, and
withMetastays what it is: call-path transport metadata for the paths that actually go through the reference.