@solidjs/web 2.0.0-beta.21 → 2.0.0-beta.23
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.
- package/dist/dev.cjs +51 -1
- package/dist/dev.js +47 -2
- package/dist/server.cjs +98 -34
- package/dist/server.js +94 -35
- package/dist/web.cjs +51 -1
- package/dist/web.js +47 -2
- package/frames/dist/client.cjs +1442 -0
- package/frames/dist/client.js +1430 -0
- package/frames/dist/server.cjs +1705 -0
- package/frames/dist/server.js +1694 -0
- package/frames/package.json +30 -0
- package/package.json +78 -5
- package/serialization/dist/serialization.cjs +83 -0
- package/serialization/dist/serialization.js +82 -1
- package/serialization/types/index.d.ts +12 -0
- package/serialization/types-cjs/index.d.cts +12 -0
- package/server-functions/dist/client.cjs +114 -59
- package/server-functions/dist/client.js +113 -61
- package/server-functions/dist/server.cjs +54 -10
- package/server-functions/dist/server.js +52 -11
- package/types/client.d.ts +26 -0
- package/types/core.d.ts +2 -1
- package/types/frames/client.d.ts +53 -0
- package/types/frames/frame-client.d.ts +205 -0
- package/types/frames/frame-sink.d.ts +145 -0
- package/types/frames/frame-transport.d.ts +105 -0
- package/types/frames/serializer.d.ts +151 -0
- package/types/frames/server.d.ts +21 -0
- package/types/jsx.d.ts +17 -2
- package/types/response.d.ts +27 -1
- package/types/serializer.d.ts +12 -0
- package/types/server-functions/client.d.ts +40 -5
- package/types/server-functions/server.d.ts +9 -5
- package/types/server-functions/shared.d.ts +29 -0
- package/types/server.d.ts +10 -0
- package/types-cjs/client.d.cts +26 -0
- package/types-cjs/core.d.cts +2 -1
- package/types-cjs/frames/client.d.cts +53 -0
- package/types-cjs/frames/frame-client.d.cts +205 -0
- package/types-cjs/frames/frame-sink.d.cts +145 -0
- package/types-cjs/frames/frame-transport.d.cts +105 -0
- package/types-cjs/frames/serializer.d.cts +151 -0
- package/types-cjs/frames/server.d.cts +21 -0
- package/types-cjs/jsx.d.cts +17 -2
- package/types-cjs/response.d.cts +27 -1
- package/types-cjs/serializer.d.cts +12 -0
- package/types-cjs/server-functions/client.d.cts +40 -5
- package/types-cjs/server-functions/server.d.cts +9 -5
- package/types-cjs/server-functions/shared.d.cts +29 -0
- package/types-cjs/server.d.cts +10 -0
package/types/jsx.d.ts
CHANGED
|
@@ -1120,7 +1120,7 @@ export namespace JSX {
|
|
|
1120
1120
|
|
|
1121
1121
|
interface AnchorHTMLAttributes<T> extends HTMLAttributes<T> {
|
|
1122
1122
|
download?: string | EnumeratedAcceptsEmpty | RemoveAttribute;
|
|
1123
|
-
href?: string | RemoveAttribute;
|
|
1123
|
+
href?: string | SerializableAttributeValue | RemoveAttribute;
|
|
1124
1124
|
hreflang?: string | RemoveAttribute;
|
|
1125
1125
|
ping?: string | RemoveAttribute;
|
|
1126
1126
|
referrerpolicy?: HTMLReferrerPolicy | RemoveAttribute;
|
|
@@ -1128,6 +1128,21 @@ export namespace JSX {
|
|
|
1128
1128
|
target?: "_self" | "_blank" | "_parent" | "_top" | (string & {}) | RemoveAttribute;
|
|
1129
1129
|
type?: string | RemoveAttribute;
|
|
1130
1130
|
|
|
1131
|
+
// Client-side navigation contract. These attributes are inert markup on
|
|
1132
|
+
// their own — a routing integration that delegates anchor clicks (e.g.
|
|
1133
|
+
// @solidjs/router) reads them off the element at event time. Typed here
|
|
1134
|
+
// so plain `<a>` elements participate without per-router augmentation.
|
|
1135
|
+
/** Marks the anchor as a client-navigation link when the integration requires explicit opt-in. */
|
|
1136
|
+
link?: BooleanAttribute | RemoveAttribute;
|
|
1137
|
+
/** Serialized (JSON) history state pushed alongside the navigation. */
|
|
1138
|
+
state?: string | RemoveAttribute;
|
|
1139
|
+
/** Suppress scroll restoration/reset after the navigation. */
|
|
1140
|
+
noScroll?: BooleanAttribute | RemoveAttribute;
|
|
1141
|
+
/** Replace the current history entry instead of pushing a new one. */
|
|
1142
|
+
replace?: BooleanAttribute | RemoveAttribute;
|
|
1143
|
+
/** Route preload intent; `"false"` disables the integration's default eager preload. */
|
|
1144
|
+
preload?: boolean | "false" | RemoveAttribute;
|
|
1145
|
+
|
|
1131
1146
|
/** @experimental */
|
|
1132
1147
|
attributionsrc?: string | RemoveAttribute;
|
|
1133
1148
|
|
|
@@ -1147,7 +1162,7 @@ export namespace JSX {
|
|
|
1147
1162
|
alt?: string | RemoveAttribute;
|
|
1148
1163
|
coords?: string | RemoveAttribute;
|
|
1149
1164
|
download?: string | EnumeratedAcceptsEmpty | RemoveAttribute;
|
|
1150
|
-
href?: string | RemoveAttribute;
|
|
1165
|
+
href?: string | SerializableAttributeValue | RemoveAttribute;
|
|
1151
1166
|
ping?: string | RemoveAttribute;
|
|
1152
1167
|
referrerpolicy?: HTMLReferrerPolicy | RemoveAttribute;
|
|
1153
1168
|
rel?: string | RemoveAttribute;
|
package/types/response.d.ts
CHANGED
|
@@ -23,6 +23,32 @@ export class ResponseEnvelope<T = unknown> {
|
|
|
23
23
|
*/
|
|
24
24
|
export function isResponseEnvelope(value: unknown): value is ResponseEnvelope;
|
|
25
25
|
|
|
26
|
+
/**
|
|
27
|
+
* Registered-symbol brand (`Symbol.for("solid.Href")`) marking URL-bearing
|
|
28
|
+
* values. Declared `unique symbol` type-side; the runtime value is the
|
|
29
|
+
* registered symbol, so separately bundled copies agree on identity.
|
|
30
|
+
*/
|
|
31
|
+
export declare const HREF: unique symbol;
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* A URL-bearing value: coerces to its URL via `toString()` and carries the
|
|
35
|
+
* `HREF` registered-symbol brand. Integrations mint these (e.g. a router's
|
|
36
|
+
* typed path objects answer the brand from their proxy) and URL-accepting
|
|
37
|
+
* APIs like `redirect()` accept them alongside plain strings. The brand is
|
|
38
|
+
* what makes the type meaningful — every object has `toString()`.
|
|
39
|
+
*/
|
|
40
|
+
export interface Href {
|
|
41
|
+
[HREF]: true;
|
|
42
|
+
toString(): string;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Whether `value` is an `Href`-branded URL-bearing value. Registered-symbol
|
|
47
|
+
* check, so it stays correct across duplicated module instances — same
|
|
48
|
+
* rationale as `isResponseEnvelope`.
|
|
49
|
+
*/
|
|
50
|
+
export function isHref(value: unknown): value is Href;
|
|
51
|
+
|
|
26
52
|
/** `ResponseInit` accepted by the response helpers, plus `revalidate`. */
|
|
27
53
|
export interface ResponseHelperInit extends ResponseInit {
|
|
28
54
|
/**
|
|
@@ -51,7 +77,7 @@ export interface ResponseHelperInit extends ResponseInit {
|
|
|
51
77
|
* }
|
|
52
78
|
* ```
|
|
53
79
|
*/
|
|
54
|
-
export function redirect(url: string, init?: number | ResponseHelperInit): Response;
|
|
80
|
+
export function redirect(url: string | Href, init?: number | ResponseHelperInit): Response;
|
|
55
81
|
|
|
56
82
|
/**
|
|
57
83
|
* Empty response requesting revalidation of the named cache keys — all of
|
package/types/serializer.d.ts
CHANGED
|
@@ -137,3 +137,15 @@ export function serializeJSON(value: unknown, options: JSONSerializeOptions): ()
|
|
|
137
137
|
* settles the async values referenced inside it.
|
|
138
138
|
*/
|
|
139
139
|
export function createJSONDeserializer(options?: JSONCodecOptions): <T>(node: SerovalNode) => T;
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* A resident, response-scoped decode table over the keyed JSON codec: apply
|
|
143
|
+
* each frame `data` chunk with `apply`, resolve `{ $ref }` slot args with
|
|
144
|
+
* `resolve`. The frames client host wires one per response
|
|
145
|
+
* (`applyData: c => table.apply(c)`).
|
|
146
|
+
*/
|
|
147
|
+
export interface JSONDataTable {
|
|
148
|
+
apply(chunk: { key?: string; node?: unknown; initial?: boolean }): void;
|
|
149
|
+
resolve<T = unknown>(ref: { $ref: string }): T;
|
|
150
|
+
}
|
|
151
|
+
export function createJSONDataTable(options?: JSONCodecOptions): JSONDataTable;
|
|
@@ -2,10 +2,13 @@ import { JSONCodecOptions } from "../serializer.js";
|
|
|
2
2
|
import { ServerFunction, ServerFunctionMetadata } from "./shared.js";
|
|
3
3
|
|
|
4
4
|
export {
|
|
5
|
+
ERROR_HEADER,
|
|
5
6
|
FUNCTION_HEADER,
|
|
6
7
|
INSTANCE_HEADER,
|
|
7
8
|
SINGLE_FLIGHT_HEADER,
|
|
9
|
+
decodeErrorHeaderValue,
|
|
8
10
|
decodeResponse,
|
|
11
|
+
encodeErrorHeaderValue,
|
|
9
12
|
getServerFunctionMetadata,
|
|
10
13
|
isServerFunction,
|
|
11
14
|
subscribeFlightData,
|
|
@@ -78,6 +81,30 @@ export interface ServerFunctionsClientConfig {
|
|
|
78
81
|
* ```
|
|
79
82
|
*/
|
|
80
83
|
prepareRequest?: PrepareRequestHook;
|
|
84
|
+
/**
|
|
85
|
+
* Response-side integration seam — the client mirror of the handler's
|
|
86
|
+
* `transformResult`. `handle(response, ctx)` sees every response before
|
|
87
|
+
* the transport decodes it; returning anything but undefined resolves the
|
|
88
|
+
* call with that value. `capture(info)` runs synchronously at the call
|
|
89
|
+
* site (before any await) and its return arrives as `ctx.context`, so
|
|
90
|
+
* ambient per-call state (e.g. a reactive owner) survives to response
|
|
91
|
+
* time. See `createServerComponentHandler` in frame-transport for the
|
|
92
|
+
* canonical implementation.
|
|
93
|
+
*/
|
|
94
|
+
responseHandler?: {
|
|
95
|
+
capture?(info: { id: string; meta: unknown }): unknown;
|
|
96
|
+
handle(
|
|
97
|
+
response: Response,
|
|
98
|
+
ctx: { id: string; meta: unknown; args: unknown[]; context: unknown }
|
|
99
|
+
): unknown;
|
|
100
|
+
};
|
|
101
|
+
/**
|
|
102
|
+
* Encoder for argument lists JSON can't carry faithfully. JSON-safe args
|
|
103
|
+
* always go as plain JSON (no codec in the bundle); anything else throws
|
|
104
|
+
* unless this is set. Installed by `enableRichArguments()` from the
|
|
105
|
+
* rich-args entry — set directly only for custom wire encodings.
|
|
106
|
+
*/
|
|
107
|
+
serializeArgs?(args: unknown[]): string | Promise<string>;
|
|
81
108
|
}
|
|
82
109
|
|
|
83
110
|
/**
|
|
@@ -97,10 +124,11 @@ export function configureServerFunctionsClient(config?: ServerFunctionsClientCon
|
|
|
97
124
|
*
|
|
98
125
|
* The declaration rides the metadata channel
|
|
99
126
|
* (`getServerFunctionMetadata(fn)?.method === "GET"`) for routers and
|
|
100
|
-
* integrations to detect, and the server
|
|
101
|
-
*
|
|
102
|
-
*
|
|
103
|
-
*
|
|
127
|
+
* integrations to detect, and the server honors it: GET-declared functions
|
|
128
|
+
* accept GET requests in addition to the default POST transport (declaring
|
|
129
|
+
* GET grants, it does not revoke); functions that never declared GET answer
|
|
130
|
+
* GET requests with 405. Server-side the wrapper is identity-flavored — SSR
|
|
131
|
+
* calls stay in-process.
|
|
104
132
|
*
|
|
105
133
|
* Wrap the reference at its declaration; the compiler round-trips the call
|
|
106
134
|
* in both builds:
|
|
@@ -123,9 +151,16 @@ export function GET<A extends readonly any[], R>(
|
|
|
123
151
|
* source name as the trailing argument (dev-only metadata seeded on the
|
|
124
152
|
* metadata channel; never emitted in production). Not meant for
|
|
125
153
|
* hand-written code.
|
|
154
|
+
*
|
|
155
|
+
* The optional `base` targets calls at that url verbatim instead of the
|
|
156
|
+
* configured endpoint — for integrations reconstructing a callable from a
|
|
157
|
+
* server-rendered action url (e.g. a router intercepting a form submit whose
|
|
158
|
+
* `action="/_server?id=...&args=..."` came off the wire): bound arguments
|
|
159
|
+
* stay in the query string, where the server reads them for natural-encoding
|
|
160
|
+
* bodies (FormData, urlencoded).
|
|
126
161
|
* @internal
|
|
127
162
|
*/
|
|
128
|
-
export function createServerReference(id: string, name?: string): ServerFunction;
|
|
163
|
+
export function createServerReference(id: string, name?: string, base?: string): ServerFunction;
|
|
129
164
|
|
|
130
165
|
/**
|
|
131
166
|
* Compiler ABI — only ever referenced by server-mode compiler output;
|
|
@@ -3,10 +3,13 @@ import { JSONCodecOptions } from "../serializer.js";
|
|
|
3
3
|
import { RequestEvent } from "../server.js";
|
|
4
4
|
|
|
5
5
|
export {
|
|
6
|
+
ERROR_HEADER,
|
|
6
7
|
FUNCTION_HEADER,
|
|
7
8
|
INSTANCE_HEADER,
|
|
8
9
|
SINGLE_FLIGHT_HEADER,
|
|
10
|
+
decodeErrorHeaderValue,
|
|
9
11
|
decodeResponse,
|
|
12
|
+
encodeErrorHeaderValue,
|
|
10
13
|
getServerFunctionMetadata,
|
|
11
14
|
isServerFunction,
|
|
12
15
|
subscribeFlightData,
|
|
@@ -194,8 +197,9 @@ export function createServerReference<T extends any[], R>(
|
|
|
194
197
|
* declaration on the reference's metadata channel
|
|
195
198
|
* (`getServerFunctionMetadata(fn)?.method === "GET"`) and records the
|
|
196
199
|
* declared method for the function's id so `handleServerFunctionRequest`
|
|
197
|
-
*
|
|
198
|
-
*
|
|
200
|
+
* honors it: GET-declared functions accept GET requests in addition to the
|
|
201
|
+
* default POST transport (declaring GET grants, it does not revoke);
|
|
202
|
+
* functions that never declared GET answer GET requests with 405.
|
|
199
203
|
*
|
|
200
204
|
* Wrap the reference at its declaration; the compiler round-trips the call
|
|
201
205
|
* in both builds:
|
|
@@ -282,9 +286,9 @@ export interface HandleServerFunctionOptions {
|
|
|
282
286
|
|
|
283
287
|
/**
|
|
284
288
|
* Web-standard HTTP handler for server function calls: resolves the
|
|
285
|
-
* function id from the request,
|
|
286
|
-
* request
|
|
287
|
-
*
|
|
289
|
+
* function id from the request, gates GET dispatch on the declaration (405
|
|
290
|
+
* for a GET request to a function that never declared `GET`; POST is always
|
|
291
|
+
* accepted), decodes arguments, runs the function under a request-event scope,
|
|
288
292
|
* and encodes the result (forwarding redirect/revalidation metadata
|
|
289
293
|
* through headers). Mount it on the endpoint the client transport targets
|
|
290
294
|
* (default `/_server`); platform adapters (h3, express, ...) convert their
|
|
@@ -38,6 +38,35 @@ export const FUNCTION_HEADER: string;
|
|
|
38
38
|
*/
|
|
39
39
|
export const INSTANCE_HEADER: string;
|
|
40
40
|
|
|
41
|
+
/**
|
|
42
|
+
* Response header marking a thrown server-function error
|
|
43
|
+
* (`"X-Server-Function-Error"`). The client transport rejects with the
|
|
44
|
+
* decoded body when it is present (unless redirect/revalidation metadata
|
|
45
|
+
* marks the response as control flow). The value carries the error's
|
|
46
|
+
* message — `"true"` for thrown control-flow responses and non-Error
|
|
47
|
+
* values — encoded with `encodeErrorHeaderValue`, so integrations reading
|
|
48
|
+
* it must pass it through `decodeErrorHeaderValue`.
|
|
49
|
+
*/
|
|
50
|
+
export const ERROR_HEADER: string;
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Encodes an error message for the `ERROR_HEADER` value. HTTP header values
|
|
54
|
+
* are latin1 ByteStrings — `Headers.set` throws on code points above U+00FF
|
|
55
|
+
* — so plain printable-latin1 messages ride verbatim (ASCII stays
|
|
56
|
+
* byte-identical on the wire) and everything else (CJK, emoji, controls)
|
|
57
|
+
* travels percent-encoded behind a marker. `decodeErrorHeaderValue`
|
|
58
|
+
* round-trips the message exactly, astral-plane characters included (lone
|
|
59
|
+
* surrogates are replaced with U+FFFD — they cannot survive UTF-8 anyway).
|
|
60
|
+
*/
|
|
61
|
+
export function encodeErrorHeaderValue(value: string): string;
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Decodes an `ERROR_HEADER` value produced by `encodeErrorHeaderValue`:
|
|
65
|
+
* marked values are percent-decoded, everything else (including values from
|
|
66
|
+
* peers that never encode) passes through untouched.
|
|
67
|
+
*/
|
|
68
|
+
export function decodeErrorHeaderValue(value: string): string;
|
|
69
|
+
|
|
41
70
|
/**
|
|
42
71
|
* Header driving the single-flight protocol on both legs
|
|
43
72
|
* (`"X-Single-Flight"`). On the request it opts the call into data
|
package/types/server.d.ts
CHANGED
|
@@ -205,6 +205,16 @@ export function setAttribute(node: Element, name: string, value: string): void;
|
|
|
205
205
|
/** @deprecated not supported on the server side */
|
|
206
206
|
export function setAttributeNS(node: Element, namespace: string, name: string, value: string): void;
|
|
207
207
|
|
|
208
|
+
/**
|
|
209
|
+
* Server no-op: element claims are a client-only concern, but consumers may
|
|
210
|
+
* register isomorphically. Returns a no-op unregister function.
|
|
211
|
+
*/
|
|
212
|
+
export function registerElementClaim(handler: (element: Element) => void): () => void;
|
|
213
|
+
/** Server no-op: returns `node` unchanged. Claims never fire during SSR. */
|
|
214
|
+
export function claimElement<T extends Element>(node: T): T;
|
|
215
|
+
/** Server no-op: returns `root` unchanged. Claims never fire during SSR. */
|
|
216
|
+
export function claimElementTree<T extends Node>(root: T): T;
|
|
217
|
+
|
|
208
218
|
/** @deprecated not supported on the server side */
|
|
209
219
|
export function addEvent(node: Element, name: string, handler: () => void, delegate: boolean): void;
|
|
210
220
|
|
package/types-cjs/client.d.cts
CHANGED
|
@@ -66,6 +66,32 @@ export function assign(
|
|
|
66
66
|
): void;
|
|
67
67
|
export function setAttribute(node: Element, name: string, value: string): void;
|
|
68
68
|
export function setAttributeNS(node: Element, namespace: string, name: string, value: string): void;
|
|
69
|
+
/**
|
|
70
|
+
* Register a consumer for compiler-emitted element claims. Compiled DOM
|
|
71
|
+
* output claims navigation-relevant elements (`a[href]`, `form[action]`) at
|
|
72
|
+
* creation, and compiler-owned writes to `href`/`action` re-invoke the same
|
|
73
|
+
* handlers — so handlers must be idempotent and must check the element's
|
|
74
|
+
* relevance themselves (rechecks can fire for any element whose
|
|
75
|
+
* `href`/`action` is written, e.g. `<link href>`). Handlers run under the
|
|
76
|
+
* reactive owner current at element creation; scope per-element state and
|
|
77
|
+
* cleanup through your own reactive system. Dormant until registered —
|
|
78
|
+
* without a handler the emitted claims are null checks. Returns an
|
|
79
|
+
* unregister function.
|
|
80
|
+
*/
|
|
81
|
+
export function registerElementClaim(handler: (element: Element) => void): () => void;
|
|
82
|
+
/**
|
|
83
|
+
* Claim `node` for registered consumers (see `registerElementClaim`).
|
|
84
|
+
* Emitted by the compiler at element creation; idempotent by contract.
|
|
85
|
+
*/
|
|
86
|
+
export function claimElement<T extends Element>(node: T): T;
|
|
87
|
+
/**
|
|
88
|
+
* Sweep-claim every navigation-relevant element (`a[href]`, `form[action]`)
|
|
89
|
+
* in `root` — the subtree equivalent of the per-element `claimElement`
|
|
90
|
+
* compiled output emits, for content that becomes live DOM without compiled
|
|
91
|
+
* creation code (frame streams, adopted SSR ranges). Dormant without a
|
|
92
|
+
* registered consumer.
|
|
93
|
+
*/
|
|
94
|
+
export function claimElementTree<T extends Node>(root: T): T;
|
|
69
95
|
export function className(node: Element, value: JSX.ClassValue, prev?: JSX.ClassValue): void;
|
|
70
96
|
export function setProperty(node: Element, name: string, value: any): void;
|
|
71
97
|
export function setStyleProperty(node: Element, name: string, value: any): void;
|
package/types-cjs/core.d.cts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
-
export { getOwner, runWithOwner, createComponent, createRoot as root, sharedConfig, untrack, merge as mergeProps, flatten, ssrHandleError, ssrScope } from "solid-js";
|
|
1
|
+
export { getOwner, runWithOwner, createComponent, createRoot as root, sharedConfig, untrack, merge as mergeProps, flatten, ssrHandleError, ssrScope, NoHydration, Hydration } 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
|
+
export declare const runWithHydrationScope: (id: any, fn: any) => unknown;
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @solidjs/web/frames — client half. Consume frame streams into live DOM
|
|
3
|
+
* boundaries. There is deliberately no server-component API here beyond
|
|
4
|
+
* `installServerComponents()`: `dynamic` + server functions IS the client
|
|
5
|
+
* surface — a server-function call whose response is a frame stream
|
|
6
|
+
* resolves with a stable per-call-site component.
|
|
7
|
+
*
|
|
8
|
+
* Copied next to the runtime's frame d.ts files at publish (see
|
|
9
|
+
* types:copy-frames), so the relative imports below resolve in-place.
|
|
10
|
+
*/
|
|
11
|
+
export {
|
|
12
|
+
createFrame,
|
|
13
|
+
createFrameHost,
|
|
14
|
+
createFrameInsertable,
|
|
15
|
+
FRAME_APPLIED_EVENT
|
|
16
|
+
} from "./frame-client.cjs";
|
|
17
|
+
export type {
|
|
18
|
+
Frame,
|
|
19
|
+
FrameChunk,
|
|
20
|
+
FrameHost,
|
|
21
|
+
FrameHostOptions,
|
|
22
|
+
FrameOptions,
|
|
23
|
+
FrameWrite,
|
|
24
|
+
Slot,
|
|
25
|
+
SlotContext
|
|
26
|
+
} from "./frame-client.cjs";
|
|
27
|
+
export {
|
|
28
|
+
FRAME_STREAM_HEADER,
|
|
29
|
+
applyFrameResponse,
|
|
30
|
+
isFrameStreamResponse,
|
|
31
|
+
createServerComponentHandler
|
|
32
|
+
} from "./frame-transport.cjs";
|
|
33
|
+
export { createJSONDataTable } from "./serializer.cjs";
|
|
34
|
+
export type { JSONDataTable } from "./serializer.cjs";
|
|
35
|
+
|
|
36
|
+
import type { FrameHost } from "./frame-client.cjs";
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* The app-wide frame host (created on first use): one chunk router, with
|
|
40
|
+
* codec data tables rotated per response — deserializer cross-reference
|
|
41
|
+
* space is stream-scoped by contract.
|
|
42
|
+
*/
|
|
43
|
+
export function getFrameHost(): FrameHost;
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Installs the server-component transport policy on the server-function
|
|
47
|
+
* client: boundary identity derives from the reactive owner captured at the
|
|
48
|
+
* call site, frame-stream responses resolve to stable per-call-site
|
|
49
|
+
* components, and document-SSR boundaries adopt their server-rendered
|
|
50
|
+
* ranges. Call once in the client entry (the package is sideEffects:false —
|
|
51
|
+
* a bare import would be tree-shaken); call again to rebind a custom host.
|
|
52
|
+
*/
|
|
53
|
+
export function installServerComponents(host?: FrameHost): void;
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Client frame runtime — the consumer side of a frame stream. A frame
|
|
3
|
+
* renders server-owned content into a DOM boundary from a resident keyed
|
|
4
|
+
* record store: chunks are writes, not events, so application is
|
|
5
|
+
* prerequisite-driven and order-independent. Client-owned slot ranges
|
|
6
|
+
* inside the boundary are preserved across server updates — the
|
|
7
|
+
* version is a stale-guard only ("policy A"): newer content morphs in
|
|
8
|
+
* place, and teardown is `dispose()`, never a version bump.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
/** One transport chunk of a frame stream, addressed by frame `id`. */
|
|
12
|
+
export type FrameChunk =
|
|
13
|
+
| { type: "start"; id: string; version: number }
|
|
14
|
+
| { type: "html"; id: string; version: number; html: string }
|
|
15
|
+
| { type: "fragment"; id: string; version: number; key: string; html: string }
|
|
16
|
+
| {
|
|
17
|
+
type: "reveal";
|
|
18
|
+
id: string;
|
|
19
|
+
version: number;
|
|
20
|
+
keys: string[];
|
|
21
|
+
waitForStyles?: boolean;
|
|
22
|
+
fallback?: boolean;
|
|
23
|
+
}
|
|
24
|
+
| {
|
|
25
|
+
type: "data";
|
|
26
|
+
id: string;
|
|
27
|
+
version: number;
|
|
28
|
+
key?: string;
|
|
29
|
+
node?: unknown;
|
|
30
|
+
initial?: boolean;
|
|
31
|
+
/** Eval-style hydration script — only when produced with the hydration serializer. */
|
|
32
|
+
payload?: string;
|
|
33
|
+
}
|
|
34
|
+
| {
|
|
35
|
+
type: "assets";
|
|
36
|
+
id: string;
|
|
37
|
+
version: number;
|
|
38
|
+
key: string;
|
|
39
|
+
modules?: string[];
|
|
40
|
+
styles?: string[];
|
|
41
|
+
inlineStyles?: { id: string; content?: string; attrs?: Record<string, string> }[];
|
|
42
|
+
}
|
|
43
|
+
| { type: "slot"; id: string; version: number; key: string; args: Record<string, unknown> }
|
|
44
|
+
| { type: "template"; id: string; version: number; key: string; html: string; fields: string[] }
|
|
45
|
+
| {
|
|
46
|
+
type: "block";
|
|
47
|
+
id: string;
|
|
48
|
+
version: number;
|
|
49
|
+
key: string;
|
|
50
|
+
template: string;
|
|
51
|
+
values: unknown[];
|
|
52
|
+
}
|
|
53
|
+
| { type: "complete"; id: string; version: number }
|
|
54
|
+
| { type: "error"; id: string; version: number; key?: string; error: unknown };
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Maps a wire chunk onto resident-store record writes. `data` chunks map to
|
|
58
|
+
* no records — they are response-scoped and the host applies them through
|
|
59
|
+
* its data hook.
|
|
60
|
+
*/
|
|
61
|
+
export function chunkToRecords(chunk: FrameChunk): Record<string, unknown>;
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* One store write applied to a frame: `r` maps record keys to values
|
|
65
|
+
* (`chunkToRecords` produces these from wire chunks) and `version` is the
|
|
66
|
+
* stream stamp — an older version than the frame's current one is ignored.
|
|
67
|
+
*/
|
|
68
|
+
export interface FrameWrite {
|
|
69
|
+
version: number;
|
|
70
|
+
r: Record<string, unknown>;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/** Context passed to a slot callback. */
|
|
74
|
+
export interface SlotContext {
|
|
75
|
+
/**
|
|
76
|
+
* Register cleanup for when this occurrence's range is removed from the
|
|
77
|
+
* server content, or the owning frame is disposed.
|
|
78
|
+
*/
|
|
79
|
+
onCleanup(fn: () => void): void;
|
|
80
|
+
/**
|
|
81
|
+
* The range's current interior — server-rendered client content on an
|
|
82
|
+
* adopted document-SSR boot, or the previous output on a re-call. A
|
|
83
|
+
* framework binding hydrates onto it and returns `undefined` to claim it
|
|
84
|
+
* in place (zero DOM mutation).
|
|
85
|
+
*/
|
|
86
|
+
existing: ChildNode[];
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Client content for a server-declared slot. Direct-insert occurrences
|
|
91
|
+
* call it with empty props; render-prop occurrences pass the occurrence's
|
|
92
|
+
* resolved args (primitives literal, `{$ref}` data resolved through the
|
|
93
|
+
* host, `{$frame}` regions as marker-range fragments). Return nodes to fill
|
|
94
|
+
* the range, or `undefined` to claim `ctx.existing` untouched.
|
|
95
|
+
*/
|
|
96
|
+
export type Slot = (props: Record<string, unknown>, ctx: SlotContext) => Node | Node[] | undefined;
|
|
97
|
+
|
|
98
|
+
export interface Frame {
|
|
99
|
+
/** Merge a write into the store and flush (morph/reveal/slot sync). */
|
|
100
|
+
apply(write: FrameWrite): void;
|
|
101
|
+
/** The active version, or undefined before the first apply. */
|
|
102
|
+
readonly version: number | undefined;
|
|
103
|
+
/** Read-only view of the resident record store. */
|
|
104
|
+
readonly store: Readonly<Record<string, unknown>>;
|
|
105
|
+
/** The stream's error record, if an `error` chunk arrived. */
|
|
106
|
+
readonly error: unknown;
|
|
107
|
+
/** Whether the named fragment has been revealed into the boundary. */
|
|
108
|
+
isRevealed(segment: string): boolean;
|
|
109
|
+
/** Tear down: slot cleanups cascade, later chunks are ignored. Idempotent. */
|
|
110
|
+
dispose(): void;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Routes a flat stream of addressed chunks to frames by id, buffering chunks
|
|
115
|
+
* for frames that have not registered yet (only the newest version's chunks
|
|
116
|
+
* are kept). `data` chunks are response-scoped and go to `applyData`.
|
|
117
|
+
*
|
|
118
|
+
* An id may have several frames (the same server component mounted more
|
|
119
|
+
* than once): chunks fan out to all of them, and a frame registering after
|
|
120
|
+
* delivery is seeded from a sibling's store.
|
|
121
|
+
*/
|
|
122
|
+
export interface FrameHost {
|
|
123
|
+
register(id: string, frame: Frame): void;
|
|
124
|
+
/** Remove one frame (or all frames of the id when `frame` is omitted). */
|
|
125
|
+
unregister(id: string, frame?: Frame): void;
|
|
126
|
+
apply(chunk: FrameChunk): void;
|
|
127
|
+
/** The first registered frame under the id, if any. */
|
|
128
|
+
get(id: string): Frame | undefined;
|
|
129
|
+
serialize(value: unknown): { $ref: string };
|
|
130
|
+
/** `frameId` is the resolving frame's id — route to its stream's table. */
|
|
131
|
+
resolve(ref: { $ref: string }, frameId?: string): unknown;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* The bubbling DOM event (`"frame:applied"`) a frame dispatches from its
|
|
136
|
+
* parent element whenever server content lands in the document — root
|
|
137
|
+
* materialize/morph, segment reveal, fallback materialization — with
|
|
138
|
+
* `detail: { id, version, reason }`. One document-level listener sees every
|
|
139
|
+
* boundary (nested region frames dispatch too); use it to re-apply
|
|
140
|
+
* client-owned decorations on server-owned markup (router affordance
|
|
141
|
+
* reflection, e.g. `aria-current`) without a MutationObserver.
|
|
142
|
+
*/
|
|
143
|
+
export const FRAME_APPLIED_EVENT: "frame:applied";
|
|
144
|
+
|
|
145
|
+
/** Options for `createFrameHost`. */
|
|
146
|
+
export interface FrameHostOptions {
|
|
147
|
+
/**
|
|
148
|
+
* Backs `{$ref}` slot args (typically a codec data table's `resolve`).
|
|
149
|
+
* `frameId` identifies the resolving frame — data tables are
|
|
150
|
+
* response-scoped, so multi-stream hosts route by it (nested region ids
|
|
151
|
+
* prefix-match their root).
|
|
152
|
+
*/
|
|
153
|
+
resolve?(ref: { $ref: string }, frameId?: string): unknown;
|
|
154
|
+
/** Test/host-side counterpart of `resolve`. */
|
|
155
|
+
serialize?(value: unknown): { $ref: string };
|
|
156
|
+
/**
|
|
157
|
+
* Receives each `data` chunk whole. Wire a codec table:
|
|
158
|
+
* `applyData: c => table.apply(c)` (see `createJSONDataTable`).
|
|
159
|
+
*/
|
|
160
|
+
applyData?(chunk: Extract<FrameChunk, { type: "data" }>): void;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
export function createFrameHost(options?: FrameHostOptions): FrameHost;
|
|
164
|
+
|
|
165
|
+
/** Options for `createFrame` / `createFrameInsertable`. */
|
|
166
|
+
export interface FrameOptions {
|
|
167
|
+
/** Register with this host under `id`, receiving routed/buffered chunks. */
|
|
168
|
+
host?: FrameHost;
|
|
169
|
+
id?: string;
|
|
170
|
+
/** Client content keyed by prop name (occurrences resolve by prop). */
|
|
171
|
+
slots?: Record<string, Slot>;
|
|
172
|
+
/**
|
|
173
|
+
* Adopt existing server-rendered DOM: the first apply morphs against it,
|
|
174
|
+
* and slots sync immediately (hydration attach) — a document-SSR boot
|
|
175
|
+
* needs no chunk.
|
|
176
|
+
*/
|
|
177
|
+
adopt?: boolean;
|
|
178
|
+
/** Called after each apply flush (tests/telemetry). */
|
|
179
|
+
onApply?(info: { version: number; reason: "materialize" | "morph" | "reveal" }): void;
|
|
180
|
+
/**
|
|
181
|
+
* Wraps element-claim sweeps (`a[href]`/`form[action]` in materialized
|
|
182
|
+
* server content — and only those) so claim consumers register their
|
|
183
|
+
* per-element cleanup against the boundary's reactive owner, e.g.
|
|
184
|
+
* `fn => runWithOwner(owner, fn)`. Nested region frames inherit it.
|
|
185
|
+
* Without it, sweeps run under whatever owner is current (none, for
|
|
186
|
+
* streamed chunks).
|
|
187
|
+
*/
|
|
188
|
+
ownerScope?<T>(fn: () => T): T;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
/** A frame rendering into an element boundary. */
|
|
192
|
+
export function createFrame(boundary: Element, options?: FrameOptions): Frame;
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* A branded frame-insertable value: the client runtime's `insert` recognizes
|
|
196
|
+
* it (registered `$$FRAME` symbol) and calls the mount handler the value
|
|
197
|
+
* carries — a comment range is established at the insertion point and a
|
|
198
|
+
* host-registered frame binds to it. One static mount per value; lifecycle
|
|
199
|
+
* belongs to the creator via `dispose()` (register it with your owner's
|
|
200
|
+
* cleanup).
|
|
201
|
+
*/
|
|
202
|
+
export function createFrameInsertable(options: FrameOptions): {
|
|
203
|
+
readonly frame: Frame | null;
|
|
204
|
+
dispose(): void;
|
|
205
|
+
};
|