@farm.js/islx 0.0.0-stage → 0.0.2
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/CHANGELOG.md +55 -0
- package/LICENSE +21 -0
- package/README.md +198 -2
- package/SPEC.md +220 -0
- package/dist/_chunks/actions-2sTj0A2T.mjs +306 -0
- package/dist/_chunks/actions-B8dJ8f7L.mjs +615 -0
- package/dist/_chunks/actions-BCtIGvrP.d.mts +58 -0
- package/dist/_chunks/actions-Eutk6UzT.mjs +326 -0
- package/dist/_chunks/build-COIcs7wm.mjs +9 -0
- package/dist/_chunks/codec-CK8kZp4u.d.mts +64 -0
- package/dist/_chunks/codec-DqfVYKsx.mjs +279 -0
- package/dist/_chunks/handle-C0N9ZyNV.d.mts +35 -0
- package/dist/_chunks/hooks-3VZV8f_d.d.mts +30 -0
- package/dist/_chunks/hooks-De0gNpOn.d.mts +64 -0
- package/dist/_chunks/prehydration-B1GLTLdk.mjs +36 -0
- package/dist/_chunks/schedule-BFoGKRrU.mjs +216 -0
- package/dist/_chunks/schedule-Dw14dW0H.d.mts +17 -0
- package/dist/_chunks/tree-BDB5VEao.mjs +777 -0
- package/dist/_chunks/tree-fID6sz25.d.mts +102 -0
- package/dist/_chunks/value-store-Dm-7xJ6W.d.mts +19 -0
- package/dist/_chunks/wire-C3Rpbwl6.d.mts +98 -0
- package/dist/_chunks/wire-CXOodE06.mjs +121 -0
- package/dist/client.d.mts +58 -0
- package/dist/client.mjs +197 -0
- package/dist/compiler.d.mts +2 -0
- package/dist/compiler.mjs +1 -0
- package/dist/index.d.mts +3 -0
- package/dist/index.mjs +3 -0
- package/dist/react/client.d.mts +49 -0
- package/dist/react/client.mjs +456 -0
- package/dist/react/server.d.mts +51 -0
- package/dist/react/server.mjs +84 -0
- package/dist/react.d.mts +3 -0
- package/dist/react.mjs +1 -0
- package/dist/server.d.mts +200 -0
- package/dist/server.mjs +378 -0
- package/dist/vite.d.mts +57 -0
- package/dist/vite.mjs +497 -0
- package/package.json +121 -4
|
@@ -0,0 +1,777 @@
|
|
|
1
|
+
import { i as decodeTagged, n as NOT_A_VALUE, o as encodeLeaf } from "./codec-DqfVYKsx.mjs";
|
|
2
|
+
import { A as VALUE_SCRIPT_TYPE, D as TREE_SCRIPT_TYPE, S as SLOT_ELEMENT, c as CHUNK_SCRIPT_TYPE, h as ISLAND_ELEMENT, p as HTML_ELEMENT, y as PROPS_SCRIPT_TYPE } from "./wire-CXOodE06.mjs";
|
|
3
|
+
import { l as getValueStore, t as createServerReference } from "./actions-2sTj0A2T.mjs";
|
|
4
|
+
import { t as getBuildDefaults } from "./build-COIcs7wm.mjs";
|
|
5
|
+
import * as React from "react";
|
|
6
|
+
import * as ReactDOM from "react-dom";
|
|
7
|
+
// src/react/island.ts
|
|
8
|
+
/**
|
|
9
|
+
* A client component, as the server renders it: its HTML inside `<f-island>`, and its props in
|
|
10
|
+
* a script after it. Shared by the server and the browser: inside a hydrating island, a nested
|
|
11
|
+
* client component is part of that island's React tree, not an island of its own.
|
|
12
|
+
*
|
|
13
|
+
* The compiler wraps every export of a `"use client"` module with `createIsland` in the server
|
|
14
|
+
* build. The browser build keeps the module as it is.
|
|
15
|
+
*/
|
|
16
|
+
const CLIENT_REFERENCE = Symbol.for("islx.react.client-reference");
|
|
17
|
+
/** The module export an island component renders, or undefined for any other type. */
|
|
18
|
+
function getClientReference(type) {
|
|
19
|
+
if (!type || typeof type !== "function" && typeof type !== "object") return void 0;
|
|
20
|
+
return type[CLIENT_REFERENCE];
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Makes `element`'s client components part of the React root around it (a layout that hydrates
|
|
24
|
+
* as one root), instead of islands of their own.
|
|
25
|
+
*/
|
|
26
|
+
function withinClientRoot(element) {
|
|
27
|
+
return React.createElement(getContexts().graph.Provider, { value: true }, element);
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Starts islands again below a `withinClientRoot`: the page a hydrating layout keeps as server
|
|
31
|
+
* HTML renders its client components as islands of their own.
|
|
32
|
+
*/
|
|
33
|
+
function isolateIslands(element) {
|
|
34
|
+
return React.createElement(getContexts().graph.Provider, { value: false }, element);
|
|
35
|
+
}
|
|
36
|
+
const RENDER_STATE_RESOLVER = Symbol.for("islx.react.render-state");
|
|
37
|
+
/**
|
|
38
|
+
* For a framework that knows the request being rendered without React context (a request store):
|
|
39
|
+
* returns the render's settings when no `IslandsProvider` or `renderToStream` gave them. Keep the
|
|
40
|
+
* same `sent` set for the whole response.
|
|
41
|
+
*/
|
|
42
|
+
function setRenderStateResolver(resolve) {
|
|
43
|
+
globalThis[RENDER_STATE_RESOLVER] = resolve;
|
|
44
|
+
}
|
|
45
|
+
function resolveRenderState() {
|
|
46
|
+
const resolve = globalThis[RENDER_STATE_RESOLVER];
|
|
47
|
+
return resolve ? resolve() : null;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Gives islands a render's settings when the framework renders the page itself (not through
|
|
51
|
+
* `renderToStream`): which values the browser holds, and which this render already wrote.
|
|
52
|
+
*/
|
|
53
|
+
function IslandsProvider({ held = null, children }) {
|
|
54
|
+
const state = React.useMemo(() => ({
|
|
55
|
+
held: held && held.size > 0 ? held : null,
|
|
56
|
+
sent: /* @__PURE__ */ new Set()
|
|
57
|
+
}), [held]);
|
|
58
|
+
return React.createElement(getContexts().render.Provider, { value: state }, children);
|
|
59
|
+
}
|
|
60
|
+
const SERVER_HOOK = Symbol.for("islx.react.server");
|
|
61
|
+
/** @internal */
|
|
62
|
+
function getIslandServerHook() {
|
|
63
|
+
return globalThis[SERVER_HOOK];
|
|
64
|
+
}
|
|
65
|
+
/** @internal */
|
|
66
|
+
function setIslandServerHook(hook) {
|
|
67
|
+
globalThis[SERVER_HOOK] = hook;
|
|
68
|
+
}
|
|
69
|
+
/** Tagged objects the runtime made (slots), as opposed to app data with a "$" key. */
|
|
70
|
+
const runtimeTagged = /* @__PURE__ */ new WeakSet();
|
|
71
|
+
const CONTEXTS = Symbol.for("islx.react.contexts");
|
|
72
|
+
/** @internal */
|
|
73
|
+
function getContexts() {
|
|
74
|
+
const scope = globalThis;
|
|
75
|
+
const all = scope[CONTEXTS] ??= /* @__PURE__ */ new WeakMap();
|
|
76
|
+
let contexts = all.get(React.createElement);
|
|
77
|
+
if (!contexts) {
|
|
78
|
+
contexts = {
|
|
79
|
+
graph: React.createContext(false),
|
|
80
|
+
render: React.createContext(null)
|
|
81
|
+
};
|
|
82
|
+
all.set(React.createElement, contexts);
|
|
83
|
+
}
|
|
84
|
+
return contexts;
|
|
85
|
+
}
|
|
86
|
+
function isElement(value) {
|
|
87
|
+
const type = value?.$$typeof;
|
|
88
|
+
return type === Symbol.for("react.transitional.element") || type === Symbol.for("react.element");
|
|
89
|
+
}
|
|
90
|
+
const slotStyle = { display: "contents" };
|
|
91
|
+
/**
|
|
92
|
+
* Splits props into what renders on the server and what travels: server output passed to an
|
|
93
|
+
* island (`children`, or any element prop) renders once inside `<f-slot>`, and travels as a
|
|
94
|
+
* reference to it, so the browser reads its HTML from the page instead of receiving it twice.
|
|
95
|
+
*/
|
|
96
|
+
function splitSlots(value, prefix, counter) {
|
|
97
|
+
if (isElement(value)) {
|
|
98
|
+
const name = `${prefix}${counter.next++}`;
|
|
99
|
+
const reference = {
|
|
100
|
+
["$"]: "s",
|
|
101
|
+
["v"]: name
|
|
102
|
+
};
|
|
103
|
+
runtimeTagged.add(reference);
|
|
104
|
+
return {
|
|
105
|
+
render: React.createElement(SLOT_ELEMENT, {
|
|
106
|
+
key: value.key ?? name,
|
|
107
|
+
name,
|
|
108
|
+
style: slotStyle
|
|
109
|
+
}, value),
|
|
110
|
+
wire: reference
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
if (Array.isArray(value)) {
|
|
114
|
+
const parts = value.map((entry) => splitSlots(entry, prefix, counter));
|
|
115
|
+
return {
|
|
116
|
+
render: parts.map((part) => part.render),
|
|
117
|
+
wire: parts.map((part) => part.wire)
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
if (value && typeof value === "object") {
|
|
121
|
+
const prototype = Object.getPrototypeOf(value);
|
|
122
|
+
if (prototype !== Object.prototype && prototype !== null) return {
|
|
123
|
+
render: value,
|
|
124
|
+
wire: value
|
|
125
|
+
};
|
|
126
|
+
const render = {};
|
|
127
|
+
const wire = {};
|
|
128
|
+
for (const [key, entry] of Object.entries(value)) {
|
|
129
|
+
const part = splitSlots(entry, prefix, counter);
|
|
130
|
+
render[key] = part.render;
|
|
131
|
+
wire[key] = part.wire;
|
|
132
|
+
}
|
|
133
|
+
return {
|
|
134
|
+
render,
|
|
135
|
+
wire
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
return {
|
|
139
|
+
render: value,
|
|
140
|
+
wire: value
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
const warned = /* @__PURE__ */ new Set();
|
|
144
|
+
function isThenable$1(value) {
|
|
145
|
+
return !!value && typeof value.then === "function";
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* Wraps a client component so the server renders it as an island. `src` is the module the browser
|
|
149
|
+
* imports, `exportName` the component's export there.
|
|
150
|
+
*/
|
|
151
|
+
function createIsland(Component, src, exportName, strategy = "load") {
|
|
152
|
+
function Island(props) {
|
|
153
|
+
const { graph, render } = getContexts();
|
|
154
|
+
const insideIsland = React.useContext(graph);
|
|
155
|
+
const state = React.useContext(render) ?? resolveRenderState();
|
|
156
|
+
const id = React.useId();
|
|
157
|
+
if (insideIsland) return React.createElement(Component, props);
|
|
158
|
+
const hook = getIslandServerHook();
|
|
159
|
+
if (!hook) throw new Error("islx: an island rendered on a server that did not import @farm.js/islx/react/server");
|
|
160
|
+
let encoded;
|
|
161
|
+
let renderProps;
|
|
162
|
+
try {
|
|
163
|
+
const { render: rendered, wire } = splitSlots(props, `${id}s`, { next: 0 });
|
|
164
|
+
renderProps = rendered;
|
|
165
|
+
const skip = state ? /* @__PURE__ */ new Set([...state.held ?? [], ...state.sent]) : null;
|
|
166
|
+
encoded = hook.encode(wire, skip);
|
|
167
|
+
for (const value of encoded.values) state?.sent.add(value.key);
|
|
168
|
+
} catch (error) {
|
|
169
|
+
if (isThenable$1(error)) throw error;
|
|
170
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
171
|
+
const key = `${src}#${exportName}:${message}`;
|
|
172
|
+
if (!warned.has(key)) {
|
|
173
|
+
warned.add(key);
|
|
174
|
+
console.warn(`[islx] ${exportName} (${src}) was rendered without hydration: its props cannot be sent to the browser. ${message}`);
|
|
175
|
+
}
|
|
176
|
+
return React.createElement(Component, props);
|
|
177
|
+
}
|
|
178
|
+
if (strategy === "load") for (const href of getBuildDefaults().modules?.[src] ?? []) ReactDOM.preloadModule?.(href, { as: "script" });
|
|
179
|
+
return React.createElement(React.Fragment, null, ...encoded.values.map((value) => React.createElement("script", {
|
|
180
|
+
key: value.key,
|
|
181
|
+
type: VALUE_SCRIPT_TYPE,
|
|
182
|
+
name: value.key,
|
|
183
|
+
dangerouslySetInnerHTML: { __html: hook.escape(value.json) }
|
|
184
|
+
})), React.createElement(ISLAND_ELEMENT, {
|
|
185
|
+
key: "island",
|
|
186
|
+
src,
|
|
187
|
+
export: exportName,
|
|
188
|
+
props: id,
|
|
189
|
+
strategy,
|
|
190
|
+
style: slotStyle
|
|
191
|
+
}, React.createElement(graph.Provider, { value: true }, React.createElement(Component, renderProps))), React.createElement("script", {
|
|
192
|
+
key: "props",
|
|
193
|
+
type: PROPS_SCRIPT_TYPE,
|
|
194
|
+
htmlFor: id,
|
|
195
|
+
dangerouslySetInnerHTML: { __html: hook.escape(encoded.json) }
|
|
196
|
+
}));
|
|
197
|
+
}
|
|
198
|
+
Island.displayName = `Island(${Component.displayName || Component.name || exportName})`;
|
|
199
|
+
Object.defineProperty(Island, CLIENT_REFERENCE, { value: {
|
|
200
|
+
src,
|
|
201
|
+
exportName,
|
|
202
|
+
Component
|
|
203
|
+
} });
|
|
204
|
+
return Island;
|
|
205
|
+
}
|
|
206
|
+
// src/react/tree.ts
|
|
207
|
+
/**
|
|
208
|
+
* Server trees: a page as a serialized React tree, the role React Server Components' Flight
|
|
209
|
+
* payload has, for pages under a layout that hydrates as one React root.
|
|
210
|
+
*
|
|
211
|
+
* A page that cannot hydrate (it is async, or it reads server data) used to keep its client
|
|
212
|
+
* components as islands of their own, which cannot read the layout's context. Instead the server
|
|
213
|
+
* resolves the page into host elements and client-component references, renders that tree, and
|
|
214
|
+
* embeds it as JSON (`<script type="f/tree">`). The browser rebuilds the same tree inside the
|
|
215
|
+
* layout's root, so the page's client components hydrate as the layout's descendants.
|
|
216
|
+
*
|
|
217
|
+
* Anything the resolver cannot represent (a server component that calls hooks, a class component,
|
|
218
|
+
* a client component whose props do not serialize, an unknown element type) renders as server
|
|
219
|
+
* HTML inside `<f-html>`, whose own client components hydrate as islands.
|
|
220
|
+
*/
|
|
221
|
+
const MEMO_TYPE = Symbol.for("react.memo");
|
|
222
|
+
const FORWARD_REF_TYPE = Symbol.for("react.forward_ref");
|
|
223
|
+
/** The element around server HTML a tree cannot represent; its `name` is the HTML's id. */
|
|
224
|
+
const SERVER_HTML_TAG = HTML_ELEMENT;
|
|
225
|
+
/**
|
|
226
|
+
* Marks a navigation response that carries only the page's payload. The browser rebuilds such a
|
|
227
|
+
* page in its layout's React root and never shows its HTML, so the server leaves the HTML out.
|
|
228
|
+
*/
|
|
229
|
+
const PAYLOAD_ONLY_ATTRIBUTE = "partial";
|
|
230
|
+
const SERVER_HTML_ATTRIBUTE = "name";
|
|
231
|
+
const PARSER_SENSITIVE_TAGS = /* @__PURE__ */ new Set([
|
|
232
|
+
"caption",
|
|
233
|
+
"colgroup",
|
|
234
|
+
"datalist",
|
|
235
|
+
"head",
|
|
236
|
+
"html",
|
|
237
|
+
"iframe",
|
|
238
|
+
"math",
|
|
239
|
+
"noscript",
|
|
240
|
+
"optgroup",
|
|
241
|
+
"option",
|
|
242
|
+
"script",
|
|
243
|
+
"select",
|
|
244
|
+
"style",
|
|
245
|
+
"svg",
|
|
246
|
+
"table",
|
|
247
|
+
"tbody",
|
|
248
|
+
"td",
|
|
249
|
+
"template",
|
|
250
|
+
"textarea",
|
|
251
|
+
"tfoot",
|
|
252
|
+
"th",
|
|
253
|
+
"thead",
|
|
254
|
+
"title",
|
|
255
|
+
"tr"
|
|
256
|
+
]);
|
|
257
|
+
/** Thrown where the resolver cannot represent a node; the node then renders as server HTML. */
|
|
258
|
+
var UnsupportedNode = class extends Error {};
|
|
259
|
+
/** An unsupported node inside a parser-sensitive element; its outermost such ancestor wraps. */
|
|
260
|
+
var UnsupportedInParserSensitiveElement = class extends Error {};
|
|
261
|
+
function getHooksDispatcherSlot() {
|
|
262
|
+
const runtime = React;
|
|
263
|
+
const react19 = runtime.__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE;
|
|
264
|
+
if (react19 && "H" in react19) return {
|
|
265
|
+
get: () => react19.H,
|
|
266
|
+
set: (value) => {
|
|
267
|
+
react19.H = value;
|
|
268
|
+
}
|
|
269
|
+
};
|
|
270
|
+
const react18 = runtime.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED?.ReactCurrentDispatcher;
|
|
271
|
+
if (react18) return {
|
|
272
|
+
get: () => react18.current,
|
|
273
|
+
set: (value) => {
|
|
274
|
+
react18.current = value;
|
|
275
|
+
}
|
|
276
|
+
};
|
|
277
|
+
return null;
|
|
278
|
+
}
|
|
279
|
+
const unsupportedHooksDispatcher = /* @__PURE__ */ new Proxy({}, { get(_target, name) {
|
|
280
|
+
return () => {
|
|
281
|
+
throw new UnsupportedNode(`calls ${String(name)}()`);
|
|
282
|
+
};
|
|
283
|
+
} });
|
|
284
|
+
function callServerComponent(render, props, forwardRef) {
|
|
285
|
+
const slot = getHooksDispatcherSlot();
|
|
286
|
+
const previous = slot?.get();
|
|
287
|
+
slot?.set(unsupportedHooksDispatcher);
|
|
288
|
+
try {
|
|
289
|
+
return forwardRef ? render(props, null) : render(props);
|
|
290
|
+
} finally {
|
|
291
|
+
slot?.set(previous);
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
function isThenable(value) {
|
|
295
|
+
return value !== null && (typeof value === "object" || typeof value === "function") && typeof value.then === "function";
|
|
296
|
+
}
|
|
297
|
+
function serverHtmlNode(state, node, key, context) {
|
|
298
|
+
if (context.parserSensitive) throw new UnsupportedInParserSensitiveElement();
|
|
299
|
+
const id = state.nextId++;
|
|
300
|
+
state.serverHtml.set(id, node);
|
|
301
|
+
return key === null ? {
|
|
302
|
+
$: "x",
|
|
303
|
+
i: id
|
|
304
|
+
} : {
|
|
305
|
+
$: "x",
|
|
306
|
+
i: id,
|
|
307
|
+
k: String(key)
|
|
308
|
+
};
|
|
309
|
+
}
|
|
310
|
+
async function resolveProps(state, props, context, host) {
|
|
311
|
+
const entries = await Promise.all(Object.entries(props).map(async ([name, value]) => {
|
|
312
|
+
if (value === void 0 && host) return null;
|
|
313
|
+
if (name === "ref") {
|
|
314
|
+
if (value === null) return null;
|
|
315
|
+
throw new UnsupportedNode("receives a ref");
|
|
316
|
+
}
|
|
317
|
+
if (host && typeof value === "function") throw new UnsupportedNode(`passes a function to <${String(name)}>`);
|
|
318
|
+
return [name, await resolveValue(state, value, context, /* @__PURE__ */ new Set())];
|
|
319
|
+
}));
|
|
320
|
+
return Object.fromEntries(entries.filter((entry) => entry !== null));
|
|
321
|
+
}
|
|
322
|
+
async function resolveValue(state, value, context, seen) {
|
|
323
|
+
const leaf = encodeLeaf(value);
|
|
324
|
+
if (leaf) return leaf;
|
|
325
|
+
if (value === null) return null;
|
|
326
|
+
if (typeof value === "string" || typeof value === "boolean") return value;
|
|
327
|
+
if (typeof value === "number") return Number.isFinite(value) ? value : {
|
|
328
|
+
$: "n",
|
|
329
|
+
v: String(value)
|
|
330
|
+
};
|
|
331
|
+
if (typeof value === "function") {
|
|
332
|
+
const reference = getIslandServerHook()?.action(value);
|
|
333
|
+
if (reference) {
|
|
334
|
+
state.actions.set(reference.v, value);
|
|
335
|
+
return reference;
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
if (typeof value !== "object") throw new UnsupportedNode(`passes a ${typeof value} value`);
|
|
339
|
+
if (React.isValidElement(value)) return resolveElement(state, value, context);
|
|
340
|
+
if (isThenable(value)) try {
|
|
341
|
+
return {
|
|
342
|
+
$: "P",
|
|
343
|
+
v: await resolveValue(state, await value, context, seen)
|
|
344
|
+
};
|
|
345
|
+
} catch (error) {
|
|
346
|
+
if (error instanceof UnsupportedInParserSensitiveElement) throw error;
|
|
347
|
+
throw new UnsupportedNode("passes a promise that does not resolve to a serializable value");
|
|
348
|
+
}
|
|
349
|
+
if (seen.has(value)) throw new UnsupportedNode("passes a circular value");
|
|
350
|
+
seen.add(value);
|
|
351
|
+
try {
|
|
352
|
+
if (Array.isArray(value)) return await Promise.all(value.map((entry) => resolveValue(state, entry, context, seen)));
|
|
353
|
+
if (value instanceof Map) return {
|
|
354
|
+
$: "M",
|
|
355
|
+
v: await resolveValue(state, Array.from(value), context, seen)
|
|
356
|
+
};
|
|
357
|
+
if (value instanceof Set) return {
|
|
358
|
+
$: "S",
|
|
359
|
+
v: await resolveValue(state, Array.from(value), context, seen)
|
|
360
|
+
};
|
|
361
|
+
const prototype = Object.getPrototypeOf(value);
|
|
362
|
+
if (prototype !== Object.prototype && prototype !== null) throw new UnsupportedNode("passes a class instance");
|
|
363
|
+
const entries = await Promise.all(Object.entries(value).map(async ([name, entry]) => [name, await resolveValue(state, entry, context, seen)]));
|
|
364
|
+
const object = Object.fromEntries(entries);
|
|
365
|
+
return Object.prototype.hasOwnProperty.call(object, "$") ? {
|
|
366
|
+
$: "o",
|
|
367
|
+
v: object
|
|
368
|
+
} : object;
|
|
369
|
+
} finally {
|
|
370
|
+
seen.delete(value);
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
async function resolveElement(state, element, context) {
|
|
374
|
+
const key = element.key === null || element.key === void 0 ? null : String(element.key);
|
|
375
|
+
const withKey = (node) => key === null ? node : {
|
|
376
|
+
...node,
|
|
377
|
+
k: key
|
|
378
|
+
};
|
|
379
|
+
let type = element.type;
|
|
380
|
+
const props = element.props ?? {};
|
|
381
|
+
try {
|
|
382
|
+
if (typeof type === "string") {
|
|
383
|
+
const parserSensitive = context.parserSensitive || PARSER_SENSITIVE_TAGS.has(type);
|
|
384
|
+
try {
|
|
385
|
+
const { children, ...rest } = props;
|
|
386
|
+
const resolved = await resolveProps(state, rest, context, true);
|
|
387
|
+
if (children !== void 0) resolved.children = await resolveValue(state, children, { parserSensitive }, /* @__PURE__ */ new Set());
|
|
388
|
+
return withKey({
|
|
389
|
+
$: "h",
|
|
390
|
+
t: type,
|
|
391
|
+
p: resolved
|
|
392
|
+
});
|
|
393
|
+
} catch (error) {
|
|
394
|
+
if (error instanceof UnsupportedInParserSensitiveElement && !context.parserSensitive) return serverHtmlNode(state, element, key, context);
|
|
395
|
+
throw error;
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
if (type === React.Fragment || type === React.StrictMode) return withKey({
|
|
399
|
+
$: "f",
|
|
400
|
+
c: await resolveValue(state, props.children, context, /* @__PURE__ */ new Set())
|
|
401
|
+
});
|
|
402
|
+
if (type === React.Suspense) {
|
|
403
|
+
const id = state.nextId++;
|
|
404
|
+
const fallback = await resolveValue(state, props.fallback, context, /* @__PURE__ */ new Set());
|
|
405
|
+
const encoded = resolveValue(state, props.children, context, /* @__PURE__ */ new Set()).catch((error) => {
|
|
406
|
+
if (error instanceof UnsupportedNode) return serverHtmlNode(state, React.createElement(React.Fragment, null, props.children), null, { parserSensitive: false });
|
|
407
|
+
throw error;
|
|
408
|
+
});
|
|
409
|
+
encoded.catch(() => {});
|
|
410
|
+
state.chunks.set(id, { encoded });
|
|
411
|
+
return withKey({
|
|
412
|
+
$: "p",
|
|
413
|
+
f: fallback,
|
|
414
|
+
i: id
|
|
415
|
+
});
|
|
416
|
+
}
|
|
417
|
+
const clientReference = getClientReference(type);
|
|
418
|
+
if (clientReference) {
|
|
419
|
+
const { children, ...rest } = props;
|
|
420
|
+
const resolved = await resolveProps(state, rest, context, false);
|
|
421
|
+
if (children !== void 0) resolved.children = await resolveValue(state, children, context, /* @__PURE__ */ new Set());
|
|
422
|
+
state.components.set(`${clientReference.src}#${clientReference.exportName}`, clientReference.Component);
|
|
423
|
+
return withKey({
|
|
424
|
+
$: "c",
|
|
425
|
+
r: clientReference.src,
|
|
426
|
+
e: clientReference.exportName,
|
|
427
|
+
p: resolved
|
|
428
|
+
});
|
|
429
|
+
}
|
|
430
|
+
let forwardRef = false;
|
|
431
|
+
while (type && typeof type === "object") {
|
|
432
|
+
const typeOf = type.$$typeof;
|
|
433
|
+
if (typeOf === MEMO_TYPE) {
|
|
434
|
+
type = type.type;
|
|
435
|
+
if (getClientReference(type)) return resolveElement(state, React.createElement(type, {
|
|
436
|
+
...props,
|
|
437
|
+
key: element.key
|
|
438
|
+
}), context);
|
|
439
|
+
continue;
|
|
440
|
+
}
|
|
441
|
+
if (typeOf === FORWARD_REF_TYPE) {
|
|
442
|
+
type = type.render;
|
|
443
|
+
forwardRef = true;
|
|
444
|
+
break;
|
|
445
|
+
}
|
|
446
|
+
throw new UnsupportedNode("is not a function component");
|
|
447
|
+
}
|
|
448
|
+
if (typeof type !== "function") throw new UnsupportedNode("is not a function component");
|
|
449
|
+
if (type.prototype?.isReactComponent) throw new UnsupportedNode("is a class component");
|
|
450
|
+
let rendered = callServerComponent(type, props, forwardRef);
|
|
451
|
+
if (isThenable(rendered)) rendered = await rendered;
|
|
452
|
+
const resolved = await resolveValue(state, rendered, context, /* @__PURE__ */ new Set());
|
|
453
|
+
return key === null ? resolved : {
|
|
454
|
+
$: "f",
|
|
455
|
+
k: key,
|
|
456
|
+
c: resolved
|
|
457
|
+
};
|
|
458
|
+
} catch (error) {
|
|
459
|
+
if (error instanceof UnsupportedNode) return serverHtmlNode(state, element, key, context);
|
|
460
|
+
throw error;
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
const chunkComponents = /* @__PURE__ */ new WeakMap();
|
|
464
|
+
function getChunkComponent() {
|
|
465
|
+
const existing = chunkComponents.get(React.createElement);
|
|
466
|
+
if (existing) return existing;
|
|
467
|
+
function ServerTreeChunk({ chunk }) {
|
|
468
|
+
if (chunk.status === "fulfilled") return chunk.value;
|
|
469
|
+
if (chunk.status === "rejected") throw chunk.reason;
|
|
470
|
+
return React.use(chunk.promise);
|
|
471
|
+
}
|
|
472
|
+
chunkComponents.set(React.createElement, ServerTreeChunk);
|
|
473
|
+
return ServerTreeChunk;
|
|
474
|
+
}
|
|
475
|
+
function fulfilledThenable(value) {
|
|
476
|
+
const promise = Promise.resolve(value);
|
|
477
|
+
promise.status = "fulfilled";
|
|
478
|
+
promise.value = value;
|
|
479
|
+
return promise;
|
|
480
|
+
}
|
|
481
|
+
function createTreeChildren(children, resolver) {
|
|
482
|
+
if (children === void 0) return [];
|
|
483
|
+
const node = createTreeValue(children, resolver);
|
|
484
|
+
return Array.isArray(children) ? node : [node];
|
|
485
|
+
}
|
|
486
|
+
function createTreeProps(props, resolver, key) {
|
|
487
|
+
const { children, ...rest } = props;
|
|
488
|
+
const created = {};
|
|
489
|
+
for (const [name, value] of Object.entries(rest)) created[name] = createTreeValue(value, resolver);
|
|
490
|
+
if (typeof key === "string") created.key = key;
|
|
491
|
+
return {
|
|
492
|
+
props: created,
|
|
493
|
+
children: createTreeChildren(children, resolver)
|
|
494
|
+
};
|
|
495
|
+
}
|
|
496
|
+
function createTreeValue(value, resolver) {
|
|
497
|
+
if (value === null || typeof value !== "object") return value;
|
|
498
|
+
if (Array.isArray(value)) return value.map((entry) => createTreeValue(entry, resolver));
|
|
499
|
+
switch (value.$) {
|
|
500
|
+
case "h": {
|
|
501
|
+
const { props, children } = createTreeProps(value.p, resolver, value.k);
|
|
502
|
+
return React.createElement(value.t, props, ...children);
|
|
503
|
+
}
|
|
504
|
+
case "c": {
|
|
505
|
+
const { props, children } = createTreeProps(value.p, resolver, value.k);
|
|
506
|
+
return React.createElement(resolver.component(value.r, value.e), props, ...children);
|
|
507
|
+
}
|
|
508
|
+
case "f": return React.createElement(React.Fragment, typeof value.k === "string" ? { key: value.k } : null, ...createTreeChildren(value.c, resolver));
|
|
509
|
+
case "p": return React.createElement(React.Suspense, {
|
|
510
|
+
...typeof value.k === "string" ? { key: value.k } : {},
|
|
511
|
+
fallback: createTreeValue(value.f, resolver)
|
|
512
|
+
}, React.createElement(getChunkComponent(), { chunk: resolver.chunk(value.i) }));
|
|
513
|
+
case "x": return resolver.serverHtml(value.i, typeof value.k === "string" ? value.k : null);
|
|
514
|
+
case "n": return Number(value.v);
|
|
515
|
+
case "a": return resolver.action(value.v, typeof value.b === "string" ? value.b : void 0);
|
|
516
|
+
case "P": return fulfilledThenable(createTreeValue(value.v, resolver));
|
|
517
|
+
case "o": {
|
|
518
|
+
const object = value.v;
|
|
519
|
+
return Object.fromEntries(Object.entries(object).map(([name, entry]) => [name, createTreeValue(entry, resolver)]));
|
|
520
|
+
}
|
|
521
|
+
default:
|
|
522
|
+
if (value.$ !== void 0) {
|
|
523
|
+
const decoded = decodeTagged(value, (child) => createTreeValue(child, resolver));
|
|
524
|
+
if (decoded !== NOT_A_VALUE) return decoded;
|
|
525
|
+
}
|
|
526
|
+
return Object.fromEntries(Object.entries(value).map(([name, entry]) => [name, createTreeValue(entry, resolver)]));
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
function createTreeScript(type, name, json, values = [], attributes = {}) {
|
|
530
|
+
const script = React.createElement("script", {
|
|
531
|
+
type,
|
|
532
|
+
...name ? { name } : {},
|
|
533
|
+
...attributes,
|
|
534
|
+
dangerouslySetInnerHTML: { __html: json }
|
|
535
|
+
});
|
|
536
|
+
if (values.length === 0) return script;
|
|
537
|
+
return React.createElement(React.Fragment, null, ...values.map(([valueKey, valueJson]) => React.createElement("script", {
|
|
538
|
+
key: valueKey,
|
|
539
|
+
type: VALUE_SCRIPT_TYPE,
|
|
540
|
+
name: valueKey,
|
|
541
|
+
dangerouslySetInnerHTML: { __html: valueJson }
|
|
542
|
+
})), script);
|
|
543
|
+
}
|
|
544
|
+
function createTreePage(tree, json, resolver, key, values = []) {
|
|
545
|
+
return React.createElement(React.Fragment, key === void 0 ? null : { key }, createTreeValue(tree, resolver), createTreeScript(TREE_SCRIPT_TYPE, "", json, values));
|
|
546
|
+
}
|
|
547
|
+
/** The value scripts the server sent right before a payload script. */
|
|
548
|
+
function readTreeValueScripts(script) {
|
|
549
|
+
const values = [];
|
|
550
|
+
for (let node = script.previousElementSibling; node?.tagName === "SCRIPT" && node.getAttribute("type") === "f/value"; node = node.previousElementSibling) values.unshift([node.getAttribute("name") ?? "", node.textContent ?? ""]);
|
|
551
|
+
return values;
|
|
552
|
+
}
|
|
553
|
+
/**
|
|
554
|
+
* The page as a navigation the browser renders itself receives it: its payload, every chunk's
|
|
555
|
+
* payload, and the server HTML parts, with none of the HTML the payload rebuilds. Waits for every
|
|
556
|
+
* chunk, since a navigation reads the whole response anyway. Returns null when a chunk failed,
|
|
557
|
+
* so the page renders as usual and its error boundaries apply.
|
|
558
|
+
*/
|
|
559
|
+
async function createPayloadOnlyPage(state, payload, encode, resolver) {
|
|
560
|
+
const chunks = /* @__PURE__ */ new Map();
|
|
561
|
+
for (let pending = [...state.chunks.keys()]; pending.length > 0;) {
|
|
562
|
+
const settled = await Promise.allSettled(pending.map(async (id) => [id, await state.chunks.get(id).encoded]));
|
|
563
|
+
for (const result of settled) {
|
|
564
|
+
if (result.status === "rejected") return null;
|
|
565
|
+
chunks.set(result.value[0], result.value[1]);
|
|
566
|
+
}
|
|
567
|
+
pending = [...state.chunks.keys()].filter((id) => !chunks.has(id));
|
|
568
|
+
}
|
|
569
|
+
return React.createElement(React.Fragment, null, ...Array.from(state.serverHtml.keys(), (id) => resolver.serverHtml(id, `h${id}`)), React.createElement(React.Fragment, { key: "payload" }, createTreeScript(TREE_SCRIPT_TYPE, "", payload.json, payload.values, { [PAYLOAD_ONLY_ATTRIBUTE]: "" })), ...Array.from(chunks, ([id, encoded]) => {
|
|
570
|
+
const chunk = encode({ t: encoded });
|
|
571
|
+
return React.createElement(React.Fragment, { key: `c${id}` }, createTreeScript(CHUNK_SCRIPT_TYPE, String(id), chunk.json, chunk.values));
|
|
572
|
+
}));
|
|
573
|
+
}
|
|
574
|
+
/**
|
|
575
|
+
* Resolves a server page and returns what renders in its place: the resolved tree and its
|
|
576
|
+
* payload. Errors from the page's own code (a framework's redirect or not-found) propagate
|
|
577
|
+
* unchanged. Suspense children resolve concurrently and stream as React renders them.
|
|
578
|
+
* Needs `@farm.js/islx/react/server` imported (large values and actions are encoded there).
|
|
579
|
+
*/
|
|
580
|
+
async function renderServerTree(element, options = {}) {
|
|
581
|
+
const hook = getIslandServerHook();
|
|
582
|
+
if (!hook) throw new Error("islx: renderServerTree needs @farm.js/islx/react/server imported");
|
|
583
|
+
const isolate = options.isolate ?? isolateIslands;
|
|
584
|
+
const state = {
|
|
585
|
+
nextId: 0,
|
|
586
|
+
components: /* @__PURE__ */ new Map(),
|
|
587
|
+
serverHtml: /* @__PURE__ */ new Map(),
|
|
588
|
+
chunks: /* @__PURE__ */ new Map(),
|
|
589
|
+
actions: /* @__PURE__ */ new Map()
|
|
590
|
+
};
|
|
591
|
+
const tree = await resolveValue(state, element, { parserSensitive: false }, /* @__PURE__ */ new Set());
|
|
592
|
+
const held = options.held && options.held.size > 0 ? options.held : null;
|
|
593
|
+
const encode = (payload) => {
|
|
594
|
+
const addressed = hook.address(payload, held);
|
|
595
|
+
return {
|
|
596
|
+
json: hook.escape(addressed.json),
|
|
597
|
+
values: addressed.values.map((value) => [value.key, hook.escape(value.json)])
|
|
598
|
+
};
|
|
599
|
+
};
|
|
600
|
+
const resolver = {
|
|
601
|
+
component(reference, exportName) {
|
|
602
|
+
const Component = state.components.get(`${reference}#${exportName}`);
|
|
603
|
+
if (!Component) throw new Error(`client reference ${reference}#${exportName} was not resolved`);
|
|
604
|
+
return Component;
|
|
605
|
+
},
|
|
606
|
+
chunk(id) {
|
|
607
|
+
const entry = state.chunks.get(id);
|
|
608
|
+
if (entry.chunk) return entry.chunk;
|
|
609
|
+
const chunk = {
|
|
610
|
+
status: "pending",
|
|
611
|
+
promise: entry.encoded.then((encoded) => {
|
|
612
|
+
const payload = encode({ t: encoded });
|
|
613
|
+
chunk.value = React.createElement(React.Fragment, null, createTreeValue(encoded, resolver), createTreeScript(CHUNK_SCRIPT_TYPE, String(id), payload.json, payload.values));
|
|
614
|
+
chunk.status = "fulfilled";
|
|
615
|
+
return chunk.value;
|
|
616
|
+
}, (error) => {
|
|
617
|
+
chunk.status = "rejected";
|
|
618
|
+
chunk.reason = error;
|
|
619
|
+
throw error;
|
|
620
|
+
})
|
|
621
|
+
};
|
|
622
|
+
chunk.promise.catch(() => {});
|
|
623
|
+
entry.chunk = chunk;
|
|
624
|
+
return chunk;
|
|
625
|
+
},
|
|
626
|
+
serverHtml(id, key) {
|
|
627
|
+
return React.createElement(SERVER_HTML_TAG, {
|
|
628
|
+
...key === null ? {} : { key },
|
|
629
|
+
[SERVER_HTML_ATTRIBUTE]: String(id),
|
|
630
|
+
style: { display: "contents" }
|
|
631
|
+
}, isolate(state.serverHtml.get(id)));
|
|
632
|
+
},
|
|
633
|
+
action(id) {
|
|
634
|
+
return state.actions.get(id);
|
|
635
|
+
}
|
|
636
|
+
};
|
|
637
|
+
const payload = encode({
|
|
638
|
+
v: 1,
|
|
639
|
+
t: tree
|
|
640
|
+
});
|
|
641
|
+
const page = options.payloadOnly && await createPayloadOnlyPage(state, payload, encode, resolver) || createTreePage(tree, payload.json, resolver, void 0, payload.values);
|
|
642
|
+
return React.createElement(function ServerTreePage() {
|
|
643
|
+
return page;
|
|
644
|
+
});
|
|
645
|
+
}
|
|
646
|
+
function collectReferences(value, references) {
|
|
647
|
+
if (value === null || typeof value !== "object") return;
|
|
648
|
+
if (Array.isArray(value)) {
|
|
649
|
+
for (const entry of value) collectReferences(entry, references);
|
|
650
|
+
return;
|
|
651
|
+
}
|
|
652
|
+
if (value.$ === "c") references.add(`${value.r}#${value.e}`);
|
|
653
|
+
for (const entry of Object.values(value)) collectReferences(entry, references);
|
|
654
|
+
}
|
|
655
|
+
const TREE_UPDATES = Symbol.for("islx.react.tree-updates");
|
|
656
|
+
function treeListeners() {
|
|
657
|
+
const scope = globalThis;
|
|
658
|
+
return scope[TREE_UPDATES] ??= /* @__PURE__ */ new Set();
|
|
659
|
+
}
|
|
660
|
+
/** Re-renders mounted server trees that render a hot-updated client module. */
|
|
661
|
+
function updateServerTrees(src, module) {
|
|
662
|
+
let updated = 0;
|
|
663
|
+
for (const listener of treeListeners()) if (listener(src, module)) updated++;
|
|
664
|
+
return updated;
|
|
665
|
+
}
|
|
666
|
+
function parseHtml(html) {
|
|
667
|
+
const template = document.createElement("template");
|
|
668
|
+
template.innerHTML = html;
|
|
669
|
+
return template.content;
|
|
670
|
+
}
|
|
671
|
+
function exportOf(module, src, exportName) {
|
|
672
|
+
const Component = module[exportName];
|
|
673
|
+
if (typeof Component !== "function" && (typeof Component !== "object" || Component === null)) throw new Error(`${src} has no export ${exportName}`);
|
|
674
|
+
return Component;
|
|
675
|
+
}
|
|
676
|
+
/**
|
|
677
|
+
* Rebuilds a server-tree page in the browser from its server HTML (an element, or the HTML of a
|
|
678
|
+
* navigation), after loading its client modules. Returns null for a page without a tree.
|
|
679
|
+
*/
|
|
680
|
+
async function createServerTreeElement(source, options) {
|
|
681
|
+
const values = options.values ?? getValueStore();
|
|
682
|
+
const root = typeof source === "string" ? parseHtml(source) : source;
|
|
683
|
+
const script = root.querySelector(`script[type="${TREE_SCRIPT_TYPE}"]`);
|
|
684
|
+
if (!script) return null;
|
|
685
|
+
const json = script.textContent ?? "";
|
|
686
|
+
let ingested = false;
|
|
687
|
+
const parsePayload = (text) => {
|
|
688
|
+
const parsed = JSON.parse(text);
|
|
689
|
+
if (!text.includes(`"\$":"r"`)) return parsed;
|
|
690
|
+
if (!ingested) {
|
|
691
|
+
values.ingest(root);
|
|
692
|
+
ingested = true;
|
|
693
|
+
}
|
|
694
|
+
return values.resolve(parsed);
|
|
695
|
+
};
|
|
696
|
+
const payload = parsePayload(json);
|
|
697
|
+
if (payload.v !== 1) throw new Error("unsupported server tree payload version");
|
|
698
|
+
const scripts = readTreeValueScripts(script);
|
|
699
|
+
const chunks = /* @__PURE__ */ new Map();
|
|
700
|
+
for (const chunkScript of root.querySelectorAll(`script[type="${CHUNK_SCRIPT_TYPE}"]`)) {
|
|
701
|
+
const chunkJson = chunkScript.textContent ?? "";
|
|
702
|
+
chunks.set(Number(chunkScript.getAttribute("name")), {
|
|
703
|
+
tree: parsePayload(chunkJson).t,
|
|
704
|
+
json: chunkJson,
|
|
705
|
+
values: readTreeValueScripts(chunkScript)
|
|
706
|
+
});
|
|
707
|
+
}
|
|
708
|
+
const references = /* @__PURE__ */ new Set();
|
|
709
|
+
collectReferences(payload.t, references);
|
|
710
|
+
for (const chunk of chunks.values()) collectReferences(chunk.tree, references);
|
|
711
|
+
const components = /* @__PURE__ */ new Map();
|
|
712
|
+
await Promise.all(Array.from(references, async (key) => {
|
|
713
|
+
const separator = key.lastIndexOf("#");
|
|
714
|
+
const src = key.slice(0, separator);
|
|
715
|
+
components.set(key, exportOf(await options.load(src), src, key.slice(separator + 1)));
|
|
716
|
+
}));
|
|
717
|
+
const serverHtml = /* @__PURE__ */ new Map();
|
|
718
|
+
for (const container of root.querySelectorAll(`${SERVER_HTML_TAG}[${SERVER_HTML_ATTRIBUTE}]`)) serverHtml.set(Number(container.getAttribute(SERVER_HTML_ATTRIBUTE)), container.innerHTML);
|
|
719
|
+
const resolver = {
|
|
720
|
+
component(reference, exportName) {
|
|
721
|
+
const Component = components.get(`${reference}#${exportName}`);
|
|
722
|
+
if (!Component) throw new Error(`client reference ${reference}#${exportName} was not loaded`);
|
|
723
|
+
return Component;
|
|
724
|
+
},
|
|
725
|
+
chunk(id) {
|
|
726
|
+
const chunk = chunks.get(id);
|
|
727
|
+
if (!chunk) throw new Error(`server tree chunk ${id} was not found`);
|
|
728
|
+
const value = React.createElement(React.Fragment, null, createTreeValue(chunk.tree, resolver), createTreeScript(CHUNK_SCRIPT_TYPE, String(id), chunk.json, chunk.values));
|
|
729
|
+
return {
|
|
730
|
+
status: "fulfilled",
|
|
731
|
+
value,
|
|
732
|
+
promise: Promise.resolve(value)
|
|
733
|
+
};
|
|
734
|
+
},
|
|
735
|
+
serverHtml(id, key) {
|
|
736
|
+
const html = serverHtml.get(id);
|
|
737
|
+
if (html === void 0) throw new Error(`server HTML ${id} was not found`);
|
|
738
|
+
return React.createElement(options.ServerHtml, {
|
|
739
|
+
...key === null ? {} : { key },
|
|
740
|
+
as: SERVER_HTML_TAG,
|
|
741
|
+
[SERVER_HTML_ATTRIBUTE]: String(id),
|
|
742
|
+
style: { display: "contents" },
|
|
743
|
+
suppressHydrationWarning: true,
|
|
744
|
+
dangerouslySetInnerHTML: { __html: html }
|
|
745
|
+
});
|
|
746
|
+
},
|
|
747
|
+
action(id, sealed) {
|
|
748
|
+
return createServerReference(id, sealed);
|
|
749
|
+
}
|
|
750
|
+
};
|
|
751
|
+
const build = () => createTreePage(payload.t, json, resolver, void 0, scripts);
|
|
752
|
+
build();
|
|
753
|
+
function ServerTreePage() {
|
|
754
|
+
const [, setVersion] = React.useState(0);
|
|
755
|
+
React.useEffect(() => {
|
|
756
|
+
const listener = (src, module) => {
|
|
757
|
+
let changed = false;
|
|
758
|
+
for (const key of components.keys()) {
|
|
759
|
+
const separator = key.lastIndexOf("#");
|
|
760
|
+
if (key.slice(0, separator) !== src) continue;
|
|
761
|
+
components.set(key, exportOf(module, src, key.slice(separator + 1)));
|
|
762
|
+
changed = true;
|
|
763
|
+
}
|
|
764
|
+
if (changed) setVersion((version) => version + 1);
|
|
765
|
+
return changed;
|
|
766
|
+
};
|
|
767
|
+
const listeners = treeListeners();
|
|
768
|
+
listeners.add(listener);
|
|
769
|
+
return () => {
|
|
770
|
+
listeners.delete(listener);
|
|
771
|
+
};
|
|
772
|
+
}, []);
|
|
773
|
+
return build();
|
|
774
|
+
}
|
|
775
|
+
return React.createElement(ServerTreePage, options.key === void 0 ? null : { key: options.key });
|
|
776
|
+
}
|
|
777
|
+
export { IslandsProvider as a, getContexts as c, setIslandServerHook as d, setRenderStateResolver as f, updateServerTrees as i, isolateIslands as l, createServerTreeElement as n, createIsland as o, withinClientRoot as p, renderServerTree as r, getClientReference as s, PAYLOAD_ONLY_ATTRIBUTE as t, runtimeTagged as u };
|