@solidjs/web 2.0.0-beta.28 → 2.0.0-beta.29

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. package/dist/dev.cjs +40 -2
  2. package/dist/dev.js +39 -4
  3. package/dist/server.cjs +90 -31
  4. package/dist/server.js +89 -33
  5. package/dist/web.cjs +40 -2
  6. package/dist/web.js +39 -4
  7. package/frames/dist/client.cjs +370 -209
  8. package/frames/dist/client.dev.cjs +370 -210
  9. package/frames/dist/client.dev.js +371 -211
  10. package/frames/dist/client.js +371 -210
  11. package/frames/dist/server.cjs +351 -69
  12. package/frames/dist/server.js +351 -70
  13. package/package.json +3 -3
  14. package/server-functions/dist/client.cjs +87 -3
  15. package/server-functions/dist/client.js +80 -4
  16. package/server-functions/dist/server.cjs +52 -17
  17. package/server-functions/dist/server.js +51 -17
  18. package/types/client.d.ts +15 -0
  19. package/types/core.d.ts +1 -1
  20. package/types/frames/client.d.ts +7 -5
  21. package/types/frames/frame-client.d.ts +17 -0
  22. package/types/frames/frame-sink.d.ts +29 -6
  23. package/types/frames/frame-transport.d.ts +76 -12
  24. package/types/frames/server.d.ts +1 -1
  25. package/types/index.d.ts +74 -0
  26. package/types/server-functions/client.d.ts +24 -0
  27. package/types/server-functions/server.d.ts +75 -16
  28. package/types/server-functions/shared.d.ts +9 -0
  29. package/types/server-mock.d.ts +6 -2
  30. package/types/server.d.ts +39 -1
  31. package/types-cjs/client.d.cts +15 -0
  32. package/types-cjs/core.d.cts +1 -1
  33. package/types-cjs/frames/client.d.cts +7 -5
  34. package/types-cjs/frames/frame-client.d.cts +17 -0
  35. package/types-cjs/frames/frame-sink.d.cts +29 -6
  36. package/types-cjs/frames/frame-transport.d.cts +76 -12
  37. package/types-cjs/frames/server.d.cts +1 -1
  38. package/types-cjs/index.d.cts +74 -0
  39. package/types-cjs/server-functions/client.d.cts +24 -0
  40. package/types-cjs/server-functions/server.d.cts +75 -16
  41. package/types-cjs/server-functions/shared.d.cts +9 -0
  42. package/types-cjs/server-mock.d.cts +6 -2
  43. package/types-cjs/server.d.cts +39 -1
@@ -1,4 +1,11 @@
1
1
  import { FrameChunk, FrameHost } from "./frame-client.js";
2
+ import { JSONCodecOptions } from "./serializer.js";
3
+
4
+ // Structural mirror of server-functions/shared.js's FlightDataConsumer:
5
+ // this file may only reference siblings that ship with it when integrations
6
+ // copy the frames declaration set (solid-web's types build), and the
7
+ // server-functions declarations are copied to a different root.
8
+ type FlightConsumer = (data: unknown, context: { response: Response }) => void | Promise<void>;
2
9
 
3
10
  /**
4
11
  * Header tagging a Response as a frame stream; its value is the producing
@@ -23,9 +30,24 @@ export interface ApplyFrameResponseOptions {
23
30
  * Restamp every chunk of the response with this version (one response IS
24
31
  * one version). Versions belong to the client too: the producer cannot
25
32
  * know how many streams a boundary has consumed, so pass the Nth-response
26
- * counter to make policy A's stale-guard real across navigations.
33
+ * counter to make policy A's stale-guard real across navigations. A
34
+ * single-flight response addresses several boundaries, each with its own
35
+ * history — pass a function and it is called once per frame in the
36
+ * response.
37
+ */
38
+ version?: number | ((frameId: string) => number);
39
+ /**
40
+ * Remap any frame id other than the response's own root onto a local one
41
+ * — how a consumer resolves the addresses a single-flight response uses
42
+ * for the regions it refreshed.
27
43
  */
28
- version?: number;
44
+ route?(id: string): string;
45
+ /**
46
+ * Receives the payload text of each `outcome` chunk — the response-scoped
47
+ * single-flight envelope, the caller's result rather than anything the
48
+ * host renders.
49
+ */
50
+ onOutcome?(payload: string): void;
29
51
  }
30
52
 
31
53
  /**
@@ -48,6 +70,31 @@ export function applyFrameResponse(
48
70
  options?: ApplyFrameResponseOptions
49
71
  ): Promise<string>;
50
72
 
73
+ /** Brands an inline-rendered server component with its function id. */
74
+ export const SERVER_COMPONENT: unique symbol;
75
+
76
+ /** The unwrapped server component behind an inline-render wrap. */
77
+ export const SERVER_COMPONENT_SOURCE: unique symbol;
78
+
79
+ /** The call's wire address (`frameAddress`), for regions to be emitted under. */
80
+ export const SERVER_COMPONENT_ADDRESS: unique symbol;
81
+
82
+ /**
83
+ * Seroval plugin for a server component crossing a serialization boundary:
84
+ * a branded component serializes as a REFERENCE — a per-function document
85
+ * placeholder in the hydration serializer, a live-registry lookup by call
86
+ * address in the JSON codec (single-flight envelopes) — its markup never
87
+ * rides as data.
88
+ */
89
+ export const ServerComponentPlugin: unknown;
90
+
91
+ /**
92
+ * The codec options for a single-flight envelope: `codec` plus
93
+ * `ServerComponentPlugin` (deduped by tag). Injected by the protocol on both
94
+ * legs; exported for integrations composing their own flight carriers.
95
+ */
96
+ export function flightCodec(codec?: JSONCodecOptions): JSONCodecOptions;
97
+
51
98
  /** Options for `createServerComponentHandler`. */
52
99
  export interface ServerComponentHandlerOptions<C = unknown> {
53
100
  host: FrameHost;
@@ -57,12 +104,6 @@ export interface ServerComponentHandlerOptions<C = unknown> {
57
104
  * own frame instance under the boundary id (multi-mount fans out).
58
105
  */
59
106
  component(frameId: string): C;
60
- /**
61
- * Runs synchronously at each server-function call site (before any
62
- * await); its return is the call's ambient identity — e.g. Solid's
63
- * `getOwner`. Calls sharing a captured context share one boundary.
64
- */
65
- capture?(info: { id: string; meta: unknown }): unknown;
66
107
  /**
67
108
  * A new response is about to stream into a boundary: rotate
68
109
  * response-scoped state (codec data tables) here. `version` is the
@@ -83,6 +124,18 @@ export interface ServerComponentHandlerOptions<C = unknown> {
83
124
  * never observes a pending beat.
84
125
  */
85
126
  intercept?(info: { id: string; meta: unknown; args: unknown[] }): C | undefined;
127
+ /**
128
+ * Reads the registered single-flight consumer at delivery time. The
129
+ * consumer is module state in the server-function client's SHARED
130
+ * instance; pass a getter reading that instance when your bundling gives
131
+ * this module a private copy. Defaults to the local copy's reader.
132
+ */
133
+ consumer?(): FlightConsumer | undefined;
134
+ /**
135
+ * Reads the configured codec options at decode time — same instance-
136
+ * identity contract as `consumer`. Defaults to the local copy's reader.
137
+ */
138
+ codec?(): JSONCodecOptions | undefined;
86
139
  }
87
140
 
88
141
  /**
@@ -92,15 +145,26 @@ export interface ServerComponentHandlerOptions<C = unknown> {
92
145
  * (Solid's `dynamic`) never remounts across refetches — the response streams
93
146
  * into the boundary underneath as the only observable effect.
94
147
  *
95
- * Boundary identity is derived, never declared: contexts captured per call
96
- * key a WeakMap of boundaries (dying with their call sites); ownerless calls
97
- * fall back to one boundary per function id.
148
+ * Boundary identity is derived, never declared: every call keys by its
149
+ * intrinsic (function, arguments) address the query cache's per-args rule,
150
+ * so cached components and boundaries stay one-to-one. Same-args calls
151
+ * resolve the identical component and morph in place; an args switch swaps
152
+ * boundaries, re-materialized from the host's retained state.
98
153
  */
99
154
  export function createServerComponentHandler<C>(options: ServerComponentHandlerOptions<C>): {
100
- capture?(info: { id: string; meta: unknown }): unknown;
101
155
  intercept?(info: { id: string; meta: unknown; args: unknown[] }): C | undefined;
102
156
  handle(
103
157
  response: Response,
104
158
  ctx: { id: string; meta: unknown; args: unknown[]; context: unknown }
105
159
  ): C | undefined;
160
+ /**
161
+ * Declares that the document is showing a call: hydration-data references
162
+ * carry their call's address (`_$SC.r(id, address)`) but never travel
163
+ * through the transport, so the integration forwards those records here —
164
+ * they are how a post-load call for the same (function, arguments) finds
165
+ * its way back to the adopted boundary. `component` must be the exact
166
+ * reference the integration's cache holds for the call (the per-function
167
+ * placeholder), or readers' equals-gates fail into remounts.
168
+ */
169
+ showing(address: string, functionId: string, component: C): void;
106
170
  };
@@ -26,5 +26,5 @@ import type { Element as SolidElement } from "solid-js";
26
26
  export type Slot<P = {}> = (props: P & {
27
27
  $key?: string | number;
28
28
  }) => SolidElement;
29
- export { renderToFrameStream, renderServerComponent, serverComponentResponse, frameTransformResult, createFrameSink, frameTransformDirectResult, ServerComponentPlugin, SERVER_COMPONENT_BOOTSTRAP } from "./frame-sink.js";
29
+ export { renderToFrameStream, renderServerComponent, serverComponentResponse, frameTransformResult, frameTransformFlightResult, createFrameSink, frameTransformDirectResult, ServerComponentPlugin, SERVER_COMPONENT_BOOTSTRAP } from "./frame-sink.js";
30
30
  export { FRAME_STREAM_HEADER, isFrameStreamResponse } from "./frame-transport.js";
package/types/index.d.ts CHANGED
@@ -176,3 +176,77 @@ export declare function dynamic<T extends ValidComponent>(source: () => T | Prom
176
176
  * @description https://docs.solidjs.com/reference/components/dynamic
177
177
  */
178
178
  export declare function Dynamic<T extends ValidComponent>(props: DynamicProps<T>): JSX.Element;
179
+ /**
180
+ * Wraps a dynamically imported component so it renders only in the browser.
181
+ * The server renders `props.fallback` (and nothing else); the client shows
182
+ * the fallback until the import resolves and the tree has mounted, then
183
+ * swaps the real component in.
184
+ *
185
+ * Unlike `lazy()`, this avoids Suspense entirely and never server-renders
186
+ * the wrapped component — only the fallback — so the component participates
187
+ * in no hydration asset manifest and its code is guaranteed to never run on
188
+ * the server (safe for browser-only libraries touching `window`, DOM
189
+ * measurement, etc.). The mount gate keeps hydration safe: during hydration
190
+ * the fallback is rendered exactly as the server did, and the swap happens
191
+ * only after settle, so there is no mismatch.
192
+ *
193
+ * By default the import starts as soon as `clientOnly` is called (module
194
+ * load); pass `{ lazy: true }` to defer the import to the component's first
195
+ * render.
196
+ *
197
+ * @example
198
+ * ```tsx
199
+ * const Chart = clientOnly(() => import("./Chart.jsx"));
200
+ * // <Chart fallback={<div>Loading chart…</div>} data={data()} />
201
+ * ```
202
+ */
203
+ export declare function clientOnly<T extends Component<any>>(fn: () => Promise<{
204
+ default: T;
205
+ }>, options?: {
206
+ lazy?: boolean;
207
+ }): Component<ComponentProps<T> & {
208
+ fallback?: JSX.Element;
209
+ }>;
210
+ /**
211
+ * Declares the HTTP response status (and optional status text) for the
212
+ * lifetime of the current reactive scope during SSR — call it bare in a
213
+ * component or reactive-scope body where the status is decided (a 404
214
+ * route, an error fallback). Client build: a no-op — the response head was
215
+ * sent long ago.
216
+ *
217
+ * Naming note — this is a scope-tied *declaration*, not a mutation: "while
218
+ * this reactive scope is live, the response has this status." Solid
219
+ * reserves `set*` verbs for event-time mutation; like
220
+ * `createSignal`/`onCleanup` this is called in scope bodies and un-declares
221
+ * on scope disposal.
222
+ *
223
+ * Retraction semantics (server): the write snapshots the previous
224
+ * `event.response` status at write time and restores it when the owning
225
+ * scope is disposed — so a boundary that errored, declared a status, and
226
+ * then recovered retracts its write instead of stomping a status a
227
+ * surviving part of the tree legitimately set. Once the integration marks
228
+ * the response head `committed` (head derived/sent), writes and
229
+ * retractions are no-ops.
230
+ */
231
+ export declare function httpStatus(_code: number, _text?: string): void;
232
+ /**
233
+ * Declares an HTTP response header (or with `append`, appends to one) for
234
+ * the lifetime of the current reactive scope during SSR — call it bare in a
235
+ * component or reactive-scope body. Client build: a no-op — the response
236
+ * head was sent long ago.
237
+ *
238
+ * Naming note — this is a scope-tied *declaration*, not a mutation: "while
239
+ * this reactive scope is live, the response has this header." Solid
240
+ * reserves `set*` verbs for event-time mutation; like
241
+ * `createSignal`/`onCleanup` this is called in scope bodies and un-declares
242
+ * on scope disposal.
243
+ *
244
+ * Retraction semantics (server): the header's prior value is snapshotted at
245
+ * write time and restored when the owning scope is disposed (deleted if
246
+ * there was none) — a boundary that errors or recovers retracts its writes.
247
+ * Once the integration marks the response head `committed` (head
248
+ * derived/sent), writes and retractions are no-ops.
249
+ */
250
+ export declare function httpHeader(_name: string, _value: string, _options?: {
251
+ append?: boolean;
252
+ }): void;
@@ -2,22 +2,29 @@ import { JSONCodecOptions } from "../serializer.js";
2
2
  import { ServerFunction, ServerFunctionMetadata } from "./shared.js";
3
3
 
4
4
  export {
5
+ ChunkReader,
5
6
  ERROR_HEADER,
6
7
  FLASH_COOKIE,
7
8
  FUNCTION_HEADER,
8
9
  INSTANCE_HEADER,
9
10
  SINGLE_FLIGHT_HEADER,
10
11
  clearFlashCookie,
12
+ createChunk,
11
13
  decodeErrorHeaderValue,
12
14
  decodeResponse,
13
15
  decodeResponsePayload,
16
+ deserializeStream,
14
17
  encodeErrorHeaderValue,
18
+ frameAddress,
19
+ getFlightDataConsumer,
15
20
  getServerFunctionMetadata,
21
+ getServerFunctionsCodec,
16
22
  hasFlashCookie,
17
23
  isServerFunction,
18
24
  subscribeFlightData,
19
25
  withMeta
20
26
  } from "./shared.js";
27
+ export { REVALIDATE_HEADER } from "../response.js";
21
28
  export type {
22
29
  FlightDataConsumer,
23
30
  FlightDataContext,
@@ -174,3 +181,20 @@ export function createServerReference(id: string, name?: string, base?: string):
174
181
  * @internal
175
182
  */
176
183
  export function registerServerReference(): never;
184
+
185
+ /**
186
+ * Identity of the currently executing server function call — see the
187
+ * server entry. Named here so isomorphic code can import the type from
188
+ * either entry.
189
+ */
190
+ export interface ServerFunctionInvocation {
191
+ id: string;
192
+ }
193
+
194
+ /**
195
+ * Client no-op mirror of the server entry's accessor: there is never a
196
+ * server function call in flight on the client, so this always returns
197
+ * undefined. Present so `"use server"` modules that import it stay
198
+ * import-stable in client builds before dead-code elimination.
199
+ */
200
+ export function getServerFunctionInvocation(): ServerFunctionInvocation | undefined;
@@ -32,8 +32,9 @@ import { ServerFunction } from "./shared.js";
32
32
 
33
33
  /**
34
34
  * The request event a server function call runs under: the base
35
- * `RequestEvent` (request + locals) plus `serverOnly`, set when the call is
36
- * an in-process SSR invocation whose result never serializes to a client.
35
+ * `RequestEvent` (request + locals) with `serverOnly` added, set when the
36
+ * call is an in-process SSR invocation whose result never serializes to a
37
+ * client.
37
38
  */
38
39
  export interface ServerFunctionEvent extends RequestEvent {
39
40
  serverOnly?: boolean;
@@ -196,13 +197,36 @@ export interface ServerFunctionsServerConfig {
196
197
  transformResult?(
197
198
  event: ServerFunctionEvent,
198
199
  result: unknown,
199
- context: { instance: string | null; request: Request; thrown?: boolean }
200
+ context: {
201
+ id: string;
202
+ args: unknown[];
203
+ instance: string | null;
204
+ request: Request;
205
+ thrown?: boolean;
206
+ }
200
207
  ): unknown | ResponseEnvelope | Promise<unknown | ResponseEnvelope>;
208
+ /**
209
+ * `transformResult`'s counterpart for the single-flight fold: when a
210
+ * call's flight payload needs a body only a policy knows how to build
211
+ * (frames' `frameTransformFlightResult` — an invalidated entry is
212
+ * markup), this gets first refusal on the `{ value, data }` outcome.
213
+ * Return a `Response` to carry the outcome (call headers and cookies are
214
+ * copied onto it), or `undefined` to decline and keep the plain
215
+ * serialized envelope. A per-request option overrides it.
216
+ */
217
+ transformFlightResult?(
218
+ event: ServerFunctionEvent,
219
+ outcome: { value: unknown; data: unknown },
220
+ context: { id: string; args: unknown[]; instance: string | null; request: Request }
221
+ ): Response | undefined | Promise<Response | undefined>;
201
222
  /**
202
223
  * The in-process mirror of `transformResult` for direct (same-server)
203
224
  * calls during document SSR — e.g. frames' `frameTransformDirectResult`.
204
225
  */
205
- transformDirectResult?(value: unknown, options: { id: string }): unknown;
226
+ transformDirectResult?(
227
+ value: unknown,
228
+ options: { id: string; args: unknown[]; event: ServerFunctionEvent }
229
+ ): unknown;
206
230
  /**
207
231
  * Server-wide response builder for calls made without the client runtime
208
232
  * (see `handleNoJS` in `HandleServerFunctionRequestOptions`); a
@@ -333,17 +357,33 @@ export function GET<A extends readonly any[], R>(
333
357
  fn: (...args: A) => R
334
358
  ): ServerFunction<A, Awaited<R>>;
335
359
 
336
- /** Identity of the currently executing server function. */
337
- export interface ServerFunctionMeta {
360
+ /** Identity of the currently executing server function call. */
361
+ export interface ServerFunctionInvocation {
338
362
  id: string;
339
363
  }
340
364
 
341
365
  /**
342
- * Reads the calling server function's meta (its id) off the current request
343
- * event — usable inside a server function body, e.g. to key caches or logs
344
- * by function. Returns undefined outside a server function call.
366
+ * Reads the in-flight server function invocation (its id) for the current
367
+ * request event — usable inside a server function body, e.g. to key caches
368
+ * or logs by function. Returns undefined outside a server function call.
369
+ * The state lives in a module-private WeakMap keyed by the per-call request
370
+ * event (never in `event.locals`, which derived events share with their
371
+ * outer event). Distinct from `getServerFunctionMetadata(fn)`, which reads
372
+ * a reference's static declaration metadata; this describes the call
373
+ * currently executing.
374
+ */
375
+ export function getServerFunctionInvocation(): ServerFunctionInvocation | undefined;
376
+
377
+ /**
378
+ * The event-keyed half of `getServerFunctionInvocation`, for callers handed
379
+ * an event outside its provideEvent scope (the handler's result transforms
380
+ * run after the scope has exited). Integration plumbing — application code
381
+ * reads the ambient accessor instead.
382
+ * @internal
345
383
  */
346
- export function getServerFunctionMeta(): ServerFunctionMeta | undefined;
384
+ export function getEventServerFunctionInvocation(
385
+ event: RequestEvent | undefined
386
+ ): ServerFunctionInvocation | undefined;
347
387
 
348
388
  /**
349
389
  * Hooks layering framework policy onto `handleServerFunctionRequest`.
@@ -367,16 +407,26 @@ export interface HandleServerFunctionOptions {
367
407
  * extension point for response metadata policies (headers, statuses,
368
408
  * substituted results). Runs for returned and thrown results alike
369
409
  * (`context.thrown` distinguishes); `context.instance` is null for no-JS
370
- * calls. Return the result unchanged to pass through, or a
371
- * `ResponseEnvelope` (exposed through the core entry) to send HTTP
372
- * metadata plus a structured payload. Runs before `collectFlightData`,
373
- * so the flight hook sees the transformed outcome use
374
- * `collectFlightData`, not this, to fold data into the response.
410
+ * calls. The context carries the call's identity the function `id` and
411
+ * the parsed `args` the implementation was invoked with matching the
412
+ * direct-call mirror (`transformDirectResult`), so a policy keying state
413
+ * by the call works over either dispatch path. Return the result
414
+ * unchanged to pass through, or a `ResponseEnvelope` (exposed through
415
+ * the core entry) to send HTTP metadata plus a structured payload. Runs
416
+ * before `collectFlightData`, so the flight hook sees the transformed
417
+ * outcome — use `collectFlightData`, not this, to fold data into the
418
+ * response.
375
419
  */
376
420
  transformResult?(
377
421
  event: ServerFunctionEvent,
378
422
  result: unknown,
379
- context: { instance: string | null; request: Request; thrown?: boolean }
423
+ context: {
424
+ id: string;
425
+ args: unknown[];
426
+ instance: string | null;
427
+ request: Request;
428
+ thrown?: boolean;
429
+ }
380
430
  ): unknown | ResponseEnvelope | Promise<unknown | ResponseEnvelope>;
381
431
  /**
382
432
  * Overrides the configured single-flight hook for this handler — same
@@ -384,6 +434,15 @@ export interface HandleServerFunctionOptions {
384
434
  * `CollectFlightDataHook`).
385
435
  */
386
436
  collectFlightData?: CollectFlightDataHook;
437
+ /**
438
+ * Overrides the configured single-flight fold policy for this handler —
439
+ * same contract as the `transformFlightResult` config option.
440
+ */
441
+ transformFlightResult?(
442
+ event: ServerFunctionEvent,
443
+ outcome: { value: unknown; data: unknown },
444
+ context: { id: string; args: unknown[]; instance: string | null; request: Request }
445
+ ): Response | undefined | Promise<Response | undefined>;
387
446
  /**
388
447
  * Builds the response for calls made without the client runtime (no
389
448
  * instance header — no-JS form posts, direct HTTP). Receives the
@@ -434,3 +434,12 @@ export class ChunkReader {
434
434
  constructor(stream: ReadableStream<Uint8Array>);
435
435
  next(): Promise<{ done: boolean; value: string | undefined }>;
436
436
  }
437
+
438
+ /**
439
+ * The intrinsic wire address of a server-component call: the function id,
440
+ * suffixed with a realm-stable hash of the arguments when there are any.
441
+ * Both peers derive it independently — the server names flight regions with
442
+ * it, the client routes them by it — so it must stay deterministic across
443
+ * realms and releases.
444
+ */
445
+ export function frameAddress(id: string, args?: readonly unknown[]): string;
@@ -63,7 +63,10 @@ export declare function renderToStringAsync<T>(fn: () => T, options?: {
63
63
  * boundaries settle. Good for time-to-first-byte sensitive pages.
64
64
  *
65
65
  * Returns an object with `pipe`/`pipeTo` for piping to a Node `Writable` or
66
- * a Web `WritableStream`, plus a `then` for awaiting full completion.
66
+ * a Web `WritableStream`, a lazy `readable` byte-stream view for
67
+ * `new Response(stream.readable)`, plus a `then` for awaiting full
68
+ * completion. `pipe`, `pipeTo`, and `readable` each consume the render —
69
+ * use exactly one of the three.
67
70
  *
68
71
  * @example
69
72
  * ```tsx
@@ -73,7 +76,7 @@ export declare function renderToStringAsync<T>(fn: () => T, options?: {
73
76
  * renderToStream(() => <App />).pipe(res);
74
77
  *
75
78
  * // Web (Workers / Deno):
76
- * await renderToStream(() => <App />).pipeTo(stream.writable);
79
+ * return new Response(renderToStream(() => <App />).readable);
77
80
  * ```
78
81
  */
79
82
  export declare function renderToStream<T>(fn: () => T, options?: {
@@ -102,6 +105,7 @@ export declare function renderToStream<T>(fn: () => T, options?: {
102
105
  end: () => void;
103
106
  }) => void;
104
107
  pipeTo: (writable: WritableStream) => Promise<void>;
108
+ readonly readable: ReadableStream<Uint8Array>;
105
109
  };
106
110
  /**
107
111
  * Compiler primitive — emitted by JSX-DOM-Expressions for tagged-template
package/types/server.d.ts CHANGED
@@ -97,6 +97,17 @@ export function renderToStream<T>(
97
97
  then: (fn: (html: string) => void) => void;
98
98
  pipe: (writable: { write: (v: string) => void; end: () => void }) => void;
99
99
  pipeTo: (writable: WritableStream) => Promise<void>;
100
+ /**
101
+ * Lazy `ReadableStream<Uint8Array>` view of the render — hand it straight
102
+ * to `new Response(stream.readable)`. First access starts the render
103
+ * piping through an internal `TransformStream` (chunks are UTF-8 encoded
104
+ * bytes, the same as `pipeTo` writes) and the stream is cached, so
105
+ * repeated access returns the same instance. Like `pipe`/`pipeTo`, this
106
+ * consumes the render: use exactly one of the three — mixing distinct
107
+ * consumers (`readable` after `pipe`/`pipeTo`, or vice versa) throws an
108
+ * error naming the conflict.
109
+ */
110
+ readonly readable: ReadableStream<Uint8Array>;
100
111
  };
101
112
 
102
113
  export function HydrationScript(props: { nonce?: string; eventNames?: string[] }): JSX.Element;
@@ -140,10 +151,37 @@ export function generateHydrationScript(options?: {
140
151
  * @internal
141
152
  */
142
153
  export declare const RequestContext: unique symbol;
154
+ /**
155
+ * The mutable response head an integration's handler exposes on the request
156
+ * event as `event.response`: status/statusText/headers it will apply when
157
+ * sending the response. A scaffold, not a `Response` — application code
158
+ * (e.g. JSX response components) writes to it during render, and the
159
+ * handler reads it when the head goes out. Core does not declare the
160
+ * `response` property on `RequestEvent` itself: integrations that provide
161
+ * one declare it through module augmentation (as `@solidjs/router` does),
162
+ * and this type names the shape they agree on. Core's server-function
163
+ * handler reads its `Set-Cookie` headers when folding single-flight
164
+ * cookies but never requires it.
165
+ */
166
+ export interface ResponseStub {
167
+ status?: number;
168
+ statusText?: string;
169
+ headers: Headers;
170
+ /**
171
+ * Set by the integration once the response head has been derived/sent
172
+ * from this stub — status and headers can no longer change. Consumers
173
+ * that write response metadata during render (e.g. JSX response
174
+ * components) must treat later status/header writes and cleanup-time
175
+ * retractions as no-ops.
176
+ */
177
+ committed?: boolean;
178
+ }
179
+
143
180
  /**
144
181
  * The per-request context available on the server: the incoming `Request`
145
182
  * and a `locals` bag integrations and middleware can hang state on.
146
- * Frameworks typically extend this shape with richer fields.
183
+ * Frameworks typically extend this shape with richer fields (e.g. a
184
+ * `response` head — see `ResponseStub`).
147
185
  */
148
186
  export interface RequestEvent {
149
187
  request: Request;
@@ -148,6 +148,21 @@ export function generateHydrationScript(options?: {
148
148
  eventNames?: string[];
149
149
  }): string;
150
150
  export function Assets(props: { children?: JSX.Element }): JSX.Element;
151
+ /**
152
+ * See the server entry's `ResponseStub` — the shape of the mutable response
153
+ * head integrations expose as `event.response` via module augmentation.
154
+ */
155
+ export interface ResponseStub {
156
+ status?: number;
157
+ statusText?: string;
158
+ headers: Headers;
159
+ /**
160
+ * Set by the integration once the response head has been derived/sent
161
+ * from this stub (status/headers can no longer change); consumers must
162
+ * treat later writes and cleanup-time retractions as no-ops.
163
+ */
164
+ committed?: boolean;
165
+ }
151
166
  export interface RequestEvent {
152
167
  request: Request;
153
168
  locals: Record<string | number | symbol, any>;
@@ -1,4 +1,4 @@
1
- export { getOwner, runWithOwner, createComponent, createRoot as root, sharedConfig, untrack, merge as mergeProps, flatten, ssrHandleError, ssrScope, NoHydration, Hydration } from "solid-js";
1
+ export { getOwner, runWithOwner, createComponent, createRoot as root, sharedConfig, untrack, merge as mergeProps, flatten, ssrHandleError, ssrScope, NoHydration, Hydration, runInServerComponentScope } from "solid-js";
2
2
  export declare const effect: (fn: any, effectFn: any, options: any) => void;
3
3
  export declare const memo: (fn: any) => import("solid-js").SourceAccessor<any>;
4
4
  export declare const runWithHydrationScope: (id: any, fn: any) => unknown;
@@ -5,11 +5,13 @@ export type { Slot } from "./server.cjs";
5
5
  export declare function getFrameHost(): any;
6
6
  /**
7
7
  * Installs the server-component transport policy on the server-function
8
- * client: boundary identity derives from the reactive owner captured at
9
- * each call site (`getOwner`), so distinct `dynamic()` sources get
10
- * independent boundaries with nothing declared, refetches from the same
11
- * source resolve to the identical component, and ownerless calls fall back
12
- * to one boundary per function id.
8
+ * client: boundary identity is the call's intrinsic (function, arguments)
9
+ * address per-args, exactly like the query cache, so a cached component
10
+ * always mounts the boundary showing the call it was cached for. Repeat
11
+ * calls for the same args resolve the identical component (refetches morph
12
+ * in place, cache hits pass `dynamic`'s equals-gate); a source switching
13
+ * args swaps boundaries, re-materialized instantly from the host's
14
+ * retained state.
13
15
  *
14
16
  * Call once in the client entry (an explicit call — the package is
15
17
  * `sideEffects: false`, so a bare import would be tree-shaken away);
@@ -71,6 +71,14 @@ export interface SlotContext {
71
71
  * re-call displaced (e.g. `{$frame}` region ranges) is dropped.
72
72
  */
73
73
  adopted?: boolean;
74
+ /**
75
+ * Whether this occurrence is a render-prop CALL (the producer placed it
76
+ * with arguments — possibly empty — via a slot record) as opposed to a
77
+ * direct-insert position. Consumers cannot tell from the resolved props
78
+ * alone: an argless render prop and a direct insert both arrive as `{}`,
79
+ * but one is a function to invoke and the other a value to place.
80
+ */
81
+ invoked?: boolean;
74
82
  /**
75
83
  * Register cleanup for when this occurrence's range is removed from the
76
84
  * server content, or the owning frame is disposed.
@@ -83,6 +91,15 @@ export interface SlotContext {
83
91
  * in place (zero DOM mutation).
84
92
  */
85
93
  existing: ChildNode[];
94
+ /**
95
+ * The range's own marker comments, when the occurrence has a placed range.
96
+ * A framework binding whose slot content is reactive at the top level (a
97
+ * boundary accessor, changing route children) owns the interior instead of
98
+ * returning nodes: bind before `end` with the framework's insert primitive
99
+ * and return `undefined` — the frame leaves the range alone (server morphs
100
+ * already protect slot ranges).
101
+ */
102
+ range?: { start: Comment; end: Comment };
86
103
  }
87
104
 
88
105
  /**
@@ -126,16 +126,39 @@ export function createDocumentSlotProps(
126
126
  * as `configureServerFunctionsServer({ transformDirectResult })` and a
127
127
  * direct (same-process) server-function result that is a function comes back
128
128
  * as an inline-renderable server component (frame markers + document
129
- * slot props). Non-function results pass through.
129
+ * slot props), branded with its function id and the call's wire address.
130
+ * Non-function results pass through.
130
131
  */
131
- export function frameTransformDirectResult<T>(value: T, options: { id: string }): T;
132
+ export function frameTransformDirectResult<T>(
133
+ value: T,
134
+ options: { id: string; args?: unknown[] }
135
+ ): T;
132
136
 
133
137
  /**
134
- * Seroval plugin for the hydration serializer: writes an inline server
135
- * component as a stable per-function-id placeholder reference
136
- * (`self._$SC.r(id)`) instead of meeting an unserializable function.
138
+ * The frame half of single-flight, as a `transformFlightResult` policy for
139
+ * `handleServerFunctionRequest`: when part of what a mutation invalidated is
140
+ * markup (a component-valued flight-data entry), the frame stream carries
141
+ * the whole payload — each component's content as a region addressed by its
142
+ * call, the `{ value, data }` envelope as `outcome` chunks with the
143
+ * component entries serialized as flight references. Returns `undefined`
144
+ * when nothing invalidated is markup (the response stays the plain
145
+ * single-flight envelope).
137
146
  */
138
- export const ServerComponentPlugin: unknown;
147
+ export function frameTransformFlightResult(
148
+ event: unknown,
149
+ outcome: { value: unknown; data: unknown },
150
+ context?: unknown
151
+ ): Promise<Response | undefined>;
152
+
153
+ // The brands and the codec plugin live with the transport (client bundles
154
+ // resolve flight references against the live registry); re-exported here for
155
+ // server integrations importing the document-SSR surface.
156
+ export {
157
+ SERVER_COMPONENT,
158
+ SERVER_COMPONENT_ADDRESS,
159
+ SERVER_COMPONENT_SOURCE,
160
+ ServerComponentPlugin
161
+ } from "./frame-transport.cjs";
139
162
 
140
163
  /**
141
164
  * Inline bootstrap for the document shell: installs the `self._$SC`