@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
@@ -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,21 +2,29 @@ import { JSONCodecOptions } from "../serializer.cjs";
2
2
  import { ServerFunction, ServerFunctionMetadata } from "./shared.cjs";
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,
15
+ decodeResponsePayload,
16
+ deserializeStream,
13
17
  encodeErrorHeaderValue,
18
+ frameAddress,
19
+ getFlightDataConsumer,
14
20
  getServerFunctionMetadata,
21
+ getServerFunctionsCodec,
15
22
  hasFlashCookie,
16
23
  isServerFunction,
17
24
  subscribeFlightData,
18
25
  withMeta
19
26
  } from "./shared.cjs";
27
+ export { REVALIDATE_HEADER } from "../response.cjs";
20
28
  export type {
21
29
  FlightDataConsumer,
22
30
  FlightDataContext,
@@ -173,3 +181,20 @@ export function createServerReference(id: string, name?: string, base?: string):
173
181
  * @internal
174
182
  */
175
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;
@@ -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.cjs";
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
@@ -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;