@solidjs/web 2.0.0-beta.3 → 2.0.0-beta.30
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/README.md +27 -4
- package/dist/dev.cjs +1211 -205
- package/dist/dev.js +1175 -199
- package/dist/server.cjs +1342 -234
- package/dist/server.js +1304 -231
- package/dist/web.cjs +1195 -196
- package/dist/web.js +1159 -190
- package/frames/dist/client.cjs +1746 -0
- package/frames/dist/client.dev.cjs +1759 -0
- package/frames/dist/client.dev.js +1747 -0
- package/frames/dist/client.js +1734 -0
- package/frames/dist/server.cjs +2426 -0
- package/frames/dist/server.js +2414 -0
- package/frames/package.json +30 -0
- package/package.json +287 -38
- package/serialization/dist/serialization.cjs +169 -0
- package/serialization/dist/serialization.js +159 -0
- package/serialization/package.json +20 -0
- package/serialization/types/index.d.ts +157 -0
- package/serialization/types-cjs/index.d.cts +157 -0
- package/serialization/types-cjs/package.json +3 -0
- package/server-functions/dist/client.cjs +613 -0
- package/server-functions/dist/client.js +585 -0
- package/server-functions/dist/server.cjs +904 -0
- package/server-functions/dist/server.js +875 -0
- package/server-functions/package.json +30 -0
- package/storage/package.json +8 -3
- package/storage/types/index.d.ts +26 -0
- package/storage/types-cjs/index.d.cts +28 -0
- package/storage/types-cjs/package.json +3 -0
- package/types/client.d.ts +125 -21
- package/types/core.d.ts +4 -3
- package/types/frames/client.d.ts +20 -0
- package/types/frames/frame-client.d.ts +270 -0
- package/types/frames/frame-sink.d.ts +168 -0
- package/types/frames/frame-transport.d.ts +196 -0
- package/types/frames/serializer.d.ts +157 -0
- package/types/frames/server.d.ts +30 -0
- package/types/index.d.ts +211 -26
- package/types/jsx-properties.d.ts +93 -0
- package/types/jsx.d.ts +4150 -1
- package/types/response.d.ts +129 -0
- package/types/serializer.d.ts +157 -0
- package/types/server-functions/client.d.ts +200 -0
- package/types/server-functions/flash.d.ts +38 -0
- package/types/server-functions/server.d.ts +490 -0
- package/types/server-functions/shared.d.ts +445 -0
- package/types/server-mock.d.ts +93 -0
- package/types/server.d.ts +221 -28
- package/types-cjs/client.d.cts +192 -0
- package/types-cjs/core.d.cts +4 -0
- package/types-cjs/frames/client.d.cts +20 -0
- package/types-cjs/frames/frame-client.d.cts +270 -0
- package/types-cjs/frames/frame-sink.d.cts +168 -0
- package/types-cjs/frames/frame-transport.d.cts +196 -0
- package/types-cjs/frames/serializer.d.cts +157 -0
- package/types-cjs/frames/server.d.cts +30 -0
- package/types-cjs/index.d.cts +231 -0
- package/types-cjs/jsx-properties.d.cts +93 -0
- package/types-cjs/jsx.d.cts +4150 -0
- package/types-cjs/package.json +3 -0
- package/types-cjs/response.d.cts +129 -0
- package/types-cjs/serializer.d.cts +157 -0
- package/types-cjs/server-functions/client.d.cts +200 -0
- package/types-cjs/server-functions/flash.d.cts +38 -0
- package/types-cjs/server-functions/server.d.cts +490 -0
- package/types-cjs/server-functions/shared.d.cts +445 -0
- package/types-cjs/server-mock.d.cts +165 -0
- package/types-cjs/server.d.cts +349 -0
- package/storage/types/src/client.d.ts +0 -1
- package/storage/types/src/index.d.ts +0 -46
- package/storage/types/src/server-mock.d.ts +0 -72
- package/storage/types/storage/src/index.d.ts +0 -2
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@solidjs/web/server-functions",
|
|
3
|
+
"main": "./dist/server.cjs",
|
|
4
|
+
"module": "./dist/server.js",
|
|
5
|
+
"types": "../types/server-functions/server.d.ts",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"sideEffects": false,
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"browser": {
|
|
11
|
+
"import": {
|
|
12
|
+
"types": "../types/server-functions/client.d.ts",
|
|
13
|
+
"default": "./dist/client.js"
|
|
14
|
+
},
|
|
15
|
+
"require": {
|
|
16
|
+
"types": "../types-cjs/server-functions/client.d.cts",
|
|
17
|
+
"default": "./dist/client.cjs"
|
|
18
|
+
}
|
|
19
|
+
},
|
|
20
|
+
"import": {
|
|
21
|
+
"types": "../types/server-functions/server.d.ts",
|
|
22
|
+
"default": "./dist/server.js"
|
|
23
|
+
},
|
|
24
|
+
"require": {
|
|
25
|
+
"types": "../types-cjs/server-functions/server.d.cts",
|
|
26
|
+
"default": "./dist/server.cjs"
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
package/storage/package.json
CHANGED
|
@@ -7,9 +7,14 @@
|
|
|
7
7
|
"sideEffects": false,
|
|
8
8
|
"exports": {
|
|
9
9
|
".": {
|
|
10
|
-
"
|
|
11
|
-
|
|
12
|
-
|
|
10
|
+
"import": {
|
|
11
|
+
"types": "./types/index.d.ts",
|
|
12
|
+
"default": "./dist/storage.js"
|
|
13
|
+
},
|
|
14
|
+
"require": {
|
|
15
|
+
"types": "./types-cjs/index.d.cts",
|
|
16
|
+
"default": "./dist/storage.cjs"
|
|
17
|
+
}
|
|
13
18
|
}
|
|
14
19
|
}
|
|
15
20
|
}
|
package/storage/types/index.d.ts
CHANGED
|
@@ -1,2 +1,28 @@
|
|
|
1
1
|
import type { RequestEvent } from "@solidjs/web";
|
|
2
|
+
/**
|
|
3
|
+
* Establishes the request-event scope for a server request: everything
|
|
4
|
+
* `cb` runs (across `await`s, via AsyncLocalStorage) sees `init` from
|
|
5
|
+
* `getRequestEvent()`. Call it at the top of the server's request handling,
|
|
6
|
+
* wrapping SSR and server-function dispatch; the server-functions runtime
|
|
7
|
+
* also picks the scope up automatically as its default event provider.
|
|
8
|
+
*
|
|
9
|
+
* Lives on its own subpath because it imports `node:async_hooks` — keep it
|
|
10
|
+
* out of environments without that module.
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* ```ts
|
|
14
|
+
* import { provideRequestEvent } from "@solidjs/web/storage";
|
|
15
|
+
*
|
|
16
|
+
* async function handler(request: Request) {
|
|
17
|
+
* return provideRequestEvent({ request, locals: {} }, () =>
|
|
18
|
+
* renderToStringAsync(() => <App />)
|
|
19
|
+
* );
|
|
20
|
+
* }
|
|
21
|
+
* ```
|
|
22
|
+
*
|
|
23
|
+
* @param init the event for this request — frameworks pass their richer
|
|
24
|
+
* event shapes
|
|
25
|
+
* @param cb runs synchronously; its return value is passed through
|
|
26
|
+
* @throws on the client, where there is no request to scope
|
|
27
|
+
*/
|
|
2
28
|
export declare function provideRequestEvent<T extends RequestEvent, U>(init: T, cb: () => U): U;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { RequestEvent } from "@solidjs/web";
|
|
2
|
+
/**
|
|
3
|
+
* Establishes the request-event scope for a server request: everything
|
|
4
|
+
* `cb` runs (across `await`s, via AsyncLocalStorage) sees `init` from
|
|
5
|
+
* `getRequestEvent()`. Call it at the top of the server's request handling,
|
|
6
|
+
* wrapping SSR and server-function dispatch; the server-functions runtime
|
|
7
|
+
* also picks the scope up automatically as its default event provider.
|
|
8
|
+
*
|
|
9
|
+
* Lives on its own subpath because it imports `node:async_hooks` — keep it
|
|
10
|
+
* out of environments without that module.
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* ```ts
|
|
14
|
+
* import { provideRequestEvent } from "@solidjs/web/storage";
|
|
15
|
+
*
|
|
16
|
+
* async function handler(request: Request) {
|
|
17
|
+
* return provideRequestEvent({ request, locals: {} }, () =>
|
|
18
|
+
* renderToStringAsync(() => <App />)
|
|
19
|
+
* );
|
|
20
|
+
* }
|
|
21
|
+
* ```
|
|
22
|
+
*
|
|
23
|
+
* @param init the event for this request — frameworks pass their richer
|
|
24
|
+
* event shapes
|
|
25
|
+
* @param cb runs synchronously; its return value is passed through
|
|
26
|
+
* @throws on the client, where there is no request to scope
|
|
27
|
+
*/
|
|
28
|
+
export declare function provideRequestEvent<T extends RequestEvent, U>(init: T, cb: () => U): U;
|
package/types/client.d.ts
CHANGED
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
import { JSX } from "./jsx.js";
|
|
2
|
-
export const
|
|
2
|
+
export const DOMWithState: Record<string, Record<string, 1 | 2>>;
|
|
3
3
|
export const ChildProperties: Set<string>;
|
|
4
4
|
export const DelegatedEvents: Set<string>;
|
|
5
5
|
export const DOMElements: Set<string>;
|
|
6
6
|
export const SVGElements: Set<string>;
|
|
7
|
-
export const
|
|
7
|
+
export const MathMLElements: Set<string>;
|
|
8
|
+
export const VoidElements: Set<string>;
|
|
9
|
+
export const RawTextElements: Set<string>;
|
|
10
|
+
export const Namespaces: Record<string, string>;
|
|
8
11
|
|
|
9
12
|
type MountableElement = Element | Document | ShadowRoot | DocumentFragment | Node;
|
|
10
13
|
export function render(
|
|
@@ -13,42 +16,86 @@ export function render(
|
|
|
13
16
|
init?: JSX.Element,
|
|
14
17
|
options?: { owner?: unknown }
|
|
15
18
|
): () => void;
|
|
16
|
-
|
|
17
|
-
|
|
19
|
+
/**
|
|
20
|
+
* @param flag
|
|
21
|
+
* - `undefined` — clone the template as-is (uses `cloneNode`).
|
|
22
|
+
* - `1` — use `document.importNode` instead of `cloneNode`.
|
|
23
|
+
* - `2` — the template html is wrapped; the outer tag is stripped at clone time.
|
|
24
|
+
*/
|
|
25
|
+
export function template(html: string, flag?: 1 | 2): () => Element;
|
|
26
|
+
export function scope<T extends () => any>(fn: T): T;
|
|
27
|
+
export function effect<T>(fn: (prev?: T) => T, effect: (value: T, prev?: T) => void): void;
|
|
18
28
|
export function memo<T>(fn: () => T, equal: boolean): () => T;
|
|
19
29
|
export function untrack<T>(fn: () => T): T;
|
|
20
30
|
export function insert<T>(
|
|
21
31
|
parent: MountableElement,
|
|
22
32
|
accessor: (() => T) | T,
|
|
23
33
|
marker?: Node | null,
|
|
24
|
-
init?: JSX.Element
|
|
34
|
+
init?: JSX.Element,
|
|
35
|
+
options?: {
|
|
36
|
+
/**
|
|
37
|
+
* Live accessor for the slot's logical host in the source tree (portals).
|
|
38
|
+
* Each top-level node the slot manages is tagged with a `_$host` getter
|
|
39
|
+
* backed by this accessor so delegated events retarget correctly.
|
|
40
|
+
*/
|
|
41
|
+
host?: () => Node | null;
|
|
42
|
+
/** Defer the insert effect to the queue instead of running it inline. */
|
|
43
|
+
schedule?: boolean;
|
|
44
|
+
}
|
|
25
45
|
): JSX.Element;
|
|
26
46
|
export function createComponent<T>(Comp: (props: T) => JSX.Element, props: T): JSX.Element;
|
|
27
|
-
export function delegateEvents(eventNames: string[]
|
|
28
|
-
export function
|
|
29
|
-
export function
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
skipChildren?: Boolean
|
|
47
|
+
export function delegateEvents(eventNames: string[]): void;
|
|
48
|
+
export function registerDelegatedRoot(root: MountableElement): void;
|
|
49
|
+
export function unregisterDelegatedRoot(root: MountableElement): void;
|
|
50
|
+
export function registerDelegatedContainer(
|
|
51
|
+
container: MountableElement,
|
|
52
|
+
owner?: MountableElement
|
|
34
53
|
): void;
|
|
54
|
+
export function unregisterDelegatedContainer(
|
|
55
|
+
container: MountableElement,
|
|
56
|
+
owner?: MountableElement
|
|
57
|
+
): void;
|
|
58
|
+
export function getDelegatedRoot(node: MountableElement): MountableElement | undefined;
|
|
59
|
+
export function spread<T>(node: Element, accessor: T, skipChildren?: Boolean): void;
|
|
35
60
|
export function assign(
|
|
36
61
|
node: Element,
|
|
37
62
|
props: any,
|
|
38
|
-
isSVG?: Boolean,
|
|
39
63
|
skipChildren?: Boolean,
|
|
40
64
|
prevProps?: any,
|
|
41
65
|
skipRef?: Boolean
|
|
42
66
|
): void;
|
|
43
67
|
export function setAttribute(node: Element, name: string, value: string): void;
|
|
44
68
|
export function setAttributeNS(node: Element, namespace: string, name: string, value: string): void;
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
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;
|
|
95
|
+
export function className(node: Element, value: JSX.ClassValue, prev?: JSX.ClassValue): void;
|
|
49
96
|
export function setProperty(node: Element, name: string, value: any): void;
|
|
50
97
|
export function setStyleProperty(node: Element, name: string, value: any): void;
|
|
51
|
-
export function
|
|
98
|
+
export function addEvent(
|
|
52
99
|
node: Element,
|
|
53
100
|
name: string,
|
|
54
101
|
handler: EventListener | EventListenerObject | (EventListenerObject & AddEventListenerOptions),
|
|
@@ -62,8 +109,14 @@ export function style(
|
|
|
62
109
|
export function getOwner(): unknown;
|
|
63
110
|
export function mergeProps(...sources: unknown[]): unknown;
|
|
64
111
|
export function dynamicProperty(props: unknown, key: string): unknown;
|
|
65
|
-
export function applyRef
|
|
66
|
-
|
|
112
|
+
export function applyRef<T extends Element = Element>(
|
|
113
|
+
r: ((element: NoInfer<T>) => void) | ((element: NoInfer<T>) => void)[],
|
|
114
|
+
element: T
|
|
115
|
+
): void;
|
|
116
|
+
export function ref(
|
|
117
|
+
fn: () => ((element: Element) => void) | ((element: Element) => void)[],
|
|
118
|
+
element: Element
|
|
119
|
+
): void;
|
|
67
120
|
|
|
68
121
|
export function hydrate(
|
|
69
122
|
fn: () => JSX.Element,
|
|
@@ -74,11 +127,62 @@ export function getHydrationKey(): string | undefined;
|
|
|
74
127
|
export function getNextElement(template?: () => Element): Element;
|
|
75
128
|
export function getNextMatch(start: Node, elementName: string): Element;
|
|
76
129
|
export function getNextMarker(start: Node): [Node, Array<Node>];
|
|
130
|
+
/** @deprecated Use `useHead` — removed before `0.50.0` stable. */
|
|
77
131
|
export function useAssets(fn: () => JSX.Element): void;
|
|
132
|
+
/** @deprecated Use `useHead` — removed before `0.50.0` stable. */
|
|
78
133
|
export function getAssets(): string;
|
|
134
|
+
/**
|
|
135
|
+
* A head tag descriptor. Props values may be getters (reactive on the
|
|
136
|
+
* client); `children` is the text body. `key` overrides the built-in dedupe
|
|
137
|
+
* identity (`title` is a hard singleton that `key` cannot fork).
|
|
138
|
+
*/
|
|
139
|
+
export type HeadTag = {
|
|
140
|
+
tag: "title" | "meta" | "link" | "style" | "script" | "base";
|
|
141
|
+
props: Record<string, any>;
|
|
142
|
+
key?: string | (() => string);
|
|
143
|
+
};
|
|
144
|
+
/**
|
|
145
|
+
* Registers head tags with the ambient head registry under the current
|
|
146
|
+
* owner. An array is a group — one replacement set. Resolution is
|
|
147
|
+
* last-committed group per identity; disposal restores the previous winner.
|
|
148
|
+
* During hydration the server-flushed head state stays authoritative until
|
|
149
|
+
* hydration completes. See docs/head-management-rfc.md.
|
|
150
|
+
*/
|
|
151
|
+
export function useHead(tag: HeadTag | HeadTag[]): void;
|
|
152
|
+
export type AssetDescriptor =
|
|
153
|
+
| { type: "style"; href: string; attrs?: Record<string, string> }
|
|
154
|
+
| { type: "inline-style"; id: string; content?: string; attrs?: Record<string, string> }
|
|
155
|
+
| { type: "module"; href: string }
|
|
156
|
+
| ExclusiveAssetDescriptor<any>;
|
|
157
|
+
export interface ExclusiveAssetDescriptor<T> {
|
|
158
|
+
policy: "exclusive";
|
|
159
|
+
key: string;
|
|
160
|
+
value: T;
|
|
161
|
+
get(): T;
|
|
162
|
+
set(value: T): void;
|
|
163
|
+
}
|
|
164
|
+
export function acquireAsset(descriptor: AssetDescriptor): () => void;
|
|
79
165
|
export function HydrationScript(props?: { nonce?: string; eventNames?: string[] }): JSX.Element;
|
|
80
|
-
export function generateHydrationScript(options?: {
|
|
166
|
+
export function generateHydrationScript(options?: {
|
|
167
|
+
nonce?: string;
|
|
168
|
+
eventNames?: string[];
|
|
169
|
+
}): string;
|
|
81
170
|
export function Assets(props: { children?: JSX.Element }): JSX.Element;
|
|
171
|
+
/**
|
|
172
|
+
* See the server entry's `ResponseStub` — the shape of the mutable response
|
|
173
|
+
* head integrations expose as `event.response` via module augmentation.
|
|
174
|
+
*/
|
|
175
|
+
export interface ResponseStub {
|
|
176
|
+
status?: number;
|
|
177
|
+
statusText?: string;
|
|
178
|
+
headers: Headers;
|
|
179
|
+
/**
|
|
180
|
+
* Set by the integration once the response head has been derived/sent
|
|
181
|
+
* from this stub (status/headers can no longer change); consumers must
|
|
182
|
+
* treat later writes and cleanup-time retractions as no-ops.
|
|
183
|
+
*/
|
|
184
|
+
committed?: boolean;
|
|
185
|
+
}
|
|
82
186
|
export interface RequestEvent {
|
|
83
187
|
request: Request;
|
|
84
188
|
locals: Record<string | number | symbol, any>;
|
package/types/core.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
-
export { getOwner, runWithOwner, createComponent, createRoot as root, sharedConfig, untrack, merge as mergeProps, flatten, ssrHandleError,
|
|
2
|
-
export declare const effect: (fn: any, effectFn: any,
|
|
3
|
-
export declare const memo: (fn: any
|
|
1
|
+
export { getOwner, runWithOwner, createComponent, createRoot as root, sharedConfig, untrack, merge as mergeProps, flatten, ssrHandleError, ssrScope, NoHydration, Hydration, runInServerComponentScope } from "solid-js";
|
|
2
|
+
export declare const effect: (fn: any, effectFn: any, options: any) => void;
|
|
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,20 @@
|
|
|
1
|
+
export { createFrame, createFrameHost, createFrameElement, FRAME_APPLIED_EVENT } from "./frame-client.js";
|
|
2
|
+
export { FRAME_STREAM_HEADER, applyFrameResponse, isFrameStreamResponse, createServerComponentHandler } from "./frame-transport.js";
|
|
3
|
+
export { createJSONDataTable } from "./serializer.js";
|
|
4
|
+
export type { Slot } from "./server.js";
|
|
5
|
+
export declare function getFrameHost(): any;
|
|
6
|
+
/**
|
|
7
|
+
* Installs the server-component transport policy on the server-function
|
|
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.
|
|
15
|
+
*
|
|
16
|
+
* Call once in the client entry (an explicit call — the package is
|
|
17
|
+
* `sideEffects: false`, so a bare import would be tree-shaken away);
|
|
18
|
+
* call again to rebind to a custom host.
|
|
19
|
+
*/
|
|
20
|
+
export declare function installServerComponents(host?: any): void;
|
|
@@ -0,0 +1,270 @@
|
|
|
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: "complete"; id: string; version: number }
|
|
45
|
+
| { type: "error"; id: string; version: number; key?: string; error: unknown };
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Maps a wire chunk onto resident-store record writes. `data` chunks map to
|
|
49
|
+
* no records — they are response-scoped and the host applies them through
|
|
50
|
+
* its data hook.
|
|
51
|
+
*/
|
|
52
|
+
export function chunkToRecords(chunk: FrameChunk): Record<string, unknown>;
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* One store write applied to a frame: `r` maps record keys to values
|
|
56
|
+
* (`chunkToRecords` produces these from wire chunks) and `version` is the
|
|
57
|
+
* stream stamp — an older version than the frame's current one is ignored.
|
|
58
|
+
*/
|
|
59
|
+
export interface FrameWrite {
|
|
60
|
+
version: number;
|
|
61
|
+
r: Record<string, unknown>;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/** Context passed to a slot callback. */
|
|
65
|
+
export interface SlotContext {
|
|
66
|
+
/**
|
|
67
|
+
* True only for the hydration-attach invocation of an adopted
|
|
68
|
+
* document-SSR range — the one call a consumer may answer with a claim
|
|
69
|
+
* (`existing` IS the server-rendered output for these args). Unset on
|
|
70
|
+
* stream-driven re-calls: those must render for real, or content the
|
|
71
|
+
* re-call displaced (e.g. `{$frame}` region ranges) is dropped.
|
|
72
|
+
*/
|
|
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;
|
|
82
|
+
/**
|
|
83
|
+
* Register cleanup for when this occurrence's range is removed from the
|
|
84
|
+
* server content, or the owning frame is disposed.
|
|
85
|
+
*/
|
|
86
|
+
onCleanup(fn: () => void): void;
|
|
87
|
+
/**
|
|
88
|
+
* Live-props opt-in: a binding that registers here receives the
|
|
89
|
+
* re-resolved props when a re-sent record's args CHANGE in value, instead
|
|
90
|
+
* of the occurrence being re-called — the invocation's instance (and its
|
|
91
|
+
* client state) survives the change. Register synchronously during the
|
|
92
|
+
* invocation; one updater per occurrence (last registration wins). A
|
|
93
|
+
* genuine re-call or unmount clears it before/with the binding it served.
|
|
94
|
+
*/
|
|
95
|
+
onUpdate(fn: (props: Record<string, unknown>) => void): void;
|
|
96
|
+
/**
|
|
97
|
+
* The range's current interior — server-rendered client content on an
|
|
98
|
+
* adopted document-SSR boot, or the previous output on a re-call. A
|
|
99
|
+
* framework binding hydrates onto it and returns `undefined` to claim it
|
|
100
|
+
* in place (zero DOM mutation).
|
|
101
|
+
*/
|
|
102
|
+
existing: ChildNode[];
|
|
103
|
+
/**
|
|
104
|
+
* The range's own marker comments, when the occurrence has a placed range.
|
|
105
|
+
* A framework binding whose slot content is reactive at the top level (a
|
|
106
|
+
* boundary accessor, changing route children) owns the interior instead of
|
|
107
|
+
* returning nodes: bind before `end` with the framework's insert primitive
|
|
108
|
+
* and return `undefined` — the frame leaves the range alone (server morphs
|
|
109
|
+
* already protect slot ranges).
|
|
110
|
+
*/
|
|
111
|
+
range?: { start: Comment; end: Comment };
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Client content for a server-declared slot. Direct-insert occurrences
|
|
116
|
+
* call it with empty props; render-prop occurrences pass the occurrence's
|
|
117
|
+
* resolved args (primitives literal, `{$ref}` data resolved through the
|
|
118
|
+
* host, `{$frame}` regions as marker-range fragments). Return nodes to fill
|
|
119
|
+
* the range, or `undefined` to claim `ctx.existing` untouched.
|
|
120
|
+
*/
|
|
121
|
+
export type Slot = (props: Record<string, unknown>, ctx: SlotContext) => Node | Node[] | undefined;
|
|
122
|
+
|
|
123
|
+
export interface Frame {
|
|
124
|
+
/** Merge a write into the store and flush (morph/reveal/slot sync). */
|
|
125
|
+
apply(write: FrameWrite): void;
|
|
126
|
+
/** The active version, or undefined before the first apply. */
|
|
127
|
+
readonly version: number | undefined;
|
|
128
|
+
/** Read-only view of the resident record store. */
|
|
129
|
+
readonly store: Readonly<Record<string, unknown>>;
|
|
130
|
+
/** The stream's error record, if an `error` chunk arrived. */
|
|
131
|
+
readonly error: unknown;
|
|
132
|
+
/** Whether the named fragment has been revealed into the boundary. */
|
|
133
|
+
isRevealed(segment: string): boolean;
|
|
134
|
+
/**
|
|
135
|
+
* Re-key this live frame to a different boundary id (the mount-preserving
|
|
136
|
+
* half of a call-site handoff): nothing tears down — the element, store,
|
|
137
|
+
* and slot state stay — while leaving the old id stashes a retention
|
|
138
|
+
* snapshot under it and joining the new id seeds/drains its retained
|
|
139
|
+
* store and buffered chunks. Version affinity resets: histories are per
|
|
140
|
+
* boundary id.
|
|
141
|
+
*/
|
|
142
|
+
rebind(id: string): void;
|
|
143
|
+
/**
|
|
144
|
+
* Forget the version baseline without touching content — the next write
|
|
145
|
+
* is accepted whatever its number. Called by the host after seeding a
|
|
146
|
+
* registration from a retained snapshot, whose numbering belongs to a
|
|
147
|
+
* different stream space.
|
|
148
|
+
*/
|
|
149
|
+
rebase(): void;
|
|
150
|
+
/** Tear down: slot cleanups cascade, later chunks are ignored. Idempotent. */
|
|
151
|
+
dispose(): void;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Routes a flat stream of addressed chunks to frames by id, buffering chunks
|
|
156
|
+
* for frames that have not registered yet (only the newest version's chunks
|
|
157
|
+
* are kept). `data` chunks are response-scoped and go to `applyData`.
|
|
158
|
+
*
|
|
159
|
+
* An id may have several frames (the same server component mounted more
|
|
160
|
+
* than once): chunks fan out to all of them, and a frame registering after
|
|
161
|
+
* delivery is seeded from a sibling's store.
|
|
162
|
+
*/
|
|
163
|
+
export interface FrameHost {
|
|
164
|
+
register(id: string, frame: Frame): void;
|
|
165
|
+
/** Remove one frame (or all frames of the id when `frame` is omitted). */
|
|
166
|
+
unregister(id: string, frame?: Frame): void;
|
|
167
|
+
apply(chunk: FrameChunk): void;
|
|
168
|
+
/** The first registered frame under the id, if any. */
|
|
169
|
+
get(id: string): Frame | undefined;
|
|
170
|
+
serialize(value: unknown): { $ref: string };
|
|
171
|
+
/** `frameId` is the resolving frame's id — route to its stream's table. */
|
|
172
|
+
resolve(ref: { $ref: string }, frameId?: string): unknown;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* The bubbling DOM event (`"frame:applied"`) a frame dispatches from its
|
|
177
|
+
* parent element whenever server content lands in the document — root
|
|
178
|
+
* materialize/morph, segment reveal, fallback materialization — with
|
|
179
|
+
* `detail: { id, version, reason }`. One document-level listener sees every
|
|
180
|
+
* boundary (nested region frames dispatch too); use it to re-apply
|
|
181
|
+
* client-owned decorations on server-owned markup (router affordance
|
|
182
|
+
* reflection, e.g. `aria-current`) without a MutationObserver.
|
|
183
|
+
*/
|
|
184
|
+
export const FRAME_APPLIED_EVENT: "frame:applied";
|
|
185
|
+
|
|
186
|
+
/** Options for `createFrameHost`. */
|
|
187
|
+
export interface FrameHostOptions {
|
|
188
|
+
/**
|
|
189
|
+
* Backs `{$ref}` slot args (typically a codec data table's `resolve`).
|
|
190
|
+
* `frameId` identifies the resolving frame — data tables are
|
|
191
|
+
* response-scoped, so multi-stream hosts route by it (nested region ids
|
|
192
|
+
* prefix-match their root).
|
|
193
|
+
*/
|
|
194
|
+
resolve?(ref: { $ref: string }, frameId?: string): unknown;
|
|
195
|
+
/** Test/host-side counterpart of `resolve`. */
|
|
196
|
+
serialize?(value: unknown): { $ref: string };
|
|
197
|
+
/**
|
|
198
|
+
* Receives each `data` chunk whole. Wire a codec table:
|
|
199
|
+
* `applyData: c => table.apply(c)` (see `createJSONDataTable`).
|
|
200
|
+
*/
|
|
201
|
+
applyData?(chunk: Extract<FrameChunk, { type: "data" }>): void;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
export function createFrameHost(options?: FrameHostOptions): FrameHost;
|
|
205
|
+
|
|
206
|
+
/** Options for `createFrame` / `createFrameElement`. */
|
|
207
|
+
export interface FrameOptions {
|
|
208
|
+
/** Register with this host under `id`, receiving routed/buffered chunks. */
|
|
209
|
+
host?: FrameHost;
|
|
210
|
+
id?: string;
|
|
211
|
+
/** Client content keyed by prop name (occurrences resolve by prop). */
|
|
212
|
+
slots?: Record<string, Slot>;
|
|
213
|
+
/**
|
|
214
|
+
* Adopt existing server-rendered DOM: the first apply morphs against it,
|
|
215
|
+
* and slots sync immediately (hydration attach) — a document-SSR boot
|
|
216
|
+
* needs no chunk.
|
|
217
|
+
*/
|
|
218
|
+
adopt?: boolean;
|
|
219
|
+
/** Called after each apply flush (tests/telemetry). */
|
|
220
|
+
onApply?(info: { version: number; reason: "materialize" | "morph" | "reveal" }): void;
|
|
221
|
+
/**
|
|
222
|
+
* Wraps element-claim sweeps (`a[href]`/`form[action]` in materialized
|
|
223
|
+
* server content — and only those) so claim consumers register their
|
|
224
|
+
* per-element cleanup against the boundary's reactive owner, e.g.
|
|
225
|
+
* `fn => runWithOwner(owner, fn)`. Nested region frames inherit it.
|
|
226
|
+
* Without it, sweeps run under whatever owner is current (none, for
|
|
227
|
+
* streamed chunks).
|
|
228
|
+
*/
|
|
229
|
+
ownerScope?<T>(fn: () => T): T;
|
|
230
|
+
/**
|
|
231
|
+
* Boundary-driven segment reveal. When present, `#revealSegment` hands the
|
|
232
|
+
* placeholder seam to this hook instead of swapping imperatively: the binding
|
|
233
|
+
* reconstructs a client `<Loading>` there — `fallback` is the placeholder's
|
|
234
|
+
* own template content (shown while holding), `content()` materializes the
|
|
235
|
+
* segment and renders its client fills INSIDE the boundary so their readiness
|
|
236
|
+
* gates the reveal — and inserts it before `before`. An unboundaried async
|
|
237
|
+
* fill suspends up to that boundary and is covered instead of orphaned; one
|
|
238
|
+
* boundary per revealed segment, i.e. per author-placed `<Loading>`. Omit it
|
|
239
|
+
* for the framework-agnostic imperative swap (no reactive reveal).
|
|
240
|
+
*/
|
|
241
|
+
reveal?(seam: { before: Node; fallback: Node[]; content: () => Node | DocumentFragment }): void;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
/**
|
|
245
|
+
* A frame rendering into an EXISTING element boundary. Pass `adopt: true` for
|
|
246
|
+
* the document-SSR path: the element already holds server-rendered content,
|
|
247
|
+
* so the first apply morphs against it and slots sync immediately (hydration
|
|
248
|
+
* attach), claiming their server-rendered DOM — a document boot needs no
|
|
249
|
+
* chunk.
|
|
250
|
+
*/
|
|
251
|
+
export function createFrame(boundary: Element, options?: FrameOptions): Frame;
|
|
252
|
+
|
|
253
|
+
/** The default boundary/region element tag and its id attribute — the DOM
|
|
254
|
+
* contract the producer emits at t=0 and the consumer creates/adopts. */
|
|
255
|
+
export const FRAME_TAG: "dx-frame";
|
|
256
|
+
export const FRAME_ID_ATTR: "data-fid";
|
|
257
|
+
|
|
258
|
+
/**
|
|
259
|
+
* Create a boundary/region ELEMENT and bind a host-registered frame to it.
|
|
260
|
+
* The frame mounts INTO the element (server content is its children, morphed
|
|
261
|
+
* in place). Because the boundary is a real node, `insert` places the
|
|
262
|
+
* returned `element` in any position — single, array, or fragment — with no
|
|
263
|
+
* special-casing. One frame per element; lifecycle belongs to the creator via
|
|
264
|
+
* `dispose()` (register it with your owner's cleanup).
|
|
265
|
+
*/
|
|
266
|
+
export function createFrameElement(options: FrameOptions): {
|
|
267
|
+
readonly element: Element;
|
|
268
|
+
readonly frame: Frame;
|
|
269
|
+
dispose(): void;
|
|
270
|
+
};
|