@markless/bundler 0.3.3 → 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/dev-error-DRvO28Fd.js +174 -0
- package/dist/dev-error.d.ts +14 -16
- package/dist/dev-error.js +1 -173
- package/dist/execution-log-D9cex1i1.d.ts +17 -0
- package/dist/optimized-deps-BfzxSm6C.js +89 -0
- package/dist/preload.d.ts +39 -7
- package/dist/preload.js +3 -2
- package/dist/repeat-runtime-BDnaUYXM.js +71 -0
- package/dist/rolldown-BciZw5hZ.js +9116 -0
- package/dist/rolldown.d.ts +37 -18
- package/dist/rolldown.js +3 -3
- package/dist/roster-resume-CpphsFl_.js +427 -0
- package/dist/route-pack-groups-BpefyjrE.js +2707 -0
- package/dist/shared-seed-slot-ihIVCD2S.js +113 -0
- package/dist/{types-BuooJg32.d.ts → types-BqSoEVp6.d.ts} +46 -10
- package/dist/vite.d.ts +5 -6
- package/dist/vite.js +636 -137
- package/package.json +7 -7
- package/dist/rolldown-FRjFod14.js +0 -4516
- package/dist/source-module-CZFm1uWH.js +0 -1032
- package/dist/source-module-XFgOFX1c.d.ts +0 -8
|
@@ -0,0 +1,2707 @@
|
|
|
1
|
+
import { joinURL, parsePath, withQuery, withoutLeadingSlash } from "ufo";
|
|
2
|
+
import { MARKLESS_COMPONENT_PART_BRAND } from "@markless/web/render-to-string";
|
|
3
|
+
import { ASYNC_PROTOCOL_VERSION, PROTOCOL_EVENT_ACTION_KIND, protocolEventActionKind } from "@markless/serializer";
|
|
4
|
+
import { isAbsolute, resolve } from "pathe";
|
|
5
|
+
import { emitSymbolResolverModule } from "@markless/compiler";
|
|
6
|
+
//#region src/build/preload-plan.ts
|
|
7
|
+
const DEFAULT_MIN_PROBABILITY = 0;
|
|
8
|
+
const JAVASCRIPT_MODULE_RE = /\.(?:mjs|js)(?:[?#].*)?$/;
|
|
9
|
+
const PRIORITY_RANK = {
|
|
10
|
+
high: 2,
|
|
11
|
+
auto: 1,
|
|
12
|
+
low: 0
|
|
13
|
+
};
|
|
14
|
+
function planModulePreloadUrls(input) {
|
|
15
|
+
return planModulePreloads(input).map((preload) => preload.href);
|
|
16
|
+
}
|
|
17
|
+
function planModulePreloads(input) {
|
|
18
|
+
const minProbability = input.minProbability ?? DEFAULT_MIN_PROBABILITY;
|
|
19
|
+
const maxPreloads = input.maxPreloads ?? Number.POSITIVE_INFINITY;
|
|
20
|
+
const graph = parseBundleGraph(input.bundleGraph);
|
|
21
|
+
const planned = /* @__PURE__ */ new Map();
|
|
22
|
+
const bestVisitByNode = /* @__PURE__ */ new Map();
|
|
23
|
+
let order = 0;
|
|
24
|
+
const addModule = (name, probability, priority, fetchPriority) => {
|
|
25
|
+
if (!isPreloadableModuleName(name)) return;
|
|
26
|
+
const href = preloadHref(input.base, name);
|
|
27
|
+
const existing = planned.get(href);
|
|
28
|
+
const nextRank = PRIORITY_RANK[priority];
|
|
29
|
+
const existingRank = existing ? PRIORITY_RANK[existing.priority] : -1;
|
|
30
|
+
if (existing && (existingRank > nextRank || existingRank === nextRank && existing.probability >= probability)) return;
|
|
31
|
+
planned.set(href, {
|
|
32
|
+
fetchPriority,
|
|
33
|
+
href,
|
|
34
|
+
name,
|
|
35
|
+
order: existing?.order ?? order++,
|
|
36
|
+
priority,
|
|
37
|
+
probability
|
|
38
|
+
});
|
|
39
|
+
};
|
|
40
|
+
const visit = (name, probability, priority, fetchPriority, seen, suppressed = false, suppressRootOnly = false) => {
|
|
41
|
+
if (probability < minProbability || planned.size >= maxPreloads) return;
|
|
42
|
+
if (!suppressed) {
|
|
43
|
+
const priorityRank = PRIORITY_RANK[priority];
|
|
44
|
+
const previousBest = bestVisitByNode.get(name);
|
|
45
|
+
if (previousBest && (previousBest.priorityRank > priorityRank || previousBest.priorityRank === priorityRank && previousBest.probability >= probability)) return;
|
|
46
|
+
bestVisitByNode.set(name, {
|
|
47
|
+
priorityRank,
|
|
48
|
+
probability
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
if (seen.has(name)) return;
|
|
52
|
+
const nextSeen = new Set(seen);
|
|
53
|
+
nextSeen.add(name);
|
|
54
|
+
const record = graph.get(name);
|
|
55
|
+
if (!record) {
|
|
56
|
+
if (!suppressed) addModule(name, probability, priority, fetchPriority);
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
for (const dep of record.deps.filter((item) => item.kind === "static")) visit(dep.name, probability * dep.probability, priority, fetchPriority, nextSeen, suppressed && !suppressRootOnly);
|
|
60
|
+
if (!suppressed) addModule(name, probability, priority, fetchPriority);
|
|
61
|
+
for (const dep of record.deps.filter((item) => item.kind === "dynamic")) {
|
|
62
|
+
if (input.lazyChunks?.has(dep.name)) continue;
|
|
63
|
+
const edgeProbability = JAVASCRIPT_MODULE_RE.test(name) ? dep.probability : 1;
|
|
64
|
+
visit(dep.name, probability * edgeProbability, priority, fetchPriority, nextSeen, false);
|
|
65
|
+
}
|
|
66
|
+
};
|
|
67
|
+
for (const root of input.roots) {
|
|
68
|
+
const normalized = normalizeRoot(root);
|
|
69
|
+
visit(normalized.name, 1, normalized.priority, normalized.fetchPriority, /* @__PURE__ */ new Set(), normalized.edges === "dynamic-only" || normalized.edges === "dependencies-only", normalized.edges === "dependencies-only");
|
|
70
|
+
}
|
|
71
|
+
return [...planned.values()].sort((left, right) => {
|
|
72
|
+
const priority = PRIORITY_RANK[right.priority] - PRIORITY_RANK[left.priority];
|
|
73
|
+
if (priority !== 0) return priority;
|
|
74
|
+
return left.order - right.order;
|
|
75
|
+
}).slice(0, maxPreloads).map(({ order: _order, ...preload }) => preload);
|
|
76
|
+
}
|
|
77
|
+
function parseBundleGraph(graph) {
|
|
78
|
+
const records = /* @__PURE__ */ new Map();
|
|
79
|
+
if (!graph) return records;
|
|
80
|
+
let index = 0;
|
|
81
|
+
while (index < graph.length) {
|
|
82
|
+
const name = graph[index++];
|
|
83
|
+
if (typeof name !== "string") continue;
|
|
84
|
+
const deps = [];
|
|
85
|
+
let probability = 1;
|
|
86
|
+
let kind = "static";
|
|
87
|
+
while (typeof graph[index] === "number") {
|
|
88
|
+
const marker = graph[index++];
|
|
89
|
+
if (marker < 0) {
|
|
90
|
+
probability = -marker / 10;
|
|
91
|
+
kind = "dynamic";
|
|
92
|
+
continue;
|
|
93
|
+
}
|
|
94
|
+
const dep = graph[marker];
|
|
95
|
+
if (typeof dep === "string") deps.push({
|
|
96
|
+
name: dep,
|
|
97
|
+
kind,
|
|
98
|
+
probability
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
records.set(name, {
|
|
102
|
+
name,
|
|
103
|
+
deps
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
return records;
|
|
107
|
+
}
|
|
108
|
+
function preloadHref(base, name) {
|
|
109
|
+
if (!base || /^(?:[a-z]+:)?\/\//i.test(name)) return name;
|
|
110
|
+
return joinURL(base, name);
|
|
111
|
+
}
|
|
112
|
+
function isPreloadableModuleName(name) {
|
|
113
|
+
return JAVASCRIPT_MODULE_RE.test(name) || isViteDevModuleUrl(name);
|
|
114
|
+
}
|
|
115
|
+
function isViteDevModuleUrl(name) {
|
|
116
|
+
return (name.startsWith("/") || name.startsWith("@id/")) && /(?:^|[?&])import(?:[&#]|$)/.test(name);
|
|
117
|
+
}
|
|
118
|
+
function normalizeRoot(root) {
|
|
119
|
+
if (typeof root === "string") return {
|
|
120
|
+
name: root,
|
|
121
|
+
priority: "auto",
|
|
122
|
+
fetchPriority: "auto"
|
|
123
|
+
};
|
|
124
|
+
const priority = root.priority ?? "auto";
|
|
125
|
+
return {
|
|
126
|
+
name: root.name,
|
|
127
|
+
priority,
|
|
128
|
+
edges: root.edges,
|
|
129
|
+
fetchPriority: root.fetchPriority ?? priorityToFetchPriority(priority)
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
function priorityToFetchPriority(priority) {
|
|
133
|
+
if (priority === "auto") return "auto";
|
|
134
|
+
return priority;
|
|
135
|
+
}
|
|
136
|
+
//#endregion
|
|
137
|
+
//#region src/execution-log.ts
|
|
138
|
+
const MARKLESS_EXECUTION_LOG_MODULE_ID = "virtual:markless:dev-log";
|
|
139
|
+
const MARKLESS_EXECUTION_LOG_GLOBAL = "__mxLog";
|
|
140
|
+
const EXECUTION_LOG_CHUNK_MODULE_PATTERN = /virtual:markless:dev-log|[/\\]web[/\\]src[/\\]dev-log\.ts(?:[?#].*)?$/;
|
|
141
|
+
function isExecutionLogChunk(chunk) {
|
|
142
|
+
return !!chunk.moduleIds?.length && chunk.moduleIds.every((id) => EXECUTION_LOG_CHUNK_MODULE_PATTERN.test(id));
|
|
143
|
+
}
|
|
144
|
+
const EXECUTION_LOG_DISPATCH_MODULE_IDS = ["web:render-csr", "web:resume-events"];
|
|
145
|
+
const MARKLESS_EXECUTION_LEDGER_GLOBAL = "__marklessExecutionLedger";
|
|
146
|
+
const MARKLESS_EXECUTION_LEDGER_INIT = "{ unit: 'chunk-raw-bytes', load: { app: 0, framework: 0, instrument: 0, inline: 0, modules: [] }, total: { app: 0, framework: 0, instrument: 0, inline: 0 }, turns: [], incomplete: null }";
|
|
147
|
+
const EXECUTION_LOG_GESTURE_EVENTS = [
|
|
148
|
+
"pointerdown",
|
|
149
|
+
"touchstart",
|
|
150
|
+
"keydown"
|
|
151
|
+
];
|
|
152
|
+
const EXECUTION_LOG_FRAMEWORK_ID_PATTERN = "/^(?:web|runtime|serializer|router|core|analyzer):/";
|
|
153
|
+
function normalizeExecutionLogMode(mode) {
|
|
154
|
+
return mode ?? "auto";
|
|
155
|
+
}
|
|
156
|
+
function injectExecutionLogModuleHook(source, moduleId, mode) {
|
|
157
|
+
if (normalizeExecutionLogMode(mode) === "never") return source;
|
|
158
|
+
return `globalThis.__mxLog?.add(${JSON.stringify(moduleId)});\n${source}`;
|
|
159
|
+
}
|
|
160
|
+
const EXECUTION_LOG_HOOK_LINE = /^globalThis\.__mxLog\?\.add\("(?:[^"\\]|\\.)*"\);\n/;
|
|
161
|
+
function hasExecutionLogModuleHook(source) {
|
|
162
|
+
return EXECUTION_LOG_HOOK_LINE.test(source);
|
|
163
|
+
}
|
|
164
|
+
function requalifyExecutionLogModuleHook(source, moduleId) {
|
|
165
|
+
if (!EXECUTION_LOG_HOOK_LINE.test(source)) return source;
|
|
166
|
+
return source.replace(EXECUTION_LOG_HOOK_LINE, `globalThis.__mxLog?.add(${JSON.stringify(moduleId)});\n`);
|
|
167
|
+
}
|
|
168
|
+
function executionLogVirtualModuleSource(options = {}) {
|
|
169
|
+
if (!options.moduleSizes) return executionLogVirtualModuleSourceWithOwnSize(options, null);
|
|
170
|
+
return executionLogVirtualModuleSourceWithOwnSize(options, executionLogVirtualModuleSourceWithOwnSize(options, 0).length);
|
|
171
|
+
}
|
|
172
|
+
function executionLogVirtualModuleSourceWithOwnSize(options, ownRawSize) {
|
|
173
|
+
const moduleSizes = options.moduleSizes ? Object.fromEntries([...[...options.moduleSizes].map(([id, raw]) => [id, {
|
|
174
|
+
raw,
|
|
175
|
+
estimated: true
|
|
176
|
+
}]), [MARKLESS_EXECUTION_LOG_MODULE_ID, {
|
|
177
|
+
raw: ownRawSize ?? 0,
|
|
178
|
+
estimated: true,
|
|
179
|
+
instrument: true
|
|
180
|
+
}]]) : null;
|
|
181
|
+
return `
|
|
182
|
+
globalThis.__mxLog?.add(${JSON.stringify(MARKLESS_EXECUTION_LOG_MODULE_ID)});
|
|
183
|
+
const marklessDefaultModuleSizes = ${JSON.stringify(moduleSizes)};
|
|
184
|
+
const marklessDefaultAttribution = ${JSON.stringify(options.attribution ?? null)};
|
|
185
|
+
const marklessSizesUrl = ${JSON.stringify(options.sizesUrl ?? null)};
|
|
186
|
+
let marklessSizesPromise;
|
|
187
|
+
let marklessAttribution;
|
|
188
|
+
let marklessInstallPromise;
|
|
189
|
+
let marklessRenderSummaryTurn;
|
|
190
|
+
function modules(log) { return log ? [...log] : []; }
|
|
191
|
+
function sizeRecord(input) { return !input ? undefined : typeof input === 'number' ? { raw: input, estimated: true } : input; }
|
|
192
|
+
function normalizeSizes(input) { if (!input) return undefined; const map = new Map(); for (const id of Object.keys(input)) if (id !== 'attribution' && id !== 'unshipped') map.set(id, sizeRecord(input[id])); return map; }
|
|
193
|
+
async function loadModuleSizes(input) {
|
|
194
|
+
if (input.moduleSizes) return normalizeSizes(input.moduleSizes);
|
|
195
|
+
if (marklessDefaultModuleSizes) { marklessAttribution = marklessDefaultAttribution; return normalizeSizes(marklessDefaultModuleSizes); }
|
|
196
|
+
if (!marklessSizesUrl) return undefined;
|
|
197
|
+
marklessSizesPromise ||= fetch(marklessSizesUrl).then((response) => response.ok ? response.json() : undefined).then((payload) => { marklessAttribution = payload && payload.attribution; return normalizeSizes(payload); }).catch(() => undefined);
|
|
198
|
+
return marklessSizesPromise;
|
|
199
|
+
}
|
|
200
|
+
function symbolParts(id) { const match = /^virtual:markless:symbol:([^:]+):([^:]+)$/.exec(id); if (!match) return null; try { return { source: decodeURIComponent(match[1]), symbolId: decodeURIComponent(match[2]) }; } catch { return null; } }
|
|
201
|
+
function baseSymbolId(id) { if (!id.startsWith('bound:')) return null; const separator = id.indexOf(':', 'bound:'.length); if (separator < 0) return null; try { return decodeURIComponent(id.slice('bound:'.length, separator)); } catch { return null; } }
|
|
202
|
+
function routeFile() { const text = document.querySelector?.('script[type="@markless/core/route"]')?.textContent; if (!text) return null; try { const value = JSON.parse(text).file; return typeof value === 'string' ? value : null; } catch { return null; } }
|
|
203
|
+
// Attribution tables are keyed by the bare path this lookup can name: the
|
|
204
|
+
// document's route file, or — for a routeless page — the build's single root,
|
|
205
|
+
// which is the only page it can be.
|
|
206
|
+
function attributionScopes(tables) { if (!tables) return undefined; const route = routeFile(); if (route) { if (route.includes('..') || route.includes('\\\\')) return undefined; const canonical = route.replace(/^\\/+/, ''); return Object.prototype.hasOwnProperty.call(tables, canonical) ? tables[canonical] : undefined; } const roots = Object.keys(tables); return roots.length === 1 ? tables[roots[0]] : undefined; }
|
|
207
|
+
// A handler renders inside one component but is owned by whichever component
|
|
208
|
+
// created the closure — a prop-passed onClick renders at the child's scope and
|
|
209
|
+
// belongs to the parent. The owner is therefore somewhere on the host's render
|
|
210
|
+
// ancestry, so once the host's OWN scope is one the table names, the chain is
|
|
211
|
+
// walked outward and the first scope holding exactly one candidate wins. Never a
|
|
212
|
+
// guess: a host in a scope the table does not name is refused outright, a scope
|
|
213
|
+
// with zero or several candidates is skipped, and an id nobody owns uniquely
|
|
214
|
+
// stays unmapped.
|
|
215
|
+
function scopeChain(scope) { const chain = []; for (let current = scope; ; ) { chain.push(current); if (!current) return chain; const next = current.replace(/c\\d+:$/, ''); if (next === current) return chain; current = next; } }
|
|
216
|
+
function qualifySymbolIds(ids, hostNodeId, sizes, tables) { const scopes = attributionScopes(tables); if (!scopes) return ids; const host = /^((?:c\\d+:)*)[^:]+$/.exec(hostNodeId); const hostScope = host && host[1]; return ids.map((id) => { const own = /^((?:c\\d+:)+)(.*)$/.exec(id); const scope = own ? own[1] : hostScope; if (scope === null || scope === undefined || !Object.prototype.hasOwnProperty.call(scopes, scope)) return id; const local = own ? own[2] : id; const base = baseSymbolId(local); const sizeSymbolId = base || local; if (!sizeSymbolId.startsWith('symbol:')) return id; for (const candidate of scopeChain(scope)) { if (!Object.prototype.hasOwnProperty.call(scopes, candidate)) continue; const matches = [...(sizes?.keys() || [])].filter((key) => { const match = /^virtual:markless:symbol:([^:]+):([^:]+)$/.exec(key); if (!match || match[1] !== scopes[candidate]) return false; try { return decodeURIComponent(match[2]) === sizeSymbolId; } catch { return false; } }); if (matches.length !== 1) continue; if (!base) return matches[0]; const match = /^virtual:markless:symbol:([^:]+):/.exec(matches[0]); return match ? 'virtual:markless:symbol:' + match[1] + ':' + encodeURIComponent(local) : id; } return id; }); }
|
|
217
|
+
function canonicalId(id, sizes) {
|
|
218
|
+
if (!sizes || sizes.has(id)) return id;
|
|
219
|
+
const qualified = symbolParts(id); const local = qualified ? qualified.symbolId : id.replace(/^(?:c\\d+:)+/, ''); const sizeSymbolId = baseSymbolId(local) || local;
|
|
220
|
+
if (!sizeSymbolId.startsWith('symbol:')) return id;
|
|
221
|
+
const matches = [...sizes.keys()].filter((key) => { const parts = symbolParts(key); return !!parts && parts.symbolId === sizeSymbolId && (!qualified || parts.source === qualified.source); });
|
|
222
|
+
return matches.length === 1 ? matches[0] : id;
|
|
223
|
+
}
|
|
224
|
+
function displayId(id) { const parts = symbolParts(id); return parts ? parts.symbolId + ' (' + (parts.source.split('/').pop() || parts.source) + ')' : id; }
|
|
225
|
+
function displayObservedId(id, sizes) { const parts = symbolParts(id); return displayId(baseSymbolId(parts ? parts.symbolId : id) ? id : canonicalId(id, sizes)); }
|
|
226
|
+
function accounting(items, sizes) {
|
|
227
|
+
const ids = [...new Set(items.map((id) => canonicalId(id, sizes)))];
|
|
228
|
+
if (!sizes) return { appBytes: ids.length ? null : 0, instrumentBytes: 0, appModules: ids.length, instrumentModules: 0, estimated: { app: false, instrument: false }, unmappedIds: [] };
|
|
229
|
+
let appBytes = 0, instrumentBytes = 0, appModules = 0, instrumentModules = 0, appEstimated = false, instrumentEstimated = false; const unmappedIds = [];
|
|
230
|
+
for (const id of ids) { const record = sizes.get(id); if (!record) { appModules++; unmappedIds.push(id); continue; } const bytes = record.raw; instrumentBytes += record.instrumentRaw || 0; if (record.instrument) { instrumentModules++; instrumentBytes += bytes; instrumentEstimated ||= !!record.estimated; } else { appModules++; appBytes += bytes; appEstimated ||= !!record.estimated; } }
|
|
231
|
+
return { appBytes: unmappedIds.length ? null : appBytes, instrumentBytes, appModules, instrumentModules, estimated: { app: appEstimated, instrument: instrumentEstimated }, unmappedIds };
|
|
232
|
+
}
|
|
233
|
+
function rowKb(items, sizes) { const a = accounting(items, sizes); if (a.unmappedIds.length) return 'bytes unknown'; const total = (a.appBytes || 0) + (a.instrumentBytes || 0); return (total / 1024).toFixed(1) + ' KB' + (a.estimated.app || a.estimated.instrument ? ' est. source' : ''); }
|
|
234
|
+
// One cumulative ledger owns every number the console prints. A module's charge is its own share
|
|
235
|
+
// of its chunk's raw bytes when the build map carries one, else the whole chunk; unit says which.
|
|
236
|
+
function marklessLedger() { return (globalThis.${MARKLESS_EXECUTION_LEDGER_GLOBAL} ||= ${MARKLESS_EXECUTION_LEDGER_INIT}); }
|
|
237
|
+
function ledgerCategory(id, record) { return record && record.instrument ? 'instrument' : ${EXECUTION_LOG_FRAMEWORK_ID_PATTERN}.test(id) ? 'framework' : 'app'; }
|
|
238
|
+
function ledgerCharge(l, ids, sizes) {
|
|
239
|
+
const seen = (l.chargedIds ||= new Set());
|
|
240
|
+
const seenChunks = (l.chargedChunks ||= new Set());
|
|
241
|
+
const delta = { app: 0, framework: 0, instrument: 0, gzip: 0, modules: [], unmapped: [] };
|
|
242
|
+
const charge = (raw, viaAlias) => {
|
|
243
|
+
const id = canonicalId(raw, sizes);
|
|
244
|
+
const record = sizes && sizes.get(id);
|
|
245
|
+
// A name several modules answer to charges each of them, once.
|
|
246
|
+
if (record && record.alias) { if (!viaAlias) for (const member of record.alias) charge(member, true); return; }
|
|
247
|
+
// The dedupe unit is the unit charged: the module when the map splits its chunk, else the
|
|
248
|
+
// chunk, so one chunk reached under two names is one charge. Dev estimates dedupe by id.
|
|
249
|
+
const chunk = record && !record.module && record.chunk;
|
|
250
|
+
if (chunk ? seenChunks.has(chunk) : seen.has(id)) return;
|
|
251
|
+
if (chunk) seenChunks.add(chunk); else seen.add(id);
|
|
252
|
+
if (!record) { delta.unmapped.push(id); return; }
|
|
253
|
+
if (record.estimated) l.unit = 'estimated-source-bytes'; else if (record.module && l.unit !== 'estimated-source-bytes') l.unit = 'module-raw-bytes';
|
|
254
|
+
delta[ledgerCategory(id, record)] += record.raw; delta.instrument += record.instrumentRaw || 0; delta.gzip += record.gzip || 0; delta.modules.push(id);
|
|
255
|
+
};
|
|
256
|
+
for (const raw of ids) charge(raw, false);
|
|
257
|
+
if (delta.unmapped.length) l.incomplete = { reason: 'unmapped-id', ids: [...new Set([...(l.incomplete ? l.incomplete.ids : []), ...delta.unmapped])] };
|
|
258
|
+
return delta;
|
|
259
|
+
}
|
|
260
|
+
// Load is everything charged before the FIRST USER GESTURE, which the activation
|
|
261
|
+
// script latches on the gesture itself (loadClosed) rather than on the kind of
|
|
262
|
+
// whichever turn reports first. On an SSR page nothing runs until the click, and
|
|
263
|
+
// the instrument and the wake entry both arrive with it; their render/resume
|
|
264
|
+
// turns are work that gesture caused, so their charge joins the gesture's turn —
|
|
265
|
+
// merged into it if it already exists, carried until it does if it does not.
|
|
266
|
+
function ledgerApply(l, kind, label, delta) {
|
|
267
|
+
const gesture = kind !== 'render' && kind !== 'resume';
|
|
268
|
+
if (gesture) l.loadClosed = true;
|
|
269
|
+
for (const name of ['app', 'framework', 'instrument']) { l.total[name] += delta[name]; if (!l.loadClosed) l.load[name] += delta[name]; }
|
|
270
|
+
l.totalGzip = (l.totalGzip || 0) + delta.gzip;
|
|
271
|
+
if (!l.loadClosed) { l.loadGzip = (l.loadGzip || 0) + delta.gzip; for (const id of delta.modules) l.load.modules.push(id); }
|
|
272
|
+
if (!gesture && l.loadClosed) {
|
|
273
|
+
const open = l.open;
|
|
274
|
+
if (open) { open.delta.app += delta.app; open.delta.framework += delta.framework; open.delta.instrument += delta.instrument; for (const id of delta.modules) open.modules.push(id); return open; }
|
|
275
|
+
const held = (l.carry ||= { app: 0, framework: 0, instrument: 0, modules: [] });
|
|
276
|
+
held.app += delta.app; held.framework += delta.framework; held.instrument += delta.instrument; for (const id of delta.modules) held.modules.push(id);
|
|
277
|
+
delta = { app: 0, framework: 0, instrument: 0, modules: [] };
|
|
278
|
+
}
|
|
279
|
+
const carry = gesture ? l.carry : undefined;
|
|
280
|
+
if (carry) l.carry = undefined;
|
|
281
|
+
const turn = { kind, label, delta: { app: delta.app + (carry ? carry.app : 0), framework: delta.framework + (carry ? carry.framework : 0), instrument: delta.instrument + (carry ? carry.instrument : 0) }, modules: carry ? [...carry.modules, ...delta.modules] : delta.modules };
|
|
282
|
+
l.turns.push(turn); if (gesture) l.open = turn;
|
|
283
|
+
return turn;
|
|
284
|
+
}
|
|
285
|
+
function ledgerKb(bytes) { return (bytes / 1024).toFixed(1) + ' KB'; }
|
|
286
|
+
// The instrument is dev-only weight, so it stays out of the headline totals and
|
|
287
|
+
// keeps its own category, exactly as the pre-ledger lines did.
|
|
288
|
+
function ledgerVisible(part) { return part.app + part.framework + part.inline; }
|
|
289
|
+
function ledgerLine(l, turn) {
|
|
290
|
+
let line = 'markless: ' + ledgerKb(ledgerVisible(l.load)) + ' executed at load';
|
|
291
|
+
if (turn && turn.kind !== 'render' && turn.kind !== 'resume') line += ' · this ' + turn.kind + ' +' + ledgerKb(turn.delta.app + turn.delta.framework);
|
|
292
|
+
line += ' · total ' + ledgerKb(ledgerVisible(l.total));
|
|
293
|
+
if (l.totalGzip) line += ' (gzip ' + ledgerKb(l.totalGzip) + ')';
|
|
294
|
+
if (l.incomplete) line += ' · ' + l.incomplete.ids.length + ' unmapped (excluded)';
|
|
295
|
+
return line;
|
|
296
|
+
}
|
|
297
|
+
function ledgerMirror(l, turn, text, attribute) {
|
|
298
|
+
const root = document.documentElement; if (!root) return;
|
|
299
|
+
root.setAttribute(attribute, text);
|
|
300
|
+
root.setAttribute('data-markless-log-unit', l.unit);
|
|
301
|
+
root.setAttribute('data-markless-log-load-bytes', String(ledgerVisible(l.load)));
|
|
302
|
+
root.setAttribute('data-markless-log-total-bytes', String(ledgerVisible(l.total)));
|
|
303
|
+
root.setAttribute('data-markless-log-inline-bytes', String(l.total.inline));
|
|
304
|
+
if (turn) {
|
|
305
|
+
root.setAttribute('data-markless-log-app-bytes', String(turn.delta.app));
|
|
306
|
+
root.setAttribute('data-markless-log-framework-bytes', String(turn.delta.framework));
|
|
307
|
+
root.setAttribute('data-markless-log-instrument-bytes', String(turn.delta.instrument));
|
|
308
|
+
root.setAttribute('data-markless-log-turn-bytes', String(turn.delta.app + turn.delta.framework));
|
|
309
|
+
root.setAttribute('data-markless-log-turn-modules', turn.modules.join(' '));
|
|
310
|
+
// The headline carries no selector, so the turn's identity lives here.
|
|
311
|
+
root.setAttribute('data-markless-log-turn-label', turn.kind + (turn.label ? ' ' + turn.label : ''));
|
|
312
|
+
}
|
|
313
|
+
if (l.incomplete) root.setAttribute('data-markless-log-incomplete', String(l.incomplete.ids.length)); else root.removeAttribute('data-markless-log-incomplete');
|
|
314
|
+
}
|
|
315
|
+
function warmIds(event) { return [...new Set([event.dispatchModuleId, ...((event.eventRecord && event.eventRecord.symbolIds) || [])].filter(Boolean))]; }
|
|
316
|
+
function causeRows(input) {
|
|
317
|
+
const before = input.before || new Set(); const after = input.after || new Set(); const woken = [...after].filter((id) => !before.has(id)); const record = input.eventRecord;
|
|
318
|
+
const cause = record ? input.eventName + ' matched event record ' + record.hostNodeId : input.eventName + ' matched runtime records';
|
|
319
|
+
const label = (id) => displayObservedId(id, input.moduleSizes) + (input.moduleSizes ? ' (' + rowKb([id], input.moduleSizes) + (input.moduleSizes.get(canonicalId(id, input.moduleSizes))?.instrument ? ' instrument' : '') + ')' : '');
|
|
320
|
+
const rows = woken.map((id) => 'woke ' + label(id) + ' <- ' + cause);
|
|
321
|
+
if (record) for (const id of warmIds(input)) rows.push('ran warm ' + label(id) + ' <- ' + cause);
|
|
322
|
+
if (record && !(input.view?.behaviors || []).some((b) => b.hostNodeId === record.hostNodeId)) rows.push('skip behavior — no matching record touched');
|
|
323
|
+
return rows;
|
|
324
|
+
}
|
|
325
|
+
function mirrorInteraction(l, turn, text) {
|
|
326
|
+
const root = document.documentElement; if (!root) return;
|
|
327
|
+
root.setAttribute('data-markless-log-interactions', String(Number(root.getAttribute('data-markless-log-interactions') || '0') + 1));
|
|
328
|
+
ledgerMirror(l, turn, text, 'data-markless-log-last');
|
|
329
|
+
}
|
|
330
|
+
export async function installMarklessExecutionLog(input = {}) {
|
|
331
|
+
const log = globalThis.__mxLog; if (!log || globalThis.__mxLogInteraction) return;
|
|
332
|
+
if (marklessInstallPromise) return marklessInstallPromise;
|
|
333
|
+
marklessInstallPromise = (async () => {
|
|
334
|
+
const moduleSizes = await loadModuleSizes(input); const current = modules(log);
|
|
335
|
+
if (input.printResumeSummary !== false) {
|
|
336
|
+
const l = marklessLedger(); const turn = ledgerApply(l, 'resume', undefined, ledgerCharge(l, current, moduleSizes)); const summary = ledgerLine(l, turn);
|
|
337
|
+
// This summary publishes into the ledger and mirrors; it never prints. The
|
|
338
|
+
// ledger's turn line is the one printed line, and a resume that lands
|
|
339
|
+
// beside a render turn would otherwise print the same load figure twice.
|
|
340
|
+
// A turn merged into an open gesture belongs to that gesture, so it mirrors
|
|
341
|
+
// onto the interaction attribute instead of the load one.
|
|
342
|
+
ledgerMirror(l, turn, summary, turn.kind === 'resume' ? 'data-markless-log-summary' : 'data-markless-log-last');
|
|
343
|
+
}
|
|
344
|
+
globalThis.__mxLogInteraction = (event) => {
|
|
345
|
+
const capturedAfter = modules(event.after || log);
|
|
346
|
+
const symbolIds = qualifySymbolIds((event.eventRecord && event.eventRecord.symbolIds) || [], event.eventRecord && event.eventRecord.hostNodeId, moduleSizes, marklessAttribution);
|
|
347
|
+
if (event.eventRecord) event = { ...event, eventRecord: { ...event.eventRecord, symbolIds } };
|
|
348
|
+
const after = capturedAfter; const before = event.before || new Set(); if (log.has(${JSON.stringify(MARKLESS_EXECUTION_LOG_MODULE_ID)}) && !after.includes(${JSON.stringify(MARKLESS_EXECUTION_LOG_MODULE_ID)})) after.push(${JSON.stringify(MARKLESS_EXECUTION_LOG_MODULE_ID)}); const woken = after.filter((id) => !before.has(id)); const warm = warmIds(event);
|
|
349
|
+
const l = marklessLedger(); const label = '[' + (event.selector || 'event target') + ']';
|
|
350
|
+
if (event.noMatch) {
|
|
351
|
+
const turn = ledgerApply(l, event.eventName, label, ledgerCharge(l, [], moduleSizes)); const line = ledgerLine(l, turn) + ' · no event record matched ' + label;
|
|
352
|
+
console.info(line); mirrorInteraction(l, turn, line); return;
|
|
353
|
+
}
|
|
354
|
+
const turn = ledgerApply(l, event.eventName, label, ledgerCharge(l, [...woken, ...warm], moduleSizes)); const header = ledgerLine(l, turn);
|
|
355
|
+
console.groupCollapsed(header);
|
|
356
|
+
console.log(event.eventName + ' ' + label + ' · woke ' + woken.length + ' modules · ran warm ' + warm.length + ' modules');
|
|
357
|
+
for (const row of causeRows({ ...event, after: new Set(after), moduleSizes })) console.log(row);
|
|
358
|
+
console.groupEnd(); mirrorInteraction(l, turn, header);
|
|
359
|
+
};
|
|
360
|
+
log.__marklessInstalled = true;
|
|
361
|
+
})();
|
|
362
|
+
return marklessInstallPromise;
|
|
363
|
+
}
|
|
364
|
+
export async function logMarklessInteraction(event) { await installMarklessExecutionLog({ printResumeSummary: false }); globalThis.__mxLogInteraction?.(event); }
|
|
365
|
+
// The inline resumer wakes the runtime with no event (self-wake at load, overlay
|
|
366
|
+
// primer on pointerdown). That is a resume, not a gesture: counting it would
|
|
367
|
+
// close load early and charge one user click as two interactions.
|
|
368
|
+
export async function logMarklessSpecializedInteraction(input, before) {
|
|
369
|
+
if (!input.event) return logMarklessSpecializedWake();
|
|
370
|
+
const target = input.element ?? input.event.target;
|
|
371
|
+
const tag = typeof target?.tagName === 'string' ? target.tagName.toLowerCase() : 'element';
|
|
372
|
+
await logMarklessInteraction({ eventName: input.event.type ?? 'event', eventRecord: input.eventRecord, before, selector: tag + (target?.id ? '#' + target.id : ''), after: new Set(globalThis.__mxLog) });
|
|
373
|
+
}
|
|
374
|
+
async function logMarklessSpecializedWake() {
|
|
375
|
+
await installMarklessExecutionLog({ printResumeSummary: false });
|
|
376
|
+
const log = globalThis.__mxLog; if (!log) return;
|
|
377
|
+
const moduleSizes = await loadModuleSizes({}); const l = marklessLedger();
|
|
378
|
+
const turn = ledgerApply(l, 'resume', undefined, ledgerCharge(l, modules(log), moduleSizes));
|
|
379
|
+
ledgerMirror(l, turn, ledgerLine(l, turn), turn.kind === 'resume' ? 'data-markless-log-summary' : 'data-markless-log-last');
|
|
380
|
+
}
|
|
381
|
+
function marklessRenderSummaryTurnEnd() {
|
|
382
|
+
// Each injected entry module calls in from its own dynamic-import
|
|
383
|
+
// resolution, so a microtask window closes between callers of one render;
|
|
384
|
+
// a frame holds them all. The timeout is the backstop for a document that
|
|
385
|
+
// never paints, and whichever lands first ends the turn.
|
|
386
|
+
if (typeof requestAnimationFrame !== 'function') return Promise.resolve();
|
|
387
|
+
return new Promise((resolve) => { let ended; const end = () => { if (ended) return; ended = true; resolve(); }; requestAnimationFrame(end); setTimeout(end, 50); });
|
|
388
|
+
}
|
|
389
|
+
// Only the console line coalesces: the attribute mirror keeps its per-call
|
|
390
|
+
// last-wins timing, which the interaction mirror interleaves with.
|
|
391
|
+
function coalesceMarklessRenderSummaryLine(summary) {
|
|
392
|
+
if (marklessRenderSummaryTurn) { marklessRenderSummaryTurn.summary = summary; return marklessRenderSummaryTurn.printed; }
|
|
393
|
+
const turn = { summary };
|
|
394
|
+
turn.printed = marklessRenderSummaryTurnEnd().then(() => { marklessRenderSummaryTurn = undefined; console.log(turn.summary); });
|
|
395
|
+
marklessRenderSummaryTurn = turn;
|
|
396
|
+
return turn.printed;
|
|
397
|
+
}
|
|
398
|
+
export async function logMarklessRenderSummary(input = {}) {
|
|
399
|
+
const log = globalThis.__mxLog; if (!log) return;
|
|
400
|
+
const moduleSizes = await loadModuleSizes(input); const l = marklessLedger();
|
|
401
|
+
const turn = ledgerApply(l, 'render', undefined, ledgerCharge(l, modules(log), moduleSizes));
|
|
402
|
+
const summary = ledgerLine(l, turn);
|
|
403
|
+
if (turn.kind !== 'render') { ledgerMirror(l, turn, summary, 'data-markless-log-last'); return; }
|
|
404
|
+
ledgerMirror(l, turn, summary, 'data-markless-log-summary');
|
|
405
|
+
return coalesceMarklessRenderSummaryLine(summary);
|
|
406
|
+
}
|
|
407
|
+
`;
|
|
408
|
+
}
|
|
409
|
+
function executionLogActivationInjection(mode) {
|
|
410
|
+
if (normalizeExecutionLogMode(mode) === "never") return null;
|
|
411
|
+
return {
|
|
412
|
+
tag: "script",
|
|
413
|
+
location: "head",
|
|
414
|
+
children: `(() => { const l = location; if (${mode === "always" ? "true" : "/^https?:\\/\\/(?:localhost|127\\.0\\.0\\.1|\\[::1\\])(?::|$)/.test(l.origin) || new URLSearchParams(l.search).has('markless-log') || (() => { try { return localStorage.getItem('marklessLog') === '1'; } catch { return false; } })()"}) { globalThis.__mxLog = globalThis.__mxLog || new Set(); const g = globalThis.${MARKLESS_EXECUTION_LEDGER_GLOBAL} = globalThis.${MARKLESS_EXECUTION_LEDGER_GLOBAL} || ${MARKLESS_EXECUTION_LEDGER_INIT}; const s = document.currentScript && document.currentScript.textContent; if (s) { g.total.inline += s.length; if (!g.loadClosed) g.load.inline += s.length; } const c = (e) => { if (e.isTrusted) g.loadClosed = true; }; for (const t of ${JSON.stringify(EXECUTION_LOG_GESTURE_EVENTS)}) addEventListener(t, c, { capture: true, passive: true }); } })();`
|
|
415
|
+
};
|
|
416
|
+
}
|
|
417
|
+
//#endregion
|
|
418
|
+
//#region src/scalar-plan-loader.ts
|
|
419
|
+
function emitScalarPlanLoader(actions, routes) {
|
|
420
|
+
const ordered = routes.filter((route) => route.prefix.length > 0).toSorted((left, right) => right.prefix.length - left.prefix.length);
|
|
421
|
+
const scalarActions = actions.filter((action) => action.recordKind === "event" && action.plan?.kind === "scalar");
|
|
422
|
+
if (scalarActions.length === 0 && ordered.length === 0) return "";
|
|
423
|
+
return [
|
|
424
|
+
"export function loadScalarActionPlan(symbolId) {",
|
|
425
|
+
...scalarActions.map((action) => `\tif (symbolId === ${JSON.stringify(action.plan.symbolId)}) return ${JSON.stringify({
|
|
426
|
+
hostNodeId: action.hostNodeId,
|
|
427
|
+
eventName: action.eventName,
|
|
428
|
+
scope: "",
|
|
429
|
+
plan: action.plan
|
|
430
|
+
})};`),
|
|
431
|
+
...ordered.flatMap((route) => [
|
|
432
|
+
`\tif (symbolId.startsWith(${JSON.stringify(route.prefix)})) {`,
|
|
433
|
+
"importSource" in route ? `\t\treturn import(${JSON.stringify(route.importSource)}).then(module => module.loadScalarActionPlan?.(symbolId.slice(${route.prefix.length}))).then(action => action && ({ ...action, scope: ${JSON.stringify(route.prefix)} + action.scope }));` : `\t\treturn Promise.resolve(loadScalarActionPlan(symbolId.slice(${route.prefix.length}))).then(action => action && ({ ...action, scope: ${JSON.stringify(route.prefix)} + action.scope }));`,
|
|
434
|
+
" }"
|
|
435
|
+
]),
|
|
436
|
+
"}"
|
|
437
|
+
].join("\n");
|
|
438
|
+
}
|
|
439
|
+
//#endregion
|
|
440
|
+
//#region src/scalar-plan-source.ts
|
|
441
|
+
const MARKLESS_SCALAR_PLAN_SOURCE_QUERY = "markless-scalar-plans";
|
|
442
|
+
function scalarPlanSourceReference(source) {
|
|
443
|
+
return withQuery(source, {
|
|
444
|
+
"markless-symbols": null,
|
|
445
|
+
[MARKLESS_SCALAR_PLAN_SOURCE_QUERY]: null
|
|
446
|
+
});
|
|
447
|
+
}
|
|
448
|
+
//#endregion
|
|
449
|
+
//#region src/rebound-symbol-route.ts
|
|
450
|
+
function emitReboundSymbolRoute(route, reboundName, localLoader, unboundLoad, indent) {
|
|
451
|
+
if (!hasBoundSymbols(route)) return [];
|
|
452
|
+
const rest = `symbolId.slice(${route.prefix.length})`;
|
|
453
|
+
return [`${indent}const bound = ${JSON.stringify(route.boundSymbols)};`, `${indent}if (Object.hasOwn(bound, ${rest})) return ${reboundName}(${localLoader}(bound[${rest}]), ${JSON.stringify(route.prefix)}, () => ${unboundLoad});`];
|
|
454
|
+
}
|
|
455
|
+
function emitReboundSymbolImport(routes, reboundName) {
|
|
456
|
+
return routes.some(hasBoundSymbols) ? `import { marklessReboundSymbol as ${reboundName} } from '@markless/web/fns/rebound-symbol';` : "";
|
|
457
|
+
}
|
|
458
|
+
function hasBoundSymbols(route) {
|
|
459
|
+
return route.boundSymbols !== void 0 && Object.keys(route.boundSymbols).length > 0;
|
|
460
|
+
}
|
|
461
|
+
//#endregion
|
|
462
|
+
//#region src/source-module.ts
|
|
463
|
+
const MARKLESS_VIRTUAL_PREFIX = "virtual:markless:";
|
|
464
|
+
const MARKLESS_BOUND_SYMBOLS_EXPORT = "marklessBoundSymbols";
|
|
465
|
+
const RESUME_ENTRY_SPECIFIER = "@markless/core/web/resume";
|
|
466
|
+
const STORAGE_FREE_RESUME_ENTRY_SPECIFIER = "@markless/core/web/resume-storage-free";
|
|
467
|
+
const SYMBOL_VIRTUAL_PREFIX = `${MARKLESS_VIRTUAL_PREFIX}symbol:`;
|
|
468
|
+
function symbolVirtualModuleId(filename, symbolId) {
|
|
469
|
+
return `${SYMBOL_VIRTUAL_PREFIX}${encodeURIComponent(filename)}:${encodeURIComponent(symbolId)}`;
|
|
470
|
+
}
|
|
471
|
+
function symbolEntryVirtualModuleId(filename, symbolId) {
|
|
472
|
+
return `${symbolVirtualModuleId(filename, symbolId)}:entry`;
|
|
473
|
+
}
|
|
474
|
+
function encodedSymbolSource(filename) {
|
|
475
|
+
return encodeURIComponent(filename);
|
|
476
|
+
}
|
|
477
|
+
function symbolVirtualModuleSourceFile(moduleId) {
|
|
478
|
+
const bare = moduleId.startsWith("\0") ? moduleId.slice(1) : moduleId;
|
|
479
|
+
if (!bare.startsWith(SYMBOL_VIRTUAL_PREFIX)) return null;
|
|
480
|
+
const encoded = bare.slice(SYMBOL_VIRTUAL_PREFIX.length);
|
|
481
|
+
const separator = encoded.indexOf(":");
|
|
482
|
+
if (separator <= 0 || separator === encoded.length - 1) return null;
|
|
483
|
+
if (encoded.includes(":", separator + 1)) return null;
|
|
484
|
+
try {
|
|
485
|
+
return decodeURIComponent(encoded.slice(0, separator));
|
|
486
|
+
} catch {
|
|
487
|
+
return null;
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
function resumeVirtualModuleId(filename) {
|
|
491
|
+
return `${MARKLESS_VIRTUAL_PREFIX}resume:${encodeURIComponent(filename)}`;
|
|
492
|
+
}
|
|
493
|
+
function prerenderWakeVirtualModuleId(filename) {
|
|
494
|
+
return `${MARKLESS_VIRTUAL_PREFIX}prerender-wake:${encodeURIComponent(filename)}`;
|
|
495
|
+
}
|
|
496
|
+
function settleVirtualModuleId(filename) {
|
|
497
|
+
return `${MARKLESS_VIRTUAL_PREFIX}settle:${encodeURIComponent(filename)}`;
|
|
498
|
+
}
|
|
499
|
+
/**
|
|
500
|
+
* The settle module: import targets and data, no logic. It carries the DOM
|
|
501
|
+
* filler, each boundary's async runner keyed by graph node, and each bound
|
|
502
|
+
* symbol's compiled derive function keyed by the id the fill plan references.
|
|
503
|
+
* Rolldown resolves the real chunk urls, so nothing here is a url the document
|
|
504
|
+
* has to carry, and a page that never settles an arm never loads it.
|
|
505
|
+
*/
|
|
506
|
+
function emitSettleModule(input) {
|
|
507
|
+
const imports = [...input.runners, ...input.derives].map((entry) => entry.symbol);
|
|
508
|
+
const seen = /* @__PURE__ */ new Map();
|
|
509
|
+
const lines = ["export { marklessApplySettlePlan as f } from '@markless/web/fns/settle-plan';"];
|
|
510
|
+
for (const symbol of imports) {
|
|
511
|
+
if (seen.has(symbol.id)) continue;
|
|
512
|
+
seen.set(symbol.id, symbol.exportName);
|
|
513
|
+
lines.push(`import { ${symbol.exportName} } from ${JSON.stringify(symbol.chunk)};`);
|
|
514
|
+
}
|
|
515
|
+
const entries = (rows) => `{${rows.map((row) => `${JSON.stringify(row.key)}: ${seen.get(row.symbolId)}`).join(",")}}`;
|
|
516
|
+
lines.push(`export const r = ${entries(input.runners.map((entry) => ({
|
|
517
|
+
key: entry.node,
|
|
518
|
+
symbolId: entry.symbol.id
|
|
519
|
+
})))};`);
|
|
520
|
+
lines.push(`export const d = ${entries(input.derives.map((entry) => ({
|
|
521
|
+
key: entry.id,
|
|
522
|
+
symbolId: entry.symbol.id
|
|
523
|
+
})))};`);
|
|
524
|
+
return `${lines.join("\n")}\n`;
|
|
525
|
+
}
|
|
526
|
+
function prerenderWakeVirtualModuleSourceFile(moduleId) {
|
|
527
|
+
const bare = moduleId.startsWith("\0") ? moduleId.slice(1) : moduleId;
|
|
528
|
+
const prefix = `${MARKLESS_VIRTUAL_PREFIX}prerender-wake:`;
|
|
529
|
+
if (!bare.startsWith(prefix)) return null;
|
|
530
|
+
try {
|
|
531
|
+
return decodeURIComponent(bare.slice(prefix.length));
|
|
532
|
+
} catch {
|
|
533
|
+
return null;
|
|
534
|
+
}
|
|
535
|
+
}
|
|
536
|
+
function scopedSymbolExportName(moduleId, exportName) {
|
|
537
|
+
return `${exportName}_${stringHash(moduleId)}`;
|
|
538
|
+
}
|
|
539
|
+
function rewriteSymbolModuleExport(source, fromExportName, toExportName) {
|
|
540
|
+
return source.replace(`export function ${fromExportName}`, `export function ${toExportName}`).replace(`export async function ${fromExportName}`, `export async function ${toExportName}`);
|
|
541
|
+
}
|
|
542
|
+
function payloadModule(payload) {
|
|
543
|
+
return [
|
|
544
|
+
`export const state = ${JSON.stringify(payload.state, null, " ")};`,
|
|
545
|
+
`export const runtimeDemandMap = ${JSON.stringify(withoutFirstUse(payload.runtimeDemandMap), null, " ")};`,
|
|
546
|
+
`export const view = ${JSON.stringify(payload.view, null, " ")};`,
|
|
547
|
+
""
|
|
548
|
+
].join("\n");
|
|
549
|
+
}
|
|
550
|
+
function withoutFirstUse(runtimeDemandMap) {
|
|
551
|
+
const map = runtimeDemandMap;
|
|
552
|
+
if (!map?.actions?.some((action) => "firstUse" in action) && !(map && ("firstUsePage" in map || "armActions" in map))) return runtimeDemandMap;
|
|
553
|
+
const { firstUsePage: _firstUsePage, armActions: _armActions, ...rest } = map;
|
|
554
|
+
return {
|
|
555
|
+
...rest,
|
|
556
|
+
...map.actions ? { actions: map.actions.map((action) => {
|
|
557
|
+
const { firstUse: _firstUse, ...fields } = action;
|
|
558
|
+
return fields;
|
|
559
|
+
}) } : {}
|
|
560
|
+
};
|
|
561
|
+
}
|
|
562
|
+
/** The module-local name a direct-CSR module's root renders under. */
|
|
563
|
+
function csrRootLocalName(rootExportName) {
|
|
564
|
+
return `marklessCsr${rootExportName}`;
|
|
565
|
+
}
|
|
566
|
+
function emitPublicRenderModule(source, input) {
|
|
567
|
+
if (!input.rootExportName) return source;
|
|
568
|
+
const declaration = `export function ${input.rootExportName}()`;
|
|
569
|
+
if (!source.includes(declaration)) throw new Error(`MARKLESS_CSR_ROOT_DECLARATION_MISSING: ${input.rootExportName}`);
|
|
570
|
+
const localName = csrRootLocalName(input.rootExportName);
|
|
571
|
+
if (!input.executionLogEnabled) return source.replace(declaration, `function ${localName}()`);
|
|
572
|
+
const implementationName = `marklessRender${input.rootExportName}`;
|
|
573
|
+
return [
|
|
574
|
+
source.replace(declaration, `function ${implementationName}()`),
|
|
575
|
+
`function ${localName}() {`,
|
|
576
|
+
`\tconst output = ${implementationName}();`,
|
|
577
|
+
" if (globalThis.__mxLog) globalThis.__mxLoadLog().then(log => log.logMarklessRenderSummary());",
|
|
578
|
+
" return output;",
|
|
579
|
+
"}"
|
|
580
|
+
].join("\n");
|
|
581
|
+
}
|
|
582
|
+
function emitSourceModule(input) {
|
|
583
|
+
if (input.directCsr && input.prerenderRecords) input = {
|
|
584
|
+
...input,
|
|
585
|
+
prerenderRecords: false
|
|
586
|
+
};
|
|
587
|
+
const symbolsOnly = input.environment === "client" && input.clientOutput === "symbols-only";
|
|
588
|
+
const routeSymbols = input.environment === "client" && input.symbolRoutes.length > 0;
|
|
589
|
+
const canonicalRenderData = input.environment === "client" && !symbolsOnly && !input.prerenderRecords && input.canonicalRenderData === true && input.publicRenderRootExportName === null && !!input.publicSsrModuleSource && !!input.renderDataId;
|
|
590
|
+
const emitsSsrBody = !!input.publicSsrModuleSource && !(input.environment === "client" && (symbolsOnly || !input.prerenderRecords && !input.dev));
|
|
591
|
+
const csrRootName = input.environment !== "server" && !symbolsOnly && !input.prerenderRecords && input.publicRenderRootExportName ? csrRootLocalName(input.publicRenderRootExportName) : null;
|
|
592
|
+
return [
|
|
593
|
+
!input.publicSsrModuleSource && !input.publicRenderModuleSource || symbolsOnly || !input.renderDataId || input.environment === "client" && !input.publicRenderModuleSource && !input.prerenderRecords && !input.dev && !canonicalRenderData ? "" : canonicalRenderData ? emitsSsrBody ? `import { marklessPrerenderData, marklessRenderData } from '${input.renderDataId}';` : `import { marklessPrerenderData } from '${input.renderDataId}';` : `import { marklessRenderData } from '${input.renderDataId}';`,
|
|
594
|
+
symbolsOnly ? "" : `import { state as payloadState, view as payloadView, runtimeDemandMap as payloadRuntimeDemandMap } from '${input.payloadId}';`,
|
|
595
|
+
input.environment === "client" && input.directCsr === true && input.hasComputedState === true ? ["import { installMarklessDerivedReconcile } from '@markless/web/fns/reconcile-plane';", "installMarklessDerivedReconcile();"].join("\n") : null,
|
|
596
|
+
input.environment === "client" && input.hasComputedState === true ? emitRosterResumeLoaderInstall() : null,
|
|
597
|
+
input.environment === "client" && input.hasOverlayMarks === true ? emitOverlayLoaderInstall() : null,
|
|
598
|
+
input.environment === "client" && input.hasIdleEvents === true && !symbolsOnly ? emitIdleTriggerInstall() : null,
|
|
599
|
+
input.environment === "client" ? clientRowMintLoader(input) : null,
|
|
600
|
+
"",
|
|
601
|
+
emitLoadSymbol(input),
|
|
602
|
+
symbolsOnly && input.scalarActions ? emitScalarPlanLoader(input.scalarActions, input.symbolRoutes.map((route) => "importSource" in route ? {
|
|
603
|
+
...route,
|
|
604
|
+
importSource: scalarPlanSourceReference(route.importSource)
|
|
605
|
+
} : route)) : "",
|
|
606
|
+
symbolsOnly && input.closureActions && emitsClosurePlanLoader(input.closureActions, input.closurePlanRoutes ?? []) ? `${emitClosurePlanRoute(input.closurePlanRoutes ?? [], "loadClosureActionPlan", input.closureActions)}\nexport { loadClosureActionPlan };` : null,
|
|
607
|
+
input.behaviorSymbols?.length ? emitDirectSourceSymbolLoader(input.behaviorSymbols, "readMarklessBehaviorSourceSymbol").replace("function loadSymbol(", "function loadBehaviorSymbol(") : "",
|
|
608
|
+
input.environment === "client" && input.executionLog !== "never" ? emitExecutionLogLoader() : "",
|
|
609
|
+
routeSymbols ? "const marklessLoadLocalSymbol = loadSymbol;" : "",
|
|
610
|
+
(symbolsOnly || input.publicRenderRootExportName === null) && !routeSymbols ? "export { loadSymbol };" : "",
|
|
611
|
+
symbolsOnly ? "" : "export { payloadView };",
|
|
612
|
+
symbolsOnly ? "" : "export { payloadRuntimeDemandMap };",
|
|
613
|
+
input.devResumeReexport && input.environment === "client" ? `export { resumeContainerEvent } from '${resumeVirtualModuleId(input.filename)}';` : "",
|
|
614
|
+
"",
|
|
615
|
+
input.environment === "server" || symbolsOnly || input.prerenderRecords ? "" : emitPublicRenderModule(input.publicRenderModuleSource, {
|
|
616
|
+
executionLogEnabled: input.executionLog !== "never",
|
|
617
|
+
rootExportName: input.publicRenderRootExportName
|
|
618
|
+
}),
|
|
619
|
+
emitsSsrBody ? input.publicSsrModuleSource : "",
|
|
620
|
+
routeSymbols ? emitLazySymbolRouteFunction(input.symbolRoutes, "marklessSsrLoadSymbolRoute", "marklessLoadLocalSymbol", input.scalarActions !== void 0) : "",
|
|
621
|
+
routeSymbols && !symbolsOnly && input.directCsr === true ? emitComposedArmRecordsInstall() : null,
|
|
622
|
+
symbolsOnly && routeSymbols ? "export { marklessSsrLoadSymbolRoute as loadSymbol };" : "",
|
|
623
|
+
symbolsOnly ? "" : emitCompiledAppDefault({
|
|
624
|
+
environment: input.environment,
|
|
625
|
+
executionLog: input.executionLog,
|
|
626
|
+
headInjections: input.headInjections,
|
|
627
|
+
storageSeeds: input.storageSeeds,
|
|
628
|
+
inlineResumerSources: input.inlineResumerSources,
|
|
629
|
+
prerenderBoot: input.prerenderBoot,
|
|
630
|
+
resumeModuleUrl: input.resumeModuleUrl,
|
|
631
|
+
prerenderWakeModuleUrl: input.prerenderWakeModuleUrl,
|
|
632
|
+
rootExportName: input.publicRenderRootExportName,
|
|
633
|
+
csrRootName,
|
|
634
|
+
symbolLoaderName: routeSymbols ? "marklessSsrLoadSymbolRoute" : "loadSymbol",
|
|
635
|
+
ssrExportName: input.publicRenderSsrExportName,
|
|
636
|
+
ssrComponents: input.publicRenderSsrComponentExports ?? [],
|
|
637
|
+
canonicalRenderData,
|
|
638
|
+
prerenderRecords: input.prerenderRecords,
|
|
639
|
+
dev: input.dev
|
|
640
|
+
}),
|
|
641
|
+
...emitSharedDefinitionExports(input.sharedDefinitionExports ?? [], input.publicRenderSsrComponentExports ?? []),
|
|
642
|
+
""
|
|
643
|
+
].filter((line) => line !== null).join("\n");
|
|
644
|
+
}
|
|
645
|
+
const MARKLESS_SHARED_CALL_UNCOMPILED = "MARKLESS_SHARED_CALL_UNCOMPILED";
|
|
646
|
+
/**
|
|
647
|
+
* One real ES named export per authored shared definition, bound to a callable
|
|
648
|
+
* that only ever throws.
|
|
649
|
+
*
|
|
650
|
+
* The export exists for two reasons, and neither is "run the definition".
|
|
651
|
+
* Linking first: a `.ts` barrel writing `export { nscShared as state } from
|
|
652
|
+
* './family.tsrx'` is resolved before anything runs, and the emitted module
|
|
653
|
+
* replacing the authored one wholesale made that a SyntaxError. Failing closed
|
|
654
|
+
* second: a compiled consumer's `family.state()` is lowered by the compiler and
|
|
655
|
+
* never reaches this binding, so anything that DOES reach it is an uncompiled
|
|
656
|
+
* call site, which cannot be served correctly — a shared instance resolves
|
|
657
|
+
* against the widget the caller renders under, and an uncompiled call has no
|
|
658
|
+
* such caller. It throws by name rather than quietly becoming a second,
|
|
659
|
+
* unratified runtime API.
|
|
660
|
+
*/
|
|
661
|
+
function emitSharedDefinitionExports(sharedExports, ssrComponents) {
|
|
662
|
+
if (sharedExports.length === 0) return [];
|
|
663
|
+
const taken = /* @__PURE__ */ new Set([...RESERVED_MODULE_BINDINGS, ...ssrComponents.map((component) => component.exportName)]);
|
|
664
|
+
return sharedExports.map((name) => {
|
|
665
|
+
if (taken.has(name)) throw new Error(`MARKLESS_SHARED_EXPORT_NAME_RESERVED: ${name}`);
|
|
666
|
+
taken.add(name);
|
|
667
|
+
const message = `${MARKLESS_SHARED_CALL_UNCOMPILED}: ${name} — a shared definition can only be called from a compiled module; this call site was not compiled.`;
|
|
668
|
+
return `export const ${name} = () => { throw new Error(${JSON.stringify(message)}); };`;
|
|
669
|
+
});
|
|
670
|
+
}
|
|
671
|
+
/**
|
|
672
|
+
* Whether this app's serialized state defines any computed node. Computed
|
|
673
|
+
* definitions are what `createRuntimeGraph` turns into derived graph nodes on
|
|
674
|
+
* resume, so they are exactly the apps derived reconciliation can act on.
|
|
675
|
+
*/
|
|
676
|
+
function payloadHasComputed(payloadState) {
|
|
677
|
+
const computed = payloadState?.computed;
|
|
678
|
+
return Array.isArray(computed) && computed.length > 0;
|
|
679
|
+
}
|
|
680
|
+
function emitIdleTriggerInstall() {
|
|
681
|
+
return ["import { installMarklessIdle } from '@markless/web/fns/idle-trigger';", "installMarklessIdle();"].join("\n");
|
|
682
|
+
}
|
|
683
|
+
/**
|
|
684
|
+
* The one line that makes elevation cost nothing until it is used.
|
|
685
|
+
*
|
|
686
|
+
* Writing the `import()` specifier HERE rather than in the runtime is the whole
|
|
687
|
+
* mechanism: a module every app loads cannot name the behaviour without making
|
|
688
|
+
* every app ship its chunk. The handoff is a global rather than a slot module
|
|
689
|
+
* for the same reason - a slot module is bytes every app pays for a capability
|
|
690
|
+
* most of them do not have. Nothing is fetched until the runtime calls it.
|
|
691
|
+
*/
|
|
692
|
+
function emitOverlayLoaderInstall() {
|
|
693
|
+
return [
|
|
694
|
+
"globalThis.__marklessOverlay = (root) =>",
|
|
695
|
+
" root.querySelector('[overlay]')",
|
|
696
|
+
" ? import('@markless/web/fns/overlay').then((m) => m.installOverlayBehavior(root))",
|
|
697
|
+
" : undefined;"
|
|
698
|
+
].join("\n");
|
|
699
|
+
}
|
|
700
|
+
/**
|
|
701
|
+
* The same one line for the live roster, gated on the same fact the reconcile
|
|
702
|
+
* plane is gated on: a payload with no computed nodes can hold no roster
|
|
703
|
+
* derivation, so it never names the module and never emits its chunk.
|
|
704
|
+
*
|
|
705
|
+
* Both call sites (the start-up wiring and a sync computed's re-derive) sit in
|
|
706
|
+
* modules every resumed page runs, which is why neither may write the specifier.
|
|
707
|
+
* A composed MDX route writes it too: it cannot see its islands' computeds.
|
|
708
|
+
*/
|
|
709
|
+
function emitRosterResumeLoaderInstall() {
|
|
710
|
+
return "globalThis.__marklessRosterResume ??= () => import('@markless/web/fns/roster-resume');";
|
|
711
|
+
}
|
|
712
|
+
/**
|
|
713
|
+
* The same one line for the row mint, and for the same reason.
|
|
714
|
+
*
|
|
715
|
+
* `resume-keyed-repeats` is loaded by EVERY repeat, so it cannot name the
|
|
716
|
+
* building half: doing so makes an app whose rows only reorder emit the mint
|
|
717
|
+
* chunk anyway. Written here, the specifier exists only in a build the compiler
|
|
718
|
+
* recorded a mintable repeat for; the repeat runtime reads the loader off the
|
|
719
|
+
* global and refuses to build without one.
|
|
720
|
+
*/
|
|
721
|
+
function emitRowMintLoaderInstall() {
|
|
722
|
+
return "globalThis.__marklessRowMint ??= () => import('@markless/web/fns/row-mint');";
|
|
723
|
+
}
|
|
724
|
+
/**
|
|
725
|
+
* The same one line for per-enclosing-row wiring: only a page holding a `@for`
|
|
726
|
+
* inside another `@for`'s rows names the module, so no other app emits its chunk.
|
|
727
|
+
*/
|
|
728
|
+
function nestedRepeatsLoader(runtimeDemandMap) {
|
|
729
|
+
return demandsKeyedRepeatModule(runtimeDemandMap, NESTED_REPEATS_RUNTIME_MODULE_ID) ? ["globalThis.__marklessNestedRepeats ??= (...input) =>", " import('@markless/web/fns/nested-repeats').then((nested) => nested.wireNestedRepeats(...input));"].join("\n") : null;
|
|
730
|
+
}
|
|
731
|
+
/**
|
|
732
|
+
* True when the compiler folded the mint into a keyed-repeat record - which it
|
|
733
|
+
* does exactly for a record carrying `rowTemplate` or `emptyArm`, the two fields
|
|
734
|
+
* that make a repeat able to build nodes.
|
|
735
|
+
*/
|
|
736
|
+
function demandsRowMint(runtimeDemandMap) {
|
|
737
|
+
const records = runtimeDemandMap?.payloadRecords;
|
|
738
|
+
return Array.isArray(records) && records.some((record) => record.kind === "keyed-repeat" && record.runtimeModuleIds?.includes(ROW_MINT_RUNTIME_MODULE_ID) === true);
|
|
739
|
+
}
|
|
740
|
+
/**
|
|
741
|
+
* The component-rooted mint, which also needs this page's render-data surface.
|
|
742
|
+
*
|
|
743
|
+
* It answers the SAME global as the template mint, whose builders it re-exports:
|
|
744
|
+
* the repeat runtime carries no second loader, so a page with no component row
|
|
745
|
+
* ships not one byte of this path. Nothing page-specific is written here - every
|
|
746
|
+
* page emits this same line - because one document can hold several modules that
|
|
747
|
+
* install a mint, and the one that builds every kind of row must keep the global
|
|
748
|
+
* whichever is evaluated last. The surface travels on the runtime input instead
|
|
749
|
+
* and arrives as the loader's first argument.
|
|
750
|
+
*/
|
|
751
|
+
function emitRowComponentMintLoaderInstall() {
|
|
752
|
+
return [
|
|
753
|
+
"globalThis.__marklessRowMint = Object.assign(",
|
|
754
|
+
" (...input) => import('@markless/web/fns/row-component-mint').then((mint) => mint.marklessRowComponentMint(...input)),",
|
|
755
|
+
" { components: true },",
|
|
756
|
+
");"
|
|
757
|
+
].join("\n");
|
|
758
|
+
}
|
|
759
|
+
function componentRowLoader(input) {
|
|
760
|
+
if (!input.renderDataId) return void 0;
|
|
761
|
+
if (input.linkedRowComponents || demandsKeyedRepeatModule(input.runtimeDemandMap, ROW_COMPONENT_MINT_RUNTIME_MODULE_ID)) return emitRowComponentMintLoaderInstall();
|
|
762
|
+
return input.linkedRowSlotReaders || demandsKeyedRepeatModule(input.runtimeDemandMap, ROW_SLOT_MINT_RUNTIME_MODULE_ID) ? emitRowSlotMintLoaderInstall() : void 0;
|
|
763
|
+
}
|
|
764
|
+
function clientRowMintLoader(input) {
|
|
765
|
+
const mint = demandsRowComponentMint(input.runtimeDemandMap) ? emitRowComponentMintLoaderInstall() : componentRowLoader(input) ?? (demandsRowMint(input.runtimeDemandMap) ? emitRowMintLoaderInstall() : null);
|
|
766
|
+
const nested = nestedRepeatsLoader(input.runtimeDemandMap);
|
|
767
|
+
return mint && nested ? `${mint}\n${nested}` : mint ?? nested;
|
|
768
|
+
}
|
|
769
|
+
function emitRowSlotMintLoaderInstall() {
|
|
770
|
+
return ["if (!globalThis.__marklessRowMint?.components) globalThis.__marklessRowMint = (...input) =>", " import('@markless/web/fns/row-slot-mint').then((mint) => mint.marklessRowSlotMint(...input));"].join("\n");
|
|
771
|
+
}
|
|
772
|
+
/**
|
|
773
|
+
* True when the compiler folded the component mint into a keyed-repeat record -
|
|
774
|
+
* which it does exactly for a record carrying `rowComponent`.
|
|
775
|
+
*/
|
|
776
|
+
function demandsRowComponentMint(runtimeDemandMap) {
|
|
777
|
+
return demandsKeyedRepeatModule(runtimeDemandMap, ROW_COMPONENT_MINT_RUNTIME_MODULE_ID);
|
|
778
|
+
}
|
|
779
|
+
function demandsKeyedRepeatModule(runtimeDemandMap, moduleId) {
|
|
780
|
+
const records = runtimeDemandMap?.payloadRecords;
|
|
781
|
+
return Array.isArray(records) && records.some((record) => record.kind === "keyed-repeat" && record.runtimeModuleIds?.includes(moduleId) === true);
|
|
782
|
+
}
|
|
783
|
+
const ROW_MINT_RUNTIME_MODULE_ID = "web/fns/row-mint";
|
|
784
|
+
const NESTED_REPEATS_RUNTIME_MODULE_ID = "web/fns/nested-repeats";
|
|
785
|
+
const ROW_COMPONENT_MINT_RUNTIME_MODULE_ID = "web/fns/row-component-mint";
|
|
786
|
+
const ROW_SLOT_MINT_RUNTIME_MODULE_ID = "web/fns/row-slot-mint";
|
|
787
|
+
/** True when the compiler recorded an `overlay` mark for this module. */
|
|
788
|
+
function demandsOverlay(runtimeDemandMap) {
|
|
789
|
+
const records = runtimeDemandMap?.payloadRecords;
|
|
790
|
+
return Array.isArray(records) && records.some((record) => record.kind === "overlay");
|
|
791
|
+
}
|
|
792
|
+
function emitResumeModule(input) {
|
|
793
|
+
const routeSymbols = input.symbolRoutes.length > 0;
|
|
794
|
+
const resumeSymbolLoader = routeSymbols ? "marklessSsrLoadSymbolRoute" : "loadSymbol";
|
|
795
|
+
const servedPlansOnly = leanResumeMode(input.runtimeDemandMap) === "none" && leanResumeMode(input.servedScalarPlans) !== "none";
|
|
796
|
+
const scalarSpecializations = scalarDispatcherSpecializations(servedPlansOnly ? {
|
|
797
|
+
...input,
|
|
798
|
+
runtimeDemandMap: input.servedScalarPlans
|
|
799
|
+
} : input);
|
|
800
|
+
const stagedPrerender = (input.prerenderTriggerGroups?.length ?? 0) > 0;
|
|
801
|
+
const resumeContainerEvent = emitQueuedResumeContainerEvent(emitResumeContainerEvent(resumeSymbolLoader, input.needsFullResume ?? false, input.payloadState?.version === ASYNC_PROTOCOL_VERSION, servedPlansOnly ? "scalar" : composedLeanMode(input.symbolRoutes.length > 0, leanResumeMode(input.runtimeDemandMap)), scalarSpecializations, input.runtimeDemandMap, input.executionLog !== "never", input.prerenderDataId, stagedPrerender, componentRowLoader(input) ? input.lazyRenderDataId ?? input.renderDataId : void 0, input.symbolRoutes.length > 0 || servedPlansOnly, (input.closurePlanRoutes ?? []).some((route) => "importSource" in route) && scalarSpecializations.length > 0 && !input.prerenderDataId, input.streamsArms === true));
|
|
802
|
+
const closureImports = [
|
|
803
|
+
"marklessClosureRouted",
|
|
804
|
+
"marklessPrimeClosure",
|
|
805
|
+
"marklessPrimeRoutedClosure",
|
|
806
|
+
"marklessRunClosureAction",
|
|
807
|
+
"marklessRunRoutedClosure"
|
|
808
|
+
].filter((name) => resumeContainerEvent.includes(name));
|
|
809
|
+
return [
|
|
810
|
+
input.boundSymbolDescriptors ? `export const ${MARKLESS_BOUND_SYMBOLS_EXPORT} = ${emitBoundSymbolDescriptors(input.boundSymbolDescriptors)};` : null,
|
|
811
|
+
input.prerenderDataId && !stagedPrerender && resumeContainerEvent.includes("marklessPrerenderData") ? `import { marklessPrerenderData } from '${input.prerenderDataId}';` : null,
|
|
812
|
+
stagedPrerender && input.prerenderDataId ? `async function marklessLoadPrerenderData() { const module = await import('${input.lazyRenderDataId ?? input.prerenderDataId}'); return module.marklessPrerenderData; }` : null,
|
|
813
|
+
`import { runtimeDemandMap as payloadRuntimeDemandMap } from '${input.payloadId}';`,
|
|
814
|
+
payloadHasComputed(input.payloadState) ? [
|
|
815
|
+
"import { installMarklessDerivedReconcile } from '@markless/web/fns/reconcile-plane';",
|
|
816
|
+
"installMarklessDerivedReconcile();",
|
|
817
|
+
emitRosterResumeLoaderInstall()
|
|
818
|
+
].join("\n") : input.rosterDerivation === true ? emitRosterResumeLoaderInstall() : null,
|
|
819
|
+
demandsOverlay(input.runtimeDemandMap) ? emitOverlayLoaderInstall() : null,
|
|
820
|
+
input.hasIdleEvents ? emitIdleTriggerInstall() : null,
|
|
821
|
+
componentRowLoader(input) ?? (demandsRowMint(input.runtimeDemandMap) ? emitRowMintLoaderInstall() : null),
|
|
822
|
+
nestedRepeatsLoader(input.runtimeDemandMap),
|
|
823
|
+
scalarSpecializations.length > 0 ? [
|
|
824
|
+
"import { marklessDecodeScalarCell, marklessReadScalarCell, marklessScalarSpecializedError } from '@markless/web/fns/scalar-specialized';",
|
|
825
|
+
resumeContainerEvent.includes("marklessScalarServedOwner") ? `import { marklessScalarLocate, marklessScalarServedOwner, marklessScalarServedPrime${resumeContainerEvent.includes("marklessScalarServedRecord") ? ", marklessScalarServedRecord" : ""} } from '@markless/web/fns/scalar-served';` : "",
|
|
826
|
+
...closureImports.length > 0 ? [`import { ${closureImports.join(", ")} } from '@markless/web/fns/closure-action';`] : [],
|
|
827
|
+
"import { marklessWriteScalar } from '@markless/web/fns/write-scalar';",
|
|
828
|
+
"import { marklessUpdateText } from '@markless/web/fns/update-text';"
|
|
829
|
+
].join("\n") : "",
|
|
830
|
+
scalarSpecializations.length > 0 || stagedPrerender ? "import { marklessFindElementAtDomOrderIndex } from '@markless/web/fns/dom-order';" : "",
|
|
831
|
+
"",
|
|
832
|
+
input.executionLog === "never" ? "" : emitExecutionLogLoader(),
|
|
833
|
+
input.installResumeSummary && input.executionLog !== "never" ? "if (globalThis.__mxLog) globalThis.__mxLoadLog().then(log => log.installMarklessExecutionLog());" : null,
|
|
834
|
+
"",
|
|
835
|
+
emitLoadSymbol(input, stagedPrerender ? "readMarklessWakeSourceSymbol" : void 0),
|
|
836
|
+
routeSymbols ? "const marklessLoadLocalSymbol = loadSymbol;" : "",
|
|
837
|
+
routeSymbols ? emitLazySymbolRouteFunction(input.symbolRoutes, "marklessSsrLoadSymbolRoute", "marklessLoadLocalSymbol") : "",
|
|
838
|
+
routeSymbols ? "export { marklessSsrLoadSymbolRoute as loadSymbol };" : "",
|
|
839
|
+
routeSymbols ? emitComposedArmRecordsInstall() : null,
|
|
840
|
+
stagedPrerender ? emitPrerenderTriggerGroupLoader(input.prerenderTriggerGroups ?? []) : "",
|
|
841
|
+
resumeContainerEvent.includes("marklessClosurePlanRoute") ? emitClosurePlanRoute(input.closurePlanRoutes ?? [], "marklessClosurePlanRoute", []) : null,
|
|
842
|
+
resumeContainerEvent,
|
|
843
|
+
""
|
|
844
|
+
].filter((line) => line !== null).join("\n");
|
|
845
|
+
}
|
|
846
|
+
function emitBoundSymbolDescriptors(descriptors) {
|
|
847
|
+
return JSON.stringify(Object.fromEntries(Object.keys(descriptors).sort().map((boundaryId) => [boundaryId, descriptors[boundaryId]])));
|
|
848
|
+
}
|
|
849
|
+
function emitQueuedResumeContainerEvent(source, { censusRepair = true } = {}) {
|
|
850
|
+
const implementation = source.replace("export async function resumeContainerEvent", "async function marklessResumeContainerEvent");
|
|
851
|
+
if (implementation === source) throw new Error("MARKLESS_RESUME_DISPATCH_EXPORT_MISSING");
|
|
852
|
+
return [
|
|
853
|
+
implementation,
|
|
854
|
+
"export function resumeContainerEvent(input) {",
|
|
855
|
+
" const root = input.root;",
|
|
856
|
+
censusRepair ? " if (root.__marklessShifted) return import('@markless/web/fns/served-census-repair').then((census) => (census.marklessRepairCensus(root), resumeContainerEvent(input)));" : "",
|
|
857
|
+
" if (!root.__marklessDispatch) {",
|
|
858
|
+
" let handler = marklessResumeContainerEvent, tail = Promise.resolve(), done = tail, lastEvent = null, lastEventTime = undefined;",
|
|
859
|
+
" root.__marklessRegisterDispatch = next => {",
|
|
860
|
+
" const fallback = handler;",
|
|
861
|
+
" handler = input => next(input, fallback);",
|
|
862
|
+
" };",
|
|
863
|
+
" root.__marklessDispatch = next => {",
|
|
864
|
+
" const event = next.event, eventTime = event?.timeStamp;",
|
|
865
|
+
" if (event && next.eventRecord && next.element) event.__marklessPressHost ??= next.element;",
|
|
866
|
+
" if (event === lastEvent && eventTime === lastEventTime) return done;",
|
|
867
|
+
" lastEvent = event;",
|
|
868
|
+
" lastEventTime = eventTime;",
|
|
869
|
+
" let release;",
|
|
870
|
+
" const started = new Promise(resolve => release = resolve);",
|
|
871
|
+
" const run = () => (root.__marklessReleaseDispatch = release, handler(next));",
|
|
872
|
+
" done = tail.then(run, run);",
|
|
873
|
+
" done.then(release, release);",
|
|
874
|
+
" tail = started;",
|
|
875
|
+
" return done;",
|
|
876
|
+
" };",
|
|
877
|
+
" }",
|
|
878
|
+
" return root.__marklessDispatch(input);",
|
|
879
|
+
"}"
|
|
880
|
+
].join("\n");
|
|
881
|
+
}
|
|
882
|
+
function emitComposedArmRecordsInstall() {
|
|
883
|
+
return ["import { installMarklessComposedArmRecords } from '@markless/web/fns/instance-scope';", "installMarklessComposedArmRecords();"].join("\n");
|
|
884
|
+
}
|
|
885
|
+
function emitLazySymbolRouteFunction(routes, functionName, fallbackName = "loadSymbol", includeScalarActionPlans = false) {
|
|
886
|
+
if (routes.length === 0) return "";
|
|
887
|
+
const ordered = [...routes].sort((left, right) => right.prefix.length - left.prefix.length);
|
|
888
|
+
const reboundName = `${functionName}Rebound`;
|
|
889
|
+
return [
|
|
890
|
+
emitReboundSymbolImport(ordered, reboundName),
|
|
891
|
+
`function ${functionName}(symbolId) {`,
|
|
892
|
+
...ordered.flatMap((route) => {
|
|
893
|
+
const unbound = "importSource" in route ? `import(${JSON.stringify(symbolRouteImportSource$1(route.importSource, includeScalarActionPlans))}).then((mod) => mod.loadSymbol ? mod.loadSymbol(symbolId.slice(${route.prefix.length})) : Promise.reject(new Error(\`Unknown child async symbol \${symbolId}\`)))` : `${route.prefix.length > 0 ? functionName : fallbackName}(symbolId.slice(${route.prefix.length}))`;
|
|
894
|
+
return [
|
|
895
|
+
` if (symbolId.startsWith(${JSON.stringify(route.prefix)})) {`,
|
|
896
|
+
...emitReboundSymbolRoute(route, reboundName, fallbackName, unbound, " "),
|
|
897
|
+
` return ${unbound};`,
|
|
898
|
+
" }"
|
|
899
|
+
];
|
|
900
|
+
}),
|
|
901
|
+
` return ${fallbackName}(symbolId);`,
|
|
902
|
+
"}"
|
|
903
|
+
].filter((line) => line !== "").join("\n");
|
|
904
|
+
}
|
|
905
|
+
function emitsClosurePlanLoader(actions, routes) {
|
|
906
|
+
return routes.some((route) => "importSource" in route) || actions.some((action) => action.plan?.kind === "closure");
|
|
907
|
+
}
|
|
908
|
+
function emitClosurePlanRoute(routes, functionName, actions) {
|
|
909
|
+
const shapes = [];
|
|
910
|
+
const local = actions.filter((action) => action.recordKind === "event" && action.plan?.kind === "closure").map((action) => {
|
|
911
|
+
const { symbolId, cells, computed, updates } = action.plan;
|
|
912
|
+
const shape = JSON.stringify({
|
|
913
|
+
cells,
|
|
914
|
+
computed,
|
|
915
|
+
updates
|
|
916
|
+
});
|
|
917
|
+
if (!shapes.includes(shape)) shapes.push(shape);
|
|
918
|
+
return `\t${JSON.stringify(symbolId)}: { symbolId: ${JSON.stringify(symbolId)}, ...marklessLocalClosureShapes[${shapes.indexOf(shape)}] },`;
|
|
919
|
+
});
|
|
920
|
+
const ordered = [...routes].sort((left, right) => right.prefix.length - left.prefix.length);
|
|
921
|
+
const localLookup = local.length > 0 ? "Object.hasOwn(marklessLocalClosurePlans, symbolId) ? marklessLocalClosurePlans[symbolId] : undefined" : "undefined";
|
|
922
|
+
return [
|
|
923
|
+
local.length > 0 ? [
|
|
924
|
+
`const marklessLocalClosureShapes = [${shapes.join(", ")}];`,
|
|
925
|
+
"const marklessLocalClosurePlans = {",
|
|
926
|
+
...local,
|
|
927
|
+
"};"
|
|
928
|
+
].join("\n") : "",
|
|
929
|
+
`function ${functionName}(symbolId) {`,
|
|
930
|
+
...ordered.flatMap((route) => [
|
|
931
|
+
` if (symbolId.startsWith(${JSON.stringify(route.prefix)})) {`,
|
|
932
|
+
"importSource" in route ? ` return import(${JSON.stringify(symbolRouteImportSource$1(route.importSource))}).then((mod) => mod.loadClosureActionPlan?.(symbolId.slice(${route.prefix.length})));` : route.prefix.length > 0 ? ` return ${functionName}(symbolId.slice(${route.prefix.length}));` : ` return ${localLookup};`,
|
|
933
|
+
" }"
|
|
934
|
+
]),
|
|
935
|
+
` return ${localLookup};`,
|
|
936
|
+
"}"
|
|
937
|
+
].filter(Boolean).join("\n");
|
|
938
|
+
}
|
|
939
|
+
function symbolRouteImportSource$1(importSource, includeScalarActionPlans = false) {
|
|
940
|
+
if (includeScalarActionPlans) return scalarPlanSourceReference(importSource);
|
|
941
|
+
return importSource.includes("?") ? `${importSource}&markless-symbols` : `${importSource}?markless-symbols`;
|
|
942
|
+
}
|
|
943
|
+
function emitCompiledAppDefault(input) {
|
|
944
|
+
const renderCsrEntry = input.csrRootName ? [` renderCsr: ${input.csrRootName},`] : [];
|
|
945
|
+
const symbolLoaderEntry = input.environment !== "server" && input.rootExportName === null ? [`\tloadSymbol: ${input.symbolLoaderName},`] : input.prerenderRecords === true ? [`\tloadSymbol: ${input.symbolLoaderName},`] : [];
|
|
946
|
+
const renderDataEntry = input.canonicalRenderData ? [" renderData: marklessPrerenderData,"] : [];
|
|
947
|
+
const servesSsr = !!input.ssrExportName && (input.environment !== "client" || input.prerenderRecords || input.dev);
|
|
948
|
+
const renderSsrEntry = servesSsr ? [
|
|
949
|
+
" renderSsr(props, marklessRenderContext) {",
|
|
950
|
+
` return ${input.ssrExportName}(props, marklessRenderContext);`,
|
|
951
|
+
" },"
|
|
952
|
+
] : [];
|
|
953
|
+
const renderSsrComponentsEntry = servesSsr && input.ssrComponents.length > 0 ? [` renderSsrComponents: { ${input.ssrComponents.map((component) => `${JSON.stringify(component.exportName)}: { renderSsr: ${component.ssrFunctionName} }`).join(", ")} },`] : [];
|
|
954
|
+
const resumeModuleEntry = input.resumeModuleUrl && input.environment !== "client" ? [` resumeModuleUrl: ${JSON.stringify(input.resumeModuleUrl)},`] : [];
|
|
955
|
+
const prerenderWakeModuleEntry = input.prerenderWakeModuleUrl && input.environment !== "client" ? [`\tprerenderWakeModuleUrl: ${JSON.stringify(input.prerenderWakeModuleUrl)},`] : [];
|
|
956
|
+
const inlineResumerEntry = input.inlineResumerSources && input.environment !== "client" ? [`\tinlineResumerSources: ${JSON.stringify(input.inlineResumerSources)},`] : [];
|
|
957
|
+
const prerenderBootEntry = input.prerenderBoot && input.environment !== "client" ? [`\tprerenderBoot: ${JSON.stringify(input.prerenderBoot)},`] : [];
|
|
958
|
+
const headInjectionEntry = input.headInjections?.length && input.environment !== "client" ? [` headInjections: ${JSON.stringify(input.headInjections)},`] : [];
|
|
959
|
+
const storageSeedEntry = input.storageSeeds?.length && input.environment !== "client" ? [`\tstorageSeeds: ${JSON.stringify(input.storageSeeds)},`] : [];
|
|
960
|
+
const modulePreloadEntry = input.resumeModuleUrl && input.environment === "server" ? [` modulePreloads: [{ href: ${JSON.stringify(input.resumeModuleUrl)}, fetchPriority: "high" }],`] : [];
|
|
961
|
+
const metadataEntries = input.environment === "client" ? [] : [
|
|
962
|
+
...headInjectionEntry,
|
|
963
|
+
...storageSeedEntry,
|
|
964
|
+
...resumeModuleEntry,
|
|
965
|
+
...prerenderWakeModuleEntry,
|
|
966
|
+
...inlineResumerEntry,
|
|
967
|
+
...prerenderBootEntry,
|
|
968
|
+
...modulePreloadEntry,
|
|
969
|
+
input.executionLog && input.executionLog !== "never" ? ` executionLog: ${JSON.stringify(input.executionLog)},` : "",
|
|
970
|
+
" payloadView,"
|
|
971
|
+
];
|
|
972
|
+
return [
|
|
973
|
+
"const marklessCompiledApp = {",
|
|
974
|
+
...renderCsrEntry,
|
|
975
|
+
...renderDataEntry,
|
|
976
|
+
...symbolLoaderEntry,
|
|
977
|
+
...renderSsrEntry,
|
|
978
|
+
...renderSsrComponentsEntry,
|
|
979
|
+
...metadataEntries,
|
|
980
|
+
"};",
|
|
981
|
+
"export default marklessCompiledApp;",
|
|
982
|
+
...emitComponentExports({
|
|
983
|
+
ssrComponents: input.ssrComponents,
|
|
984
|
+
rootExportName: input.ssrComponents.find((component) => component.ssrFunctionName === input.ssrExportName)?.exportName,
|
|
985
|
+
servesSsr: !!servesSsr
|
|
986
|
+
})
|
|
987
|
+
].join("\n");
|
|
988
|
+
}
|
|
989
|
+
const RESERVED_MODULE_BINDINGS = /* @__PURE__ */ new Set([
|
|
990
|
+
"loadBehaviorSymbol",
|
|
991
|
+
"loadClosureActionPlan",
|
|
992
|
+
"loadScalarActionPlan",
|
|
993
|
+
"loadSymbol",
|
|
994
|
+
"marklessCompiledApp",
|
|
995
|
+
"marklessLoadLocalSymbol",
|
|
996
|
+
"marklessLocalClosurePlans",
|
|
997
|
+
"marklessLocalClosureShapes",
|
|
998
|
+
"marklessPrerenderData",
|
|
999
|
+
"marklessRenderData",
|
|
1000
|
+
"marklessRenderSsr",
|
|
1001
|
+
"marklessSsrLoadSymbolRoute",
|
|
1002
|
+
"payloadRuntimeDemandMap",
|
|
1003
|
+
"payloadState",
|
|
1004
|
+
"payloadView",
|
|
1005
|
+
"readMarklessBehaviorSourceSymbol",
|
|
1006
|
+
"resumeContainerEvent"
|
|
1007
|
+
]);
|
|
1008
|
+
/**
|
|
1009
|
+
* One real ES named export per component the module serves, so plain ESM reaches
|
|
1010
|
+
* them: a `.ts` barrel writing `export { CheckboxRoot as root } from
|
|
1011
|
+
* './checkbox.tsrx'`, and an app writing `import { Gallery } from
|
|
1012
|
+
* './Gallery.tsrx'`, are both named reads the bundler resolves at LINK time.
|
|
1013
|
+
* Publishing components only on the default export's map answered neither
|
|
1014
|
+
* (MISSING_EXPORT at build, "does not provide an export named" in the browser).
|
|
1015
|
+
*
|
|
1016
|
+
* The root is published merged - the module surface plus its own part's
|
|
1017
|
+
* `renderSsr` - because both consumers reach it by that one name: `render(Gallery,
|
|
1018
|
+
* { target })` reads the surface (`renderCsr`/`renderData`/`loadSymbol`), while a
|
|
1019
|
+
* barrel re-export composed as a member tag reads the part. Every other component
|
|
1020
|
+
* is published as the part `marklessSsrComponentPart` hands a composed child,
|
|
1021
|
+
* carrying nothing of the module surface. A part rendered AS A PAGE would
|
|
1022
|
+
* therefore be served inert, so the export is branded and `renderSsrOutput`
|
|
1023
|
+
* refuses it; composition reads the unbranded map entry and is untouched.
|
|
1024
|
+
*/
|
|
1025
|
+
function emitComponentExports(input) {
|
|
1026
|
+
return input.ssrComponents.map((component) => {
|
|
1027
|
+
if (RESERVED_MODULE_BINDINGS.has(component.exportName)) throw new Error(`MARKLESS_COMPONENT_EXPORT_NAME_RESERVED: ${component.exportName}`);
|
|
1028
|
+
const part = `marklessCompiledApp.renderSsrComponents[${JSON.stringify(component.exportName)}]`;
|
|
1029
|
+
const isRoot = component.exportName === input.rootExportName;
|
|
1030
|
+
const brand = `${MARKLESS_COMPONENT_PART_BRAND}: ${JSON.stringify(component.exportName)}`;
|
|
1031
|
+
if (input.servesSsr) return `export const ${component.exportName} = ${isRoot ? `{ ...marklessCompiledApp, renderSsr: ${part}.renderSsr }` : `{ ...${part}, ${brand} }`};`;
|
|
1032
|
+
return isRoot ? `export const ${component.exportName} = marklessCompiledApp;` : `export const ${component.exportName} = { marklessCsrOnlyPart: ${JSON.stringify(component.exportName)}, ${brand} };`;
|
|
1033
|
+
});
|
|
1034
|
+
}
|
|
1035
|
+
function emitResumeContainerEvent(loadSymbolName, needsFullResume, storageFreePayload, leanMode, scalarSpecializations, runtimeDemandMap, executionLogEnabled, prerenderDataId, stagedPrerender = false, componentRowRenderDataId, servedOnly = false, routedClosures = false, streamsArms = false) {
|
|
1036
|
+
const armsOpen = (indent) => streamsArms ? [`${indent}const marklessArms = (await import('@markless/web/fns/streamed-arms')).marklessStreamedArms(handoff.root, ${loadSymbolName});`] : [];
|
|
1037
|
+
const armsStarted = (indent) => streamsArms ? [`${indent}marklessArms.started();`] : [];
|
|
1038
|
+
const wakeLoadSymbol = streamsArms ? "marklessArms.loadSymbol" : loadSymbolName;
|
|
1039
|
+
const resumeEntry = storageFreePayload ? STORAGE_FREE_RESUME_ENTRY_SPECIFIER : RESUME_ENTRY_SPECIFIER;
|
|
1040
|
+
const renderDataEntry = (indent, expression) => componentRowRenderDataId ? [`${indent}renderData: () => ${expression},`] : [];
|
|
1041
|
+
const fullResumeHandoff = stagedPrerender && prerenderDataId ? [
|
|
1042
|
+
"async function marklessFullResumeHandoff(handoff) {",
|
|
1043
|
+
" handoff.root.__asyncResumeRuntimeStarted = true;",
|
|
1044
|
+
" const group = await marklessLoadPrerenderTriggerGroup(handoff);",
|
|
1045
|
+
" const { mergePrerenderPayloadRecords, resumePrerenderTriggerGroup } = await import('@markless/web/fns/prerender-trigger-resume');",
|
|
1046
|
+
" if (!group) {",
|
|
1047
|
+
" const { derivePrerenderResumeRecords, renderPrerenderBoundary, resumeFromPrerenderRecords } = await import('@markless/web/fns/prerender-resume');",
|
|
1048
|
+
" handoff.root.__marklessFullResumeRuntime ??= (async () => {",
|
|
1049
|
+
" const marklessPrerenderData = await marklessLoadPrerenderData();",
|
|
1050
|
+
`\t\t\tconst fallbackRecords = mergePrerenderPayloadRecords(await derivePrerenderResumeRecords(marklessPrerenderData, ${loadSymbolName}), handoff.root);`,
|
|
1051
|
+
" return (await resumeFromPrerenderRecords({",
|
|
1052
|
+
" ...fallbackRecords,",
|
|
1053
|
+
" root: handoff.root,",
|
|
1054
|
+
`\t\t\t\tloadSymbol: ${loadSymbolName},`,
|
|
1055
|
+
`\t\t\t\trenderAsyncBoundary: (boundaryId, status, graph) => renderPrerenderBoundary(marklessPrerenderData, boundaryId, status, graph, ${loadSymbolName}),`,
|
|
1056
|
+
...renderDataEntry(" ", "marklessPrerenderData"),
|
|
1057
|
+
" })).runtime;",
|
|
1058
|
+
" })();",
|
|
1059
|
+
" const marklessFullRuntime = await handoff.root.__marklessFullResumeRuntime;",
|
|
1060
|
+
" await marklessFullRuntime.dispatch(handoff.event, { syncPolicyAlreadyApplied: handoff.syncPolicyAlreadyApplied === true, ignoreUnmatched: true });",
|
|
1061
|
+
" return;",
|
|
1062
|
+
" }",
|
|
1063
|
+
" const records = mergePrerenderPayloadRecords({ state: group.state, view: group.view }, handoff.root);",
|
|
1064
|
+
" const { runtime } = await resumePrerenderTriggerGroup({",
|
|
1065
|
+
" ...records, groupId: group.groupId, graphNodeIds: group.graphNodeIds,",
|
|
1066
|
+
" root: handoff.root, loadSymbol: group.loadSymbol, prepareBoundaryArm: group.prepareBoundaryArm, renderBoundaryArm: group.renderBoundaryArm,",
|
|
1067
|
+
" });",
|
|
1068
|
+
" await runtime.dispatch(handoff.event, { syncPolicyAlreadyApplied: handoff.syncPolicyAlreadyApplied === true, ignoreUnmatched: true });",
|
|
1069
|
+
"}"
|
|
1070
|
+
].join("\n") : prerenderDataId ? [
|
|
1071
|
+
"async function marklessFullResumeHandoff(handoff) {",
|
|
1072
|
+
" handoff.root.__marklessLinkedRenderDataBoot = true;",
|
|
1073
|
+
" handoff.root.__asyncResumeRuntimeStarted = true;",
|
|
1074
|
+
" const runtime = await (handoff.root.__marklessFullResumeRuntime ??= (async () => {",
|
|
1075
|
+
" const { derivePrerenderResumeRecords, mergePrerenderPayloadRecords, renderPrerenderBoundary, resumeFromPrerenderRecords } = await import('@markless/web/fns/prerender-resume');",
|
|
1076
|
+
`\t\tconst records = await derivePrerenderResumeRecords(marklessPrerenderData, ${loadSymbolName});`,
|
|
1077
|
+
" const mergedRecords = mergePrerenderPayloadRecords(records, handoff.root);",
|
|
1078
|
+
...armsOpen(" "),
|
|
1079
|
+
streamsArms ? " const resumed = await resumeFromPrerenderRecords({" : " return (await resumeFromPrerenderRecords({",
|
|
1080
|
+
" ...mergedRecords,",
|
|
1081
|
+
" root: handoff.root,",
|
|
1082
|
+
`\t\t\tloadSymbol: ${wakeLoadSymbol},`,
|
|
1083
|
+
`\t\t\trenderAsyncBoundary: (boundaryId, status, graph) => renderPrerenderBoundary(marklessPrerenderData, boundaryId, status, graph, ${loadSymbolName}),`,
|
|
1084
|
+
...renderDataEntry(" ", "marklessPrerenderData"),
|
|
1085
|
+
...streamsArms ? [
|
|
1086
|
+
" });",
|
|
1087
|
+
...armsStarted(" "),
|
|
1088
|
+
" return resumed.runtime;"
|
|
1089
|
+
] : [" })).runtime;"],
|
|
1090
|
+
" })());",
|
|
1091
|
+
" await runtime.dispatch(handoff.event, { syncPolicyAlreadyApplied: handoff.syncPolicyAlreadyApplied === true, ignoreUnmatched: true });",
|
|
1092
|
+
"}"
|
|
1093
|
+
].join("\n") : [
|
|
1094
|
+
"async function marklessFullResumeHandoff(handoff) {",
|
|
1095
|
+
" handoff.root.__asyncResumeRuntimeStarted = true;",
|
|
1096
|
+
...armsOpen(" "),
|
|
1097
|
+
` const { resumeFromPayloadDocument } = await import('${resumeEntry}');`,
|
|
1098
|
+
" const { runtime } = await resumeFromPayloadDocument({",
|
|
1099
|
+
" document: handoff.document,",
|
|
1100
|
+
" root: handoff.root,",
|
|
1101
|
+
` loadSymbol: ${wakeLoadSymbol},`,
|
|
1102
|
+
...renderDataEntry(" ", `import('${componentRowRenderDataId}').then((data) => data.marklessPrerenderData)`),
|
|
1103
|
+
" });",
|
|
1104
|
+
...armsStarted(" "),
|
|
1105
|
+
" await runtime.dispatch(handoff.event, { syncPolicyAlreadyApplied: handoff.syncPolicyAlreadyApplied === true, ignoreUnmatched: true });",
|
|
1106
|
+
"}"
|
|
1107
|
+
].join("\n");
|
|
1108
|
+
const scalarOnlySpecialized = leanMode === "scalar" && scalarSpecializations.length > 0 && !needsFullResume && !servedOnly && allEventActionsHaveScalarPlan(runtimeDemandMap);
|
|
1109
|
+
const scalarDispatcher = scalarSpecializations.length > 0 ? emitSpecializedScalarDispatcher(scalarSpecializations, loadSymbolName, scalarOnlySpecialized ? "fail" : "full", scalarOnlySpecialized ? "static" : servedOnly ? "served-only" : "served", !scalarOnlySpecialized && routedClosures) : emitSpecializedScalarDispatcher([], loadSymbolName, "full", "static");
|
|
1110
|
+
const actionScopedLean = leanMode === "scalar" && scalarSpecializations.length > 0 && !prerenderDataId;
|
|
1111
|
+
if (needsFullResume && !actionScopedLean) return [
|
|
1112
|
+
fullResumeHandoff,
|
|
1113
|
+
"export async function resumeContainerEvent(input) {",
|
|
1114
|
+
" await marklessFullResumeHandoff({ ...input, document: input.root });",
|
|
1115
|
+
"}"
|
|
1116
|
+
].join("\n");
|
|
1117
|
+
if (leanMode !== "none") {
|
|
1118
|
+
const row = [
|
|
1119
|
+
" const { resumeScalarRowEventFromPayloadDocument } = await import('@markless/web/event-only-lean/row');",
|
|
1120
|
+
" await resumeScalarRowEventFromPayloadDocument({",
|
|
1121
|
+
...[
|
|
1122
|
+
" document: input.root,",
|
|
1123
|
+
" root: input.root,",
|
|
1124
|
+
" event: input.event,",
|
|
1125
|
+
" element: input.element,",
|
|
1126
|
+
" runtimeDemandMap: payloadRuntimeDemandMap,",
|
|
1127
|
+
" syncPolicyAlreadyApplied: input.syncPolicyAlreadyApplied === true,",
|
|
1128
|
+
` loadSymbol: ${loadSymbolName},`,
|
|
1129
|
+
" });"
|
|
1130
|
+
]
|
|
1131
|
+
];
|
|
1132
|
+
if (leanMode === "scalar") return [
|
|
1133
|
+
scalarOnlySpecialized ? "" : fullResumeHandoff,
|
|
1134
|
+
scalarDispatcher,
|
|
1135
|
+
"export async function resumeContainerEvent(input) {",
|
|
1136
|
+
executionLogEnabled ? " const marklessLogBefore = globalThis.__mxLog && new Set(globalThis.__mxLog);" : "",
|
|
1137
|
+
" await marklessResumeSpecializedScalarEvent(input);",
|
|
1138
|
+
executionLogEnabled ? " if (marklessLogBefore) globalThis.__mxLoadLog().then(log => log.logMarklessSpecializedInteraction(input, marklessLogBefore));" : "",
|
|
1139
|
+
"}"
|
|
1140
|
+
].join("\n");
|
|
1141
|
+
if (leanMode === "row") return [
|
|
1142
|
+
"export async function resumeContainerEvent(input) {",
|
|
1143
|
+
...row,
|
|
1144
|
+
"}"
|
|
1145
|
+
].join("\n");
|
|
1146
|
+
return [
|
|
1147
|
+
fullResumeHandoff,
|
|
1148
|
+
scalarDispatcher,
|
|
1149
|
+
"export async function resumeContainerEvent(input) {",
|
|
1150
|
+
" if (marklessScalarSpecializedAction(input)) {",
|
|
1151
|
+
" await marklessResumeSpecializedScalarEvent(input);",
|
|
1152
|
+
" return;",
|
|
1153
|
+
" }",
|
|
1154
|
+
...row,
|
|
1155
|
+
"}"
|
|
1156
|
+
].join("\n");
|
|
1157
|
+
}
|
|
1158
|
+
if (prerenderDataId) return "export async function resumeContainerEvent(_input) {}";
|
|
1159
|
+
return [
|
|
1160
|
+
fullResumeHandoff,
|
|
1161
|
+
"export async function resumeContainerEvent(input) {",
|
|
1162
|
+
" await marklessFullResumeHandoff({ ...input, document: input.root });",
|
|
1163
|
+
"}"
|
|
1164
|
+
].join("\n");
|
|
1165
|
+
}
|
|
1166
|
+
function emitPrerenderTriggerGroupLoader(groups) {
|
|
1167
|
+
return [
|
|
1168
|
+
"function marklessPrerenderTriggerMatches(input, index, tagName, eventName) {",
|
|
1169
|
+
" if (input.event?.type !== eventName) return false;",
|
|
1170
|
+
" const host = marklessFindElementAtDomOrderIndex(input.root, index);",
|
|
1171
|
+
" if (!host || (tagName !== \"*\" && host.tagName.toLowerCase() !== tagName)) return false;",
|
|
1172
|
+
" const target = input.event?.target;",
|
|
1173
|
+
" return host === target || (!!target?.nodeType && typeof host.contains === \"function\" && host.contains(target));",
|
|
1174
|
+
"}",
|
|
1175
|
+
"function marklessPrerenderBranchTriggerMatches(input, startIndex, endIndex, hostPath, eventName) {",
|
|
1176
|
+
" if (input.event?.type !== eventName) return false;",
|
|
1177
|
+
" const comments = [];",
|
|
1178
|
+
" const collect = node => { for (const child of node.childNodes ?? []) { if (child.nodeType === 8) comments.push(child); collect(child); } };",
|
|
1179
|
+
" collect(input.root);",
|
|
1180
|
+
" const start = comments[startIndex], end = comments[endIndex], armNodes = [];",
|
|
1181
|
+
" let within = false, done = false;",
|
|
1182
|
+
" const between = node => { for (const child of node.childNodes ?? []) { if (child === start) { within = true; continue; } if (child === end) { done = true; return; } if (within) armNodes.push(child); else between(child); if (done) return; } };",
|
|
1183
|
+
" between(input.root);",
|
|
1184
|
+
" let host = armNodes[hostPath[0]];",
|
|
1185
|
+
" for (let index = 1; host && index < hostPath.length; index++) host = host.childNodes?.[hostPath[index]];",
|
|
1186
|
+
" const target = input.event?.target;",
|
|
1187
|
+
" return host?.nodeType === 1 && (host === target || (!!target?.nodeType && typeof host.contains === \"function\" && host.contains(target)));",
|
|
1188
|
+
"}",
|
|
1189
|
+
"function marklessLoadPrerenderTriggerGroup(input) {",
|
|
1190
|
+
...groups.map((group) => {
|
|
1191
|
+
return `\tif (${group.eventName === "self-wake" ? "input.event === 0" : group.hostPath ? `marklessPrerenderBranchTriggerMatches(input, ${group.branchStartIndex}, ${group.branchEndIndex}, ${JSON.stringify(group.hostPath)}, ${JSON.stringify(group.eventName)})` : `marklessPrerenderTriggerMatches(input, ${group.hostIndex}, ${JSON.stringify(group.hostTagName.toLowerCase())}, ${JSON.stringify(group.eventName)})`}) return import(${JSON.stringify(group.moduleId)});`;
|
|
1192
|
+
}),
|
|
1193
|
+
" return Promise.resolve(null);",
|
|
1194
|
+
"}"
|
|
1195
|
+
].join("\n");
|
|
1196
|
+
}
|
|
1197
|
+
function scalarDispatcherSpecializations(input) {
|
|
1198
|
+
const state = input.payloadState;
|
|
1199
|
+
const view = input.payloadView;
|
|
1200
|
+
const map = input.runtimeDemandMap;
|
|
1201
|
+
const cells = state?.cells ?? [];
|
|
1202
|
+
const locators = view?.locators ?? [];
|
|
1203
|
+
return (map?.actions ?? []).flatMap((action, index) => {
|
|
1204
|
+
const plan = action?.plan;
|
|
1205
|
+
if (action?.recordKind !== "event" || plan?.version !== 1) return [];
|
|
1206
|
+
if (plan.kind === "closure") return closureSpecialization(action, plan, index, view, input);
|
|
1207
|
+
if (plan.kind !== "scalar") return [];
|
|
1208
|
+
const cellIndex = cells.findIndex((cell) => cell?.graphNodeId === plan.cell);
|
|
1209
|
+
const initialCell = cells[cellIndex];
|
|
1210
|
+
const host = locators.find((locator) => locator?.hostNodeId === action.hostNodeId);
|
|
1211
|
+
const event = view?.events?.find((candidate) => candidate?.hostNodeId === action.hostNodeId && candidate?.eventName === action.eventName);
|
|
1212
|
+
const symbolId = scalarActionSymbolId(plan.symbolId, event, input.symbolRoutes ?? []);
|
|
1213
|
+
if (!symbolId) return [];
|
|
1214
|
+
if (cellIndex < 0 || !initialCell || !host || typeof host.index !== "number") return [];
|
|
1215
|
+
if (event && syncPolicyGraphNodeIds(event.syncPolicy).some((graphNodeId) => graphNodeId !== plan.cell)) return [];
|
|
1216
|
+
const textUpdates = (plan.textUpdates ?? []).flatMap((update) => {
|
|
1217
|
+
const locator = locators.find((candidate) => candidate?.hostNodeId === update.hostNodeId);
|
|
1218
|
+
return locator && typeof locator.index === "number" ? [{
|
|
1219
|
+
hostNodeId: update.hostNodeId,
|
|
1220
|
+
index: locator.index,
|
|
1221
|
+
tagName: String(locator.tagName ?? "*"),
|
|
1222
|
+
...update.prefix ? { prefix: update.prefix } : {},
|
|
1223
|
+
...update.suffix ? { suffix: update.suffix } : {}
|
|
1224
|
+
}] : [];
|
|
1225
|
+
});
|
|
1226
|
+
if (textUpdates.length !== (plan.textUpdates ?? []).length) return [];
|
|
1227
|
+
return [{
|
|
1228
|
+
kind: "scalar",
|
|
1229
|
+
name: `marklessRunScalar${index}`,
|
|
1230
|
+
hostNodeId: action.hostNodeId,
|
|
1231
|
+
eventName: action.eventName,
|
|
1232
|
+
symbolId,
|
|
1233
|
+
cell: plan.cell,
|
|
1234
|
+
cellIndex,
|
|
1235
|
+
initialCell,
|
|
1236
|
+
hostIndex: host.index,
|
|
1237
|
+
hostTagName: String(host.tagName ?? "*"),
|
|
1238
|
+
syncPolicy: event?.syncPolicy ?? null,
|
|
1239
|
+
write: plan.write,
|
|
1240
|
+
textUpdates
|
|
1241
|
+
}];
|
|
1242
|
+
});
|
|
1243
|
+
}
|
|
1244
|
+
function closureSpecialization(action, plan, index, view, input) {
|
|
1245
|
+
const host = view?.locators?.find((locator) => locator?.hostNodeId === action.hostNodeId);
|
|
1246
|
+
const event = view?.events?.find((candidate) => candidate?.hostNodeId === action.hostNodeId && candidate?.eventName === action.eventName);
|
|
1247
|
+
const symbolId = scalarActionSymbolId(plan.symbolId, event, input.symbolRoutes ?? []);
|
|
1248
|
+
if (symbolId === null || symbolId !== plan.symbolId || !host || typeof host.index !== "number" || !plan.cells || !plan.computed || !plan.updates) return [];
|
|
1249
|
+
return [{
|
|
1250
|
+
kind: "closure",
|
|
1251
|
+
name: `marklessRunClosure${index}`,
|
|
1252
|
+
hostNodeId: action.hostNodeId,
|
|
1253
|
+
eventName: action.eventName,
|
|
1254
|
+
symbolId,
|
|
1255
|
+
hostIndex: host.index,
|
|
1256
|
+
hostTagName: String(host.tagName ?? "*"),
|
|
1257
|
+
syncPolicy: event?.syncPolicy ?? null,
|
|
1258
|
+
plan: {
|
|
1259
|
+
cells: plan.cells,
|
|
1260
|
+
computed: plan.computed,
|
|
1261
|
+
updates: plan.updates
|
|
1262
|
+
}
|
|
1263
|
+
}];
|
|
1264
|
+
}
|
|
1265
|
+
function scalarActionSymbolId(plannedSymbolId, event, routes) {
|
|
1266
|
+
if (typeof plannedSymbolId !== "string") return null;
|
|
1267
|
+
const eventSymbolIds = Array.isArray(event?.symbolIds) ? event.symbolIds.filter((symbolId) => typeof symbolId === "string") : [];
|
|
1268
|
+
const routedSymbolId = eventSymbolIds.find((symbolId) => routes.some((route) => symbolId.startsWith(route.prefix)));
|
|
1269
|
+
if (routedSymbolId) return routedSymbolId;
|
|
1270
|
+
if (eventSymbolIds.some((symbolId) => /^[^:]+:symbol:/.test(symbolId))) return null;
|
|
1271
|
+
if (eventSymbolIds.length > 0 && !eventSymbolIds.includes(plannedSymbolId)) return null;
|
|
1272
|
+
return plannedSymbolId;
|
|
1273
|
+
}
|
|
1274
|
+
function allEventActionsHaveScalarPlan(runtimeDemandMap) {
|
|
1275
|
+
const eventActions = (runtimeDemandMap?.actions ?? []).filter((action) => action.recordKind === "event");
|
|
1276
|
+
return eventActions.length > 0 && eventActions.every((action) => action.plan?.kind === "scalar");
|
|
1277
|
+
}
|
|
1278
|
+
function emitSpecializedScalarDispatcher(actions, loadSymbolName, fallback, resolution, routedClosures = false) {
|
|
1279
|
+
const shapes = closureShapes(actions);
|
|
1280
|
+
const fallbackName = fallback === "fail" ? "marklessScalarSpecializedHostMiss" : "marklessScalarSpecializedFallback";
|
|
1281
|
+
const fallbackBody = fallback === "full" ? [
|
|
1282
|
+
"async function marklessScalarSpecializedFallback(input, site, syncPolicyAlreadyApplied = input.syncPolicyAlreadyApplied === true) {",
|
|
1283
|
+
" if (import.meta.env?.DEV) console.warn(Object.assign(new Error(\"MARKLESS_SCALAR_SPECIALIZED_FALLBACK\"), { code: \"MARKLESS_SCALAR_SPECIALIZED_FALLBACK\", site }));",
|
|
1284
|
+
" await marklessFullResumeHandoff({ ...input, document: input.root, syncPolicyAlreadyApplied });",
|
|
1285
|
+
"}"
|
|
1286
|
+
] : [];
|
|
1287
|
+
return [
|
|
1288
|
+
"async function marklessResumeSpecializedScalarEvent(input) {",
|
|
1289
|
+
...fallback === "full" ? [" if (input.root.__asyncResumeRuntimeStarted) return marklessFullResumeHandoff({ ...input, document: input.root });"] : [],
|
|
1290
|
+
" const action = marklessScalarSpecializedAction(input);",
|
|
1291
|
+
" if (action) {",
|
|
1292
|
+
" try {",
|
|
1293
|
+
" return await action(input);",
|
|
1294
|
+
" } catch (error) {",
|
|
1295
|
+
` if (error?.code === "MARKLESS_SCALAR_SPECIALIZED_ESCALATE") return ${fallbackName}(input, error.site ?? "escalate", error.syncPolicyAlreadyApplied === true);`,
|
|
1296
|
+
" throw error;",
|
|
1297
|
+
" }",
|
|
1298
|
+
" }",
|
|
1299
|
+
fallback === "fail" ? " return;" : ` return ${fallbackName}(input, "event-match");`,
|
|
1300
|
+
"}",
|
|
1301
|
+
fallback === "fail" ? "function marklessScalarSpecializedHostMiss(_input, site) { return marklessScalarSpecializedError(\"MARKLESS_SCALAR_SPECIALIZED_HOST_MISS\", site); }" : "function marklessScalarSpecializedHostMiss(input, site) { return marklessScalarSpecializedFallback(input, site); }",
|
|
1302
|
+
...resolution === "static" ? [
|
|
1303
|
+
"function marklessScalarSpecializedAction(input) {",
|
|
1304
|
+
...emitStaticScalarMatches(actions),
|
|
1305
|
+
"}"
|
|
1306
|
+
] : emitServedScalarMatches(actions, loadSymbolName, resolution, shapes, routedClosures),
|
|
1307
|
+
"function marklessScalarEventMatches(input, host, tagName, eventName, hostNodeId) {",
|
|
1308
|
+
" const eventTypeMatches = input.event?.type === eventName;",
|
|
1309
|
+
" if (!eventTypeMatches) return false;",
|
|
1310
|
+
" if (!host || (tagName !== \"*\" && host.tagName.toLowerCase() !== tagName)) return false;",
|
|
1311
|
+
" const eventTarget = input.event?.target;",
|
|
1312
|
+
" return host === eventTarget || (!!eventTarget?.nodeType && typeof host.contains === \"function\" && host.contains(eventTarget));",
|
|
1313
|
+
"}",
|
|
1314
|
+
...shapes.length > 0 ? [`const marklessClosureShapes = [${shapes.join(", ")}];`] : [],
|
|
1315
|
+
...actions.map((action) => action.kind === "closure" ? emitClosureAction(action, loadSymbolName, shapes) : emitScalarAction(action, loadSymbolName, resolution)),
|
|
1316
|
+
...fallbackBody
|
|
1317
|
+
].join("\n");
|
|
1318
|
+
}
|
|
1319
|
+
function emitStaticScalarMatches(actions) {
|
|
1320
|
+
return actions.filter((action) => action.kind === "scalar").map((action) => ` if (marklessScalarEventMatches(input, marklessFindElementAtDomOrderIndex(input.root, ${action.hostIndex}), ${JSON.stringify(action.hostTagName.toLowerCase())}, ${JSON.stringify(action.eventName)}, ${JSON.stringify(action.hostNodeId)})) return ${action.name};`);
|
|
1321
|
+
}
|
|
1322
|
+
function emitServedScalarMatches(actions, loadSymbolName, resolution, shapes, routedClosures) {
|
|
1323
|
+
const closures = routedClosures || actions.some((action) => action.kind === "closure");
|
|
1324
|
+
return [
|
|
1325
|
+
`const marklessScalarOwners = ${JSON.stringify(actions.map((action) => [
|
|
1326
|
+
action.hostNodeId,
|
|
1327
|
+
action.eventName,
|
|
1328
|
+
action.symbolId
|
|
1329
|
+
]))};`,
|
|
1330
|
+
`const marklessScalarRuns = [${actions.map((action) => action.name).join(", ")}];`,
|
|
1331
|
+
...closures ? [`const marklessScalarPrimes = [${actions.map((action) => action.kind === "closure" ? `() => marklessPrimeClosure(${emitClosurePlan(action, shapes)}, "", ${loadSymbolName})` : `() => ${loadSymbolName}(${JSON.stringify(action.symbolId)})`).join(", ")}];`] : [],
|
|
1332
|
+
"function marklessScalarSpecializedAction(input) {",
|
|
1333
|
+
" if (input.event === 0) {",
|
|
1334
|
+
...closures ? [` const primed = marklessScalarServedPrime(input.root, input.element, marklessScalarOwners${routedClosures ? ", marklessClosureRouted" : ""});`, ` return primed && (() => Promise.all(primed.map((owner) => typeof owner === "number" ? marklessScalarPrimes[owner]() : marklessPrimeRoutedClosure(owner, marklessClosurePlanRoute, ${loadSymbolName}))).then(() => undefined));`] : [" const primed = marklessScalarServedPrime(input.root, input.element, marklessScalarOwners);", ` return primed && (() => Promise.all(primed.map((index) => ${loadSymbolName}(marklessScalarOwners[index][2]))).then(() => undefined));`],
|
|
1335
|
+
" }",
|
|
1336
|
+
" const owner = input.event && marklessScalarServedOwner(input.root, input.event, marklessScalarOwners);",
|
|
1337
|
+
...routedClosures ? [
|
|
1338
|
+
" if (owner === -1) {",
|
|
1339
|
+
" const record = marklessScalarServedRecord(input.root, input.event);",
|
|
1340
|
+
` if (record && record !== -1 && marklessClosureRouted(record)) return (input) => marklessRunRoutedClosure(input, record, marklessClosurePlanRoute, ${loadSymbolName});`,
|
|
1341
|
+
" }"
|
|
1342
|
+
] : [],
|
|
1343
|
+
...resolution === "served" ? [
|
|
1344
|
+
" if (owner === undefined) {",
|
|
1345
|
+
...emitStaticScalarMatches(actions).map((line) => `\t${line}`),
|
|
1346
|
+
" return;",
|
|
1347
|
+
" }"
|
|
1348
|
+
] : [],
|
|
1349
|
+
" return owner >= 0 ? marklessScalarRuns[owner] : undefined;",
|
|
1350
|
+
"}"
|
|
1351
|
+
];
|
|
1352
|
+
}
|
|
1353
|
+
function emitScalarHostLookup(resolution, hostNodeId, index, tagName) {
|
|
1354
|
+
return resolution === "static" ? `marklessFindElementAtDomOrderIndex(input.root, ${index})` : `marklessScalarLocate(input.root, ${JSON.stringify(hostNodeId)}, ${resolution === "served" ? index : -1}, ${JSON.stringify(tagName.toLowerCase())})`;
|
|
1355
|
+
}
|
|
1356
|
+
function closureShapes(actions) {
|
|
1357
|
+
return [...new Set(actions.flatMap((action) => action.kind === "closure" ? [JSON.stringify(action.plan)] : []))];
|
|
1358
|
+
}
|
|
1359
|
+
function emitClosurePlan(action, shapes) {
|
|
1360
|
+
return `{ symbolId: ${JSON.stringify(action.symbolId)}, ...marklessClosureShapes[${shapes.indexOf(JSON.stringify(action.plan))}] }`;
|
|
1361
|
+
}
|
|
1362
|
+
function emitClosureAction(action, loadSymbolName, shapes) {
|
|
1363
|
+
return [
|
|
1364
|
+
`function ${action.name}(input) {`,
|
|
1365
|
+
` return marklessRunClosureAction(input, ${emitClosurePlan(action, shapes)}, "", ${JSON.stringify(action.hostNodeId)}, ${loadSymbolName}${action.syncPolicy ? `, ${JSON.stringify(action.syncPolicy)}` : ""});`,
|
|
1366
|
+
"}"
|
|
1367
|
+
].join("\n");
|
|
1368
|
+
}
|
|
1369
|
+
function emitScalarAction(action, loadSymbolName, resolution) {
|
|
1370
|
+
return [
|
|
1371
|
+
`async function ${action.name}(input) {`,
|
|
1372
|
+
" let syncPolicyAlreadyApplied = input.syncPolicyAlreadyApplied === true;",
|
|
1373
|
+
" const values = input.root.__marklessEventOnlyGraph || new Map();",
|
|
1374
|
+
" input.root.__marklessEventOnlyGraph = values;",
|
|
1375
|
+
` if (!values.has(${JSON.stringify(action.cell)})) values.set(${JSON.stringify(action.cell)}, marklessDecodeScalarCell(marklessReadScalarCell(input.root, ${JSON.stringify(action.cell)})${resolution === "served-only" ? "" : ` ?? ${JSON.stringify(action.initialCell)}`}, ${JSON.stringify(action.cell)}, ${JSON.stringify(`markless/state cell ${action.cell}`)}));`,
|
|
1376
|
+
` const state = { value: values.get(${JSON.stringify(action.cell)}), dirty: false };`,
|
|
1377
|
+
" try {",
|
|
1378
|
+
` const host = ${emitScalarHostLookup(resolution, action.hostNodeId, action.hostIndex, action.hostTagName)};`,
|
|
1379
|
+
` if (!host || (${JSON.stringify(action.hostTagName.toLowerCase())} !== "*" && host.tagName.toLowerCase() !== ${JSON.stringify(action.hostTagName.toLowerCase())})) return marklessScalarSpecializedHostMiss(input, "host");`,
|
|
1380
|
+
...action.textUpdates.map((update, index) => ` const textTarget${index} = ${emitScalarHostLookup(resolution, update.hostNodeId, update.index, update.tagName)};`),
|
|
1381
|
+
...action.textUpdates.map((update, index) => ` if (!textTarget${index} || (${JSON.stringify(update.tagName.toLowerCase())} !== "*" && textTarget${index}.tagName.toLowerCase() !== ${JSON.stringify(update.tagName.toLowerCase())})) return marklessScalarSpecializedHostMiss(input, "text-target");`),
|
|
1382
|
+
" const graph = {",
|
|
1383
|
+
` hasCell(graphNodeId) { return graphNodeId === ${JSON.stringify(action.cell)}; },`,
|
|
1384
|
+
` read(graphNodeId, path = []) { if (graphNodeId !== ${JSON.stringify(action.cell)} || path.length) return marklessScalarSpecializedError("MARKLESS_SCALAR_SPECIALIZED_ESCALATE", "read"); return state.value; },`,
|
|
1385
|
+
` write(write) { if (write.graphNodeId !== ${JSON.stringify(action.cell)} || (write.path?.length ?? 0)) return marklessScalarSpecializedError("MARKLESS_SCALAR_SPECIALIZED_ESCALATE", "write"); if (!Object.is(state.value, write.value)) { state.value = write.value; values.set(${JSON.stringify(action.cell)}, state.value); state.dirty = true; } },`,
|
|
1386
|
+
` update(update) { if (update.graphNodeId !== ${JSON.stringify(action.cell)} || (update.path?.length ?? 0)) return marklessScalarSpecializedError("MARKLESS_SCALAR_SPECIALIZED_ESCALATE", "update"); const previous = state.value; const next = update.update(previous); if (!Object.is(previous, next)) { state.value = next; values.set(${JSON.stringify(action.cell)}, state.value); state.dirty = true; } return update.returnValue === "previous" ? previous : update.returnValue === "next" ? next : undefined; },`,
|
|
1387
|
+
" call() { return marklessScalarSpecializedError(\"MARKLESS_SCALAR_SPECIALIZED_ESCALATE\", \"call\"); },",
|
|
1388
|
+
" async flush() {},",
|
|
1389
|
+
" };",
|
|
1390
|
+
...action.syncPolicy ? [
|
|
1391
|
+
` const syncPolicy = ${JSON.stringify(action.syncPolicy)};`,
|
|
1392
|
+
" if (syncPolicy && !syncPolicyAlreadyApplied) {",
|
|
1393
|
+
" const { runSyncPolicyActions } = await import('@markless/web/inline/sync-policy-core-lazy');",
|
|
1394
|
+
" runSyncPolicyActions(syncPolicy, graph, input.event);",
|
|
1395
|
+
" syncPolicyAlreadyApplied = true;",
|
|
1396
|
+
" }"
|
|
1397
|
+
] : [],
|
|
1398
|
+
...emitScalarWrite(action),
|
|
1399
|
+
` const symbol = await Promise.resolve(${loadSymbolName}(${JSON.stringify(action.symbolId)}));`,
|
|
1400
|
+
" await Promise.resolve(symbol({ graph: { ...graph, write() {}, update(update) { const value = graph.read(update.graphNodeId, update.path ?? []); return update.returnValue === \"previous\" || update.returnValue === \"next\" ? value : undefined; } }, event: input.event, element: host, getElementHandle: () => undefined }));",
|
|
1401
|
+
" if (state.dirty) {",
|
|
1402
|
+
" state.dirty = false;",
|
|
1403
|
+
...action.textUpdates.map((update, index) => ` textTarget${index}.textContent = marklessUpdateText({ domUpdate: { hostNodeId: ${JSON.stringify(update.hostNodeId)} }, value: ${JSON.stringify(update.prefix ?? "")} + (state.value == null ? '' : String(state.value))${update.suffix ? ` + ${JSON.stringify(update.suffix)}` : ""} }, ${JSON.stringify(update.hostNodeId)}).value;`),
|
|
1404
|
+
" }",
|
|
1405
|
+
" } catch (error) {",
|
|
1406
|
+
" if (error?.code === \"MARKLESS_SCALAR_SPECIALIZED_ESCALATE\") error.syncPolicyAlreadyApplied = syncPolicyAlreadyApplied;",
|
|
1407
|
+
" throw error;",
|
|
1408
|
+
" }",
|
|
1409
|
+
"}"
|
|
1410
|
+
].join("\n");
|
|
1411
|
+
}
|
|
1412
|
+
function emitScalarWrite(action) {
|
|
1413
|
+
if (action.write.kind === "update") return [
|
|
1414
|
+
" marklessWriteScalar({ graph }, {",
|
|
1415
|
+
` graphNodeId: ${JSON.stringify(action.cell)},`,
|
|
1416
|
+
" returnValue: \"next\",",
|
|
1417
|
+
" update(value) {",
|
|
1418
|
+
` return typeof value === "bigint" ? value ${action.write.updateOperator === "--" ? "-" : "+"} BigInt(1) : Number(value) ${action.write.updateOperator === "--" ? "-" : "+"} 1;`,
|
|
1419
|
+
" },",
|
|
1420
|
+
" });"
|
|
1421
|
+
];
|
|
1422
|
+
return [
|
|
1423
|
+
" marklessWriteScalar({ graph }, {",
|
|
1424
|
+
` graphNodeId: ${JSON.stringify(action.cell)},`,
|
|
1425
|
+
` value: ${action.write.valueKind === "undefined" ? "undefined" : JSON.stringify(action.write.value)},`,
|
|
1426
|
+
" });"
|
|
1427
|
+
];
|
|
1428
|
+
}
|
|
1429
|
+
function syncPolicyGraphNodeIds(policy) {
|
|
1430
|
+
if (!policy || typeof policy !== "object") return [];
|
|
1431
|
+
const branches = Array.isArray(policy.branches) ? policy.branches : [policy];
|
|
1432
|
+
return [...new Set(branches.flatMap((branch) => conditionGraphNodeIds(branch.when)))].sort();
|
|
1433
|
+
}
|
|
1434
|
+
function conditionGraphNodeIds(condition) {
|
|
1435
|
+
if (!condition || typeof condition !== "object") return [];
|
|
1436
|
+
const typed = condition;
|
|
1437
|
+
if (typed.type === "graph-truthy" && typeof typed.graphNodeId === "string") return [typed.graphNodeId];
|
|
1438
|
+
if (typed.type === "not") return conditionGraphNodeIds(typed.condition);
|
|
1439
|
+
if ((typed.type === "and" || typed.type === "or") && Array.isArray(typed.conditions)) return typed.conditions.flatMap(conditionGraphNodeIds);
|
|
1440
|
+
return [];
|
|
1441
|
+
}
|
|
1442
|
+
function composedLeanMode(composed, mode) {
|
|
1443
|
+
return composed && mode !== "scalar" ? "none" : mode;
|
|
1444
|
+
}
|
|
1445
|
+
function leanResumeMode(runtimeDemandMap) {
|
|
1446
|
+
const recordKinds = runtimeDemandMap?.recordKinds;
|
|
1447
|
+
if (!recordKinds) return "none";
|
|
1448
|
+
const replaced = new Map(recordKinds.map((record) => [record.kind, record.replaced]));
|
|
1449
|
+
const scalar = replaced.get("event") === true && replaced.get("dom-update") === true;
|
|
1450
|
+
const row = replaced.get("keyed-repeat") === true && replaced.get("dom-update") === true;
|
|
1451
|
+
if (scalar && row) return "mixed";
|
|
1452
|
+
if (scalar) return "scalar";
|
|
1453
|
+
if (row) return "row";
|
|
1454
|
+
return "none";
|
|
1455
|
+
}
|
|
1456
|
+
function emitExecutionLogLoader() {
|
|
1457
|
+
return `globalThis.__mxLoadLog ||= () => import(${JSON.stringify(MARKLESS_EXECUTION_LOG_MODULE_ID)});`;
|
|
1458
|
+
}
|
|
1459
|
+
function emitLoadSymbol(input, sourceReaderName) {
|
|
1460
|
+
if (input.symbols.length > 0 && input.symbols.length <= 8) {
|
|
1461
|
+
const direct = emitDirectSourceSymbolLoader(input.symbols, sourceReaderName);
|
|
1462
|
+
if (!input.hasBoundSymbols) return direct;
|
|
1463
|
+
return [`const marklessSymbolResolverModule = () => import('${input.resolverId}');`, direct.replace(" return Promise.reject(new Error(`Unknown async symbol ${symbolId}`));", " return marklessSymbolResolverModule().then((mod) => mod.loadSymbol(symbolId));")].join("\n");
|
|
1464
|
+
}
|
|
1465
|
+
return [
|
|
1466
|
+
`const marklessSymbolResolverModule = () => import('${input.resolverId}');`,
|
|
1467
|
+
"function loadSymbol(symbolId) {",
|
|
1468
|
+
" return marklessSymbolResolverModule().then((mod) => mod.loadSymbol(symbolId));",
|
|
1469
|
+
"}"
|
|
1470
|
+
].join("\n");
|
|
1471
|
+
}
|
|
1472
|
+
function emitDirectSourceSymbolLoader(symbols, readerName = "readMarklessSourceSymbol") {
|
|
1473
|
+
return [
|
|
1474
|
+
"function loadSymbol(symbolId) {",
|
|
1475
|
+
...symbols.flatMap((symbol) => [` if (symbolId === ${JSON.stringify(symbol.id)}) return import('${symbol.lazyChunk ?? symbol.chunk}')`, ` .then((mod) => ${readerName}(mod, ${JSON.stringify(symbol.exportName)}));`]),
|
|
1476
|
+
" return Promise.reject(new Error(`Unknown async symbol ${symbolId}`));",
|
|
1477
|
+
"}",
|
|
1478
|
+
emitSourceSymbolExportReader(readerName)
|
|
1479
|
+
].join("\n");
|
|
1480
|
+
}
|
|
1481
|
+
function emitSourceSymbolExportReader(name = "readMarklessSourceSymbol") {
|
|
1482
|
+
return [
|
|
1483
|
+
`function ${name}(mod, exportName) {`,
|
|
1484
|
+
" mod.init__virtual_markless_symbol?.();",
|
|
1485
|
+
" return mod[exportName];",
|
|
1486
|
+
"}"
|
|
1487
|
+
].join("\n");
|
|
1488
|
+
}
|
|
1489
|
+
function stringHash(value) {
|
|
1490
|
+
let hash = 2166136261;
|
|
1491
|
+
for (let index = 0; index < value.length; index++) {
|
|
1492
|
+
hash ^= value.charCodeAt(index);
|
|
1493
|
+
hash = Math.imul(hash, 16777619);
|
|
1494
|
+
}
|
|
1495
|
+
return (hash >>> 0).toString(36);
|
|
1496
|
+
}
|
|
1497
|
+
const MARKLESS_BUILD_PREFIX = `build/`;
|
|
1498
|
+
const MARKLESS_BUNDLE_GRAPH = `${MARKLESS_BUILD_PREFIX}bundle-graph.json`;
|
|
1499
|
+
const MARKLESS_EXECUTION_SIZES = `${MARKLESS_BUILD_PREFIX}execution-sizes.json`;
|
|
1500
|
+
const MARKLESS_EXECUTION_DEMAND = `${MARKLESS_BUILD_PREFIX}execution-demand.json`;
|
|
1501
|
+
const MARKLESS_INTERACTION_CLOSURES = `${MARKLESS_BUILD_PREFIX}interaction-closures.json`;
|
|
1502
|
+
const MARKLESS_BYTE_ATTRIBUTION = `${MARKLESS_BUILD_PREFIX}byte-attribution.json`;
|
|
1503
|
+
const MARKLESS_BUILD_METADATA_FILES = [
|
|
1504
|
+
MARKLESS_BUNDLE_GRAPH,
|
|
1505
|
+
MARKLESS_EXECUTION_SIZES,
|
|
1506
|
+
MARKLESS_EXECUTION_DEMAND,
|
|
1507
|
+
MARKLESS_INTERACTION_CLOSURES,
|
|
1508
|
+
MARKLESS_BYTE_ATTRIBUTION
|
|
1509
|
+
];
|
|
1510
|
+
const WEB_RUNTIME_CAPABILITY_GROUPS = [
|
|
1511
|
+
{
|
|
1512
|
+
name: "markless-direct-renderer",
|
|
1513
|
+
test: fileBasenamePattern("direct")
|
|
1514
|
+
},
|
|
1515
|
+
{
|
|
1516
|
+
name: "markless-resume-branches",
|
|
1517
|
+
test: fileBasenamePattern("resume-branches", "resume-arm-records-lazy")
|
|
1518
|
+
},
|
|
1519
|
+
{
|
|
1520
|
+
name: "markless-resume-behaviors",
|
|
1521
|
+
test: fileBasenamePattern("resume-behaviors")
|
|
1522
|
+
},
|
|
1523
|
+
{
|
|
1524
|
+
name: "markless-resume-repeats",
|
|
1525
|
+
test: fileBasenamePattern("resume-keyed-repeats", "resume-keyed-repeats-lazy", "repeat-runtime")
|
|
1526
|
+
},
|
|
1527
|
+
{
|
|
1528
|
+
name: "markless-resume-async",
|
|
1529
|
+
test: fileBasenamePattern("resume-async-boundaries", "resume-async-wiring")
|
|
1530
|
+
},
|
|
1531
|
+
{
|
|
1532
|
+
name: "markless-resume-shared-patch",
|
|
1533
|
+
test: fileBasenamePattern("resume-shared-patch")
|
|
1534
|
+
},
|
|
1535
|
+
{
|
|
1536
|
+
name: "markless-resume-runtime",
|
|
1537
|
+
test: fileBasenamePattern("resume-runtime")
|
|
1538
|
+
},
|
|
1539
|
+
{
|
|
1540
|
+
name: "markless-resume-runtime-start",
|
|
1541
|
+
test: fileBasenamePattern("resume-runtime-start")
|
|
1542
|
+
},
|
|
1543
|
+
{
|
|
1544
|
+
name: "markless-resume-runtime-shared",
|
|
1545
|
+
test: fileBasenamePattern("resume-runtime-shared")
|
|
1546
|
+
},
|
|
1547
|
+
{
|
|
1548
|
+
name: "markless-resume-events",
|
|
1549
|
+
test: fileBasenamePattern("resume-events")
|
|
1550
|
+
},
|
|
1551
|
+
{
|
|
1552
|
+
name: "markless-resume-handoff",
|
|
1553
|
+
test: fileBasenamePattern("resume-handoff")
|
|
1554
|
+
},
|
|
1555
|
+
{
|
|
1556
|
+
name: "markless-resume-locators",
|
|
1557
|
+
test: fileBasenamePattern("resume-locators", "resume-census")
|
|
1558
|
+
},
|
|
1559
|
+
{
|
|
1560
|
+
name: "markless-resume-errors",
|
|
1561
|
+
test: fileBasenamePattern("resume-errors")
|
|
1562
|
+
},
|
|
1563
|
+
{
|
|
1564
|
+
name: "markless-resume-sync-computed",
|
|
1565
|
+
test: fileBasenamePattern("resume-sync-computed")
|
|
1566
|
+
},
|
|
1567
|
+
{
|
|
1568
|
+
name: "markless-resume-sync-demand",
|
|
1569
|
+
test: fileBasenamePattern("resume-sync-demand")
|
|
1570
|
+
},
|
|
1571
|
+
{
|
|
1572
|
+
name: "markless-payload-full",
|
|
1573
|
+
test: fileBasenamePattern("payload-full", "payload-full-storage-free")
|
|
1574
|
+
},
|
|
1575
|
+
{
|
|
1576
|
+
name: "markless-payload-resume",
|
|
1577
|
+
test: fileBasenamePattern("payload-resume", "payload-resume-lazy")
|
|
1578
|
+
},
|
|
1579
|
+
{
|
|
1580
|
+
name: "markless-inline-payload-document",
|
|
1581
|
+
test: /[/\\]web[/\\]src[/\\]inline[/\\]payload-document\.ts(?:[?#].*)?$/
|
|
1582
|
+
},
|
|
1583
|
+
{
|
|
1584
|
+
name: "markless-payload-document",
|
|
1585
|
+
test: fileBasenamePattern("payload-document", "payload-document-common", "payload-resume-registry")
|
|
1586
|
+
},
|
|
1587
|
+
{
|
|
1588
|
+
name: "markless-payload-graph-construct",
|
|
1589
|
+
test: fileBasenamePattern("payload-graph-construct")
|
|
1590
|
+
},
|
|
1591
|
+
{
|
|
1592
|
+
name: "markless-dom-journal",
|
|
1593
|
+
test: fileBasenamePattern("dom-journal")
|
|
1594
|
+
},
|
|
1595
|
+
{
|
|
1596
|
+
name: "markless-protocol-decode",
|
|
1597
|
+
test: fileBasenamePattern("protocol-client", "protocol-client-storage", "protocol-state", "storage-record-client")
|
|
1598
|
+
},
|
|
1599
|
+
{
|
|
1600
|
+
name: "markless-value-decode",
|
|
1601
|
+
test: fileBasenamePattern("value-decode-client", "value-decode-client-lazy", "value-decode-extensions")
|
|
1602
|
+
},
|
|
1603
|
+
{
|
|
1604
|
+
name: "markless-payload-leaves",
|
|
1605
|
+
test: fileBasenamePattern("payload", "payload-document")
|
|
1606
|
+
},
|
|
1607
|
+
{
|
|
1608
|
+
name: "markless-dev-log",
|
|
1609
|
+
test: EXECUTION_LOG_CHUNK_MODULE_PATTERN
|
|
1610
|
+
},
|
|
1611
|
+
{
|
|
1612
|
+
name: "markless-resume-core",
|
|
1613
|
+
test: fileBasenamePattern("resume")
|
|
1614
|
+
},
|
|
1615
|
+
{
|
|
1616
|
+
name: "markless-runtime-graph-core",
|
|
1617
|
+
test: fileBasenamePattern("graph-core")
|
|
1618
|
+
},
|
|
1619
|
+
{
|
|
1620
|
+
name: "markless-runtime-graph-collections",
|
|
1621
|
+
test: fileBasenamePattern("graph-collections")
|
|
1622
|
+
},
|
|
1623
|
+
{
|
|
1624
|
+
name: "markless-runtime-graph-computed",
|
|
1625
|
+
test: fileBasenamePattern("graph-computed")
|
|
1626
|
+
},
|
|
1627
|
+
{
|
|
1628
|
+
name: "markless-runtime-graph-async",
|
|
1629
|
+
test: fileBasenamePattern("graph-async")
|
|
1630
|
+
},
|
|
1631
|
+
{
|
|
1632
|
+
name: "markless-runtime-graph-scheduler",
|
|
1633
|
+
test: fileBasenamePattern("graph-scheduler")
|
|
1634
|
+
},
|
|
1635
|
+
{
|
|
1636
|
+
name: "markless-runtime-graph-shared",
|
|
1637
|
+
test: fileBasenamePattern("graph-shared")
|
|
1638
|
+
},
|
|
1639
|
+
{
|
|
1640
|
+
name: "markless-graph",
|
|
1641
|
+
test: /[/\\](?:runtime[/\\]src[/\\](?!graph-(?:core|collections|computed|async|scheduler|shared)\.ts)|core[/\\]src[/\\]runtime)[/\\]?/
|
|
1642
|
+
},
|
|
1643
|
+
{
|
|
1644
|
+
name: "markless-serializer",
|
|
1645
|
+
test: /[/\\]serializer[/\\]src[/\\](?!protocol-validation)/
|
|
1646
|
+
}
|
|
1647
|
+
];
|
|
1648
|
+
function fileBasenamePattern(...names) {
|
|
1649
|
+
return new RegExp(`[/\\\\](?:${names.join("|")})\\.ts(?:[?#].*)?$`);
|
|
1650
|
+
}
|
|
1651
|
+
const MARKLESS_RUNTIME_GROUPS = [...WEB_RUNTIME_CAPABILITY_GROUPS, {
|
|
1652
|
+
name: "markless-runtime",
|
|
1653
|
+
test: /[/\\]@markless[/\\]runtime[/\\]/
|
|
1654
|
+
}];
|
|
1655
|
+
function outputDefaults(output, environment) {
|
|
1656
|
+
if (environment === "lib") return output;
|
|
1657
|
+
const next = {
|
|
1658
|
+
...output,
|
|
1659
|
+
hoistTransitiveImports: false
|
|
1660
|
+
};
|
|
1661
|
+
if (environment === "server") {
|
|
1662
|
+
next.entryFileNames ??= "[name].js";
|
|
1663
|
+
next.chunkFileNames ??= "chunk-[hash].js";
|
|
1664
|
+
next.codeSplitting = marklessCodeSplitting(next.codeSplitting);
|
|
1665
|
+
return next;
|
|
1666
|
+
}
|
|
1667
|
+
next.entryFileNames ??= `${MARKLESS_BUILD_PREFIX}chunk-[hash].js`;
|
|
1668
|
+
next.chunkFileNames ??= `${MARKLESS_BUILD_PREFIX}chunk-[hash].js`;
|
|
1669
|
+
next.minifyInternalExports = true;
|
|
1670
|
+
next.strictExecutionOrder = true;
|
|
1671
|
+
next.codeSplitting = marklessCodeSplitting(next.codeSplitting);
|
|
1672
|
+
return next;
|
|
1673
|
+
}
|
|
1674
|
+
function marklessCodeSplitting(codeSplitting) {
|
|
1675
|
+
if (typeof codeSplitting === "boolean") throw new Error("@markless/bundler requires output.codeSplitting to be an object so runtime chunks can be grouped.");
|
|
1676
|
+
return {
|
|
1677
|
+
...codeSplitting,
|
|
1678
|
+
groups: [...MARKLESS_RUNTIME_GROUPS, ...codeSplitting?.groups ?? []]
|
|
1679
|
+
};
|
|
1680
|
+
}
|
|
1681
|
+
//#endregion
|
|
1682
|
+
//#region src/bound-resolver.ts
|
|
1683
|
+
function adaptImportedCaptureResolver(source, hasImportedRows) {
|
|
1684
|
+
if (!hasImportedRows) return source;
|
|
1685
|
+
const original = " return (context) => base({ ...context, capture: createCaptureContext(context, bound) });";
|
|
1686
|
+
const replacement = [
|
|
1687
|
+
" return async (context) => {",
|
|
1688
|
+
" const pendingCallbacks = [];",
|
|
1689
|
+
" const capture = createCaptureContext(context, bound);",
|
|
1690
|
+
" const result = await base({ ...context, graph: createBoundGraph(context, bound, capture, pendingCallbacks), capture });",
|
|
1691
|
+
" await Promise.all(pendingCallbacks);",
|
|
1692
|
+
" return result;",
|
|
1693
|
+
" };"
|
|
1694
|
+
].join("\n");
|
|
1695
|
+
const helper = [
|
|
1696
|
+
"function createBoundGraph(context, bound, capture, pendingCallbacks) {",
|
|
1697
|
+
" const legacySlots = new Map(bound.captureSlots.flatMap((slot) => slot.legacyGraphRead ? [[JSON.stringify([slot.legacyGraphRead.graphNodeId, slot.legacyGraphRead.path ?? []]), slot]] : []));",
|
|
1698
|
+
" let lastCallback = Promise.resolve();",
|
|
1699
|
+
" return {",
|
|
1700
|
+
" ...context.graph,",
|
|
1701
|
+
" read(graphNodeId, path = []) {",
|
|
1702
|
+
" const slot = legacySlots.get(JSON.stringify([graphNodeId, path]));",
|
|
1703
|
+
" if (!slot) return context.graph.read(graphNodeId, path);",
|
|
1704
|
+
" if (slot.route.kind === callbackRoute) return (...args) => {",
|
|
1705
|
+
" const pending = lastCallback.then(() => capture.invoke(slot.slotId, args));",
|
|
1706
|
+
" lastCallback = pending.catch(() => {});",
|
|
1707
|
+
" pendingCallbacks.push(pending);",
|
|
1708
|
+
" return pending;",
|
|
1709
|
+
" };",
|
|
1710
|
+
" return capture.read(slot.slotId);",
|
|
1711
|
+
" },",
|
|
1712
|
+
" };",
|
|
1713
|
+
"}",
|
|
1714
|
+
""
|
|
1715
|
+
].join("\n");
|
|
1716
|
+
return source.replace(original, replacement).replace("function createCaptureContext(context, bound) {", `${helper}function createCaptureContext(context, bound) {`);
|
|
1717
|
+
}
|
|
1718
|
+
//#endregion
|
|
1719
|
+
//#region src/trigger-groups.ts
|
|
1720
|
+
const EVENT_STAGES_BROWSER_TRIGGER = {
|
|
1721
|
+
[PROTOCOL_EVENT_ACTION_KIND.event]: true,
|
|
1722
|
+
[PROTOCOL_EVENT_ACTION_KIND.externalDelegate]: false
|
|
1723
|
+
};
|
|
1724
|
+
function triggerGroupVirtualModuleId(filename, index) {
|
|
1725
|
+
return `virtual:markless:trigger-group:${encodeURIComponent(filename)}:${index}`;
|
|
1726
|
+
}
|
|
1727
|
+
function triggerGroupVirtualModuleSourceFile(moduleId) {
|
|
1728
|
+
const bare = moduleId.startsWith("\0") ? moduleId.slice(1) : moduleId;
|
|
1729
|
+
const match = /^virtual:markless:trigger-group:([^:]+):\d+$/.exec(bare);
|
|
1730
|
+
if (!match?.[1]) return null;
|
|
1731
|
+
try {
|
|
1732
|
+
return decodeURIComponent(match[1]);
|
|
1733
|
+
} catch {
|
|
1734
|
+
return null;
|
|
1735
|
+
}
|
|
1736
|
+
}
|
|
1737
|
+
function emitPrerenderTriggerGroupModule(input) {
|
|
1738
|
+
const armRendererModuleId = input.armRendererModuleId ?? input.group.armRendererModuleId;
|
|
1739
|
+
const ids = new Set(input.group.symbolIds);
|
|
1740
|
+
const boundRows = input.boundRows.filter((row) => ids.has(row.id));
|
|
1741
|
+
let source = emitSymbolResolverModule({
|
|
1742
|
+
symbols: input.symbols.filter((symbol) => ids.has(symbol.id)),
|
|
1743
|
+
boundSymbols: boundRows,
|
|
1744
|
+
literalImports: input.literalImports,
|
|
1745
|
+
bundlerVisibleImports: input.literalImports
|
|
1746
|
+
});
|
|
1747
|
+
const routes = (input.symbolRoutes ?? []).map((route) => route.boundSymbols ? {
|
|
1748
|
+
...route,
|
|
1749
|
+
boundSymbols: Object.fromEntries(Object.entries(route.boundSymbols).filter(([, rowId]) => ids.has(rowId)))
|
|
1750
|
+
} : route);
|
|
1751
|
+
if (routes.length > 0) {
|
|
1752
|
+
source = source.replaceAll("loadSymbol(", "marklessLoadLocalSymbol(");
|
|
1753
|
+
source += [
|
|
1754
|
+
emitReboundSymbolImport(routes, "marklessLoadSymbolRebound"),
|
|
1755
|
+
"export function loadSymbol(symbolId) {",
|
|
1756
|
+
...routes.flatMap((route) => {
|
|
1757
|
+
const unbound = "importSource" in route ? `import(${JSON.stringify(symbolRouteImportSource(route.importSource))}).then((mod) => mod.loadSymbol ? mod.loadSymbol(symbolId.slice(${route.prefix.length})) : Promise.reject(new Error(\`Unknown child async symbol \${symbolId}\`)))` : `${route.prefix.length > 0 ? "loadSymbol" : "marklessLoadLocalSymbol"}(symbolId.slice(${route.prefix.length}))`;
|
|
1758
|
+
return [
|
|
1759
|
+
`\tif (symbolId.startsWith(${JSON.stringify(route.prefix)})) {`,
|
|
1760
|
+
...emitReboundSymbolRoute(route, "marklessLoadSymbolRebound", "marklessLoadLocalSymbol", unbound, " "),
|
|
1761
|
+
`\t\treturn ${unbound};`,
|
|
1762
|
+
" }"
|
|
1763
|
+
];
|
|
1764
|
+
}),
|
|
1765
|
+
" return marklessLoadLocalSymbol(symbolId);",
|
|
1766
|
+
"}"
|
|
1767
|
+
].filter((line) => line !== "").join("\n");
|
|
1768
|
+
}
|
|
1769
|
+
return [
|
|
1770
|
+
armRendererModuleId ? [
|
|
1771
|
+
`import { prepareBoundaryArm as marklessPrepareBoundaryArm, renderBoundaryArm as marklessRenderBoundaryArm } from ${JSON.stringify(armRendererModuleId)};`,
|
|
1772
|
+
"export function prepareBoundaryArm(boundaryId, status, graph) {",
|
|
1773
|
+
" return marklessPrepareBoundaryArm(boundaryId, status, graph, loadSymbol);",
|
|
1774
|
+
"}",
|
|
1775
|
+
"export function renderBoundaryArm(boundaryId, status, graph) {",
|
|
1776
|
+
" return marklessRenderBoundaryArm(boundaryId, status, graph, loadSymbol);",
|
|
1777
|
+
"}"
|
|
1778
|
+
].join("\n") : "",
|
|
1779
|
+
adaptImportedCaptureResolver(source, boundRows.some((row) => row.loaderSymbolId !== void 0)),
|
|
1780
|
+
`export const groupId=${JSON.stringify(input.group.id)};`,
|
|
1781
|
+
`export const state=${JSON.stringify(input.group.state)};`,
|
|
1782
|
+
`export const view=${JSON.stringify(input.group.view)};`,
|
|
1783
|
+
`export const graphNodeIds=${JSON.stringify(input.group.graphNodeIds)};`
|
|
1784
|
+
].join("\n");
|
|
1785
|
+
}
|
|
1786
|
+
function emitPrerenderBoundaryRendererModule(renderDataId) {
|
|
1787
|
+
return [
|
|
1788
|
+
`import { marklessPrerenderData } from ${JSON.stringify(renderDataId)};`,
|
|
1789
|
+
"import { renderPrerenderBoundary } from '@markless/web/fns/prerender-resume';",
|
|
1790
|
+
"const marklessPreparedBoundaryArms = new WeakMap();",
|
|
1791
|
+
"export async function prepareBoundaryArm(boundaryId, status, graph, loadSymbol) {",
|
|
1792
|
+
" const rendered = await renderPrerenderBoundary(marklessPrerenderData, boundaryId, status, graph, loadSymbol);",
|
|
1793
|
+
" if (!marklessPreparedBoundaryArms.has(graph)) marklessPreparedBoundaryArms.set(graph, new Map());",
|
|
1794
|
+
" marklessPreparedBoundaryArms.get(graph).set(`${boundaryId}:${status}`, rendered);",
|
|
1795
|
+
"}",
|
|
1796
|
+
"export function renderBoundaryArm(boundaryId, status, graph) {",
|
|
1797
|
+
" const key = `${boundaryId}:${status}`;",
|
|
1798
|
+
" const prepared = marklessPreparedBoundaryArms.get(graph);",
|
|
1799
|
+
" const rendered = prepared?.get(key);",
|
|
1800
|
+
" if (!rendered) throw new Error(`Markless prerender arm ${key} was not prepared.`);",
|
|
1801
|
+
" prepared.delete(key);",
|
|
1802
|
+
" return rendered;",
|
|
1803
|
+
"}"
|
|
1804
|
+
].join("\n");
|
|
1805
|
+
}
|
|
1806
|
+
function symbolRouteImportSource(importSource) {
|
|
1807
|
+
return importSource.includes("?") ? `${importSource}&markless-symbols` : `${importSource}?markless-symbols`;
|
|
1808
|
+
}
|
|
1809
|
+
function planPrerenderTriggerGroups(input) {
|
|
1810
|
+
const symbols = new Map(input.symbolResolver.symbols.map((symbol) => [symbol.id, symbol]));
|
|
1811
|
+
const bounds = new Map(input.boundRows.map((row) => [row.id, row]));
|
|
1812
|
+
const completeView = input.completeView ?? input.view;
|
|
1813
|
+
const completeBoundaries = new Map(completeView.asyncBoundaries.map((boundary) => [boundary.id, boundary]));
|
|
1814
|
+
const selfWakeBoundaries = unsettledAsyncBoundaryIndexes(input.state, input.view);
|
|
1815
|
+
const groups = [
|
|
1816
|
+
...selfWakeBoundaries.length > 0 ? [{
|
|
1817
|
+
event: {
|
|
1818
|
+
hostNodeId: "self-wake",
|
|
1819
|
+
eventName: "self-wake",
|
|
1820
|
+
symbolIds: []
|
|
1821
|
+
},
|
|
1822
|
+
selfWakeBoundaries
|
|
1823
|
+
}] : [],
|
|
1824
|
+
...input.view.events.filter((event) => EVENT_STAGES_BROWSER_TRIGGER[protocolEventActionKind(event)]).map((event) => ({ event })),
|
|
1825
|
+
...(input.view.branches ?? []).flatMap((branch, branchIndex) => {
|
|
1826
|
+
const events = /* @__PURE__ */ new Map();
|
|
1827
|
+
for (const arm of branch.armRecords ?? []) for (const event of arm.events) {
|
|
1828
|
+
const key = `${event.eventName}:${event.hostPath.join(".")}`;
|
|
1829
|
+
const route = events.get(key) ?? {
|
|
1830
|
+
eventName: event.eventName,
|
|
1831
|
+
hostPath: event.hostPath,
|
|
1832
|
+
symbolIds: /* @__PURE__ */ new Set()
|
|
1833
|
+
};
|
|
1834
|
+
for (const symbolId of event.symbolIds ?? []) route.symbolIds.add(symbolId);
|
|
1835
|
+
events.set(key, route);
|
|
1836
|
+
}
|
|
1837
|
+
return [...events.values()].map((route) => ({
|
|
1838
|
+
event: {
|
|
1839
|
+
hostNodeId: `branch:${branch.id}:${route.hostPath.join(".")}`,
|
|
1840
|
+
eventName: route.eventName,
|
|
1841
|
+
symbolIds: [...route.symbolIds]
|
|
1842
|
+
},
|
|
1843
|
+
branch,
|
|
1844
|
+
branchIndex,
|
|
1845
|
+
hostPath: route.hostPath
|
|
1846
|
+
}));
|
|
1847
|
+
})
|
|
1848
|
+
].flatMap(({ event, branch, branchIndex, hostPath, selfWakeBoundaries }) => {
|
|
1849
|
+
const closureView = selfWakeBoundaries ? completeView : input.view;
|
|
1850
|
+
const host = branch ? void 0 : input.view.locators.find((locator) => locator.hostNodeId === event.hostNodeId);
|
|
1851
|
+
if (!host && !branch && !selfWakeBoundaries) return [];
|
|
1852
|
+
const compilerGroup = input.triggerGroups.groups.find((group) => group.hostNodeId === event.hostNodeId && group.eventName === event.eventName);
|
|
1853
|
+
const graphNodeIds = /* @__PURE__ */ new Set();
|
|
1854
|
+
const symbolIds = new Set(event.symbolIds ?? []);
|
|
1855
|
+
const selected = {
|
|
1856
|
+
domUpdates: /* @__PURE__ */ new Set(),
|
|
1857
|
+
behaviors: /* @__PURE__ */ new Set(),
|
|
1858
|
+
branches: /* @__PURE__ */ new Set(),
|
|
1859
|
+
boundaries: /* @__PURE__ */ new Set(),
|
|
1860
|
+
repeats: /* @__PURE__ */ new Set(),
|
|
1861
|
+
elementHandles: /* @__PURE__ */ new Set()
|
|
1862
|
+
};
|
|
1863
|
+
if (branch && branchIndex !== void 0) {
|
|
1864
|
+
selected.branches.add(branchIndex);
|
|
1865
|
+
if (branch.symbolId) symbolIds.add(branch.symbolId);
|
|
1866
|
+
for (const read of branch.testReads ?? []) graphNodeIds.add(read.graphNodeId);
|
|
1867
|
+
for (const arm of branch.armRecords ?? []) collectCompleteArmRecordClosure(arm, graphNodeIds, symbolIds);
|
|
1868
|
+
}
|
|
1869
|
+
const adoptedBoundaryIds = /* @__PURE__ */ new Set();
|
|
1870
|
+
const escalatedHostNodeIds = /* @__PURE__ */ new Set();
|
|
1871
|
+
for (const boundaryIndex of selfWakeBoundaries ?? []) {
|
|
1872
|
+
const servedBoundary = input.view.asyncBoundaries[boundaryIndex];
|
|
1873
|
+
selected.boundaries.add(boundaryIndex);
|
|
1874
|
+
collectSettledBoundaryClosure(input, completeBoundaries.get(servedBoundary.id) ?? servedBoundary, graphNodeIds, symbolIds);
|
|
1875
|
+
}
|
|
1876
|
+
closureView.behaviors.forEach((behavior, index) => {
|
|
1877
|
+
if (selfWakeBoundaries) return;
|
|
1878
|
+
const recordId = `behavior:${behavior.hostNodeId}:${behavior.symbolId ?? ""}`;
|
|
1879
|
+
if ((behavior.inputGraphReads?.length ?? 0) > 0 && !compilerGroup?.payloadRecordIds.includes(recordId)) return;
|
|
1880
|
+
selected.behaviors.add(index);
|
|
1881
|
+
if (behavior.symbolId) symbolIds.add(behavior.symbolId);
|
|
1882
|
+
});
|
|
1883
|
+
let changed = true;
|
|
1884
|
+
while (changed) {
|
|
1885
|
+
const before = closureSize(graphNodeIds, symbolIds, selected);
|
|
1886
|
+
const currentSymbolIds = Array.from(symbolIds);
|
|
1887
|
+
for (const symbolId of currentSymbolIds) {
|
|
1888
|
+
const bound = bounds.get(symbolId);
|
|
1889
|
+
if (bound) {
|
|
1890
|
+
symbolIds.add(bound.loaderSymbolId ?? bound.baseSymbolId);
|
|
1891
|
+
for (const slot of bound.captureSlots) {
|
|
1892
|
+
if (slot.route.kind === "graph-reference") graphNodeIds.add(slot.route.graphNodeId);
|
|
1893
|
+
if (slot.route.kind === "callback-route") symbolIds.add(slot.route.callbackSymbolId);
|
|
1894
|
+
}
|
|
1895
|
+
}
|
|
1896
|
+
const symbol = symbols.get(symbolId);
|
|
1897
|
+
if (!symbol) continue;
|
|
1898
|
+
if ("reads" in symbol) for (const read of symbol.reads ?? []) graphNodeIds.add(read.graphNodeId);
|
|
1899
|
+
if ("writes" in symbol) for (const write of symbol.writes ?? []) graphNodeIds.add(write.graphNodeId);
|
|
1900
|
+
if (symbol.kind === "async-computed-runner" || symbol.kind === "sync-computed-derive") {
|
|
1901
|
+
graphNodeIds.add(symbol.graphNodeId);
|
|
1902
|
+
for (const dependency of symbol.dependencies ?? []) graphNodeIds.add(dependency.graphNodeId);
|
|
1903
|
+
}
|
|
1904
|
+
if ("elementHandleCalls" in symbol) for (const call of symbol.elementHandleCalls ?? []) closureView.elementHandles.forEach((handle, index) => {
|
|
1905
|
+
if (handle.name === call.handleName) selected.elementHandles.add(index);
|
|
1906
|
+
});
|
|
1907
|
+
if ("elementHandleReads" in symbol) for (const read of symbol.elementHandleReads ?? []) closureView.elementHandles.forEach((handle, index) => {
|
|
1908
|
+
if (handle.handleId === read.handleId || handle.name === read.handleName) selected.elementHandles.add(index);
|
|
1909
|
+
});
|
|
1910
|
+
}
|
|
1911
|
+
closeComputedGraph(graphNodeIds, input.state);
|
|
1912
|
+
for (const computed of input.state.computed) if (computed.deriveSymbolId && graphNodeIds.has(computed.graphNodeId)) symbolIds.add(computed.deriveSymbolId);
|
|
1913
|
+
for (const graphNodeId of graphNodeIds) {
|
|
1914
|
+
const runnerSymbolId = closureView.asyncRunners?.[graphNodeId];
|
|
1915
|
+
if (runnerSymbolId) symbolIds.add(runnerSymbolId);
|
|
1916
|
+
}
|
|
1917
|
+
selectViewSubscribers(closureView, graphNodeIds, symbolIds, selected);
|
|
1918
|
+
const escalatedBranchIds = /* @__PURE__ */ new Set();
|
|
1919
|
+
for (const index of selected.branches) {
|
|
1920
|
+
const record = closureView.branches?.[index];
|
|
1921
|
+
if (record?.escalates) escalatedBranchIds.add(record.id);
|
|
1922
|
+
}
|
|
1923
|
+
for (const index of selected.boundaries) {
|
|
1924
|
+
const served = closureView.asyncBoundaries[index];
|
|
1925
|
+
for (const arm of boundaryArmRecordSets(completeBoundaries.get(served.id) ?? served)) collectEscalatedArmBranchIds(arm, escalatedBranchIds);
|
|
1926
|
+
}
|
|
1927
|
+
for (const branchId of escalatedBranchIds) collectEscalatedBranchClosure(input, branchId, graphNodeIds, symbolIds, escalatedHostNodeIds);
|
|
1928
|
+
if (!selfWakeBoundaries) input.view.asyncBoundaries.forEach((servedBoundary, index) => {
|
|
1929
|
+
if (adoptedBoundaryIds.has(servedBoundary.id)) return;
|
|
1930
|
+
const boundary = completeBoundaries.get(servedBoundary.id) ?? servedBoundary;
|
|
1931
|
+
if (!settledArmReadGraphNodeIds(input, boundary).some((graphNodeId) => graphNodeIds.has(graphNodeId))) return;
|
|
1932
|
+
adoptedBoundaryIds.add(servedBoundary.id);
|
|
1933
|
+
selected.boundaries.add(index);
|
|
1934
|
+
collectSettledBoundaryClosure(input, boundary, graphNodeIds, symbolIds);
|
|
1935
|
+
collectSettledArmHostRecordClosure(completeView, boundary, graphNodeIds, symbolIds);
|
|
1936
|
+
});
|
|
1937
|
+
if (selfWakeBoundaries || adoptedBoundaryIds.size > 0) {
|
|
1938
|
+
for (const symbol of symbols.values()) if (symbol.kind === "state-initializer" && graphNodeIds.has(symbol.graphNodeId)) symbolIds.add(symbol.id);
|
|
1939
|
+
}
|
|
1940
|
+
changed = closureSize(graphNodeIds, symbolIds, selected) !== before;
|
|
1941
|
+
}
|
|
1942
|
+
dropEscalatedHostRecords(closureView, selected, escalatedHostNodeIds);
|
|
1943
|
+
const neededHosts = new Set(branch ? [] : [event.hostNodeId]);
|
|
1944
|
+
for (const index of selected.domUpdates) neededHosts.add(closureView.domUpdates[index].hostNodeId);
|
|
1945
|
+
for (const index of selected.behaviors) neededHosts.add(closureView.behaviors[index].hostNodeId);
|
|
1946
|
+
for (const index of selected.repeats) neededHosts.add(closureView.keyedRepeats[index].parentHostNodeId);
|
|
1947
|
+
for (const index of selected.elementHandles) neededHosts.add(closureView.elementHandles[index].hostNodeId);
|
|
1948
|
+
const selectedBoundaryIds = new Set([...selected.boundaries].map((index) => closureView.asyncBoundaries[index].id));
|
|
1949
|
+
const view = {
|
|
1950
|
+
...input.view,
|
|
1951
|
+
locators: closureView.locators.filter((locator) => neededHosts.has(locator.hostNodeId)),
|
|
1952
|
+
events: branch || selfWakeBoundaries ? [] : [event],
|
|
1953
|
+
domUpdates: closureView.domUpdates.filter((_, index) => selected.domUpdates.has(index)),
|
|
1954
|
+
behaviors: closureView.behaviors.filter((_, index) => selected.behaviors.has(index)),
|
|
1955
|
+
elementHandles: closureView.elementHandles.filter((_, index) => selected.elementHandles.has(index)),
|
|
1956
|
+
keyedRepeats: closureView.keyedRepeats?.filter((_, index) => selected.repeats.has(index)),
|
|
1957
|
+
branches: closureView.branches?.filter((_, index) => selected.branches.has(index)),
|
|
1958
|
+
asyncBoundaries: closureView.asyncBoundaries.filter((boundary) => selectedBoundaryIds.has(boundary.id)).map((boundary) => adoptedBoundaryIds.has(boundary.id) ? completeBoundaries.get(boundary.id) ?? boundary : boundary),
|
|
1959
|
+
...closureView.asyncRunners ? { asyncRunners: Object.fromEntries(Object.entries(closureView.asyncRunners).filter(([graphNodeId]) => graphNodeIds.has(graphNodeId))) } : {}
|
|
1960
|
+
};
|
|
1961
|
+
const graphIds = [...graphNodeIds].sort();
|
|
1962
|
+
return [{
|
|
1963
|
+
id: selfWakeBoundaries ? "self-wake" : branch ? `branch:${branch.id}:${event.eventName}:${hostPath.join(".")}` : `${event.hostNodeId}:${event.eventName}`,
|
|
1964
|
+
hostNodeId: event.hostNodeId,
|
|
1965
|
+
eventName: event.eventName,
|
|
1966
|
+
hostIndex: host?.index ?? -1,
|
|
1967
|
+
hostTagName: host?.tagName ?? "*",
|
|
1968
|
+
...branch ? {
|
|
1969
|
+
branchStartIndex: branch.startAnchor.index,
|
|
1970
|
+
branchEndIndex: branch.endAnchor.index,
|
|
1971
|
+
hostPath
|
|
1972
|
+
} : {},
|
|
1973
|
+
graphNodeIds: graphIds,
|
|
1974
|
+
symbolIds: [...symbolIds].sort(),
|
|
1975
|
+
state: filterState(input.state, new Set(graphIds)),
|
|
1976
|
+
view,
|
|
1977
|
+
...adoptedBoundaryIds.size > 0 ? { adoptsSettledArm: true } : {}
|
|
1978
|
+
}];
|
|
1979
|
+
});
|
|
1980
|
+
const armRendererModuleId = groups.some((group) => group.id === "self-wake") ? triggerGroupVirtualModuleId(input.filename, groups.length) : void 0;
|
|
1981
|
+
return groups.map(({ adoptsSettledArm, ...group }) => adoptsSettledArm && armRendererModuleId ? {
|
|
1982
|
+
...group,
|
|
1983
|
+
armRendererModuleId
|
|
1984
|
+
} : group);
|
|
1985
|
+
}
|
|
1986
|
+
function settledArmReadGraphNodeIds(input, boundary) {
|
|
1987
|
+
const graphNodeIds = /* @__PURE__ */ new Set();
|
|
1988
|
+
for (const arm of boundaryArmRecordSets(boundary)) collectCompleteArmRecordClosure(arm, graphNodeIds, /* @__PURE__ */ new Set());
|
|
1989
|
+
for (const edge of input.componentEdges ?? []) if (edge.asyncBoundaryId === boundary.id) {
|
|
1990
|
+
for (const prop of edge.props) if (prop.kind === "graph-reference") graphNodeIds.add(prop.graphNodeId);
|
|
1991
|
+
}
|
|
1992
|
+
return [...graphNodeIds];
|
|
1993
|
+
}
|
|
1994
|
+
function collectSettledBoundaryClosure(input, boundary, graphNodeIds, symbolIds) {
|
|
1995
|
+
if (boundary.updateSymbolId) symbolIds.add(boundary.updateSymbolId);
|
|
1996
|
+
for (const read of boundary.asyncReads) {
|
|
1997
|
+
graphNodeIds.add(read.graphNodeId);
|
|
1998
|
+
if (read.runnerSymbolId) symbolIds.add(read.runnerSymbolId);
|
|
1999
|
+
}
|
|
2000
|
+
for (const arm of boundaryArmRecordSets(boundary)) collectCompleteArmRecordClosure(arm, graphNodeIds, symbolIds);
|
|
2001
|
+
const settledEdgeIds = /* @__PURE__ */ new Set();
|
|
2002
|
+
for (const edge of input.componentEdges ?? []) {
|
|
2003
|
+
if (edge.asyncBoundaryId !== boundary.id) continue;
|
|
2004
|
+
settledEdgeIds.add(edge.id);
|
|
2005
|
+
for (const prop of edge.props) if (prop.kind === "graph-reference") graphNodeIds.add(prop.graphNodeId);
|
|
2006
|
+
}
|
|
2007
|
+
for (const row of input.boundRows) if (row.componentEdgePath.some((edgeId) => settledEdgeIds.has(edgeId))) symbolIds.add(row.id);
|
|
2008
|
+
}
|
|
2009
|
+
function collectSettledArmHostRecordClosure(completeView, boundary, graphNodeIds, symbolIds) {
|
|
2010
|
+
const armHosts = /* @__PURE__ */ new Set();
|
|
2011
|
+
for (const arm of boundaryArmRecordSets(boundary)) for (const locator of arm.locators ?? []) armHosts.add(locator.hostNodeId);
|
|
2012
|
+
if (armHosts.size === 0) return;
|
|
2013
|
+
for (const record of completeView.events) if (armHosts.has(record.hostNodeId)) for (const symbolId of record.symbolIds ?? []) symbolIds.add(symbolId);
|
|
2014
|
+
for (const record of completeView.domUpdates) if (armHosts.has(record.hostNodeId)) {
|
|
2015
|
+
graphNodeIds.add(record.graphNodeId);
|
|
2016
|
+
if (record.symbolId) symbolIds.add(record.symbolId);
|
|
2017
|
+
}
|
|
2018
|
+
for (const record of completeView.behaviors) if (armHosts.has(record.hostNodeId)) {
|
|
2019
|
+
for (const read of record.inputGraphReads ?? []) graphNodeIds.add(read.graphNodeId);
|
|
2020
|
+
if (record.symbolId) symbolIds.add(record.symbolId);
|
|
2021
|
+
}
|
|
2022
|
+
for (const record of completeView.keyedRepeats ?? []) if (armHosts.has(record.parentHostNodeId)) {
|
|
2023
|
+
if (record.collectionGraphNodeId) graphNodeIds.add(record.collectionGraphNodeId);
|
|
2024
|
+
for (const rowEvent of record.rowEvents) for (const symbolId of rowEvent.symbolIds ?? []) symbolIds.add(symbolId);
|
|
2025
|
+
}
|
|
2026
|
+
}
|
|
2027
|
+
function dropEscalatedHostRecords(view, selected, hostNodeIds) {
|
|
2028
|
+
if (hostNodeIds.size === 0) return;
|
|
2029
|
+
const drop = (indexes, hostOf) => {
|
|
2030
|
+
for (const index of indexes) if (hostNodeIds.has(hostOf(index) ?? "")) indexes.delete(index);
|
|
2031
|
+
};
|
|
2032
|
+
drop(selected.domUpdates, (index) => view.domUpdates[index]?.hostNodeId);
|
|
2033
|
+
drop(selected.behaviors, (index) => view.behaviors[index]?.hostNodeId);
|
|
2034
|
+
drop(selected.elementHandles, (index) => view.elementHandles[index]?.hostNodeId);
|
|
2035
|
+
drop(selected.repeats, (index) => view.keyedRepeats?.[index]?.parentHostNodeId);
|
|
2036
|
+
}
|
|
2037
|
+
function collectEscalatedArmBranchIds(arm, branchIds) {
|
|
2038
|
+
for (const branch of arm.branches ?? []) {
|
|
2039
|
+
if (!branch.symbolId && (branch.testReads?.length ?? 0) > 0) branchIds.add(branch.id);
|
|
2040
|
+
for (const nested of branch.armRecords ?? []) collectEscalatedArmBranchIds(nested, branchIds);
|
|
2041
|
+
}
|
|
2042
|
+
}
|
|
2043
|
+
function collectEscalatedBranchClosure(input, branchSiteId, graphNodeIds, symbolIds, hostNodeIds) {
|
|
2044
|
+
const renderData = input.renderData;
|
|
2045
|
+
const branch = renderData?.branches.find((candidate) => candidate.branchSiteId === branchSiteId);
|
|
2046
|
+
if (!renderData || !branch) return;
|
|
2047
|
+
const chunks = new Map(renderData.chunks.map((chunk) => [chunk.id, chunk]));
|
|
2048
|
+
const edgeIds = /* @__PURE__ */ new Set();
|
|
2049
|
+
const walked = /* @__PURE__ */ new Set();
|
|
2050
|
+
const walk = (chunkId) => {
|
|
2051
|
+
if (!chunkId || walked.has(chunkId)) return;
|
|
2052
|
+
walked.add(chunkId);
|
|
2053
|
+
const chunk = chunks.get(chunkId);
|
|
2054
|
+
if (!chunk) return;
|
|
2055
|
+
for (const host of chunk.hosts) hostNodeIds.add(host.hostNodeId);
|
|
2056
|
+
for (const slot of chunk.slots) if (slot.kind === "child-component") {
|
|
2057
|
+
edgeIds.add(slot.componentEdgeId);
|
|
2058
|
+
walk(slot.childTemplateId);
|
|
2059
|
+
walk(slot.projectionChunkId);
|
|
2060
|
+
} else if (slot.kind === "branch") for (const armId of slot.armTemplateIds) walk(armId);
|
|
2061
|
+
else if (slot.kind === "repeat") {
|
|
2062
|
+
walk(slot.rowTemplateId);
|
|
2063
|
+
walk(slot.emptyTemplateId);
|
|
2064
|
+
} else if (slot.kind === "async") {
|
|
2065
|
+
walk(slot.armTemplateIds.try);
|
|
2066
|
+
walk(slot.armTemplateIds.pending);
|
|
2067
|
+
walk(slot.armTemplateIds.catch);
|
|
2068
|
+
} else if (slot.kind === "dynamic-host") walk(slot.childChunkId);
|
|
2069
|
+
else if ("residue" in slot && slot.residue.kind === "graph-read") graphNodeIds.add(slot.residue.graphNodeId);
|
|
2070
|
+
};
|
|
2071
|
+
for (const armId of branch.armChunkIds) walk(armId);
|
|
2072
|
+
for (const symbol of input.symbolResolver.symbols) if (symbol.kind === "dom-update" && hostNodeIds.has(symbol.hostNodeId)) graphNodeIds.add(symbol.graphNodeId);
|
|
2073
|
+
for (const symbol of input.symbolResolver.symbols) if ("hostNodeId" in symbol && hostNodeIds.has(symbol.hostNodeId) || symbol.kind === "callback-prop" && edgeIds.has(symbol.componentEdgeId) || (symbol.kind === "state-initializer" || symbol.kind === "sync-computed-derive" || symbol.kind === "async-computed-runner") && graphNodeIds.has(symbol.graphNodeId)) symbolIds.add(symbol.id);
|
|
2074
|
+
for (const row of input.boundRows) if (row.componentEdgePath.some((edgeId) => edgeIds.has(edgeId))) symbolIds.add(row.id);
|
|
2075
|
+
}
|
|
2076
|
+
function boundaryArmRecordSets(boundary) {
|
|
2077
|
+
return Array.isArray(boundary.armRecords) ? boundary.armRecords : boundary.armRecords ? [boundary.armRecords] : [];
|
|
2078
|
+
}
|
|
2079
|
+
function unsettledAsyncBoundaryIndexes(state, view) {
|
|
2080
|
+
const computed = new Map(state.computed.map((record) => [record.graphNodeId, record]));
|
|
2081
|
+
const runners = { ...view.asyncRunners };
|
|
2082
|
+
for (const boundary of view.asyncBoundaries) for (const read of boundary.asyncReads) if (read.runnerSymbolId) runners[read.graphNodeId] ??= read.runnerSymbolId;
|
|
2083
|
+
return view.asyncBoundaries.flatMap((boundary, index) => {
|
|
2084
|
+
const reachable = new Set(boundary.asyncReads.map((read) => read.graphNodeId));
|
|
2085
|
+
for (const graphNodeId of reachable) {
|
|
2086
|
+
const record = computed.get(graphNodeId);
|
|
2087
|
+
if (!record) continue;
|
|
2088
|
+
if (runners[graphNodeId] && record.snapshot?.status !== "fulfilled" && record.snapshot?.status !== "rejected") return [index];
|
|
2089
|
+
for (const dependency of record.dependencies ?? []) reachable.add(dependency.graphNodeId);
|
|
2090
|
+
}
|
|
2091
|
+
return [];
|
|
2092
|
+
});
|
|
2093
|
+
}
|
|
2094
|
+
function selectViewSubscribers(view, graphNodeIds, symbolIds, selected) {
|
|
2095
|
+
view.domUpdates.forEach((record, index) => {
|
|
2096
|
+
if (!graphNodeIds.has(record.graphNodeId)) return;
|
|
2097
|
+
selected.domUpdates.add(index);
|
|
2098
|
+
if (record.symbolId) symbolIds.add(record.symbolId);
|
|
2099
|
+
});
|
|
2100
|
+
view.behaviors.forEach((record, index) => {
|
|
2101
|
+
if (!(record.inputGraphReads ?? []).some((read) => graphNodeIds.has(read.graphNodeId))) return;
|
|
2102
|
+
selected.behaviors.add(index);
|
|
2103
|
+
if (record.symbolId) symbolIds.add(record.symbolId);
|
|
2104
|
+
});
|
|
2105
|
+
(view.branches ?? []).forEach((record, index) => {
|
|
2106
|
+
if (!(record.testReads ?? []).some((read) => graphNodeIds.has(read.graphNodeId))) return;
|
|
2107
|
+
selected.branches.add(index);
|
|
2108
|
+
if (record.symbolId) symbolIds.add(record.symbolId);
|
|
2109
|
+
for (const arm of record.armRecords ?? []) collectCompleteArmRecordClosure(arm, graphNodeIds, symbolIds);
|
|
2110
|
+
});
|
|
2111
|
+
view.asyncBoundaries.forEach((record, index) => {
|
|
2112
|
+
if (!record.asyncReads.some((read) => graphNodeIds.has(read.graphNodeId))) return;
|
|
2113
|
+
selected.boundaries.add(index);
|
|
2114
|
+
if (record.updateSymbolId) symbolIds.add(record.updateSymbolId);
|
|
2115
|
+
for (const read of record.asyncReads) if (read.runnerSymbolId) symbolIds.add(read.runnerSymbolId);
|
|
2116
|
+
const arms = Array.isArray(record.armRecords) ? record.armRecords : record.armRecords ? [record.armRecords] : [];
|
|
2117
|
+
for (const arm of arms) collectCompleteArmRecordClosure(arm, graphNodeIds, symbolIds);
|
|
2118
|
+
});
|
|
2119
|
+
(view.keyedRepeats ?? []).forEach((record, index) => {
|
|
2120
|
+
if (!record.collectionGraphNodeId || !graphNodeIds.has(record.collectionGraphNodeId)) return;
|
|
2121
|
+
selected.repeats.add(index);
|
|
2122
|
+
for (const rowEvent of record.rowEvents) for (const symbolId of rowEvent.symbolIds ?? []) symbolIds.add(symbolId);
|
|
2123
|
+
});
|
|
2124
|
+
}
|
|
2125
|
+
function collectCompleteArmRecordClosure(arm, graphNodeIds, symbolIds) {
|
|
2126
|
+
for (const event of arm.events ?? []) for (const symbolId of event.symbolIds ?? []) symbolIds.add(symbolId);
|
|
2127
|
+
for (const update of arm.domUpdates ?? []) {
|
|
2128
|
+
if (typeof update.graphNodeId === "string") graphNodeIds.add(update.graphNodeId);
|
|
2129
|
+
if (typeof update.symbolId === "string") symbolIds.add(update.symbolId);
|
|
2130
|
+
}
|
|
2131
|
+
for (const behavior of arm.behaviors ?? []) {
|
|
2132
|
+
for (const read of behavior.inputGraphReads ?? []) if (typeof read.graphNodeId === "string") graphNodeIds.add(read.graphNodeId);
|
|
2133
|
+
if (typeof behavior.symbolId === "string") symbolIds.add(behavior.symbolId);
|
|
2134
|
+
}
|
|
2135
|
+
for (const repeat of arm.keyedRepeats ?? []) {
|
|
2136
|
+
if (repeat.collectionGraphNodeId) graphNodeIds.add(repeat.collectionGraphNodeId);
|
|
2137
|
+
for (const event of repeat.rowEvents) for (const symbolId of event.symbolIds ?? []) symbolIds.add(symbolId);
|
|
2138
|
+
}
|
|
2139
|
+
for (const branch of arm.branches ?? []) {
|
|
2140
|
+
if (branch.symbolId) symbolIds.add(branch.symbolId);
|
|
2141
|
+
for (const read of branch.testReads ?? []) graphNodeIds.add(read.graphNodeId);
|
|
2142
|
+
for (const nested of branch.armRecords ?? []) collectCompleteArmRecordClosure(nested, graphNodeIds, symbolIds);
|
|
2143
|
+
}
|
|
2144
|
+
}
|
|
2145
|
+
function closeComputedGraph(graphNodeIds, state) {
|
|
2146
|
+
let changed = true;
|
|
2147
|
+
while (changed) {
|
|
2148
|
+
changed = false;
|
|
2149
|
+
for (const computed of state.computed) {
|
|
2150
|
+
const dependencies = computed.dependencies ?? [];
|
|
2151
|
+
if (!graphNodeIds.has(computed.graphNodeId) && !dependencies.some((dependency) => graphNodeIds.has(dependency.graphNodeId))) continue;
|
|
2152
|
+
if (!graphNodeIds.has(computed.graphNodeId)) {
|
|
2153
|
+
graphNodeIds.add(computed.graphNodeId);
|
|
2154
|
+
changed = true;
|
|
2155
|
+
}
|
|
2156
|
+
for (const dependency of dependencies) if (!graphNodeIds.has(dependency.graphNodeId)) {
|
|
2157
|
+
graphNodeIds.add(dependency.graphNodeId);
|
|
2158
|
+
changed = true;
|
|
2159
|
+
}
|
|
2160
|
+
}
|
|
2161
|
+
}
|
|
2162
|
+
}
|
|
2163
|
+
function filterState(state, graphNodeIds) {
|
|
2164
|
+
return {
|
|
2165
|
+
...state,
|
|
2166
|
+
cells: state.cells.filter((cell) => graphNodeIds.has(cell.graphNodeId)),
|
|
2167
|
+
computed: state.computed.filter((computed) => graphNodeIds.has(computed.graphNodeId)),
|
|
2168
|
+
storage: state.storage?.filter((cell) => graphNodeIds.has(cell.graphNodeId)),
|
|
2169
|
+
sharedDefinitions: state.sharedDefinitions?.filter((definition) => definition.graphNodeIds.some((graphNodeId) => graphNodeIds.has(graphNodeId)))
|
|
2170
|
+
};
|
|
2171
|
+
}
|
|
2172
|
+
function closureSize(graphNodeIds, symbolIds, selected) {
|
|
2173
|
+
return graphNodeIds.size + symbolIds.size + Object.values(selected).reduce((total, records) => total + records.size, 0);
|
|
2174
|
+
}
|
|
2175
|
+
//#endregion
|
|
2176
|
+
//#region src/virtual-ids.ts
|
|
2177
|
+
const TSRX_SOURCE_FILE = /\.tsrx(?:[?#].*)?$/;
|
|
2178
|
+
const MARKLESS_SYMBOL_SOURCE_QUERY_RE = /[?&]markless-symbols(?:[&#]|$)/;
|
|
2179
|
+
const MARKLESS_RESUME_SOURCE_QUERY_RE = /[?&]markless-resume(?:[&#]|$)/;
|
|
2180
|
+
const MARKLESS_RENDER_DATA_SOURCE_QUERY_RE = /[?&]markless-render-data(?:[&#]|$)/;
|
|
2181
|
+
const MARKLESS_REACHED_FROM_SOURCE_QUERY = "markless-reached-from";
|
|
2182
|
+
const MARKLESS_REACHED_FROM_ROUTE = "route";
|
|
2183
|
+
const MARKLESS_PRERENDER_WAKE_SOURCE_QUERY_RE = /[?&]markless-prerender-wake(?:[&#]|$)/;
|
|
2184
|
+
const MARKLESS_ROUTE_SOURCE_QUERY_RE = /[?&]markless-route(?:[&#]|$)/;
|
|
2185
|
+
const RESUME_VIRTUAL_ID_RE = /^virtual:markless:resume:([^:]+)$/;
|
|
2186
|
+
const PRERENDER_WAKE_VIRTUAL_ID_RE = /^virtual:markless:prerender-wake:([^:]+)$/;
|
|
2187
|
+
const SETTLE_VIRTUAL_ID_RE = /^virtual:markless:settle:([^:]+)$/;
|
|
2188
|
+
const SYMBOL_VIRTUAL_STRING_RE = /(["'`])((?:virtual:markless:symbol:)[^"'`]+)\1/g;
|
|
2189
|
+
function stripBuildPrefix(fileName) {
|
|
2190
|
+
return fileName.startsWith(MARKLESS_BUILD_PREFIX) ? fileName.slice(MARKLESS_BUILD_PREFIX.length) : fileName;
|
|
2191
|
+
}
|
|
2192
|
+
function virtualModuleSourceForLoad(module, options) {
|
|
2193
|
+
if (!options.dev || module.type !== "resolver") return module.source;
|
|
2194
|
+
if (!module.source.includes("moduleUrls[row[0]]")) return module.source;
|
|
2195
|
+
return module.source.replace(SYMBOL_VIRTUAL_STRING_RE, (_match, _quote, virtualId) => JSON.stringify(devBrowserVirtualModuleUrl(virtualId, options.publicPath)));
|
|
2196
|
+
}
|
|
2197
|
+
function devBrowserSourceModuleUrl(source, _root, publicPath) {
|
|
2198
|
+
const path = withQuery(joinURL("@fs", withoutLeadingSlash(source)), { import: null });
|
|
2199
|
+
return publicPath ? publicPath(path) : joinURL("/", path);
|
|
2200
|
+
}
|
|
2201
|
+
function devBrowserVirtualModuleUrl(virtualId, publicPath) {
|
|
2202
|
+
const path = joinURL("@id", resolveVirtualId(virtualId).replace("\0", "__x00__"));
|
|
2203
|
+
return publicPath ? publicPath(path) : joinURL("/", path);
|
|
2204
|
+
}
|
|
2205
|
+
function clientSymbolEntries(input, root) {
|
|
2206
|
+
if (!input || typeof input !== "object" || Array.isArray(input)) return [];
|
|
2207
|
+
const sources = [];
|
|
2208
|
+
for (const [name, value] of Object.entries(input)) {
|
|
2209
|
+
if (!/symbol/i.test(name)) continue;
|
|
2210
|
+
for (const entry of inputEntryValues(value)) if (typeof entry === "string" && TSRX_SOURCE_FILE.test(entry)) sources.push(normalizeInputSource(entry, root));
|
|
2211
|
+
}
|
|
2212
|
+
return sources;
|
|
2213
|
+
}
|
|
2214
|
+
function inputEntryValues(value) {
|
|
2215
|
+
return Array.isArray(value) ? value.flatMap(inputEntryValues) : [value];
|
|
2216
|
+
}
|
|
2217
|
+
function normalizeInputSource(source, root) {
|
|
2218
|
+
const path = pathname(source);
|
|
2219
|
+
if (isAbsolute(path)) return path;
|
|
2220
|
+
return pathname(resolve(root ?? "", path));
|
|
2221
|
+
}
|
|
2222
|
+
function isSymbolOnlySourceRequest(id) {
|
|
2223
|
+
return MARKLESS_SYMBOL_SOURCE_QUERY_RE.test(id);
|
|
2224
|
+
}
|
|
2225
|
+
function isScalarPlanSourceRequest(id) {
|
|
2226
|
+
return isSymbolOnlySourceRequest(id) && new URLSearchParams(parsePath(id).search).has("markless-scalar-plans");
|
|
2227
|
+
}
|
|
2228
|
+
function isResumeSourceRequest(id) {
|
|
2229
|
+
return MARKLESS_RESUME_SOURCE_QUERY_RE.test(id);
|
|
2230
|
+
}
|
|
2231
|
+
function isRenderDataSourceRequest(id) {
|
|
2232
|
+
return MARKLESS_RENDER_DATA_SOURCE_QUERY_RE.test(id);
|
|
2233
|
+
}
|
|
2234
|
+
function renderDataReachedFromQuery(id) {
|
|
2235
|
+
return new URLSearchParams(parsePath(id).search).get(MARKLESS_REACHED_FROM_SOURCE_QUERY) ?? void 0;
|
|
2236
|
+
}
|
|
2237
|
+
function isRouteNavigationSourceRequest(id) {
|
|
2238
|
+
return MARKLESS_ROUTE_SOURCE_QUERY_RE.test(id) || isRenderDataSourceRequest(id) && renderDataReachedFromQuery(id) === void 0;
|
|
2239
|
+
}
|
|
2240
|
+
function materializedReachedRenderDataSource(source) {
|
|
2241
|
+
return withQuery(source, {
|
|
2242
|
+
"markless-render-data": null,
|
|
2243
|
+
[MARKLESS_REACHED_FROM_SOURCE_QUERY]: MARKLESS_REACHED_FROM_ROUTE
|
|
2244
|
+
});
|
|
2245
|
+
}
|
|
2246
|
+
function sharedReachedRenderDataRequest(id) {
|
|
2247
|
+
if (!isRenderDataSourceRequest(id)) return void 0;
|
|
2248
|
+
const reachedFrom = renderDataReachedFromQuery(id);
|
|
2249
|
+
return reachedFrom === void 0 || reachedFrom === MARKLESS_REACHED_FROM_ROUTE ? void 0 : withQuery(id, { [MARKLESS_REACHED_FROM_SOURCE_QUERY]: MARKLESS_REACHED_FROM_ROUTE });
|
|
2250
|
+
}
|
|
2251
|
+
function isPrerenderWakeSourceRequest(id) {
|
|
2252
|
+
return MARKLESS_PRERENDER_WAKE_SOURCE_QUERY_RE.test(id);
|
|
2253
|
+
}
|
|
2254
|
+
function isClientPrimarySourceRequest(id) {
|
|
2255
|
+
return !(MARKLESS_SYMBOL_SOURCE_QUERY_RE.test(id) || MARKLESS_RESUME_SOURCE_QUERY_RE.test(id) || MARKLESS_RENDER_DATA_SOURCE_QUERY_RE.test(id) || MARKLESS_PRERENDER_WAKE_SOURCE_QUERY_RE.test(id) || MARKLESS_ROUTE_SOURCE_QUERY_RE.test(id));
|
|
2256
|
+
}
|
|
2257
|
+
function clientRouteArtifactReference(source) {
|
|
2258
|
+
const symbolSource = source.includes("?") ? `${source}&markless-symbols` : `${source}?markless-symbols`;
|
|
2259
|
+
const renderDataSource = source.includes("?") ? `${source}&markless-render-data` : `${source}?markless-render-data`;
|
|
2260
|
+
return [
|
|
2261
|
+
`const [symbolModule, renderDataModule] = await Promise.all([import(${JSON.stringify(symbolSource)}), import(${JSON.stringify(renderDataSource)})]);`,
|
|
2262
|
+
"const marklessRouteArtifact = {",
|
|
2263
|
+
" renderData: renderDataModule.marklessPrerenderData,",
|
|
2264
|
+
" loadSymbol: symbolModule.loadSymbol,",
|
|
2265
|
+
"};",
|
|
2266
|
+
"export default marklessRouteArtifact;"
|
|
2267
|
+
].join("\n");
|
|
2268
|
+
}
|
|
2269
|
+
function sourceForSymbolVirtualImporter(importer) {
|
|
2270
|
+
if (!importer) return null;
|
|
2271
|
+
return symbolVirtualModuleSourceFile(normalizeVirtualId(importer));
|
|
2272
|
+
}
|
|
2273
|
+
function sourceForResumeVirtualImporter(importer) {
|
|
2274
|
+
if (!importer) return null;
|
|
2275
|
+
const match = normalizeVirtualId(importer).match(RESUME_VIRTUAL_ID_RE);
|
|
2276
|
+
return match?.[1] ? decodeURIComponent(match[1]) : null;
|
|
2277
|
+
}
|
|
2278
|
+
function resolverVirtualModuleSourceFile(id) {
|
|
2279
|
+
const prefix = `${MARKLESS_VIRTUAL_PREFIX}resolver:`;
|
|
2280
|
+
return id.startsWith(prefix) ? decodeURIComponent(id.slice(prefix.length)) : null;
|
|
2281
|
+
}
|
|
2282
|
+
function sourceForTriggerGroupVirtualImporter(importer) {
|
|
2283
|
+
if (!importer) return null;
|
|
2284
|
+
return triggerGroupVirtualModuleSourceFile(normalizeVirtualId(importer));
|
|
2285
|
+
}
|
|
2286
|
+
function sourceForPrerenderWakeVirtualImporter(importer) {
|
|
2287
|
+
if (!importer) return null;
|
|
2288
|
+
const match = normalizeVirtualId(importer).match(PRERENDER_WAKE_VIRTUAL_ID_RE);
|
|
2289
|
+
return match?.[1] ? decodeURIComponent(match[1]) : null;
|
|
2290
|
+
}
|
|
2291
|
+
function sourceForSettleVirtualImporter(importer) {
|
|
2292
|
+
if (!importer) return null;
|
|
2293
|
+
const match = normalizeVirtualId(importer).match(SETTLE_VIRTUAL_ID_RE);
|
|
2294
|
+
return match?.[1] ? decodeURIComponent(match[1]) : null;
|
|
2295
|
+
}
|
|
2296
|
+
function isRelativeImport(source) {
|
|
2297
|
+
return source.startsWith("./") || source.startsWith("../");
|
|
2298
|
+
}
|
|
2299
|
+
function isMarklessRuntimeModule(id) {
|
|
2300
|
+
const path = pathname(id);
|
|
2301
|
+
return /[/\\](?:web|runtime|serializer)[/\\]src[/\\].+\.ts$/.test(path);
|
|
2302
|
+
}
|
|
2303
|
+
function executionLogRuntimeModuleId(id) {
|
|
2304
|
+
const path = pathname(id);
|
|
2305
|
+
const match = path.match(/[/\\](web|runtime|serializer)[/\\]src[/\\]([^?#]+)\.ts$/);
|
|
2306
|
+
return match ? `${match[1]}:${match[2].replace(/[/\\]/g, "/")}` : path;
|
|
2307
|
+
}
|
|
2308
|
+
function normalizeVirtualId(id) {
|
|
2309
|
+
const bare = id.startsWith("\0") ? id.slice(1) : id;
|
|
2310
|
+
if (!bare.startsWith("virtual:markless:")) return bare;
|
|
2311
|
+
const queryIndex = bare.indexOf("?");
|
|
2312
|
+
const segments = (queryIndex === -1 ? bare : bare.slice(0, queryIndex)).slice(17).split(":").map((segment) => {
|
|
2313
|
+
try {
|
|
2314
|
+
return encodeURIComponent(decodeURIComponent(segment));
|
|
2315
|
+
} catch {
|
|
2316
|
+
return segment;
|
|
2317
|
+
}
|
|
2318
|
+
});
|
|
2319
|
+
return `${MARKLESS_VIRTUAL_PREFIX}${segments.join(":")}`;
|
|
2320
|
+
}
|
|
2321
|
+
function emittedBundleModuleIds(bundle) {
|
|
2322
|
+
const ids = /* @__PURE__ */ new Set();
|
|
2323
|
+
for (const output of Object.values(bundle)) {
|
|
2324
|
+
if (!output || typeof output !== "object") continue;
|
|
2325
|
+
const chunk = output;
|
|
2326
|
+
if (chunk.type !== "chunk") continue;
|
|
2327
|
+
if (typeof chunk.facadeModuleId === "string") ids.add(normalizeVirtualId(chunk.facadeModuleId));
|
|
2328
|
+
if (Array.isArray(chunk.moduleIds)) {
|
|
2329
|
+
for (const id of chunk.moduleIds) if (typeof id === "string") ids.add(normalizeVirtualId(id));
|
|
2330
|
+
}
|
|
2331
|
+
}
|
|
2332
|
+
return ids;
|
|
2333
|
+
}
|
|
2334
|
+
function resolveVirtualId(id) {
|
|
2335
|
+
if (id.startsWith("\0")) return id;
|
|
2336
|
+
return `\0${id}`;
|
|
2337
|
+
}
|
|
2338
|
+
function pathname(id) {
|
|
2339
|
+
return parsePath(id).pathname;
|
|
2340
|
+
}
|
|
2341
|
+
const CLIENT_ONLY_SOURCE_QUERY_KEYS = /* @__PURE__ */ new Set([
|
|
2342
|
+
"markless-render-data",
|
|
2343
|
+
"markless-reached-from",
|
|
2344
|
+
"markless-symbols",
|
|
2345
|
+
"markless-scalar-plans"
|
|
2346
|
+
]);
|
|
2347
|
+
function serverSharedSourceRequest(id, environment) {
|
|
2348
|
+
if (environment !== "server") return void 0;
|
|
2349
|
+
const { pathname: path, search } = parsePath(id);
|
|
2350
|
+
if (!search || !TSRX_SOURCE_FILE.test(path)) return void 0;
|
|
2351
|
+
const keys = [...new URLSearchParams(search).keys()];
|
|
2352
|
+
return keys.length > 0 && keys.every((key) => CLIENT_ONLY_SOURCE_QUERY_KEYS.has(key)) ? path : void 0;
|
|
2353
|
+
}
|
|
2354
|
+
//#endregion
|
|
2355
|
+
//#region src/build/planned-landing.ts
|
|
2356
|
+
const MARKLESS_INTERACTION_CLOSURES_ASSET = MARKLESS_INTERACTION_CLOSURES;
|
|
2357
|
+
function plannedLandingFiles(bundle) {
|
|
2358
|
+
const asset = bundle[MARKLESS_INTERACTION_CLOSURES_ASSET];
|
|
2359
|
+
if (asset?.type !== "asset" || asset.source === void 0) return void 0;
|
|
2360
|
+
const parsed = JSON.parse(typeof asset.source === "string" ? asset.source : new TextDecoder().decode(asset.source));
|
|
2361
|
+
const planned = /* @__PURE__ */ new Map();
|
|
2362
|
+
for (const route of parsed.routes) if (!route.fallback && route.files) planned.set(route.route, new Set(route.files.firstUse));
|
|
2363
|
+
return planned;
|
|
2364
|
+
}
|
|
2365
|
+
//#endregion
|
|
2366
|
+
//#region src/build/pack-cost-model.ts
|
|
2367
|
+
const PACK_COST_MODEL = {
|
|
2368
|
+
roundMs: 162,
|
|
2369
|
+
parallelRequestMs: 0,
|
|
2370
|
+
bootMsPerGzipKb: 2.8,
|
|
2371
|
+
interactionMsPerGzipKb: 1.5,
|
|
2372
|
+
decodedMsPerKb: 0,
|
|
2373
|
+
fileOverheadGzipKb: .5,
|
|
2374
|
+
lazySiblingOverheadGzipKb: 1,
|
|
2375
|
+
minDeferredGzipKb: 1,
|
|
2376
|
+
sourceGzipRatio: .25
|
|
2377
|
+
};
|
|
2378
|
+
function estimatedGzipKb(sourceLength, model = PACK_COST_MODEL) {
|
|
2379
|
+
return sourceLength * model.sourceGzipRatio / 1024;
|
|
2380
|
+
}
|
|
2381
|
+
function mergeIntoWiderPack(input, model = PACK_COST_MODEL) {
|
|
2382
|
+
return input.loadersNotNeeding * input.gzipKb * input.msPerGzipKb < input.loadersNeeding * (model.parallelRequestMs + model.fileOverheadGzipKb * input.msPerGzipKb);
|
|
2383
|
+
}
|
|
2384
|
+
function deferralPays(gzipKb, model = PACK_COST_MODEL) {
|
|
2385
|
+
return gzipKb >= model.minDeferredGzipKb;
|
|
2386
|
+
}
|
|
2387
|
+
function lazySiblingPays(input, model = PACK_COST_MODEL) {
|
|
2388
|
+
return deferralPays(input.gzipKb, model) && input.loadersSpedUp * input.gzipKb > input.loaders * model.lazySiblingOverheadGzipKb;
|
|
2389
|
+
}
|
|
2390
|
+
function frameworkSplitPays(input, model = PACK_COST_MODEL) {
|
|
2391
|
+
return deferralPays(input.appGzipKb, model) && deferralPays(input.frameworkGzipKb, model);
|
|
2392
|
+
}
|
|
2393
|
+
//#endregion
|
|
2394
|
+
//#region src/build/route-pack-groups.ts
|
|
2395
|
+
const MARKLESS_DEFERRED_PACK = "markless-deferred";
|
|
2396
|
+
const MARKLESS_NAVIGATION_PACK_PREFIX = "navigation:";
|
|
2397
|
+
function isMarklessDeferredPack(name) {
|
|
2398
|
+
return name === "markless-deferred" || !!name?.startsWith(`markless-deferred-`);
|
|
2399
|
+
}
|
|
2400
|
+
/** Deferred packs plus the module-free facade chunks Rolldown emits in front of their entries. */
|
|
2401
|
+
function marklessDeferredChunkFileNames(chunks) {
|
|
2402
|
+
const all = [...chunks];
|
|
2403
|
+
const deferred = new Set(all.filter((chunk) => isMarklessDeferredPack(chunk.name)).map((chunk) => chunk.fileName));
|
|
2404
|
+
for (let grew = deferred.size > 0; grew;) {
|
|
2405
|
+
grew = false;
|
|
2406
|
+
for (const chunk of all) if (!deferred.has(chunk.fileName) && chunk.moduleIds?.length === 0 && chunk.imports.length > 0 && chunk.imports.every((name) => deferred.has(name))) {
|
|
2407
|
+
deferred.add(chunk.fileName);
|
|
2408
|
+
grew = true;
|
|
2409
|
+
}
|
|
2410
|
+
}
|
|
2411
|
+
return deferred;
|
|
2412
|
+
}
|
|
2413
|
+
function isMarklessNavigationPack(name) {
|
|
2414
|
+
return !!name?.startsWith(MARKLESS_NAVIGATION_PACK_PREFIX);
|
|
2415
|
+
}
|
|
2416
|
+
function isStartupPack(group) {
|
|
2417
|
+
return !isMarklessDeferredPack(group) && !isMarklessNavigationPack(group);
|
|
2418
|
+
}
|
|
2419
|
+
function planRoutePackGroups(input) {
|
|
2420
|
+
const sources = /* @__PURE__ */ new Map();
|
|
2421
|
+
for (const [id, module] of input.modules) {
|
|
2422
|
+
if (!module.source || module.reachedFrom !== void 0) continue;
|
|
2423
|
+
push(sources, module.source, id);
|
|
2424
|
+
}
|
|
2425
|
+
const roots = /* @__PURE__ */ new Map();
|
|
2426
|
+
for (const [route, ids] of input.routes) for (const id of ids) {
|
|
2427
|
+
if (!input.modules.has(id)) throw new Error(`Markless route ${route} has missing root ${id}.`);
|
|
2428
|
+
roots.set(id, route);
|
|
2429
|
+
}
|
|
2430
|
+
const owners = /* @__PURE__ */ new Map();
|
|
2431
|
+
const walk = (route, start, visited) => {
|
|
2432
|
+
const reached = [];
|
|
2433
|
+
const pending = [...start];
|
|
2434
|
+
while (pending.length) {
|
|
2435
|
+
const id = pending.pop();
|
|
2436
|
+
if (visited.has(id)) continue;
|
|
2437
|
+
visited.add(id);
|
|
2438
|
+
if (roots.has(id) && roots.get(id) !== route) continue;
|
|
2439
|
+
const module = input.modules.get(id);
|
|
2440
|
+
if (!module) continue;
|
|
2441
|
+
add(owners, id, route);
|
|
2442
|
+
reached.push(id);
|
|
2443
|
+
pending.push(...module.dependencies, ...module.dynamicDependencies ?? []);
|
|
2444
|
+
}
|
|
2445
|
+
return reached;
|
|
2446
|
+
};
|
|
2447
|
+
const visitedByRoute = /* @__PURE__ */ new Map();
|
|
2448
|
+
for (const [route, ids] of input.routes) {
|
|
2449
|
+
const visited = /* @__PURE__ */ new Set();
|
|
2450
|
+
walk(route, ids, visited);
|
|
2451
|
+
visitedByRoute.set(route, visited);
|
|
2452
|
+
}
|
|
2453
|
+
const imported = new Set(owners.keys());
|
|
2454
|
+
for (const [route, visited] of visitedByRoute) {
|
|
2455
|
+
const expanded = /* @__PURE__ */ new Set();
|
|
2456
|
+
let frontier = [...visited].filter((id) => owners.get(id)?.has(route));
|
|
2457
|
+
while (frontier.length) {
|
|
2458
|
+
const next = [];
|
|
2459
|
+
for (const id of frontier) {
|
|
2460
|
+
const source = input.modules.get(id)?.source;
|
|
2461
|
+
if (!source || expanded.has(source)) continue;
|
|
2462
|
+
expanded.add(source);
|
|
2463
|
+
const siblings = (sources.get(source) ?? []).filter((sibling) => !imported.has(sibling));
|
|
2464
|
+
next.push(...walk(route, siblings, visited));
|
|
2465
|
+
}
|
|
2466
|
+
frontier = next;
|
|
2467
|
+
}
|
|
2468
|
+
}
|
|
2469
|
+
const reachedOutsideRoutes = /* @__PURE__ */ new Set();
|
|
2470
|
+
const outside = [...input.modules.keys()].filter((id) => !owners.has(id));
|
|
2471
|
+
while (outside.length) {
|
|
2472
|
+
const id = outside.pop();
|
|
2473
|
+
for (const dependency of input.modules.get(id)?.dependencies ?? []) {
|
|
2474
|
+
if (roots.has(dependency) || !owners.has(dependency)) continue;
|
|
2475
|
+
if (reachedOutsideRoutes.has(dependency)) continue;
|
|
2476
|
+
reachedOutsideRoutes.add(dependency);
|
|
2477
|
+
outside.push(dependency);
|
|
2478
|
+
}
|
|
2479
|
+
}
|
|
2480
|
+
const routeSpecific = (id) => owners.get(id).size < input.routes.size && (owners.get(id).size === 1 || input.modules.get(id)?.reachedFrom !== void 0);
|
|
2481
|
+
const rootedLandings = new Set([...roots].filter(([id]) => !input.modules.get(id).navigationOnly).map(([, route]) => route));
|
|
2482
|
+
const undemanded = input.undemandedDynamicTargets ?? /* @__PURE__ */ new Set();
|
|
2483
|
+
const reach = (landingOnly) => {
|
|
2484
|
+
const reached = /* @__PURE__ */ new Set();
|
|
2485
|
+
const pending = [...input.modules.keys()].filter((id) => roots.has(id) || !owners.has(id));
|
|
2486
|
+
while (pending.length) {
|
|
2487
|
+
const id = pending.pop();
|
|
2488
|
+
if (reached.has(id)) continue;
|
|
2489
|
+
const module = input.modules.get(id);
|
|
2490
|
+
if (landingOnly && module?.navigationOnly && roots.has(id)) continue;
|
|
2491
|
+
reached.add(id);
|
|
2492
|
+
if (!module) continue;
|
|
2493
|
+
pending.push(...module.dependencies);
|
|
2494
|
+
for (const target of module.dynamicDependencies ?? []) if (landingOnly || !undemanded.has(target)) pending.push(target);
|
|
2495
|
+
if (module.source) pending.push(...sources.get(module.source) ?? []);
|
|
2496
|
+
}
|
|
2497
|
+
return reached;
|
|
2498
|
+
};
|
|
2499
|
+
const eager = reach(false);
|
|
2500
|
+
const landing = reach(true);
|
|
2501
|
+
const firstUse = /* @__PURE__ */ new Map();
|
|
2502
|
+
const render = /* @__PURE__ */ new Map();
|
|
2503
|
+
const known = /* @__PURE__ */ new Set();
|
|
2504
|
+
for (const route of input.closures ?? []) {
|
|
2505
|
+
if (route.fallback) continue;
|
|
2506
|
+
known.add(route.route);
|
|
2507
|
+
for (const consumer of route.consumers) for (const id of consumer.modules) add(consumer.kind === "render" ? render : firstUse, id, route.route);
|
|
2508
|
+
}
|
|
2509
|
+
const planned = [...owners.keys()].filter((id) => eager.has(id) && !(reachedOutsideRoutes.has(id) && routeSpecific(id)));
|
|
2510
|
+
for (const id of planned) for (const route of owners.get(id)) if (!known.has(route)) add(landing.has(id) || !rootedLandings.has(route) ? firstUse : render, id, route);
|
|
2511
|
+
const gzipKb = (ids) => estimatedGzipKb(ids.reduce((sum, id) => sum + (input.modules.get(id)?.size ?? 0), 0));
|
|
2512
|
+
const renderIds = planned.filter((id) => !firstUse.has(id) && render.has(id));
|
|
2513
|
+
const deferRender = deferralPays(gzipKb(renderIds));
|
|
2514
|
+
if (!deferRender) for (const id of renderIds) firstUse.set(id, render.get(id));
|
|
2515
|
+
const idleIds = planned.filter((id) => !firstUse.has(id) && !render.has(id));
|
|
2516
|
+
const deferIdle = deferralPays(gzipKb(idleIds));
|
|
2517
|
+
if (!deferIdle) for (const id of idleIds) firstUse.set(id, owners.get(id));
|
|
2518
|
+
const groups = /* @__PURE__ */ new Map();
|
|
2519
|
+
for (const id of owners.keys()) if (!eager.has(id) && !(reachedOutsideRoutes.has(id) && routeSpecific(id))) groups.set(id, MARKLESS_DEFERRED_PACK);
|
|
2520
|
+
const critical = /* @__PURE__ */ new Map();
|
|
2521
|
+
for (const id of planned) {
|
|
2522
|
+
const routes = firstUse.get(id);
|
|
2523
|
+
if (!routes) continue;
|
|
2524
|
+
const foreign = [...owners.get(id)].some((route) => !routes.has(route));
|
|
2525
|
+
push(critical, `${routeKey(routes)}${foreign ? SHARED_MARK : ""}`, id);
|
|
2526
|
+
}
|
|
2527
|
+
const landingRoutes = new Set([...critical.keys()].flatMap(routesOf));
|
|
2528
|
+
for (const [marked, ids] of critical) {
|
|
2529
|
+
const key = marked.replace(SHARED_MARK, "");
|
|
2530
|
+
const routes = routesOf(key);
|
|
2531
|
+
const name = routes.length === 1 && key === marked ? `route:${routes[0]}` : routes.length === landingRoutes.size || !input.pagesLoadOneRoute || mergeIntoWiderPack({
|
|
2532
|
+
gzipKb: gzipKb(ids),
|
|
2533
|
+
loadersNeeding: routes.length,
|
|
2534
|
+
loadersNotNeeding: landingRoutes.size - routes.length,
|
|
2535
|
+
msPerGzipKb: PACK_COST_MODEL.bootMsPerGzipKb
|
|
2536
|
+
}) ? "shared" : `shared:${routeSetId(key)}`;
|
|
2537
|
+
if (name.startsWith("shared:")) input.routeSets?.set(name, routes);
|
|
2538
|
+
for (const id of ids) groups.set(id, name);
|
|
2539
|
+
}
|
|
2540
|
+
if (deferRender) {
|
|
2541
|
+
const rendered = /* @__PURE__ */ new Map();
|
|
2542
|
+
for (const id of renderIds) push(rendered, routeKey(render.get(id)), id);
|
|
2543
|
+
const renderRoutes = new Set([...rendered.keys()].flatMap(routesOf));
|
|
2544
|
+
for (const [key, ids] of rendered) {
|
|
2545
|
+
const routes = routesOf(key);
|
|
2546
|
+
const name = routes.length === 1 ? `${MARKLESS_NAVIGATION_PACK_PREFIX}${routes[0]}` : routes.length === renderRoutes.size || mergeIntoWiderPack({
|
|
2547
|
+
gzipKb: gzipKb(ids),
|
|
2548
|
+
loadersNeeding: routes.length,
|
|
2549
|
+
loadersNotNeeding: renderRoutes.size - routes.length,
|
|
2550
|
+
msPerGzipKb: PACK_COST_MODEL.interactionMsPerGzipKb
|
|
2551
|
+
}) ? `${MARKLESS_NAVIGATION_PACK_PREFIX}shared` : `${MARKLESS_NAVIGATION_PACK_PREFIX}shared:${routeSetId(key)}`;
|
|
2552
|
+
for (const id of ids) groups.set(id, name);
|
|
2553
|
+
}
|
|
2554
|
+
}
|
|
2555
|
+
if (deferIdle) {
|
|
2556
|
+
const deferred = /* @__PURE__ */ new Map();
|
|
2557
|
+
for (const id of idleIds) push(deferred, routeKey(owners.get(id)), id);
|
|
2558
|
+
const reachingRoutes = new Set([...deferred.keys()].flatMap(routesOf));
|
|
2559
|
+
for (const [key, ids] of deferred) {
|
|
2560
|
+
const routes = routesOf(key);
|
|
2561
|
+
const name = routes.length === reachingRoutes.size || mergeIntoWiderPack({
|
|
2562
|
+
gzipKb: gzipKb(ids),
|
|
2563
|
+
loadersNeeding: routes.length,
|
|
2564
|
+
loadersNotNeeding: reachingRoutes.size - routes.length,
|
|
2565
|
+
msPerGzipKb: PACK_COST_MODEL.interactionMsPerGzipKb
|
|
2566
|
+
}) ? MARKLESS_DEFERRED_PACK : `${MARKLESS_DEFERRED_PACK}-${routeSetId(key)}`;
|
|
2567
|
+
for (const id of ids) groups.set(id, name);
|
|
2568
|
+
}
|
|
2569
|
+
for (let moved = true; moved;) {
|
|
2570
|
+
moved = false;
|
|
2571
|
+
for (const id of idleIds) {
|
|
2572
|
+
const own = groups.get(id);
|
|
2573
|
+
if (!isMarklessDeferredPack(own)) continue;
|
|
2574
|
+
const target = input.modules.get(id).dependencies.map((dependency) => groups.get(dependency)).filter((name) => name !== void 0 && name !== own && (isMarklessNavigationPack(name) || isMarklessDeferredPack(name))).sort()[0];
|
|
2575
|
+
if (!target) continue;
|
|
2576
|
+
for (const other of idleIds) if (groups.get(other) === own) groups.set(other, target);
|
|
2577
|
+
moved = true;
|
|
2578
|
+
}
|
|
2579
|
+
}
|
|
2580
|
+
}
|
|
2581
|
+
return input.pagesLoadOneRoute ? splitFrameworkFromApp(splitStartupPacksByStaticReach(groups, roots, sources, owners, input), input) : groups;
|
|
2582
|
+
}
|
|
2583
|
+
function splitFrameworkFromApp(groups, input) {
|
|
2584
|
+
const halves = /* @__PURE__ */ new Map();
|
|
2585
|
+
for (const [id, group] of groups) {
|
|
2586
|
+
if (!isStartupPack(group)) continue;
|
|
2587
|
+
const half = halves.get(group) ?? {
|
|
2588
|
+
app: [],
|
|
2589
|
+
framework: []
|
|
2590
|
+
};
|
|
2591
|
+
halves.set(group, half);
|
|
2592
|
+
(input.modules.get(id)?.app ? half.app : half.framework).push(id);
|
|
2593
|
+
}
|
|
2594
|
+
const gzipKb = (ids) => estimatedGzipKb(ids.reduce((sum, id) => sum + (input.modules.get(id)?.size ?? 0), 0));
|
|
2595
|
+
for (const [group, half] of halves) {
|
|
2596
|
+
if (!frameworkSplitPays({
|
|
2597
|
+
appGzipKb: gzipKb(half.app),
|
|
2598
|
+
frameworkGzipKb: gzipKb(half.framework)
|
|
2599
|
+
})) continue;
|
|
2600
|
+
for (const id of half.framework) groups.set(id, `${group}${FRAMEWORK_PIECE}`);
|
|
2601
|
+
}
|
|
2602
|
+
return groups;
|
|
2603
|
+
}
|
|
2604
|
+
function splitStartupPacksByStaticReach(groups, roots, sources, owners, input) {
|
|
2605
|
+
const gzipKbOf = (ids) => estimatedGzipKb(ids.reduce((sum, id) => sum + (input.modules.get(id)?.size ?? 0), 0));
|
|
2606
|
+
const flagged = [...input.modules.values()].some((module) => module.firstEvent !== void 0);
|
|
2607
|
+
const seed = (id) => {
|
|
2608
|
+
const module = input.modules.get(id);
|
|
2609
|
+
return !!module && !module.navigationOnly && (!flagged || !!module.firstEvent);
|
|
2610
|
+
};
|
|
2611
|
+
const handler = (id) => !!input.modules.get(id)?.handler;
|
|
2612
|
+
const closure = (route, start, path) => {
|
|
2613
|
+
const reached = /* @__PURE__ */ new Set();
|
|
2614
|
+
const handlers = /* @__PURE__ */ new Set();
|
|
2615
|
+
const pending = [...start];
|
|
2616
|
+
while (pending.length) {
|
|
2617
|
+
const id = pending.pop();
|
|
2618
|
+
if (path.has(id) || reached.has(id) || roots.has(id) && roots.get(id) !== route) continue;
|
|
2619
|
+
const module = input.modules.get(id);
|
|
2620
|
+
if (!module) continue;
|
|
2621
|
+
reached.add(id);
|
|
2622
|
+
pending.push(...module.dependencies);
|
|
2623
|
+
for (const sibling of module.source ? sources.get(module.source) ?? [] : []) if (seed(sibling) && sibling !== id) if (handler(sibling)) handlers.add(sibling);
|
|
2624
|
+
else pending.push(sibling);
|
|
2625
|
+
}
|
|
2626
|
+
return {
|
|
2627
|
+
reached,
|
|
2628
|
+
handlers
|
|
2629
|
+
};
|
|
2630
|
+
};
|
|
2631
|
+
const staticReach = /* @__PURE__ */ new Set();
|
|
2632
|
+
for (const [route, ids] of input.routes) {
|
|
2633
|
+
const visited = /* @__PURE__ */ new Set();
|
|
2634
|
+
const first = closure(route, ids.filter((id) => seed(id) && !handler(id)), visited);
|
|
2635
|
+
for (const id of first.reached) visited.add(id);
|
|
2636
|
+
const candidates = [...first.handlers, ...[...owners].filter(([id, routes]) => routes.size === 1 && routes.has(route) && seed(id) && handler(id)).map(([id]) => id)];
|
|
2637
|
+
while (candidates.length) {
|
|
2638
|
+
const id = candidates.pop();
|
|
2639
|
+
if (visited.has(id)) continue;
|
|
2640
|
+
const extra = closure(route, [id], visited);
|
|
2641
|
+
if (deferralPays(gzipKbOf([...extra.reached].filter((reached) => reached !== id)))) continue;
|
|
2642
|
+
for (const reached of extra.reached) visited.add(reached);
|
|
2643
|
+
candidates.push(...extra.handlers);
|
|
2644
|
+
}
|
|
2645
|
+
for (const id of visited) staticReach.add(id);
|
|
2646
|
+
}
|
|
2647
|
+
const known = new Map((input.closures ?? []).filter((route) => !route.fallback).map((route) => [route.route, route]));
|
|
2648
|
+
const firstEventNeedsRuntime = (route) => !known.has(route) || known.get(route).consumers.some((consumer) => consumer.kind === "action" && !consumer.lean);
|
|
2649
|
+
const firstEventIsLean = (route) => !firstEventNeedsRuntime(route) && known.get(route).consumers.some((consumer) => consumer.kind === "action");
|
|
2650
|
+
const routePacks = new Set([...groups.values()].filter((group) => group.startsWith("route:")));
|
|
2651
|
+
const lazy = /* @__PURE__ */ new Map();
|
|
2652
|
+
const packSizes = /* @__PURE__ */ new Map();
|
|
2653
|
+
for (const [id, group] of groups) {
|
|
2654
|
+
if (!isStartupPack(group)) continue;
|
|
2655
|
+
packSizes.set(group, (packSizes.get(group) ?? 0) + 1);
|
|
2656
|
+
if (!staticReach.has(id)) push(lazy, group, id);
|
|
2657
|
+
}
|
|
2658
|
+
for (const [group, ids] of lazy) {
|
|
2659
|
+
if (ids.length === packSizes.get(group)) continue;
|
|
2660
|
+
const loaders = new Set(ids.flatMap((id) => [...owners.get(id) ?? []]));
|
|
2661
|
+
const eager = [...loaders].filter(firstEventNeedsRuntime);
|
|
2662
|
+
const loadersSpedUp = [...loaders].filter(firstEventIsLean).length;
|
|
2663
|
+
if (!lazySiblingPays({
|
|
2664
|
+
gzipKb: gzipKbOf(ids),
|
|
2665
|
+
loadersSpedUp,
|
|
2666
|
+
loaders: loaders.size
|
|
2667
|
+
})) continue;
|
|
2668
|
+
if (eager.some((route) => !routePacks.has(`route:${route}`))) continue;
|
|
2669
|
+
const name = lazyStartupPackName(group);
|
|
2670
|
+
for (const id of ids) groups.set(id, name);
|
|
2671
|
+
if (eager.length) input.eagerLazyRoutes?.set(name, eager.sort());
|
|
2672
|
+
}
|
|
2673
|
+
return groups;
|
|
2674
|
+
}
|
|
2675
|
+
function lazyStartupPackName(group) {
|
|
2676
|
+
return group.replace(/^(route|shared)/, `$1${LAZY_PACK_MARK}`);
|
|
2677
|
+
}
|
|
2678
|
+
function startupPackOfLazy(name) {
|
|
2679
|
+
return name.replace(new RegExp(`^(route|shared)${LAZY_PACK_MARK}`), "$1");
|
|
2680
|
+
}
|
|
2681
|
+
const LAZY_PACK_MARK = "-lazy";
|
|
2682
|
+
const FRAMEWORK_PIECE = "~1";
|
|
2683
|
+
const ROUTE_KEY_SEPARATOR = "\n";
|
|
2684
|
+
const SHARED_MARK = " ";
|
|
2685
|
+
function routeKey(routes) {
|
|
2686
|
+
return [...routes].sort().join(ROUTE_KEY_SEPARATOR);
|
|
2687
|
+
}
|
|
2688
|
+
function routeSetId(key) {
|
|
2689
|
+
let hash = 2166136261;
|
|
2690
|
+
for (let index = 0; index < key.length; index++) hash = Math.imul(hash ^ key.charCodeAt(index), 16777619) >>> 0;
|
|
2691
|
+
return hash.toString(36);
|
|
2692
|
+
}
|
|
2693
|
+
function routesOf(key) {
|
|
2694
|
+
return key.replace(SHARED_MARK, "").split(ROUTE_KEY_SEPARATOR);
|
|
2695
|
+
}
|
|
2696
|
+
function add(map, key, value) {
|
|
2697
|
+
const values = map.get(key);
|
|
2698
|
+
if (values) values.add(value);
|
|
2699
|
+
else map.set(key, /* @__PURE__ */ new Set([value]));
|
|
2700
|
+
}
|
|
2701
|
+
function push(map, key, value) {
|
|
2702
|
+
const values = map.get(key);
|
|
2703
|
+
if (values) values.push(value);
|
|
2704
|
+
else map.set(key, [value]);
|
|
2705
|
+
}
|
|
2706
|
+
//#endregion
|
|
2707
|
+
export { MARKLESS_EXECUTION_DEMAND as $, normalizeVirtualId as A, isExecutionLogChunk as At, sourceForSymbolVirtualImporter as B, isRelativeImport as C, EXECUTION_LOG_DISPATCH_MODULE_IDS as Ct, isScalarPlanSourceRequest as D, executionLogVirtualModuleSource as Dt, isRouteNavigationSourceRequest as E, executionLogActivationInjection as Et, serverSharedSourceRequest as F, emitPrerenderTriggerGroupModule as G, stripBuildPrefix as H, sharedReachedRenderDataRequest as I, adaptImportedCaptureResolver as J, planPrerenderTriggerGroups as K, sourceForPrerenderWakeVirtualImporter as L, renderDataReachedFromQuery as M, requalifyExecutionLogModuleHook as Mt, resolveVirtualId as N, planModulePreloadUrls as Nt, isSymbolOnlySourceRequest as O, hasExecutionLogModuleHook as Ot, resolverVirtualModuleSourceFile as P, planModulePreloads as Pt, MARKLESS_BYTE_ATTRIBUTION as Q, sourceForResumeVirtualImporter as R, isPrerenderWakeSourceRequest as S, withoutFirstUse as St, isResumeSourceRequest as T, MARKLESS_EXECUTION_LOG_MODULE_ID as Tt, virtualModuleSourceForLoad as U, sourceForTriggerGroupVirtualImporter as V, emitPrerenderBoundaryRendererModule as W, MARKLESS_BUILD_PREFIX as X, MARKLESS_BUILD_METADATA_FILES as Y, MARKLESS_BUNDLE_GRAPH as Z, devBrowserVirtualModuleUrl as _, scopedSymbolExportName as _t, isStartupPack as a, STORAGE_FREE_RESUME_ENTRY_SPECIFIER as at, isClientPrimarySourceRequest as b, symbolVirtualModuleId as bt, planRoutePackGroups as c, emitResumeModule as ct, plannedLandingFiles as d, encodedSymbolSource as dt, MARKLESS_EXECUTION_SIZES as et, MARKLESS_ROUTE_SOURCE_QUERY_RE as f, payloadModule as ft, devBrowserSourceModuleUrl as g, rewriteSymbolModuleExport as gt, clientSymbolEntries as h, resumeVirtualModuleId as ht, isMarklessNavigationPack as i, RESUME_ENTRY_SPECIFIER as it, pathname as j, normalizeExecutionLogMode as jt, materializedReachedRenderDataSource as k, injectExecutionLogModuleHook as kt, startupPackOfLazy as l, emitSettleModule as lt, clientRouteArtifactReference as m, prerenderWakeVirtualModuleSourceFile as mt, MARKLESS_NAVIGATION_PACK_PREFIX as n, outputDefaults as nt, lazyStartupPackName as o, demandsOverlay as ot, TSRX_SOURCE_FILE as p, prerenderWakeVirtualModuleId as pt, triggerGroupVirtualModuleId as q, isMarklessDeferredPack as r, MARKLESS_VIRTUAL_PREFIX as rt, marklessDeferredChunkFileNames as s, emitOverlayLoaderInstall as st, MARKLESS_DEFERRED_PACK as t, MARKLESS_INTERACTION_CLOSURES as tt, MARKLESS_INTERACTION_CLOSURES_ASSET as u, emitSourceModule as ut, emittedBundleModuleIds as v, settleVirtualModuleId as vt, isRenderDataSourceRequest as w, MARKLESS_EXECUTION_LOG_GLOBAL as wt, isMarklessRuntimeModule as x, symbolVirtualModuleSourceFile as xt, executionLogRuntimeModuleId as y, symbolEntryVirtualModuleId as yt, sourceForSettleVirtualImporter as z };
|