@solidjs/web 2.0.0-beta.27 → 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 +113 -37
  4. package/dist/server.js +112 -39
  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 +373 -74
  12. package/frames/dist/server.js +373 -75
  13. package/package.json +3 -3
  14. package/server-functions/dist/client.cjs +100 -3
  15. package/server-functions/dist/client.js +92 -4
  16. package/server-functions/dist/server.cjs +85 -17
  17. package/server-functions/dist/server.js +83 -17
  18. package/types/client.d.ts +15 -0
  19. package/types/core.d.ts +1 -1
  20. package/types/frames/client.d.ts +8 -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 +29 -1
  25. package/types/index.d.ts +74 -0
  26. package/types/server-functions/client.d.ts +25 -0
  27. package/types/server-functions/server.d.ts +105 -18
  28. package/types/server-functions/shared.d.ts +22 -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 +8 -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 +29 -1
  38. package/types-cjs/index.d.cts +74 -0
  39. package/types-cjs/server-functions/client.d.cts +25 -0
  40. package/types-cjs/server-functions/server.d.cts +105 -18
  41. package/types-cjs/server-functions/shared.d.cts +22 -0
  42. package/types-cjs/server-mock.d.cts +6 -2
  43. package/types-cjs/server.d.cts +39 -1
@@ -11,6 +11,7 @@ export {
11
11
  clearFlashCookie,
12
12
  decodeErrorHeaderValue,
13
13
  decodeResponse,
14
+ decodeResponsePayload,
14
15
  encodeErrorHeaderValue,
15
16
  getServerFunctionMetadata,
16
17
  hasFlashCookie,
@@ -31,8 +32,9 @@ import { ServerFunction } from "./shared.js";
31
32
 
32
33
  /**
33
34
  * The request event a server function call runs under: the base
34
- * `RequestEvent` (request + locals) plus `serverOnly`, set when the call is
35
- * 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.
36
38
  */
37
39
  export interface ServerFunctionEvent extends RequestEvent {
38
40
  serverOnly?: boolean;
@@ -68,6 +70,29 @@ export interface ServerFunctionOutcome {
68
70
  request: Request;
69
71
  /** Whether the result was thrown rather than returned. */
70
72
  thrown: boolean;
73
+ /**
74
+ * The URL the client will show after the mutation — the redirect
75
+ * `Location` when the outcome carries one (resolved against the request
76
+ * URL, as a browser would), the referring page otherwise. Undefined
77
+ * without a usable referer (a non-browser caller has no page to produce
78
+ * data for) and for redirects leaving the app's origin: produce no data
79
+ * when this is undefined.
80
+ */
81
+ targetUrl: string | undefined;
82
+ /**
83
+ * The outcome's `X-Revalidate` keys, split — the invalidation scope the
84
+ * mutation declared. Undefined when the outcome carries none (integrations
85
+ * typically collect everything for the target in that case).
86
+ */
87
+ revalidateKeys: string[] | undefined;
88
+ /**
89
+ * The request headers with the mutation's cookie effects applied: the
90
+ * event response's `Set-Cookie`s (set during the call), then the
91
+ * outcome's own (e.g. `redirect(to, { headers })`), later winning on
92
+ * conflict, deletions honored. Build the data-collection request from
93
+ * these so re-run reads observe post-mutation cookie state.
94
+ */
95
+ foldedHeaders: Headers;
71
96
  }
72
97
 
73
98
  /**
@@ -82,8 +107,12 @@ export interface ServerFunctionOutcome {
82
107
  * Runs after `transformResult`, only for scripted calls that sent
83
108
  * `SINGLE_FLIGHT_HEADER` on the request, on returned results and thrown
84
109
  * `Response`/`ResponseEnvelope` control-flow signals alike (plain thrown
85
- * errors never collect). The handler owns the enveloping: contributed data
86
- * ships as `{ value, data }` under the single-flight response header.
110
+ * errors never collect, and neither do raw body-carrying `Response` values
111
+ * those are the caller's verbatim payload). The handler owns the
112
+ * enveloping: contributed data ships as `{ value, data }` under the
113
+ * single-flight response header. The generic halves of collection arrive
114
+ * pre-digested on the outcome (`targetUrl`, `revalidateKeys`,
115
+ * `foldedHeaders`); the hook supplies only the data strategy.
87
116
  */
88
117
  export type CollectFlightDataHook = (
89
118
  event: ServerFunctionEvent,
@@ -168,13 +197,36 @@ export interface ServerFunctionsServerConfig {
168
197
  transformResult?(
169
198
  event: ServerFunctionEvent,
170
199
  result: unknown,
171
- 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
+ }
172
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>;
173
222
  /**
174
223
  * The in-process mirror of `transformResult` for direct (same-server)
175
224
  * calls during document SSR — e.g. frames' `frameTransformDirectResult`.
176
225
  */
177
- transformDirectResult?(value: unknown, options: { id: string }): unknown;
226
+ transformDirectResult?(
227
+ value: unknown,
228
+ options: { id: string; args: unknown[]; event: ServerFunctionEvent }
229
+ ): unknown;
178
230
  /**
179
231
  * Server-wide response builder for calls made without the client runtime
180
232
  * (see `handleNoJS` in `HandleServerFunctionRequestOptions`); a
@@ -305,17 +357,33 @@ export function GET<A extends readonly any[], R>(
305
357
  fn: (...args: A) => R
306
358
  ): ServerFunction<A, Awaited<R>>;
307
359
 
308
- /** Identity of the currently executing server function. */
309
- export interface ServerFunctionMeta {
360
+ /** Identity of the currently executing server function call. */
361
+ export interface ServerFunctionInvocation {
310
362
  id: string;
311
363
  }
312
364
 
313
365
  /**
314
- * Reads the calling server function's meta (its id) off the current request
315
- * event — usable inside a server function body, e.g. to key caches or logs
316
- * 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
317
383
  */
318
- export function getServerFunctionMeta(): ServerFunctionMeta | undefined;
384
+ export function getEventServerFunctionInvocation(
385
+ event: RequestEvent | undefined
386
+ ): ServerFunctionInvocation | undefined;
319
387
 
320
388
  /**
321
389
  * Hooks layering framework policy onto `handleServerFunctionRequest`.
@@ -339,16 +407,26 @@ export interface HandleServerFunctionOptions {
339
407
  * extension point for response metadata policies (headers, statuses,
340
408
  * substituted results). Runs for returned and thrown results alike
341
409
  * (`context.thrown` distinguishes); `context.instance` is null for no-JS
342
- * calls. Return the result unchanged to pass through, or a
343
- * `ResponseEnvelope` (exposed through the core entry) to send HTTP
344
- * metadata plus a structured payload. Runs before `collectFlightData`,
345
- * so the flight hook sees the transformed outcome use
346
- * `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.
347
419
  */
348
420
  transformResult?(
349
421
  event: ServerFunctionEvent,
350
422
  result: unknown,
351
- 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
+ }
352
430
  ): unknown | ResponseEnvelope | Promise<unknown | ResponseEnvelope>;
353
431
  /**
354
432
  * Overrides the configured single-flight hook for this handler — same
@@ -356,6 +434,15 @@ export interface HandleServerFunctionOptions {
356
434
  * `CollectFlightDataHook`).
357
435
  */
358
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>;
359
446
  /**
360
447
  * Builds the response for calls made without the client runtime (no
361
448
  * instance header — no-JS form posts, direct HTTP). Receives the
@@ -405,6 +405,19 @@ export function decodeResponse<T = unknown>(
405
405
  codecOptions?: JSONCodecOptions
406
406
  ): Promise<T | undefined>;
407
407
 
408
+ /**
409
+ * `decodeResponse` plus the single-flight envelope split: when the response
410
+ * carries the single-flight header the decoded `{ value, data }` payload is
411
+ * unwrapped into `{ value, flightData }`; otherwise the decoded body (or
412
+ * undefined for body-less responses) rides as `{ value }`. Integrations
413
+ * that apply response metadata themselves use this so the payload shape
414
+ * stays core's own.
415
+ */
416
+ export function decodeResponsePayload<T = unknown, D = unknown>(
417
+ response: Response,
418
+ codecOptions?: JSONCodecOptions
419
+ ): Promise<{ value: T | undefined; flightData?: D }>;
420
+
408
421
  /**
409
422
  * Frame one payload for the server-function wire: a `;0x<len32>;` length
410
423
  * prefix followed by the utf-8 data. Both transports (server-function
@@ -421,3 +434,12 @@ export class ChunkReader {
421
434
  constructor(stream: ReadableStream<Uint8Array>);
422
435
  next(): Promise<{ done: boolean; value: string | undefined }>;
423
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;
@@ -1,14 +1,17 @@
1
1
  export { createFrame, createFrameHost, createFrameElement, FRAME_APPLIED_EVENT } from "./frame-client.cjs";
2
2
  export { FRAME_STREAM_HEADER, applyFrameResponse, isFrameStreamResponse, createServerComponentHandler } from "./frame-transport.cjs";
3
3
  export { createJSONDataTable } from "./serializer.cjs";
4
+ export type { Slot } from "./server.cjs";
4
5
  export declare function getFrameHost(): any;
5
6
  /**
6
7
  * Installs the server-component transport policy on the server-function
7
- * client: boundary identity derives from the reactive owner captured at
8
- * each call site (`getOwner`), so distinct `dynamic()` sources get
9
- * independent boundaries with nothing declared, refetches from the same
10
- * source resolve to the identical component, and ownerless calls fall back
11
- * 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.
12
15
  *
13
16
  * Call once in the client entry (an explicit call — the package is
14
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`
@@ -1,4 +1,11 @@
1
1
  import { FrameChunk, FrameHost } from "./frame-client.cjs";
2
+ import { JSONCodecOptions } from "./serializer.cjs";
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
  };
@@ -1,2 +1,30 @@
1
- export { renderToFrameStream, renderServerComponent, serverComponentResponse, frameTransformResult, createFrameSink, frameTransformDirectResult, ServerComponentPlugin, SERVER_COMPONENT_BOOTSTRAP } from "./frame-sink.cjs";
1
+ import type { Element as SolidElement } from "solid-js";
2
+ /**
3
+ * A client position in a server component: a prop the server renders (as JSX
4
+ * or by calling it) where client-owned markup belongs. `P` is the client
5
+ * component's own props, so a server component can reference the client
6
+ * component's type directly instead of restating it.
7
+ *
8
+ * Arguments are classified by VALUE, not by name — any prop may carry any of
9
+ * these:
10
+ *
11
+ * - primitives ride the chunk;
12
+ * - server JSX streams as a nested region (html once, never data);
13
+ * - anything else serializes as a data record.
14
+ *
15
+ * Async server JSX in an argument needs its own boundary: the region is
16
+ * emitted as one finished string, so a bare async read has no fallback to
17
+ * show and no fragment to reveal into.
18
+ *
19
+ * `$key` names the occurrence so client state follows an entity across
20
+ * responses rather than being positional — the slot-level analogue of `For`'s
21
+ * `keyed`, for when references can't carry identity because every response
22
+ * re-creates everything. It is occurrence identity, not client data: it is
23
+ * stripped before the client component sees its props. Positional identity is
24
+ * the right default; `$key` matters when a live list reorders.
25
+ */
26
+ export type Slot<P = {}> = (props: P & {
27
+ $key?: string | number;
28
+ }) => SolidElement;
29
+ export { renderToFrameStream, renderServerComponent, serverComponentResponse, frameTransformResult, frameTransformFlightResult, createFrameSink, frameTransformDirectResult, ServerComponentPlugin, SERVER_COMPONENT_BOOTSTRAP } from "./frame-sink.cjs";
2
30
  export { FRAME_STREAM_HEADER, isFrameStreamResponse } from "./frame-transport.cjs";