@markless/router 0.4.0 → 0.5.0
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/debug-channel-CewnC9fH.js +2 -0
- package/dist/{debug-channel-CSzo-Zj8.js → debug-channel-Di6SppZv.js} +36 -17
- package/dist/fragment-navigation-CIiNnFFc.js +323 -0
- package/dist/index.d.ts +51 -30
- package/dist/index.js +50 -31
- package/dist/link-attributes-nY1Qkmx2.js +7 -0
- package/dist/protocol-constants-B4WxrZAS.js +26 -0
- package/dist/{request-files-C-0LLSsx.js → request-files-Ca8E0FOo.js} +3 -2
- package/dist/request-locals-BFPF6Vdm.js +28 -0
- package/dist/route-href-CGooO_g6.js +18 -0
- package/dist/{route-manifest-DPgGIyrS.js → route-manifest-Bw_nFohL.js} +9 -6
- package/dist/typescript-plugin.cjs +8 -5
- package/dist/typescript-plugin.d.ts +3 -11194
- package/dist/typescript-plugin.js +8 -5
- package/dist/vite/route-href.d.ts +2 -3
- package/dist/vite/route-href.js +1 -17
- package/dist/vite/runtime/create-route-discovery.d.ts +3 -4
- package/dist/vite/runtime/create-route-discovery.js +1 -1
- package/dist/vite/runtime/create-server-entry.d.ts +11 -5
- package/dist/vite/runtime/create-server-entry.js +354 -96
- package/dist/vite/runtime/mdx-route.d.ts +1097 -17
- package/dist/vite/runtime/mdx-route.js +1094 -31
- package/dist/vite.d.ts +10 -6
- package/dist/vite.js +782 -188
- package/package.json +7 -6
- package/src/vite/entries/client-entry.ts +10 -1
- package/src/vite/entries/fragment-entry.ts +543 -0
- package/src/vite/entries/resume-entry.ts +41 -7
|
@@ -1,5 +1,756 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
import { i as protocolStateVersion, n as protocolInstanceQualifies, t as protocolInstancePath } from "../../protocol-constants-B4WxrZAS.js";
|
|
2
|
+
//#region ../serializer/src/storage-cookie.ts
|
|
3
|
+
const STORAGE_RENDER_CONTEXT_READER = "storageValue";
|
|
4
|
+
//#endregion
|
|
5
|
+
//#region ../web/src/ssr-data/awaitable.ts
|
|
6
|
+
function marklessIsThenable(value) {
|
|
7
|
+
return typeof value?.then === "function";
|
|
8
|
+
}
|
|
9
|
+
function marklessThen(value, next) {
|
|
10
|
+
return marklessIsThenable(value) ? value.then(next) : next(value);
|
|
11
|
+
}
|
|
12
|
+
/** `then` with the `finally` of the await it replaces: released on both edges. */
|
|
13
|
+
function marklessSettled(value, release, next) {
|
|
14
|
+
if (!marklessIsThenable(value)) {
|
|
15
|
+
release();
|
|
16
|
+
return next(value);
|
|
17
|
+
}
|
|
18
|
+
return value.then((settled) => {
|
|
19
|
+
release();
|
|
20
|
+
return next(settled);
|
|
21
|
+
}, (error) => {
|
|
22
|
+
release();
|
|
23
|
+
throw error;
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
//#endregion
|
|
27
|
+
//#region ../web/src/fns/instance-scope.ts
|
|
28
|
+
const INSTANCE_PATH = /^(?:[cpm]\d+:|r:[^:]*:)+/;
|
|
29
|
+
function marklessInstancePath(prefix) {
|
|
30
|
+
return prefix && INSTANCE_PATH.exec(prefix)?.[0] || "";
|
|
31
|
+
}
|
|
32
|
+
const ROW_SEGMENT = /r:[^:]*:/g;
|
|
33
|
+
function marklessRowFreeSymbolId(symbolId, instancePath) {
|
|
34
|
+
const path = instancePath ?? INSTANCE_PATH.exec(symbolId)?.[0] ?? "";
|
|
35
|
+
if (!path.includes("r:")) return symbolId;
|
|
36
|
+
return path.replace(ROW_SEGMENT, "") + symbolId.slice(path.length);
|
|
37
|
+
}
|
|
38
|
+
function qualifyingGraph(scoped, graph, qualify) {
|
|
39
|
+
const outerQualify = graph.marklessQualifyGraphNodeId;
|
|
40
|
+
const qualified = (method) => (operation) => graph[method].call(graph, {
|
|
41
|
+
...operation,
|
|
42
|
+
graphNodeId: qualify(operation.graphNodeId)
|
|
43
|
+
});
|
|
44
|
+
return Object.assign(scoped, {
|
|
45
|
+
marklessQualifyGraphNodeId: (graphNodeId) => outerQualify ? outerQualify(qualify(graphNodeId)) : qualify(graphNodeId),
|
|
46
|
+
read: (graphNodeId, path) => graph.read(qualify(graphNodeId), path),
|
|
47
|
+
write: qualified("write"),
|
|
48
|
+
update: qualified("update"),
|
|
49
|
+
call: qualified("call"),
|
|
50
|
+
delete: qualified("delete"),
|
|
51
|
+
subscribe: qualified("subscribe")
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
const composedSymbols = /* @__PURE__ */ new WeakSet();
|
|
55
|
+
const ISLAND_SEGMENT = /^m\d+:/;
|
|
56
|
+
/**
|
|
57
|
+
* An island's symbol invokes callbacks by ids spelled in the island's own module
|
|
58
|
+
* space, but the page loader that answers them only knows page-space ids, so an
|
|
59
|
+
* id without an island segment is sent back with this island's.
|
|
60
|
+
*/
|
|
61
|
+
function marklessIslandSpacedSymbol(symbol, symbolPrefix) {
|
|
62
|
+
const island = ISLAND_SEGMENT.exec(symbolPrefix)?.[0];
|
|
63
|
+
if (!island) return symbol;
|
|
64
|
+
const spaced = (context) => {
|
|
65
|
+
const invokeSymbol = context.invokeSymbol;
|
|
66
|
+
return symbol(invokeSymbol ? {
|
|
67
|
+
...context,
|
|
68
|
+
invokeSymbol: (symbolId, invokeContext) => invokeSymbol(ISLAND_SEGMENT.test(symbolId) ? symbolId : island + symbolId, invokeContext)
|
|
69
|
+
} : context);
|
|
70
|
+
};
|
|
71
|
+
if (composedSymbols.has(symbol)) composedSymbols.add(spaced);
|
|
72
|
+
return spaced;
|
|
73
|
+
}
|
|
74
|
+
function marklessInstanceScopedLoadSymbol(loadSymbol) {
|
|
75
|
+
return (symbolId) => {
|
|
76
|
+
const instancePath = INSTANCE_PATH.exec(symbolId)?.[0];
|
|
77
|
+
if (!instancePath) return loadSymbol(symbolId);
|
|
78
|
+
return marklessThen(loadSymbol(marklessRowFreeSymbolId(symbolId, instancePath)), (symbol) => scopeSymbol(symbol, instancePath));
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
function scopeSymbol(symbol, instancePath) {
|
|
82
|
+
if (composedSymbols.has(symbol)) return symbol;
|
|
83
|
+
const rescope = symbol.marklessRescope;
|
|
84
|
+
if (rescope) return rescope(instancePath, scopeSymbol);
|
|
85
|
+
const { marklessScopedBase, marklessScopedPath } = symbol;
|
|
86
|
+
if (marklessScopedBase && (marklessScopedPath === instancePath || marklessScopedPath === instancePath.replace(ROW_SEGMENT, ""))) return scopeSymbol(marklessScopedBase, instancePath);
|
|
87
|
+
const scoped = (context) => {
|
|
88
|
+
const graph = context.graph;
|
|
89
|
+
return symbol({
|
|
90
|
+
...context,
|
|
91
|
+
...graph ? { graph: marklessInstanceScopedGraph(graph, instancePath) } : {},
|
|
92
|
+
getElementHandle: marklessInstanceScopedElementHandle(context.getElementHandle, instancePath, graph),
|
|
93
|
+
...context.read && graph ? { read: (graphNodeId, path) => graph.read(marklessComposedGraphNodeId(graphNodeId, instancePath, marklessGraphWidgetRegistry(graph)), path) } : {}
|
|
94
|
+
});
|
|
95
|
+
};
|
|
96
|
+
return Object.assign(scoped, {
|
|
97
|
+
marklessScopedBase: symbol,
|
|
98
|
+
marklessScopedPath: instancePath
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* A widget-scoped `element()` handle names one element PER RENDERED WIDGET, and
|
|
103
|
+
* its compiled id is one module-level string every instance of that widget
|
|
104
|
+
* spells. Qualifying it exactly as the widget's graph nodes are qualified is
|
|
105
|
+
* what turns it back into a key: the registration and the reading handler both
|
|
106
|
+
* land on the rendered widget's own root path, so a page carrying two instances
|
|
107
|
+
* answers each handler with its own element instead of the last registration.
|
|
108
|
+
*
|
|
109
|
+
* A handle that is not widget-scoped is returned untouched. A component-local
|
|
110
|
+
* handle is already one element per key, and a page-scoped `shared()` graph is
|
|
111
|
+
* page space by design.
|
|
112
|
+
*/
|
|
113
|
+
function marklessWidgetHandleId(handleId, instancePath, registry = marklessWidgetScope.active) {
|
|
114
|
+
if (!instancePath) return handleId;
|
|
115
|
+
const pageSpace = PAGE_SPACE_ID.exec(handleId);
|
|
116
|
+
if (!pageSpace || pageSpace[2] !== "shared") return handleId;
|
|
117
|
+
return marklessComposedGraphNodeId(handleId, instancePath, registry);
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* The reading half of the same key.
|
|
121
|
+
*
|
|
122
|
+
* The compiled symbol asks for the module-level id, so the instance it is
|
|
123
|
+
* running in is what has to be added here. Every registration a rendered widget
|
|
124
|
+
* makes is qualified the same way - the page-level ones at composition, the
|
|
125
|
+
* ones an `@if` arm files at resume - so a reader whose instance is named asks
|
|
126
|
+
* the qualified key and nothing else.
|
|
127
|
+
*/
|
|
128
|
+
function marklessInstanceScopedElementHandle(getElementHandle, instancePath, graph) {
|
|
129
|
+
if (typeof getElementHandle !== "function") return getElementHandle;
|
|
130
|
+
if (getElementHandle.marklessHandleScope === instancePath) return getElementHandle;
|
|
131
|
+
const scopedReader = (handleIdOrName) => {
|
|
132
|
+
const scoped = marklessWidgetHandleId(handleIdOrName, instancePath, graph ? marklessGraphWidgetRegistry(graph) : marklessWidgetScope.active);
|
|
133
|
+
return scoped === handleIdOrName ? getElementHandle(handleIdOrName) : getElementHandle(scoped);
|
|
134
|
+
};
|
|
135
|
+
scopedReader.marklessHandleScope = instancePath;
|
|
136
|
+
return scopedReader;
|
|
137
|
+
}
|
|
138
|
+
function marklessInstanceScopedGraph(graph, instancePath) {
|
|
139
|
+
if (!instancePath) return graph;
|
|
140
|
+
if (graph.marklessInstancePath === instancePath) return graph;
|
|
141
|
+
const registry = marklessGraphWidgetRegistry(graph);
|
|
142
|
+
if (!composedRegistryViews.has(registry)) marklessNoteGraphWidgetRoots(registry, graph);
|
|
143
|
+
const takeWidgetView = () => composedRegistryViews.has(registry) ? registry : composedWidgetRegistryView(registry, instancePath);
|
|
144
|
+
let widgetView = takeWidgetView();
|
|
145
|
+
const scoped = qualifyingGraph({
|
|
146
|
+
...graph,
|
|
147
|
+
marklessPageGraph: graph.marklessPageGraph ?? graph,
|
|
148
|
+
marklessInstancePath: instancePath,
|
|
149
|
+
get marklessComposedWidgets() {
|
|
150
|
+
return widgetView ??= takeWidgetView();
|
|
151
|
+
}
|
|
152
|
+
}, graph, (graphNodeId) => marklessComposedGraphNodeId(graphNodeId, instancePath, registry));
|
|
153
|
+
if (widgetView) marklessShareWidgetRegistry(scoped, widgetView);
|
|
154
|
+
return scoped;
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* The rendered widgets a part reading in THIS scope's LOCAL space can name.
|
|
158
|
+
*
|
|
159
|
+
* A component's compiled parts spell every id against that module's own edges.
|
|
160
|
+
* When the component is itself composed one wrapper deep - its markup projected
|
|
161
|
+
* through another component's children - the family root it composes is filed in
|
|
162
|
+
* the registry at its PAGE path (`c0:p1:c0:`) while the part reads with the
|
|
163
|
+
* module-local path (`c0:p1:p2:p3:`). Neither is a prefix of the other and
|
|
164
|
+
* `widgetRootPathFor` can only chop segments off the right, so every lookup
|
|
165
|
+
* misses, the id stays bare, and the write lands on a node no rendered widget
|
|
166
|
+
* owns. Re-spelling the roots that stand under this adapter's path without it
|
|
167
|
+
* puts reader and registry in one space; the adapter's own rule for an
|
|
168
|
+
* already-prefixed `shared:` id then carries the answer back to page space.
|
|
169
|
+
*
|
|
170
|
+
* Roots OUTSIDE the path are dropped rather than carried through unstripped,
|
|
171
|
+
* because a local path and a page path can spell the same segments: an
|
|
172
|
+
* unstripped entry would answer a local reader with a page root and this adapter
|
|
173
|
+
* would prefix it a second time. An id no root here claims is left bare, which
|
|
174
|
+
* is exactly what hands it back to this adapter, which resolves it as before.
|
|
175
|
+
*/
|
|
176
|
+
function composedWidgetRegistryView(registry, instancePath) {
|
|
177
|
+
let view;
|
|
178
|
+
for (const [id, rootPath] of registry.rootPaths) {
|
|
179
|
+
if (!id.startsWith(instancePath) || !rootPath.startsWith(instancePath)) continue;
|
|
180
|
+
view ??= {
|
|
181
|
+
rootPaths: /* @__PURE__ */ new Map(),
|
|
182
|
+
rowRooted: /* @__PURE__ */ new Set()
|
|
183
|
+
};
|
|
184
|
+
marklessNoteWidgetRoot(view, id.slice(instancePath.length), rootPath.slice(instancePath.length));
|
|
185
|
+
}
|
|
186
|
+
if (view) composedRegistryViews.add(view);
|
|
187
|
+
return view;
|
|
188
|
+
}
|
|
189
|
+
const PAGE_SPACE_ID = /^((?:[cpm]\d+:|r:[^:]*:)*)(shared|storage):/;
|
|
190
|
+
/**
|
|
191
|
+
* One registry per RUNTIME GRAPH, and a graph is one rendered page.
|
|
192
|
+
*
|
|
193
|
+
* A widget root path is only ever an answer about the container that rendered
|
|
194
|
+
* it, so a realm-wide map is a category error: two Markless roots on one page
|
|
195
|
+
* each render their own widgets, and a torn-down container's roots must not
|
|
196
|
+
* answer for the render that replaces it. Filing the registry against the graph
|
|
197
|
+
* makes both true by construction - the entries die with the graph, and a
|
|
198
|
+
* lookup can only reach the rendered widgets of the graph it was asked about.
|
|
199
|
+
*
|
|
200
|
+
* The key is the PAGE graph, recovered through `marklessPageGraph`, because
|
|
201
|
+
* every scope adapter below hands its symbol a fresh wrapper object over the
|
|
202
|
+
* same page graph and all of them must reach the same answer.
|
|
203
|
+
*/
|
|
204
|
+
const graphRegistries = /* @__PURE__ */ new WeakMap();
|
|
205
|
+
const composedRegistryViews = /* @__PURE__ */ new WeakSet();
|
|
206
|
+
/**
|
|
207
|
+
* Which registry the lookups below read when the CALLER names no graph.
|
|
208
|
+
*
|
|
209
|
+
* Two readers cannot name one. Inside a compose there is no graph yet, so
|
|
210
|
+
* composition.ts swaps this to the render's own registry and swaps it back.
|
|
211
|
+
* And the generated symbol-resolver module's bound-symbol adapter holds the
|
|
212
|
+
* dispatching graph in a closure it never passes on, so it reaches this field
|
|
213
|
+
* instead - which is why every entry point that DOES hold a graph points this
|
|
214
|
+
* at that graph's registry before running anything (`marklessScopeWidgetsTo`).
|
|
215
|
+
*
|
|
216
|
+
* `composing` is what keeps a dispatch from stealing the field out from under a
|
|
217
|
+
* compose. Only a server ever composes and dispatches at once.
|
|
218
|
+
*/
|
|
219
|
+
const marklessWidgetScope = {
|
|
220
|
+
active: {
|
|
221
|
+
rootPaths: /* @__PURE__ */ new Map(),
|
|
222
|
+
rowRooted: /* @__PURE__ */ new Set()
|
|
223
|
+
},
|
|
224
|
+
composing: false
|
|
225
|
+
};
|
|
226
|
+
/**
|
|
227
|
+
* Points the graph-less readers at the registry of the graph now being read.
|
|
228
|
+
*
|
|
229
|
+
* A dispatch is not synchronous, so this is a pointer re-aimed per entry rather
|
|
230
|
+
* than a scope that unwinds: the next entry re-aims it, and a torn-down
|
|
231
|
+
* container's registry is never re-aimed at again. Two containers dispatching
|
|
232
|
+
* async bodies that interleave their writes are the one case it cannot separate
|
|
233
|
+
* - the readers that hold a graph are exact regardless.
|
|
234
|
+
*/
|
|
235
|
+
function marklessScopeWidgetsTo(registry) {
|
|
236
|
+
if (!marklessWidgetScope.composing) marklessWidgetScope.active = registry;
|
|
237
|
+
return registry;
|
|
238
|
+
}
|
|
239
|
+
function marklessRegistryHolder(graph) {
|
|
240
|
+
const scoped = graph;
|
|
241
|
+
return scoped.marklessPageGraph ?? scoped.marklessWidgetHostGraph ?? graph;
|
|
242
|
+
}
|
|
243
|
+
/**
|
|
244
|
+
* This graph's rendered widgets, minted on first ask and filled from the graph
|
|
245
|
+
* itself: the payload's widget-scoped definitions carry their qualified root ids
|
|
246
|
+
* and the projection sites composition registered them under, which is every
|
|
247
|
+
* answer a lookup needs and the only place resume has ever read them from.
|
|
248
|
+
*/
|
|
249
|
+
function marklessGraphWidgetRegistry(graph) {
|
|
250
|
+
if (!graph) return marklessWidgetScope.active;
|
|
251
|
+
const own = graph.marklessComposedWidgets ?? graphRegistries.get(graph);
|
|
252
|
+
if (own) return marklessScopeWidgetsTo(own);
|
|
253
|
+
const holder = marklessRegistryHolder(graph);
|
|
254
|
+
const held = graphRegistries.get(holder);
|
|
255
|
+
if (held) return marklessScopeWidgetsTo(held);
|
|
256
|
+
const registry = {
|
|
257
|
+
rootPaths: /* @__PURE__ */ new Map(),
|
|
258
|
+
rowRooted: /* @__PURE__ */ new Set()
|
|
259
|
+
};
|
|
260
|
+
graphRegistries.set(holder, registry);
|
|
261
|
+
marklessNoteGraphWidgetRoots(registry, graph);
|
|
262
|
+
return marklessScopeWidgetsTo(registry);
|
|
263
|
+
}
|
|
264
|
+
function marklessShareWidgetRegistry(graph, registry) {
|
|
265
|
+
graphRegistries.set(graph, registry);
|
|
266
|
+
}
|
|
267
|
+
function marklessNoteGraphWidgetRoots(registry, graph) {
|
|
268
|
+
for (const definition of graph.listSharedDefinitions?.() ?? []) if (definition.scope === "widget") {
|
|
269
|
+
const rootPath = marklessInstancePath(definition.id);
|
|
270
|
+
marklessNoteWidgetRoot(registry, definition.id, rootPath);
|
|
271
|
+
for (const projectionId of definition.projectionIds ?? []) marklessNoteWidgetRoot(registry, projectionId, rootPath);
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
function marklessNoteWidgetRoot(registry, id, rootPath) {
|
|
275
|
+
registry.rootPaths.set(id, rootPath);
|
|
276
|
+
if (rootPath.includes("r:")) registry.rowRooted.add(id.slice(marklessInstancePath(id).length));
|
|
277
|
+
}
|
|
278
|
+
/**
|
|
279
|
+
* The rendered widgets a client-minted repeat row is standing INSIDE.
|
|
280
|
+
*
|
|
281
|
+
* A minted row composes alone, so the registry that render builds holds only the
|
|
282
|
+
* roots the row itself is. A part reading a widget rooted in an ancestor - the
|
|
283
|
+
* collection the repeat iterates, most of all - would find nothing and mint a
|
|
284
|
+
* second instance of it, whose writes no served node ever sees. The mint reads
|
|
285
|
+
* the live page's roots off its graph and installs them here for the row's
|
|
286
|
+
* render; the `rowSegment` guard is what keeps a render running beside it from
|
|
287
|
+
* reading the answer, the same separation `marklessWidgetScope` settles for.
|
|
288
|
+
*/
|
|
289
|
+
const enclosingWidgetScope = {
|
|
290
|
+
rowSegment: "",
|
|
291
|
+
roots: void 0
|
|
292
|
+
};
|
|
293
|
+
function enclosingRootPathFor(graphNodeId, roots) {
|
|
294
|
+
return byDefinition(graphNodeId, (key) => roots.get(key));
|
|
295
|
+
}
|
|
296
|
+
/**
|
|
297
|
+
* The same question asked of a path that may carry rows the ROOT does not.
|
|
298
|
+
*
|
|
299
|
+
* One root per rendered widget means a root inside a repeat is registered under
|
|
300
|
+
* its row and a root outside one is registered without any - but a part
|
|
301
|
+
* projected INTO an outside root from inside the repeat carries the row in its
|
|
302
|
+
* own path regardless. The walk below chops segments off the right, so a leading
|
|
303
|
+
* `r:<key>:` blocks it from ever reaching that root. Rows are asked first, so a
|
|
304
|
+
* per-row root always wins over the row-free reading; only a path no rendered
|
|
305
|
+
* row answers is asked again with its rows dropped.
|
|
306
|
+
*/
|
|
307
|
+
function marklessWidgetRootPathThroughRows(graphNodeId, instancePath, registry) {
|
|
308
|
+
const withRows = widgetRootPathFor(graphNodeId, instancePath, registry);
|
|
309
|
+
if (withRows !== void 0) return withRows;
|
|
310
|
+
const rowFree = instancePath.replace(ROW_SEGMENT, "");
|
|
311
|
+
if (rowFree === instancePath) return "";
|
|
312
|
+
return widgetRootPathFor(graphNodeId, rowFree, registry) ?? "";
|
|
313
|
+
}
|
|
314
|
+
/**
|
|
315
|
+
* The two spellings of one containment, and the one that has to wait for a row.
|
|
316
|
+
*
|
|
317
|
+
* A widget root is filed under the path it RENDERED at, rows and all
|
|
318
|
+
* (`r:page%3A5:c0:p2:`). A dispatching bound symbol runs at the compose tree's
|
|
319
|
+
* path for that same containment, which names the component edges and no row
|
|
320
|
+
* (`c0:p2:p3:`) - a bound symbol is minted per edge, and a row key is a runtime
|
|
321
|
+
* value no symbol id can hold. Neither spelling is a prefix of the other, so the
|
|
322
|
+
* walk above can never get from one to the other on its own.
|
|
323
|
+
*
|
|
324
|
+
* Leaving the id in page space here is what breaks the projection bridge: the
|
|
325
|
+
* write lands on an id no rendered widget owns, the record matches, the symbol
|
|
326
|
+
* runs, and nothing moves. Keeping the reading path ON the id instead hands the
|
|
327
|
+
* question to `marklessRowWidgetGraphNodeId`, which is spelled for exactly this
|
|
328
|
+
* input - it holds the dispatched record's rows and re-asks the registry with
|
|
329
|
+
* them, reaching the ONE root this row rendered. An alias resolved at lookup,
|
|
330
|
+
* never a second registry entry, so one containment still yields one instance.
|
|
331
|
+
*
|
|
332
|
+
* Two guards keep that from minting a phantom instance instead. The path must
|
|
333
|
+
* carry no row of its own, because a path that already names its rows and STILL
|
|
334
|
+
* found no root is not waiting on one. And the definition must be one some row
|
|
335
|
+
* really did root: an id belonging to no widget at all - a page-scoped
|
|
336
|
+
* `shared()` graph, a storage slot - is page space and stays exactly as spelled.
|
|
337
|
+
*/
|
|
338
|
+
function marklessUnresolvedWidgetGraphNodeId(graphNodeId, instancePath, registry) {
|
|
339
|
+
const rowRooted = registry.rowRooted;
|
|
340
|
+
if (instancePath.includes("r:") || rowRooted.size === 0) return graphNodeId;
|
|
341
|
+
return byDefinition(graphNodeId, (key) => rowRooted.has(key) || void 0) ? instancePath + graphNodeId : graphNodeId;
|
|
342
|
+
}
|
|
343
|
+
function widgetRootPathFor(graphNodeId, instancePath, registry) {
|
|
344
|
+
const widgetRootPaths = registry.rootPaths;
|
|
345
|
+
if (widgetRootPaths.size === 0) return void 0;
|
|
346
|
+
for (let end = instancePath.length; end > 0; end--) {
|
|
347
|
+
if (instancePath[end - 1] !== ":") continue;
|
|
348
|
+
const prefix = instancePath.slice(0, end);
|
|
349
|
+
const rootPath = byDefinition(graphNodeId, (key) => widgetRootPaths.get(prefix + key));
|
|
350
|
+
if (rootPath !== void 0) return rootPath;
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
function byDefinition(id, lookup) {
|
|
354
|
+
const slash = id.lastIndexOf("/");
|
|
355
|
+
return lookup(id) ?? (slash > 0 ? lookup(id.slice(0, slash)) : void 0);
|
|
356
|
+
}
|
|
357
|
+
function marklessComposedGraphNodeId(graphNodeId, instancePath, registry = marklessWidgetScope.active) {
|
|
358
|
+
if (!instancePath) return graphNodeId;
|
|
359
|
+
const pageSpace = PAGE_SPACE_ID.exec(graphNodeId);
|
|
360
|
+
if (!pageSpace) return instancePath + graphNodeId;
|
|
361
|
+
if (pageSpace[2] === "storage") return graphNodeId;
|
|
362
|
+
if (pageSpace[1]) {
|
|
363
|
+
const sharedId = graphNodeId.slice(pageSpace[1].length);
|
|
364
|
+
return ((!pageSpace[1].includes("r:") && instancePath.includes("r:") ? widgetRootPathFor(sharedId, instancePath + pageSpace[1], registry) : void 0) ?? instancePath + pageSpace[1]) + sharedId;
|
|
365
|
+
}
|
|
366
|
+
const rootPath = marklessWidgetRootPathThroughRows(graphNodeId, instancePath, registry);
|
|
367
|
+
if (rootPath) return rootPath + graphNodeId;
|
|
368
|
+
const enclosing = enclosingWidgetScope.roots && instancePath.startsWith(enclosingWidgetScope.rowSegment) ? enclosingRootPathFor(graphNodeId, enclosingWidgetScope.roots) : void 0;
|
|
369
|
+
if (enclosing) return enclosing + graphNodeId;
|
|
370
|
+
return marklessUnresolvedWidgetGraphNodeId(graphNodeId, instancePath, registry);
|
|
371
|
+
}
|
|
372
|
+
//#endregion
|
|
373
|
+
//#region ../web/src/fns/projection-html.ts
|
|
374
|
+
const PROJECTION_MARK_PREFIX = "<!--markless-projection:";
|
|
375
|
+
//#endregion
|
|
376
|
+
//#region ../web/src/prerender/shared-seed-slot.ts
|
|
377
|
+
/**
|
|
378
|
+
* The seed-map key a widget root's instance token travels under, restating
|
|
379
|
+
* MARKLESS_WIDGET_INSTANCE_KEY in @markless/compiler (public-render's
|
|
380
|
+
* residue-reader) so the browser never imports the compiler. It is not a graph
|
|
381
|
+
* node id: it names WHICH rendered widget the parts seeded from this map belong
|
|
382
|
+
* to, which is what a shared() element() handle's minted id has to carry.
|
|
383
|
+
* shared-seed-widget.test.ts pins this spelling; element-handle-idref.test.ts
|
|
384
|
+
* proves the two sides agree end to end.
|
|
385
|
+
*/
|
|
386
|
+
const MARKLESS_WIDGET_INSTANCE_KEY = "markless:widget-instance";
|
|
387
|
+
/**
|
|
388
|
+
* The same token, filed PER shared definition.
|
|
389
|
+
*
|
|
390
|
+
* One element can carry handles declared by two different widget families - a
|
|
391
|
+
* radio item's own field handle and the group's set, a tree label's item handle
|
|
392
|
+
* and the root's - so "which rendered widget am I inside" has no single answer
|
|
393
|
+
* on that element. It has one answer per DEFINITION, which is what the reading
|
|
394
|
+
* handle names: a handle's graph node id is `<definitionId>/element:<name>`, so
|
|
395
|
+
* the mint asks for its own family's token and gets the instance the handle was
|
|
396
|
+
* DECLARED in, never whichever family last seeded this scope.
|
|
397
|
+
*
|
|
398
|
+
* The plain key above stays filed too: a page with one widget family reads it
|
|
399
|
+
* exactly as before, and it is the fallback when no per-definition token exists.
|
|
400
|
+
*/
|
|
401
|
+
function marklessWidgetInstanceKey(definitionId) {
|
|
402
|
+
return `${MARKLESS_WIDGET_INSTANCE_KEY}|${definitionId}`;
|
|
403
|
+
}
|
|
404
|
+
/**
|
|
405
|
+
* Where a part stands in its family's roster, answered for the two regimes that
|
|
406
|
+
* ask it: at render there is no DOM, so the answer is the order this widget
|
|
407
|
+
* instance EMITS its members; after resume the roster is live, so the answer is
|
|
408
|
+
* its document order. The compiler lowers `roster.indexOf(mine)` to one call in
|
|
409
|
+
* each regime with the same two node ids, so the two cannot disagree.
|
|
410
|
+
*/
|
|
411
|
+
/**
|
|
412
|
+
* The seed-map key this render's position counter travels under. The seed map
|
|
413
|
+
* is the one channel that reaches both the compiled SSR child render and the
|
|
414
|
+
* CSR component evaluation, and it already carries the widget-instance token a
|
|
415
|
+
* position counts within.
|
|
416
|
+
*/
|
|
417
|
+
const MARKLESS_ROSTER_POSITIONS_KEY = "markless:roster-positions";
|
|
418
|
+
function marklessRosterPositions(seeds) {
|
|
419
|
+
const held = seeds?.get(MARKLESS_ROSTER_POSITIONS_KEY);
|
|
420
|
+
return held && typeof held === "object" ? held : void 0;
|
|
421
|
+
}
|
|
422
|
+
/** The render's seed map with a counter filed on it, minted once per render. */
|
|
423
|
+
function marklessRosterPositionSeeds(seeds) {
|
|
424
|
+
if (marklessRosterPositions(seeds)) return seeds;
|
|
425
|
+
const filed = new Map(seeds ?? []);
|
|
426
|
+
filed.set(MARKLESS_ROSTER_POSITIONS_KEY, { taken: /* @__PURE__ */ new Map() });
|
|
427
|
+
return filed;
|
|
428
|
+
}
|
|
429
|
+
/**
|
|
430
|
+
* Which rendered widget a roster belongs to, asked of its own family exactly as
|
|
431
|
+
* a minted handle id asks it: a handle's graph node id carries a module path, so
|
|
432
|
+
* the family is everything up to the LAST slash.
|
|
433
|
+
*/
|
|
434
|
+
function rosterInstance(scope, rosterGraphNodeId) {
|
|
435
|
+
const family = rosterGraphNodeId.slice(0, rosterGraphNodeId.lastIndexOf("/"));
|
|
436
|
+
return scope?.get(marklessWidgetInstanceKey(family)) ?? scope?.get("markless:widget-instance");
|
|
437
|
+
}
|
|
438
|
+
/**
|
|
439
|
+
* The nth member of this instance's roster to ask is the nth in it.
|
|
440
|
+
*
|
|
441
|
+
* Render walks a widget instance in document order, so counting the asks IS
|
|
442
|
+
* that order — there is no DOM yet to compare against. The count is per widget
|
|
443
|
+
* instance, which is what keeps a second collection on the page starting at
|
|
444
|
+
* zero.
|
|
445
|
+
*/
|
|
446
|
+
function renderRosterPosition(positions, seeds, rosterGraphNodeId, handleGraphNodeId) {
|
|
447
|
+
const instance = rosterInstance(seeds ?? positions.seeds, rosterGraphNodeId);
|
|
448
|
+
const key = `${String(instance)}|${rosterGraphNodeId}|${handleGraphNodeId}`;
|
|
449
|
+
const ledger = positions.seeding ?? positions.taken;
|
|
450
|
+
const taken = ledger.get(key) ?? 0;
|
|
451
|
+
ledger.set(key, taken + 1);
|
|
452
|
+
return taken;
|
|
453
|
+
}
|
|
454
|
+
/**
|
|
455
|
+
* How many parts this instance puts in the roster - asked, unlike a position,
|
|
456
|
+
* by a part that may render BEFORE the members it is counting. The root asking
|
|
457
|
+
* `ui-max` is exactly that: server render is one forward pass.
|
|
458
|
+
*
|
|
459
|
+
* So the ask does not answer. It writes a placeholder naming the roster's own
|
|
460
|
+
* registration key — the widget instance's path then the module-level handle id,
|
|
461
|
+
* which is how marklessWidgetHandleId qualifies it at composition — and the
|
|
462
|
+
* surface resolves it once composition has made the count a fact.
|
|
463
|
+
*/
|
|
464
|
+
function renderRosterCount(positions, seeds, rosterGraphNodeId) {
|
|
465
|
+
const instance = rosterInstance(seeds ?? positions.seeds, rosterGraphNodeId);
|
|
466
|
+
const key = (typeof instance === "string" ? instance : "") + rosterGraphNodeId;
|
|
467
|
+
positions.counted = true;
|
|
468
|
+
return "" + key + "";
|
|
469
|
+
}
|
|
470
|
+
/**
|
|
471
|
+
* The two roster questions a derive may ask at render time, on one context.
|
|
472
|
+
* Every render-side evaluation site hands the symbol this same object, so a
|
|
473
|
+
* compiled call cannot reach a regime that answers only one of them.
|
|
474
|
+
*/
|
|
475
|
+
function marklessRosterRenderContext(positions, seeds) {
|
|
476
|
+
if (!positions) return {};
|
|
477
|
+
return {
|
|
478
|
+
rosterPosition: (roster, handle) => renderRosterPosition(positions, seeds, roster, handle),
|
|
479
|
+
rosterCount: (roster) => renderRosterCount(positions, seeds, roster),
|
|
480
|
+
deferCount: (thunk) => positions.counted ? "" + String((positions.deferred ??= []).push(thunk) - 1) + "" : thunk(resolvedCount)
|
|
481
|
+
};
|
|
482
|
+
}
|
|
483
|
+
function resolvedCount(value) {
|
|
484
|
+
if (typeof value !== "number") throw new Error("MARKLESS_ROSTER_COUNT_UNRESOLVED");
|
|
485
|
+
return value;
|
|
486
|
+
}
|
|
487
|
+
//#endregion
|
|
488
|
+
//#region ../web/src/ssr-data/renderer.ts
|
|
489
|
+
const projectionSpans = /* @__PURE__ */ new Map();
|
|
490
|
+
let nextProjectionKey = 0;
|
|
491
|
+
function openProjectionSpan(tokens) {
|
|
492
|
+
const key = String(nextProjectionKey++);
|
|
493
|
+
const span = {
|
|
494
|
+
key,
|
|
495
|
+
mark: `${PROJECTION_MARK_PREFIX}${key}-->`,
|
|
496
|
+
tokens,
|
|
497
|
+
consumed: false
|
|
498
|
+
};
|
|
499
|
+
projectionSpans.set(key, span);
|
|
500
|
+
return span;
|
|
501
|
+
}
|
|
502
|
+
/**
|
|
503
|
+
* Runs one child render with a projection's token span registered, for a caller
|
|
504
|
+
* that places a projection itself rather than through `renderChunk` - the client
|
|
505
|
+
* mint of a `@for` row whose component projects children. `consumed` answers
|
|
506
|
+
* whether the child's own `{children}` slot reported the span back.
|
|
507
|
+
*/
|
|
508
|
+
function withProjectionSpan(tokens, render) {
|
|
509
|
+
const span = openProjectionSpan(tokens);
|
|
510
|
+
const release = () => projectionSpans.delete(span.key);
|
|
511
|
+
let rendered;
|
|
512
|
+
try {
|
|
513
|
+
rendered = render(span.mark);
|
|
514
|
+
} catch (error) {
|
|
515
|
+
release();
|
|
516
|
+
throw error;
|
|
517
|
+
}
|
|
518
|
+
return marklessSettled(rendered, release, (result) => ({
|
|
519
|
+
result,
|
|
520
|
+
consumed: span.consumed
|
|
521
|
+
}));
|
|
522
|
+
}
|
|
523
|
+
//#endregion
|
|
524
|
+
//#region ../web/src/fns/roster-position.ts
|
|
525
|
+
/**
|
|
526
|
+
* The server render context with the roster channels on it. One counter per
|
|
527
|
+
* render, held by the closure rather than by a module, so two page renders in
|
|
528
|
+
* one process never share a count.
|
|
529
|
+
*/
|
|
530
|
+
function marklessSsrRosterPositionContext(renderContext) {
|
|
531
|
+
const context = renderContext ?? {};
|
|
532
|
+
if (typeof context.rosterPosition === "function") return renderContext;
|
|
533
|
+
const sharedSeeds = marklessRosterPositionSeeds(context.sharedSeeds);
|
|
534
|
+
return {
|
|
535
|
+
...context,
|
|
536
|
+
sharedSeeds,
|
|
537
|
+
...marklessRosterRenderContext(marklessRosterPositions(sharedSeeds), void 0)
|
|
538
|
+
};
|
|
539
|
+
}
|
|
540
|
+
//#endregion
|
|
541
|
+
//#region ../web/src/prerender/island-roster.ts
|
|
542
|
+
/** The counts and count-spending expressions one island minted, made numbers. */
|
|
543
|
+
async function marklessSsrIslandRosterAnswered(renderContext, surface) {
|
|
544
|
+
const positions = marklessRosterPositions(renderContext?.sharedSeeds);
|
|
545
|
+
if (!positions?.counted) return surface;
|
|
546
|
+
const roster = await globalThis.__marklessRosterResume?.();
|
|
547
|
+
if (!roster) throw new Error("MARKLESS_ROSTER_COUNT_UNRESOLVED");
|
|
548
|
+
return roster.marklessResolveRosterCounts(roster.marklessResolveDeferredCounts(surface, positions.deferred ?? []));
|
|
549
|
+
}
|
|
550
|
+
//#endregion
|
|
551
|
+
//#region ../web/src/resume-anchor-census.ts
|
|
552
|
+
function isArmBranchAnchorComment(comment) {
|
|
553
|
+
const text = comment.data ?? comment.textContent ?? "";
|
|
554
|
+
return text.startsWith("markless:arm-branch:") || text.startsWith("/markless:arm-branch:");
|
|
555
|
+
}
|
|
556
|
+
//#endregion
|
|
557
|
+
//#region ../web/src/fns/scalar-specialized.ts
|
|
558
|
+
const e = (c, s) => {
|
|
559
|
+
throw Object.assign(Error(c), {
|
|
560
|
+
code: c,
|
|
561
|
+
site: s
|
|
562
|
+
}, c === "MARKLESS_PAYLOAD_INVALID" ? { docsUrl: "https://markless.dev/errors/MARKLESS_PAYLOAD_INVALID" } : {});
|
|
563
|
+
};
|
|
564
|
+
const marklessDecodeScalarCell = (c, g, s) => {
|
|
565
|
+
const u = c?.valueKind === "unknown";
|
|
566
|
+
try {
|
|
567
|
+
const v = c?.value, r = v?.root;
|
|
568
|
+
if (!c || c.graphNodeId !== g || c.valueKind !== "scalar" && !u || v?.version !== 1 || v.records?.length !== 0) e("MARKLESS_PAYLOAD_INVALID", s);
|
|
569
|
+
if (r == null || typeof r !== "object") return r;
|
|
570
|
+
if (r.$type === "undefined") return void 0;
|
|
571
|
+
if (r.$type === "bigint") return BigInt(r.value);
|
|
572
|
+
if (r.$type === "number") return Number(r.value);
|
|
573
|
+
if (r.$type === "date") {
|
|
574
|
+
const d = new Date(r.value);
|
|
575
|
+
if (!Number.isNaN(d.getTime())) return d;
|
|
576
|
+
}
|
|
577
|
+
} catch {}
|
|
578
|
+
e(u ? "MARKLESS_SCALAR_SPECIALIZED_ESCALATE" : "MARKLESS_PAYLOAD_INVALID", s);
|
|
579
|
+
};
|
|
580
|
+
//#endregion
|
|
581
|
+
//#region ../web/src/fns/dom-order.ts
|
|
582
|
+
const marklessFindElementAtDomOrderIndex = (r, i) => {
|
|
583
|
+
let c = r.__marklessCensus;
|
|
584
|
+
if (!c) {
|
|
585
|
+
const w = document.createTreeWalker(r, 1);
|
|
586
|
+
c = r.__marklessCensus = r.nodeType === 1 ? [r] : [];
|
|
587
|
+
for (let n; n = w.nextNode();) c.push(n);
|
|
588
|
+
}
|
|
589
|
+
return c[i];
|
|
590
|
+
};
|
|
591
|
+
//#endregion
|
|
592
|
+
//#region src/vite/runtime/mdx-scalar.ts
|
|
593
|
+
async function tryResumeMdxScalar(input, loadPlan, loadSymbol) {
|
|
594
|
+
const { root, event, element } = input;
|
|
595
|
+
if (root.__asyncResumeRuntimeStarted || root.isConnected === false || !element) return false;
|
|
596
|
+
if (root.querySelector("[overlay]:not([hidden])") || root.ownerDocument?.querySelector("script[type=\"markless/arm\"],script[type=\"markless/state-patch\"]")) return false;
|
|
597
|
+
let payload = payloads.get(root);
|
|
598
|
+
if (!payload) {
|
|
599
|
+
const state = readScript(root, "markless/state");
|
|
600
|
+
const view = readScript(root, "markless/view");
|
|
601
|
+
if (!state || !view || state.version !== protocolStateVersion(state.storage) || view.version !== 1) return false;
|
|
602
|
+
payload = {
|
|
603
|
+
state,
|
|
604
|
+
view,
|
|
605
|
+
plans: /* @__PURE__ */ new Map()
|
|
606
|
+
};
|
|
607
|
+
payloads.set(root, payload);
|
|
608
|
+
}
|
|
609
|
+
if (event !== 0 && !input.eventRecord && payload.view.events.some((record) => {
|
|
610
|
+
if (record.eventName !== event.type) return false;
|
|
611
|
+
const host = locate(root, payload.view, record.hostNodeId);
|
|
612
|
+
return host === element || host?.contains(element);
|
|
613
|
+
})) return false;
|
|
614
|
+
const priming = event === 0 || !input.eventRecord && (event.type === "focusin" || element.tagName === "BUTTON" && (event.type === "keydown" || event.type === "keyup") && (event.key === "Enter" || event.key === " "));
|
|
615
|
+
const record = priming ? payload.view.events.find((record) => record.eventName !== "visible" && locate(root, payload.view, record.hostNodeId) === element) : input.eventRecord;
|
|
616
|
+
if (!record) return false;
|
|
617
|
+
if (!priming && event.type !== record.eventName) return false;
|
|
618
|
+
if (!priming && event.bubbles === false && event.target !== element) return false;
|
|
619
|
+
if (payload.view.behaviors.some((behavior) => {
|
|
620
|
+
const host = locate(root, payload.view, behavior.hostNodeId);
|
|
621
|
+
return !host || host === element || host.contains(element);
|
|
622
|
+
})) return false;
|
|
623
|
+
const path = [{
|
|
624
|
+
record,
|
|
625
|
+
element
|
|
626
|
+
}];
|
|
627
|
+
if (!priming && event.bubbles !== false && !input.propagationStopped && !event.cancelBubble && element !== root) {
|
|
628
|
+
const candidates = payload.view.events.filter((record) => record.eventName === event.type).map((record) => ({
|
|
629
|
+
record,
|
|
630
|
+
element: locate(root, payload.view, record.hostNodeId)
|
|
631
|
+
}));
|
|
632
|
+
if (candidates.some((candidate) => !candidate.element)) return false;
|
|
633
|
+
for (let ancestor = element.parentElement; ancestor; ancestor = ancestor.parentElement) {
|
|
634
|
+
const record = candidates.find((candidate) => candidate.element === ancestor)?.record;
|
|
635
|
+
if (record) path.push({
|
|
636
|
+
record,
|
|
637
|
+
element: ancestor
|
|
638
|
+
});
|
|
639
|
+
if (ancestor === root) break;
|
|
640
|
+
}
|
|
641
|
+
}
|
|
642
|
+
const actions = [];
|
|
643
|
+
for (const matched of path) {
|
|
644
|
+
const record = matched.record;
|
|
645
|
+
if (record.action || record.symbolIds.length !== 1 || record.syncPolicy && !priming && (matched !== path[0] || !input.syncPolicyAlreadyApplied)) return false;
|
|
646
|
+
const symbolId = record.symbolIds[0];
|
|
647
|
+
const key = record.hostNodeId + "\n" + record.eventName + "\n" + symbolId;
|
|
648
|
+
let prepared = payload.plans.get(key);
|
|
649
|
+
if (prepared === void 0) {
|
|
650
|
+
const action = await loadPlan(symbolId);
|
|
651
|
+
prepared = action ? prepare(root, payload, action, record) : null;
|
|
652
|
+
payload.plans.set(key, prepared);
|
|
653
|
+
}
|
|
654
|
+
if (!prepared || prepared.element !== matched.element || prepared.element.isConnected === false || prepared.targets.some((target) => target.element.isConnected === false)) return false;
|
|
655
|
+
actions.push({
|
|
656
|
+
symbolId,
|
|
657
|
+
prepared
|
|
658
|
+
});
|
|
659
|
+
}
|
|
660
|
+
if (priming) return true;
|
|
661
|
+
for (const action of actions) {
|
|
662
|
+
const symbol = await loadSymbol(action.symbolId);
|
|
663
|
+
if (isDetached(root)) return true;
|
|
664
|
+
if (await runScalar(root, event, action.prepared, symbol)) break;
|
|
665
|
+
}
|
|
666
|
+
return true;
|
|
667
|
+
}
|
|
668
|
+
async function runScalar(root, event, prepared, symbol) {
|
|
669
|
+
const values = root.__marklessEventOnlyGraph ?? /* @__PURE__ */ new Map();
|
|
670
|
+
let value = values.has(prepared.cell) ? values.get(prepared.cell) : prepared.initial;
|
|
671
|
+
const previous = value;
|
|
672
|
+
const check = (id, path) => {
|
|
673
|
+
if (id !== prepared.cell || path?.length) throw new Error("MARKLESS_SCALAR_PLAN_MISMATCH");
|
|
674
|
+
};
|
|
675
|
+
const result = symbol({
|
|
676
|
+
graph: {
|
|
677
|
+
read(id, path) {
|
|
678
|
+
check(id, path);
|
|
679
|
+
return value;
|
|
680
|
+
},
|
|
681
|
+
write(write) {
|
|
682
|
+
check(write.graphNodeId, write.path);
|
|
683
|
+
value = write.value;
|
|
684
|
+
},
|
|
685
|
+
update(update) {
|
|
686
|
+
check(update.graphNodeId, update.path);
|
|
687
|
+
const old = value;
|
|
688
|
+
value = update.update(value);
|
|
689
|
+
return update.returnValue === "previous" ? old : update.returnValue === "next" ? value : void 0;
|
|
690
|
+
}
|
|
691
|
+
},
|
|
692
|
+
event,
|
|
693
|
+
element: prepared.element,
|
|
694
|
+
getElementHandle: () => void 0
|
|
695
|
+
});
|
|
696
|
+
const stopped = event.cancelBubble;
|
|
697
|
+
await result;
|
|
698
|
+
values.set(prepared.cell, value);
|
|
699
|
+
root.__marklessEventOnlyGraph = values;
|
|
700
|
+
if (!Object.is(previous, value)) for (const target of prepared.targets) target.element.textContent = target.prefix + (value == null ? "" : String(value)) + target.suffix;
|
|
701
|
+
return stopped || event.cancelBubble;
|
|
702
|
+
}
|
|
703
|
+
const payloads = /* @__PURE__ */ new WeakMap();
|
|
704
|
+
function prepare(root, { state, view }, action, record) {
|
|
705
|
+
const { plan, scope } = action;
|
|
706
|
+
if (plan.kind !== "scalar" || !scope || protocolInstancePath(scope) !== scope || protocolInstanceQualifies(plan.cell) !== true) return null;
|
|
707
|
+
const cell = scope + plan.cell;
|
|
708
|
+
if (scope + action.hostNodeId !== record.hostNodeId || action.eventName !== record.eventName || scope + plan.symbolId !== record.symbolIds[0]) return null;
|
|
709
|
+
const servedEvent = view.events.find((event) => event.hostNodeId === record.hostNodeId && event.eventName === record.eventName);
|
|
710
|
+
if (!servedEvent || servedEvent.symbolIds.length !== 1 || servedEvent.symbolIds[0] !== record.symbolIds[0]) return null;
|
|
711
|
+
const cells = state.cells.filter((candidate) => candidate.graphNodeId === cell);
|
|
712
|
+
if (cells.length !== 1 || cells[0].valueKind !== "scalar") return null;
|
|
713
|
+
const updates = view.domUpdates.filter((update) => update.graphNodeId === cell);
|
|
714
|
+
if (!updates.length || updates.length !== plan.textUpdates.length) return null;
|
|
715
|
+
const { cells: _cells, ...stateDependencies } = state;
|
|
716
|
+
const { domUpdates: _updates, ...viewDependencies } = view;
|
|
717
|
+
if (references(stateDependencies, cell) || references(viewDependencies, cell)) return null;
|
|
718
|
+
const element = locate(root, view, record.hostNodeId);
|
|
719
|
+
if (!element) return null;
|
|
720
|
+
const targets = [];
|
|
721
|
+
for (const expected of plan.textUpdates) {
|
|
722
|
+
const update = updates.find((candidate) => candidate.hostNodeId === scope + expected.hostNodeId && candidate.symbolId === scope + expected.symbolId);
|
|
723
|
+
if (!update || scope + expected.graphNodeId !== cell || update.path.length || update.target?.kind !== "text" || update.target.trueValue !== void 0 || update.target.falseValue !== void 0 || (update.target.prefix ?? "") !== (expected.prefix ?? "") || (update.target.suffix ?? "") !== (expected.suffix ?? "")) return null;
|
|
724
|
+
const target = locate(root, view, update.hostNodeId);
|
|
725
|
+
if (!target) return null;
|
|
726
|
+
targets.push({
|
|
727
|
+
element: target,
|
|
728
|
+
prefix: expected.prefix ?? "",
|
|
729
|
+
suffix: expected.suffix ?? ""
|
|
730
|
+
});
|
|
731
|
+
}
|
|
732
|
+
return {
|
|
733
|
+
cell,
|
|
734
|
+
initial: marklessDecodeScalarCell(cells[0], cell, "mdx-scalar"),
|
|
735
|
+
element,
|
|
736
|
+
targets
|
|
737
|
+
};
|
|
738
|
+
}
|
|
739
|
+
function locate(root, view, host) {
|
|
740
|
+
const locator = view.locators.find((locator) => locator.hostNodeId === host);
|
|
741
|
+
if (!locator || locator.strategy !== "dom-order") return null;
|
|
742
|
+
const element = marklessFindElementAtDomOrderIndex(root, locator.index);
|
|
743
|
+
return element?.tagName.toLowerCase() === locator.tagName.toLowerCase() ? element : null;
|
|
744
|
+
}
|
|
745
|
+
function readScript(root, type) {
|
|
746
|
+
const text = root.querySelector(`script[type="${type}"]`)?.textContent;
|
|
747
|
+
return text ? JSON.parse(text) : void 0;
|
|
748
|
+
}
|
|
749
|
+
function references(value, id) {
|
|
750
|
+
return value === id || !!value && typeof value === "object" && Object.values(value).some((child) => references(child, id));
|
|
751
|
+
}
|
|
752
|
+
function isDetached(root) {
|
|
753
|
+
return root.isConnected === false;
|
|
3
754
|
}
|
|
4
755
|
//#endregion
|
|
5
756
|
//#region src/vite/runtime/mdx-route.ts
|
|
@@ -120,14 +871,34 @@ function createMdxRenderDataSurface(parts, children) {
|
|
|
120
871
|
imports: Object.fromEntries(children.flatMap((child) => child.surface.rootComponentName ? [[child.surface.rootComponentName, child.surface]] : []))
|
|
121
872
|
};
|
|
122
873
|
}
|
|
123
|
-
async function renderMdxChild(children, component, props, child) {
|
|
124
|
-
const
|
|
874
|
+
async function renderMdxChild(children, component, props, child, pageRenderContext) {
|
|
875
|
+
const storageValue = pageRenderContext?.[STORAGE_RENDER_CONTEXT_READER];
|
|
876
|
+
const renderContext = marklessSsrRosterPositionContext({
|
|
877
|
+
elementIdScope: child.hostPrefix,
|
|
878
|
+
...storageValue ? { [STORAGE_RENDER_CONTEXT_READER]: storageValue } : {}
|
|
879
|
+
});
|
|
880
|
+
const rendered = await renderWithMdxChildren(component, props, child, renderContext);
|
|
881
|
+
const output = rendered && typeof rendered.html === "string" ? await marklessSsrIslandRosterAnswered(renderContext, rendered) : rendered;
|
|
125
882
|
if (output) children.push({
|
|
126
883
|
...child,
|
|
127
884
|
output
|
|
128
885
|
});
|
|
129
886
|
return output?.html ?? "";
|
|
130
887
|
}
|
|
888
|
+
async function renderWithMdxChildren(component, props, child, renderContext) {
|
|
889
|
+
const tags = child.childrenElementTags;
|
|
890
|
+
const childrenHtml = props?.children;
|
|
891
|
+
if (!tags?.length || typeof childrenHtml !== "string") return component.renderSsr?.(props, renderContext);
|
|
892
|
+
const { result } = await withProjectionSpan(tags.map((tagName, index) => ({
|
|
893
|
+
kind: "element",
|
|
894
|
+
hostNodeId: `__mdx:children:${index}`,
|
|
895
|
+
tagName
|
|
896
|
+
})), (mark) => component.renderSsr?.({
|
|
897
|
+
...props,
|
|
898
|
+
children: mark + childrenHtml
|
|
899
|
+
}, renderContext));
|
|
900
|
+
return result;
|
|
901
|
+
}
|
|
131
902
|
function composeMdxState(children) {
|
|
132
903
|
const childStates = children.map((child) => ({
|
|
133
904
|
child,
|
|
@@ -137,62 +908,285 @@ function composeMdxState(children) {
|
|
|
137
908
|
const storage = childStates.flatMap(({ state }) => state.storage ?? []);
|
|
138
909
|
return {
|
|
139
910
|
version: protocolStateVersion(storage),
|
|
140
|
-
cells: childStates.flatMap(({ state }) =>
|
|
141
|
-
|
|
142
|
-
|
|
911
|
+
cells: childStates.flatMap(({ child, state }) => {
|
|
912
|
+
const island = islandScope(child, state);
|
|
913
|
+
return (state.cells ?? []).map((cell) => islandScopedGraphRecord(cell, island));
|
|
914
|
+
}),
|
|
915
|
+
computed: childStates.flatMap(({ child, state }) => {
|
|
916
|
+
const island = islandScope(child, state);
|
|
917
|
+
return (state.computed ?? []).map((record) => ({
|
|
918
|
+
...islandScopedGraphRecord(record, island),
|
|
919
|
+
...record.dependencies ? { dependencies: record.dependencies.map((read) => islandScopedGraphRecord(read, island)) } : {},
|
|
920
|
+
...record.deriveSymbolId ? { deriveSymbolId: child.symbolPrefix + record.deriveSymbolId } : {}
|
|
921
|
+
}));
|
|
922
|
+
}),
|
|
923
|
+
...childStates.some(({ state }) => state.sharedSeeds?.length) ? { sharedSeeds: childStates.flatMap(({ child, state }) => {
|
|
924
|
+
const island = islandScope(child, state);
|
|
925
|
+
return (state.sharedSeeds ?? []).map((seed) => ({
|
|
926
|
+
...islandScopedGraphRecord(seed, island),
|
|
927
|
+
deriveSymbolId: child.symbolPrefix + seed.deriveSymbolId,
|
|
928
|
+
...seed.dependencies ? { dependencies: seed.dependencies.map((dependency) => ({
|
|
929
|
+
...islandScopedGraphRecord(dependency, island),
|
|
930
|
+
...dependency.reads ? { reads: islandScopedGraphRecord(dependency.reads, island) } : {}
|
|
931
|
+
})) } : {}
|
|
932
|
+
}));
|
|
933
|
+
}) } : {},
|
|
934
|
+
...childStates.some(({ state }) => state.sharedDefinitions?.length) ? { sharedDefinitions: childStates.flatMap(({ child, state }) => {
|
|
935
|
+
const island = islandScope(child, state);
|
|
936
|
+
return (state.sharedDefinitions ?? []).map((definition) => islandScopedSharedDefinition(definition, island));
|
|
937
|
+
}) } : {},
|
|
143
938
|
...storage.length > 0 ? { storage } : {}
|
|
144
939
|
};
|
|
145
940
|
}
|
|
146
|
-
function
|
|
147
|
-
const
|
|
941
|
+
function islandScope(child, state) {
|
|
942
|
+
const prefix = child.symbolPrefix;
|
|
943
|
+
return {
|
|
944
|
+
segment: prefix && protocolInstancePath(prefix) === prefix ? prefix : "",
|
|
945
|
+
widgetDefinitionIds: new Set((state.sharedDefinitions ?? []).filter((definition) => definition.scope === "widget").map((definition) => definition.id))
|
|
946
|
+
};
|
|
947
|
+
}
|
|
948
|
+
/**
|
|
949
|
+
* Which of a child's ids belong to the island, and which are the whole page's.
|
|
950
|
+
*
|
|
951
|
+
* Two islands of one component compose from their own roots, so both spell the
|
|
952
|
+
* same `c`/`p` path and their cells, computeds and element-handle rosters land
|
|
953
|
+
* on one node unless the island segment separates them. What must NOT take the
|
|
954
|
+
* segment is the page-space families that mean one node per page on purpose: a
|
|
955
|
+
* `scope: 'page' | 'container' | 'request'` shared() graph and a storage slot
|
|
956
|
+
* are shared BETWEEN islands, and prefixing them gives each island a private
|
|
957
|
+
* copy of state the author asked to be common.
|
|
958
|
+
*
|
|
959
|
+
* A widget-scoped shared() id is the exception inside the exception — one graph
|
|
960
|
+
* per rendered widget, so it is the island's — and a bare one (no path, because
|
|
961
|
+
* no registered widget root claimed it at compose) is recognised by asking the
|
|
962
|
+
* child's own definitions which scope it was declared at.
|
|
963
|
+
*/
|
|
964
|
+
function islandScopedGraphNodeId(graphNodeId, island) {
|
|
965
|
+
if (!island.segment) return graphNodeId;
|
|
966
|
+
if (protocolInstancePath(graphNodeId)) return island.segment + graphNodeId;
|
|
967
|
+
if (protocolInstanceQualifies(graphNodeId) !== false) return island.segment + graphNodeId;
|
|
968
|
+
const slash = graphNodeId.lastIndexOf("/");
|
|
969
|
+
return island.widgetDefinitionIds.has(graphNodeId) || slash > 0 && island.widgetDefinitionIds.has(graphNodeId.slice(0, slash)) ? island.segment + graphNodeId : graphNodeId;
|
|
970
|
+
}
|
|
971
|
+
/**
|
|
972
|
+
* The registering half of a handle key, mirrored on the reading half.
|
|
973
|
+
*
|
|
974
|
+
* Resume re-spells only a widget-scoped `shared:` handle id per rendered widget;
|
|
975
|
+
* a component-local handle is already one element per key and the reading symbol
|
|
976
|
+
* asks for it exactly as its module compiled it. Segmenting that second kind
|
|
977
|
+
* here would file it under a key nothing ever asks for.
|
|
978
|
+
*/
|
|
979
|
+
function islandScopedHandleId(handleId, island) {
|
|
980
|
+
return protocolInstanceQualifies(handleId) !== false ? handleId : islandScopedGraphNodeId(handleId, island);
|
|
981
|
+
}
|
|
982
|
+
function islandScopedGraphRecord(record, island) {
|
|
983
|
+
return record.graphNodeId ? {
|
|
984
|
+
...record,
|
|
985
|
+
graphNodeId: islandScopedGraphNodeId(record.graphNodeId, island)
|
|
986
|
+
} : record;
|
|
987
|
+
}
|
|
988
|
+
function islandScopedSharedDefinition(definition, island) {
|
|
989
|
+
const scoped = (id) => islandScopedGraphNodeId(id, island);
|
|
990
|
+
return {
|
|
991
|
+
...definition,
|
|
992
|
+
id: scoped(definition.id),
|
|
993
|
+
...definition.graphNodeIds ? { graphNodeIds: definition.graphNodeIds.map(scoped) } : {},
|
|
994
|
+
...definition.projectionIds ? { projectionIds: definition.projectionIds.map(scoped) } : {},
|
|
995
|
+
...definition.returnProperties ? { returnProperties: definition.returnProperties.map((property) => islandScopedGraphRecord(property, island)) } : {}
|
|
996
|
+
};
|
|
997
|
+
}
|
|
998
|
+
function composeMdxView(parts, children, initialElementOffset, initialCommentOffset = 0) {
|
|
999
|
+
const childCensus = children.map((child) => ({
|
|
148
1000
|
...child,
|
|
149
1001
|
view: child.output?.view,
|
|
150
|
-
|
|
151
|
-
}))
|
|
152
|
-
|
|
153
|
-
|
|
1002
|
+
census: mdxRenderedCensus(child.output)
|
|
1003
|
+
}));
|
|
1004
|
+
const firstView = childCensus.find((child) => child.view)?.view;
|
|
1005
|
+
if (!firstView) return;
|
|
1006
|
+
const childByIndex = new Map(childCensus.map((child) => [child.componentIndex, child]));
|
|
154
1007
|
const locators = [];
|
|
155
1008
|
const events = [];
|
|
156
1009
|
const domUpdates = [];
|
|
157
1010
|
const behaviors = [];
|
|
158
1011
|
const elementHandles = [];
|
|
1012
|
+
const keyedRepeats = [];
|
|
1013
|
+
const branches = [];
|
|
1014
|
+
const asyncBoundaries = [];
|
|
1015
|
+
const asyncRunners = {};
|
|
159
1016
|
let elementOffset = initialElementOffset;
|
|
1017
|
+
let commentOffset = initialCommentOffset;
|
|
160
1018
|
for (const part of parts) {
|
|
161
1019
|
if (part.kind === "html") {
|
|
162
1020
|
elementOffset += part.elementCount;
|
|
1021
|
+
commentOffset += part.commentCount ?? 0;
|
|
163
1022
|
continue;
|
|
164
1023
|
}
|
|
165
1024
|
const child = childByIndex.get(part.componentIndex);
|
|
166
1025
|
if (!child) continue;
|
|
167
|
-
appendMdxChildView({
|
|
1026
|
+
if (child.view) appendMdxChildView({
|
|
168
1027
|
child,
|
|
169
1028
|
elementOffset,
|
|
1029
|
+
commentOffset,
|
|
170
1030
|
locators,
|
|
171
1031
|
events,
|
|
172
1032
|
domUpdates,
|
|
173
1033
|
behaviors,
|
|
174
|
-
elementHandles
|
|
1034
|
+
elementHandles,
|
|
1035
|
+
keyedRepeats,
|
|
1036
|
+
branches,
|
|
1037
|
+
asyncBoundaries,
|
|
1038
|
+
asyncRunners
|
|
175
1039
|
});
|
|
176
|
-
elementOffset += child.
|
|
1040
|
+
elementOffset += child.census.elements;
|
|
1041
|
+
commentOffset += child.census.comments;
|
|
177
1042
|
}
|
|
178
1043
|
locators.sort((a, b) => a.index - b.index);
|
|
179
1044
|
return {
|
|
180
|
-
version:
|
|
1045
|
+
version: firstView.version,
|
|
181
1046
|
locators,
|
|
182
1047
|
events,
|
|
183
1048
|
domUpdates,
|
|
184
1049
|
behaviors,
|
|
185
1050
|
elementHandles,
|
|
186
|
-
|
|
1051
|
+
...keyedRepeats.length > 0 ? { keyedRepeats } : {},
|
|
1052
|
+
...branches.length > 0 ? { branches } : {},
|
|
1053
|
+
asyncBoundaries,
|
|
1054
|
+
...Object.keys(asyncRunners).length > 0 ? { asyncRunners } : {}
|
|
1055
|
+
};
|
|
1056
|
+
}
|
|
1057
|
+
/**
|
|
1058
|
+
* The tally the client keeps of one island's markup.
|
|
1059
|
+
*
|
|
1060
|
+
* Resume pins a depth-first census of every element under the container and
|
|
1061
|
+
* resolves each locator by its index into it, and it walks comments the same
|
|
1062
|
+
* way for anchors, so both offsets have to be counted the way the DOM counts
|
|
1063
|
+
* them or a later island's records land on the wrong nodes.
|
|
1064
|
+
*/
|
|
1065
|
+
function mdxRenderedCensus(output) {
|
|
1066
|
+
if (!output) return {
|
|
1067
|
+
elements: 0,
|
|
1068
|
+
comments: 0
|
|
1069
|
+
};
|
|
1070
|
+
if (output.root) return mdxDomCensus(output.root);
|
|
1071
|
+
const scanned = typeof output.html === "string" ? mdxHtmlCensus(output.html) : void 0;
|
|
1072
|
+
return {
|
|
1073
|
+
elements: typeof output.elementCount === "number" ? output.elementCount : scanned?.elements ?? 0,
|
|
1074
|
+
comments: scanned?.comments ?? 0
|
|
1075
|
+
};
|
|
1076
|
+
}
|
|
1077
|
+
function mdxDomCensus(root) {
|
|
1078
|
+
let elements = 0;
|
|
1079
|
+
let comments = 0;
|
|
1080
|
+
(function visit(node) {
|
|
1081
|
+
if (node.nodeType === 1) elements++;
|
|
1082
|
+
else if (node.nodeType === 8 && !isArmBranchAnchorComment({
|
|
1083
|
+
nodeType: 8,
|
|
1084
|
+
data: node.data
|
|
1085
|
+
})) comments++;
|
|
1086
|
+
for (const child of Array.from(node.childNodes ?? [])) visit(child);
|
|
1087
|
+
})(root);
|
|
1088
|
+
return {
|
|
1089
|
+
elements,
|
|
1090
|
+
comments
|
|
1091
|
+
};
|
|
1092
|
+
}
|
|
1093
|
+
const MDX_RAW_TEXT_TAGS = /* @__PURE__ */ new Set([
|
|
1094
|
+
"script",
|
|
1095
|
+
"style",
|
|
1096
|
+
"textarea",
|
|
1097
|
+
"title"
|
|
1098
|
+
]);
|
|
1099
|
+
function mdxHtmlCensus(html) {
|
|
1100
|
+
let elements = 0;
|
|
1101
|
+
let comments = 0;
|
|
1102
|
+
let templateDepth = 0;
|
|
1103
|
+
let at = 0;
|
|
1104
|
+
while (at < html.length) {
|
|
1105
|
+
const open = html.indexOf("<", at);
|
|
1106
|
+
if (open < 0) break;
|
|
1107
|
+
if (html.startsWith("<!--", open)) {
|
|
1108
|
+
const close = html.indexOf("-->", open + 4);
|
|
1109
|
+
const text = html.slice(open + 4, close < 0 ? html.length : close);
|
|
1110
|
+
if (templateDepth === 0 && !isArmBranchAnchorComment({
|
|
1111
|
+
nodeType: 8,
|
|
1112
|
+
data: text
|
|
1113
|
+
})) comments++;
|
|
1114
|
+
at = close < 0 ? html.length : close + 3;
|
|
1115
|
+
continue;
|
|
1116
|
+
}
|
|
1117
|
+
if (html[open + 1] === "!" || html[open + 1] === "?") {
|
|
1118
|
+
at = mdxTagEnd(html, open);
|
|
1119
|
+
continue;
|
|
1120
|
+
}
|
|
1121
|
+
const closing = html[open + 1] === "/";
|
|
1122
|
+
const name = mdxTagName(html, open + (closing ? 2 : 1));
|
|
1123
|
+
if (!name) {
|
|
1124
|
+
at = open + 1;
|
|
1125
|
+
continue;
|
|
1126
|
+
}
|
|
1127
|
+
const tagEnd = mdxTagEnd(html, open);
|
|
1128
|
+
at = tagEnd;
|
|
1129
|
+
if (closing) {
|
|
1130
|
+
if (name === "template" && templateDepth > 0) templateDepth--;
|
|
1131
|
+
continue;
|
|
1132
|
+
}
|
|
1133
|
+
if (templateDepth === 0) elements++;
|
|
1134
|
+
if (html.slice(open, tagEnd).trimEnd().endsWith("/>")) continue;
|
|
1135
|
+
if (name === "template") templateDepth++;
|
|
1136
|
+
else if (MDX_RAW_TEXT_TAGS.has(name)) {
|
|
1137
|
+
const close = html.toLowerCase().indexOf(`</${name}`, tagEnd);
|
|
1138
|
+
at = close < 0 ? html.length : close;
|
|
1139
|
+
}
|
|
1140
|
+
}
|
|
1141
|
+
return {
|
|
1142
|
+
elements,
|
|
1143
|
+
comments
|
|
187
1144
|
};
|
|
188
1145
|
}
|
|
1146
|
+
function mdxTagName(html, at) {
|
|
1147
|
+
let end = at;
|
|
1148
|
+
while (end < html.length && !/[\s/>]/.test(html[end])) end++;
|
|
1149
|
+
const name = html.slice(at, end).toLowerCase();
|
|
1150
|
+
return /^[a-z][a-z0-9:-]*$/.test(name) ? name : "";
|
|
1151
|
+
}
|
|
1152
|
+
function mdxTagEnd(html, at) {
|
|
1153
|
+
let quote = "";
|
|
1154
|
+
for (let index = at + 1; index < html.length; index++) {
|
|
1155
|
+
const character = html[index];
|
|
1156
|
+
if (quote) {
|
|
1157
|
+
if (character === quote) quote = "";
|
|
1158
|
+
continue;
|
|
1159
|
+
}
|
|
1160
|
+
if (character === "\"" || character === "'") quote = character;
|
|
1161
|
+
else if (character === ">") return index + 1;
|
|
1162
|
+
}
|
|
1163
|
+
return html.length;
|
|
1164
|
+
}
|
|
189
1165
|
function loadMdxSymbol(symbolId, children, loaders) {
|
|
190
|
-
for (const child of children)
|
|
191
|
-
|
|
1166
|
+
for (const child of children) {
|
|
1167
|
+
if (!symbolId.startsWith(child.symbolPrefix)) continue;
|
|
1168
|
+
const load = child.output?.loadSymbol?.bind(child.output);
|
|
1169
|
+
if (load) return loadScopedMdxSymbol(symbolId, child.symbolPrefix, load);
|
|
1170
|
+
}
|
|
1171
|
+
for (const loader of loaders) if (symbolId.startsWith(loader.prefix)) return loadScopedMdxSymbol(symbolId, loader.prefix, (id) => loader.loadSymbol(loader.prefix + id));
|
|
192
1172
|
return Promise.reject(/* @__PURE__ */ new Error(`Unknown Markless MDX symbol ${symbolId}`));
|
|
193
1173
|
}
|
|
1174
|
+
function loadScopedMdxSymbol(symbolId, prefix, load) {
|
|
1175
|
+
const loadChildLocal = ((childSymbolId) => marklessThen(load(childSymbolId.slice(prefix.length)), (symbol) => marklessIslandSpacedSymbol(symbol, prefix)));
|
|
1176
|
+
return marklessInstanceScopedLoadSymbol(loadChildLocal)(symbolId);
|
|
1177
|
+
}
|
|
194
1178
|
function appendMdxChildView(context) {
|
|
195
1179
|
const childView = context.child.view;
|
|
1180
|
+
const island = islandScope(context.child, context.child.output?.state ?? { version: 1 });
|
|
1181
|
+
const familyContext = {
|
|
1182
|
+
child: context.child,
|
|
1183
|
+
island,
|
|
1184
|
+
commentOffset: context.commentOffset
|
|
1185
|
+
};
|
|
1186
|
+
for (const repeat of childView.keyedRepeats ?? []) context.keyedRepeats.push(islandScopedFamilyRecord(repeat, familyContext));
|
|
1187
|
+
for (const branch of childView.branches ?? []) context.branches.push(islandScopedFamilyRecord(branch, familyContext));
|
|
1188
|
+
for (const boundary of childView.asyncBoundaries ?? []) context.asyncBoundaries.push(islandScopedFamilyRecord(boundary, familyContext));
|
|
1189
|
+
for (const [graphNodeId, symbolId] of Object.entries(childView.asyncRunners ?? {})) context.asyncRunners[islandScopedGraphNodeId(graphNodeId, island)] = context.child.symbolPrefix + symbolId;
|
|
196
1190
|
for (const locator of childView.locators ?? []) context.locators.push({
|
|
197
1191
|
...locator,
|
|
198
1192
|
hostNodeId: context.child.hostPrefix + locator.hostNodeId,
|
|
@@ -203,23 +1197,92 @@ function appendMdxChildView(context) {
|
|
|
203
1197
|
hostNodeId: context.child.hostPrefix + event.hostNodeId,
|
|
204
1198
|
symbolIds: event.symbolIds.map((symbolId) => context.child.symbolPrefix + symbolId)
|
|
205
1199
|
});
|
|
206
|
-
for (const update of childView.domUpdates ?? []) context.domUpdates.push(prefixMdxSymbolRecord(update, context.child));
|
|
207
|
-
for (const behavior of childView.behaviors ?? []) context.behaviors.push(prefixMdxSymbolRecord(behavior, context.child));
|
|
1200
|
+
for (const update of childView.domUpdates ?? []) context.domUpdates.push(prefixMdxSymbolRecord(update, context.child, island));
|
|
1201
|
+
for (const behavior of childView.behaviors ?? []) context.behaviors.push(prefixMdxSymbolRecord(behavior, context.child, island));
|
|
208
1202
|
for (const handle of childView.elementHandles ?? []) context.elementHandles.push({
|
|
209
1203
|
...handle,
|
|
210
|
-
hostNodeId: context.child.hostPrefix + handle.hostNodeId
|
|
1204
|
+
hostNodeId: context.child.hostPrefix + handle.hostNodeId,
|
|
1205
|
+
handleId: islandScopedHandleId(handle.handleId, island)
|
|
211
1206
|
});
|
|
212
1207
|
}
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
1208
|
+
const MDX_PAGE_ANCHOR_STRATEGY = "dom-order-comment";
|
|
1209
|
+
const MDX_HOST_ID_KEYS = [
|
|
1210
|
+
"hostNodeId",
|
|
1211
|
+
"parentHostNodeId",
|
|
1212
|
+
"ownerHostNodeId"
|
|
1213
|
+
];
|
|
1214
|
+
const MDX_SYMBOL_ID_KEYS = [
|
|
1215
|
+
"symbolId",
|
|
1216
|
+
"updateSymbolId",
|
|
1217
|
+
"runnerSymbolId"
|
|
1218
|
+
];
|
|
1219
|
+
const MDX_GRAPH_ID_KEYS = [
|
|
1220
|
+
"graphNodeId",
|
|
1221
|
+
"runnerGraphNodeId",
|
|
1222
|
+
"collectionGraphNodeId"
|
|
1223
|
+
];
|
|
1224
|
+
const MDX_INSTANCE_PATH_KEYS = ["instancePath", "composedInstancePath"];
|
|
1225
|
+
const MDX_NESTED_RECORD_KEYS = [
|
|
1226
|
+
"testReads",
|
|
1227
|
+
"contentReads",
|
|
1228
|
+
"asyncReads",
|
|
1229
|
+
"inputGraphReads",
|
|
1230
|
+
"composedGraphProps",
|
|
1231
|
+
"idrefSites",
|
|
1232
|
+
"locators",
|
|
1233
|
+
"events",
|
|
1234
|
+
"domUpdates",
|
|
1235
|
+
"behaviors",
|
|
1236
|
+
"elementHandles",
|
|
1237
|
+
"rowEvents",
|
|
1238
|
+
"rowElementHandles",
|
|
1239
|
+
"keyedRepeats",
|
|
1240
|
+
"branches",
|
|
1241
|
+
"armRecords",
|
|
1242
|
+
"servedArmRecords"
|
|
1243
|
+
];
|
|
1244
|
+
/**
|
|
1245
|
+
* One island's repeat / branch / boundary record in the composed page's spaces.
|
|
1246
|
+
*
|
|
1247
|
+
* Every id the record carries takes the island's own prefix for the same reason
|
|
1248
|
+
* the flat records do — two embeds of one component spell identical ids — and
|
|
1249
|
+
* the two comment anchors take the page's comment offset. Arm-relative
|
|
1250
|
+
* coordinates inside an arm record set are island-local by construction and are
|
|
1251
|
+
* deliberately left alone.
|
|
1252
|
+
*/
|
|
1253
|
+
function islandScopedFamilyRecord(record, context) {
|
|
1254
|
+
const { child, island } = context;
|
|
1255
|
+
const mapped = { ...record };
|
|
1256
|
+
if (typeof record.id === "string") mapped.id = child.hostPrefix + record.id;
|
|
1257
|
+
for (const key of MDX_HOST_ID_KEYS) if (typeof record[key] === "string") mapped[key] = child.hostPrefix + record[key];
|
|
1258
|
+
for (const key of MDX_SYMBOL_ID_KEYS) if (typeof record[key] === "string") mapped[key] = child.symbolPrefix + record[key];
|
|
1259
|
+
if (Array.isArray(record.symbolIds)) mapped.symbolIds = record.symbolIds.map((symbolId) => child.symbolPrefix + String(symbolId));
|
|
1260
|
+
for (const key of MDX_GRAPH_ID_KEYS) if (typeof record[key] === "string") mapped[key] = islandScopedGraphNodeId(record[key], island);
|
|
1261
|
+
if (typeof record.handleId === "string") mapped.handleId = islandScopedHandleId(record.handleId, island);
|
|
1262
|
+
for (const key of MDX_INSTANCE_PATH_KEYS) if (typeof record[key] === "string") mapped[key] = island.segment + record[key];
|
|
1263
|
+
if (isFamilyRecord(record.elementHandleIds)) mapped.elementHandleIds = Object.fromEntries(Object.entries(record.elementHandleIds).map(([readId, handleId]) => [readId, islandScopedHandleId(String(handleId), island)]));
|
|
1264
|
+
for (const key of ["startAnchor", "endAnchor"]) {
|
|
1265
|
+
const anchor = record[key];
|
|
1266
|
+
if (isFamilyRecord(anchor) && anchor.strategy === MDX_PAGE_ANCHOR_STRATEGY) mapped[key] = {
|
|
1267
|
+
...anchor,
|
|
1268
|
+
index: context.commentOffset + Number(anchor.index)
|
|
1269
|
+
};
|
|
1270
|
+
}
|
|
1271
|
+
for (const key of MDX_NESTED_RECORD_KEYS) {
|
|
1272
|
+
const nested = record[key];
|
|
1273
|
+
if (Array.isArray(nested)) mapped[key] = nested.map((entry) => isFamilyRecord(entry) ? islandScopedFamilyRecord(entry, context) : entry);
|
|
1274
|
+
else if (isFamilyRecord(nested)) mapped[key] = islandScopedFamilyRecord(nested, context);
|
|
1275
|
+
}
|
|
1276
|
+
return mapped;
|
|
218
1277
|
}
|
|
219
|
-
function
|
|
1278
|
+
function isFamilyRecord(value) {
|
|
1279
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
1280
|
+
}
|
|
1281
|
+
function prefixMdxSymbolRecord(record, child, island) {
|
|
220
1282
|
return {
|
|
221
|
-
...record,
|
|
1283
|
+
...islandScopedGraphRecord(record, island),
|
|
222
1284
|
hostNodeId: child.hostPrefix + record.hostNodeId,
|
|
1285
|
+
...record.inputGraphReads ? { inputGraphReads: record.inputGraphReads.map((read) => islandScopedGraphRecord(read, island)) } : {},
|
|
223
1286
|
...record.symbolId ? { symbolId: child.symbolPrefix + record.symbolId } : {}
|
|
224
1287
|
};
|
|
225
1288
|
}
|
|
@@ -227,4 +1290,4 @@ function isDefined(value) {
|
|
|
227
1290
|
return value !== void 0;
|
|
228
1291
|
}
|
|
229
1292
|
//#endregion
|
|
230
|
-
export { composeMdxState, composeMdxView, createMdxRenderDataSurface, loadMdxSymbol, renderMdxChild };
|
|
1293
|
+
export { composeMdxState, composeMdxView, createMdxRenderDataSurface, loadMdxSymbol, renderMdxChild, tryResumeMdxScalar };
|