eclipsa 0.1.0 → 0.2.0-alpha.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/action-LXy_7T1f.mjs +4086 -0
- package/action-LXy_7T1f.mjs.map +1 -0
- package/client-DDoDNYqH.mjs +104 -0
- package/client-DDoDNYqH.mjs.map +1 -0
- package/component-CIul6R1i.d.mts +10 -0
- package/core/client/mod.d.mts +2 -0
- package/core/client/mod.mjs +4 -0
- package/core/dev-client/mod.d.mts +33 -0
- package/core/dev-client/mod.mjs +78 -0
- package/core/dev-client/mod.mjs.map +1 -0
- package/core/internal.d.mts +2 -0
- package/core/internal.mjs +2 -0
- package/core/prod-client/mod.d.mts +18 -0
- package/core/prod-client/mod.mjs +43 -0
- package/core/prod-client/mod.mjs.map +1 -0
- package/internal-CKAmxezZ.d.mts +534 -0
- package/jsx/jsx-dev-runtime.d.mts +17 -0
- package/jsx/jsx-dev-runtime.mjs +2 -0
- package/jsx/jsx-runtime.d.mts +2 -0
- package/jsx/jsx-runtime.mjs +1 -0
- package/jsx/mod.d.mts +7 -0
- package/jsx/mod.mjs +7 -0
- package/jsx/mod.mjs.map +1 -0
- package/jsx-dev-runtime-CY60yQJY.mjs +28 -0
- package/jsx-dev-runtime-CY60yQJY.mjs.map +1 -0
- package/jsx-runtime-BSRqCWNF.d.mts +1 -0
- package/mod-CGP_OpcY.d.mts +25 -0
- package/mod.d.mts +114 -0
- package/mod.mjs +251 -0
- package/mod.mjs.map +1 -0
- package/package.json +66 -3
- package/resume-hmr-BRDojQNp.mjs +6 -0
- package/resume-hmr-BRDojQNp.mjs.map +1 -0
- package/router-shared-Y6ck-Mcm.mjs +103 -0
- package/router-shared-Y6ck-Mcm.mjs.map +1 -0
- package/snapshot-BbvDaNMJ.mjs +139 -0
- package/snapshot-BbvDaNMJ.mjs.map +1 -0
- package/types-DFdkOqSj.d.mts +35 -0
- package/vite/mod.d.mts +13 -0
- package/vite/mod.mjs +11488 -0
- package/vite/mod.mjs.map +1 -0
- package/core/component.ts +0 -7
- package/core/dev-client/mod.ts +0 -17
- package/core/dev-client/renderer.ts +0 -5
- package/core/dev-client/types.ts +0 -3
- package/core/mod.ts +0 -4
- package/core/signal.ts +0 -20
- package/core/types.ts +0 -6
- package/deno.json +0 -23
- package/jsx/jsx-dev-runtime.ts +0 -19
- package/jsx/jsx-runtime.ts +0 -1
- package/jsx/mod.ts +0 -34
- package/jsx/shared.ts +0 -1
- package/jsx/types.ts +0 -23
- package/mod.ts +0 -2
- package/transformers/dev-client/mod.ts +0 -39
- package/transformers/dev-ssr/mod.ts +0 -52
- package/transformers/utils/jsx.ts +0 -77
- package/utils/node-connect.ts +0 -55
- package/vite/dev-app/mod.ts +0 -88
- package/vite/mod.ts +0 -76
- package/vite/utils/routing.ts +0 -26
|
@@ -0,0 +1,4086 @@
|
|
|
1
|
+
import { i as jsxDEV, n as isSSRAttrValue, r as isSSRTemplate } from "./jsx-dev-runtime-CY60yQJY.mjs";
|
|
2
|
+
import { a as ROUTE_PREFLIGHT_REQUEST_HEADER, c as composeRouteMetadata, o as ROUTE_REPLACE_ATTR, r as ROUTE_PREFETCH_ATTR, s as ROUTE_METADATA_HEAD_ATTR, t as ROUTE_LINK_ATTR } from "./router-shared-Y6ck-Mcm.mjs";
|
|
3
|
+
import { t as __exportAll } from "./vite/mod.mjs";
|
|
4
|
+
//#region core/component.ts
|
|
5
|
+
const component$ = (component) => {
|
|
6
|
+
Object.defineProperty(component, "__eclipsa_component", {
|
|
7
|
+
configurable: true,
|
|
8
|
+
enumerable: false,
|
|
9
|
+
value: true
|
|
10
|
+
});
|
|
11
|
+
return component;
|
|
12
|
+
};
|
|
13
|
+
const $ = (value) => value;
|
|
14
|
+
//#endregion
|
|
15
|
+
//#region core/serialize.ts
|
|
16
|
+
const DEFAULT_MAX_DEPTH = 64;
|
|
17
|
+
const DEFAULT_MAX_ENTRIES = 1e4;
|
|
18
|
+
const RESERVED_KEYS = new Set([
|
|
19
|
+
"__proto__",
|
|
20
|
+
"constructor",
|
|
21
|
+
"prototype"
|
|
22
|
+
]);
|
|
23
|
+
const isPlainObject$1 = (value) => {
|
|
24
|
+
if (!value || typeof value !== "object") return false;
|
|
25
|
+
const proto = Object.getPrototypeOf(value);
|
|
26
|
+
return proto === Object.prototype || proto === null;
|
|
27
|
+
};
|
|
28
|
+
const assertSafeEntryBudget = (count, maxEntries) => {
|
|
29
|
+
if (count > maxEntries) throw new RangeError(`Serialized value exceeds the maximum entry budget of ${maxEntries}.`);
|
|
30
|
+
};
|
|
31
|
+
const assertSafeDepth = (depth, maxDepth) => {
|
|
32
|
+
if (depth > maxDepth) throw new RangeError(`Serialized value exceeds the maximum depth of ${maxDepth}.`);
|
|
33
|
+
};
|
|
34
|
+
const assertSafeObject = (value) => {
|
|
35
|
+
const descriptors = Object.getOwnPropertyDescriptors(value);
|
|
36
|
+
if (Object.getOwnPropertySymbols(value).length > 0) throw new TypeError("Objects with symbol keys cannot be serialized.");
|
|
37
|
+
for (const [key, descriptor] of Object.entries(descriptors)) if ("get" in descriptor || "set" in descriptor) throw new TypeError(`Objects with accessors cannot be serialized (${key}).`);
|
|
38
|
+
};
|
|
39
|
+
const defineDecodedProperty = (target, key, value) => {
|
|
40
|
+
Object.defineProperty(target, key, {
|
|
41
|
+
configurable: true,
|
|
42
|
+
enumerable: true,
|
|
43
|
+
value,
|
|
44
|
+
writable: true
|
|
45
|
+
});
|
|
46
|
+
};
|
|
47
|
+
const assertReferenceShape = (value) => {
|
|
48
|
+
const keys = Object.keys(value).sort();
|
|
49
|
+
const allowedKeys = value.data === void 0 ? [
|
|
50
|
+
"__eclipsa_type",
|
|
51
|
+
"kind",
|
|
52
|
+
"token"
|
|
53
|
+
] : [
|
|
54
|
+
"__eclipsa_type",
|
|
55
|
+
"data",
|
|
56
|
+
"kind",
|
|
57
|
+
"token"
|
|
58
|
+
];
|
|
59
|
+
if (keys.length !== allowedKeys.length || keys.some((key, index) => key !== allowedKeys[index])) throw new TypeError("Malformed serialized reference.");
|
|
60
|
+
if (typeof value.kind !== "string" || value.kind.length === 0) throw new TypeError("Serialized references require a non-empty kind.");
|
|
61
|
+
if (typeof value.token !== "string" || value.token.length === 0) throw new TypeError("Serialized references require a non-empty token.");
|
|
62
|
+
};
|
|
63
|
+
const serializeUnknown = (value, state, stack, depth) => {
|
|
64
|
+
assertSafeDepth(depth, state.maxDepth);
|
|
65
|
+
const reference = state.serializeReference?.(value) ?? null;
|
|
66
|
+
if (reference) {
|
|
67
|
+
state.entryCount += 1;
|
|
68
|
+
assertSafeEntryBudget(state.entryCount, state.maxEntries);
|
|
69
|
+
return reference;
|
|
70
|
+
}
|
|
71
|
+
if (value === void 0) {
|
|
72
|
+
state.entryCount += 1;
|
|
73
|
+
assertSafeEntryBudget(state.entryCount, state.maxEntries);
|
|
74
|
+
return { __eclipsa_type: "undefined" };
|
|
75
|
+
}
|
|
76
|
+
if (value === null || typeof value === "string" || typeof value === "boolean") {
|
|
77
|
+
state.entryCount += 1;
|
|
78
|
+
assertSafeEntryBudget(state.entryCount, state.maxEntries);
|
|
79
|
+
return value;
|
|
80
|
+
}
|
|
81
|
+
if (typeof value === "number") {
|
|
82
|
+
if (!Number.isFinite(value)) throw new TypeError("Non-finite numbers cannot be serialized.");
|
|
83
|
+
state.entryCount += 1;
|
|
84
|
+
assertSafeEntryBudget(state.entryCount, state.maxEntries);
|
|
85
|
+
return value;
|
|
86
|
+
}
|
|
87
|
+
if (typeof value === "function") throw new TypeError("Functions cannot be serialized.");
|
|
88
|
+
if (typeof value === "symbol") throw new TypeError("Symbols cannot be serialized.");
|
|
89
|
+
if (typeof value !== "object") throw new TypeError(`Unsupported primitive ${typeof value}.`);
|
|
90
|
+
if (value instanceof Promise) throw new TypeError("Promises cannot be serialized.");
|
|
91
|
+
if (value instanceof WeakMap || value instanceof WeakSet) throw new TypeError("Weak collections cannot be serialized.");
|
|
92
|
+
if (value instanceof Date || value instanceof RegExp || value instanceof URL || value instanceof Error || value instanceof ArrayBuffer || ArrayBuffer.isView(value)) throw new TypeError(`Unsupported object ${Object.prototype.toString.call(value)}.`);
|
|
93
|
+
if (stack.has(value)) throw new TypeError("Circular values cannot be serialized.");
|
|
94
|
+
stack.add(value);
|
|
95
|
+
try {
|
|
96
|
+
if (Array.isArray(value)) {
|
|
97
|
+
for (let index = 0; index < value.length; index += 1) if (!(index in value)) throw new TypeError("Sparse arrays cannot be serialized.");
|
|
98
|
+
state.entryCount += value.length + 1;
|
|
99
|
+
assertSafeEntryBudget(state.entryCount, state.maxEntries);
|
|
100
|
+
return value.map((entry) => serializeUnknown(entry, state, stack, depth + 1));
|
|
101
|
+
}
|
|
102
|
+
if (value instanceof Map) {
|
|
103
|
+
state.entryCount += value.size * 2 + 1;
|
|
104
|
+
assertSafeEntryBudget(state.entryCount, state.maxEntries);
|
|
105
|
+
const entries = [];
|
|
106
|
+
for (const [key, entry] of value.entries()) entries.push([serializeUnknown(key, state, stack, depth + 1), serializeUnknown(entry, state, stack, depth + 1)]);
|
|
107
|
+
return {
|
|
108
|
+
__eclipsa_type: "map",
|
|
109
|
+
entries
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
if (value instanceof Set) {
|
|
113
|
+
state.entryCount += value.size + 1;
|
|
114
|
+
assertSafeEntryBudget(state.entryCount, state.maxEntries);
|
|
115
|
+
return {
|
|
116
|
+
__eclipsa_type: "set",
|
|
117
|
+
entries: [...value].map((entry) => serializeUnknown(entry, state, stack, depth + 1))
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
if (isPlainObject$1(value)) {
|
|
121
|
+
assertSafeObject(value);
|
|
122
|
+
const descriptors = Object.entries(value);
|
|
123
|
+
state.entryCount += descriptors.length + 1;
|
|
124
|
+
assertSafeEntryBudget(state.entryCount, state.maxEntries);
|
|
125
|
+
return {
|
|
126
|
+
__eclipsa_type: "object",
|
|
127
|
+
entries: descriptors.map(([key, entry]) => [key, serializeUnknown(entry, state, stack, depth + 1)])
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
throw new TypeError(`Unsupported object ${Object.prototype.toString.call(value)}.`);
|
|
131
|
+
} finally {
|
|
132
|
+
stack.delete(value);
|
|
133
|
+
}
|
|
134
|
+
};
|
|
135
|
+
const deserializeUnknown = (value, state, depth) => {
|
|
136
|
+
assertSafeDepth(depth, state.maxDepth);
|
|
137
|
+
if (value === null || typeof value === "string" || typeof value === "boolean" || typeof value === "number") {
|
|
138
|
+
state.entryCount += 1;
|
|
139
|
+
assertSafeEntryBudget(state.entryCount, state.maxEntries);
|
|
140
|
+
return value;
|
|
141
|
+
}
|
|
142
|
+
if (Array.isArray(value)) {
|
|
143
|
+
state.entryCount += value.length + 1;
|
|
144
|
+
assertSafeEntryBudget(state.entryCount, state.maxEntries);
|
|
145
|
+
return value.map((entry) => deserializeUnknown(entry, state, depth + 1));
|
|
146
|
+
}
|
|
147
|
+
if (!value || typeof value !== "object" || typeof value.__eclipsa_type !== "string") throw new TypeError("Malformed serialized value.");
|
|
148
|
+
switch (value.__eclipsa_type) {
|
|
149
|
+
case "undefined":
|
|
150
|
+
state.entryCount += 1;
|
|
151
|
+
assertSafeEntryBudget(state.entryCount, state.maxEntries);
|
|
152
|
+
return;
|
|
153
|
+
case "object": {
|
|
154
|
+
const keys = Object.keys(value).sort();
|
|
155
|
+
if (keys.length !== 2 || keys[0] !== "__eclipsa_type" || keys[1] !== "entries") throw new TypeError("Malformed serialized object.");
|
|
156
|
+
if (!Array.isArray(value.entries)) throw new TypeError("Serialized object entries must be an array.");
|
|
157
|
+
state.entryCount += value.entries.length + 1;
|
|
158
|
+
assertSafeEntryBudget(state.entryCount, state.maxEntries);
|
|
159
|
+
const result = Object.create(null);
|
|
160
|
+
for (const entry of value.entries) {
|
|
161
|
+
if (!Array.isArray(entry) || entry.length !== 2 || typeof entry[0] !== "string") throw new TypeError("Malformed serialized object entry.");
|
|
162
|
+
const [key, child] = entry;
|
|
163
|
+
if (RESERVED_KEYS.has(key)) {
|
|
164
|
+
defineDecodedProperty(result, key, deserializeUnknown(child, state, depth + 1));
|
|
165
|
+
continue;
|
|
166
|
+
}
|
|
167
|
+
defineDecodedProperty(result, key, deserializeUnknown(child, state, depth + 1));
|
|
168
|
+
}
|
|
169
|
+
return result;
|
|
170
|
+
}
|
|
171
|
+
case "map": {
|
|
172
|
+
const keys = Object.keys(value).sort();
|
|
173
|
+
if (keys.length !== 2 || keys[0] !== "__eclipsa_type" || keys[1] !== "entries") throw new TypeError("Malformed serialized map.");
|
|
174
|
+
if (!Array.isArray(value.entries)) throw new TypeError("Serialized map entries must be an array.");
|
|
175
|
+
state.entryCount += value.entries.length * 2 + 1;
|
|
176
|
+
assertSafeEntryBudget(state.entryCount, state.maxEntries);
|
|
177
|
+
const result = /* @__PURE__ */ new Map();
|
|
178
|
+
for (const entry of value.entries) {
|
|
179
|
+
if (!Array.isArray(entry) || entry.length !== 2) throw new TypeError("Malformed serialized map entry.");
|
|
180
|
+
result.set(deserializeUnknown(entry[0], state, depth + 1), deserializeUnknown(entry[1], state, depth + 1));
|
|
181
|
+
}
|
|
182
|
+
return result;
|
|
183
|
+
}
|
|
184
|
+
case "set": {
|
|
185
|
+
const keys = Object.keys(value).sort();
|
|
186
|
+
if (keys.length !== 2 || keys[0] !== "__eclipsa_type" || keys[1] !== "entries") throw new TypeError("Malformed serialized set.");
|
|
187
|
+
if (!Array.isArray(value.entries)) throw new TypeError("Serialized set entries must be an array.");
|
|
188
|
+
state.entryCount += value.entries.length + 1;
|
|
189
|
+
assertSafeEntryBudget(state.entryCount, state.maxEntries);
|
|
190
|
+
return new Set(value.entries.map((entry) => deserializeUnknown(entry, state, depth + 1)));
|
|
191
|
+
}
|
|
192
|
+
case "ref":
|
|
193
|
+
assertReferenceShape(value);
|
|
194
|
+
state.entryCount += 1;
|
|
195
|
+
assertSafeEntryBudget(state.entryCount, state.maxEntries);
|
|
196
|
+
if (!state.deserializeReference) throw new TypeError(`Cannot deserialize reference kind "${value.kind}" in this context.`);
|
|
197
|
+
return state.deserializeReference(value);
|
|
198
|
+
default: throw new TypeError(`Unknown serialized value type "${value.__eclipsa_type}".`);
|
|
199
|
+
}
|
|
200
|
+
};
|
|
201
|
+
const serializeValue = (value, options) => serializeUnknown(value, {
|
|
202
|
+
entryCount: 0,
|
|
203
|
+
maxDepth: options?.maxDepth ?? DEFAULT_MAX_DEPTH,
|
|
204
|
+
maxEntries: options?.maxEntries ?? DEFAULT_MAX_ENTRIES,
|
|
205
|
+
serializeReference: options?.serializeReference
|
|
206
|
+
}, /* @__PURE__ */ new Set(), 0);
|
|
207
|
+
const deserializeValue = (value, options) => deserializeUnknown(value, {
|
|
208
|
+
deserializeReference: options?.deserializeReference,
|
|
209
|
+
entryCount: 0,
|
|
210
|
+
maxDepth: options?.maxDepth ?? DEFAULT_MAX_DEPTH,
|
|
211
|
+
maxEntries: options?.maxEntries ?? DEFAULT_MAX_ENTRIES
|
|
212
|
+
}, 0);
|
|
213
|
+
const escapeJSONScriptText = (json) => json.replaceAll("<", "\\u003C").replaceAll(">", "\\u003E").replaceAll("&", "\\u0026").replaceAll("\u2028", "\\u2028").replaceAll("\u2029", "\\u2029");
|
|
214
|
+
const serializeJSONScriptContent = (value, options) => escapeJSONScriptText(JSON.stringify(serializeValue(value, options)));
|
|
215
|
+
const parseSerializedJSON = (json) => {
|
|
216
|
+
return JSON.parse(json);
|
|
217
|
+
};
|
|
218
|
+
//#endregion
|
|
219
|
+
//#region core/suspense.ts
|
|
220
|
+
const PENDING_SIGNAL_ERROR_KEY = Symbol.for("eclipsa.pending-signal-error");
|
|
221
|
+
const Suspense = (props) => props.children ?? null;
|
|
222
|
+
const createPendingSignalError = (promise) => ({
|
|
223
|
+
[PENDING_SIGNAL_ERROR_KEY]: true,
|
|
224
|
+
promise
|
|
225
|
+
});
|
|
226
|
+
const isPendingSignalError = (value) => !!value && typeof value === "object" && value[PENDING_SIGNAL_ERROR_KEY] === true;
|
|
227
|
+
const isSuspenseType = (value) => value === Suspense;
|
|
228
|
+
//#endregion
|
|
229
|
+
//#region core/loader.ts
|
|
230
|
+
var loader_exports = /* @__PURE__ */ __exportAll({
|
|
231
|
+
__eclipsaLoader: () => __eclipsaLoader,
|
|
232
|
+
consumePendingSsrLoaderIds: () => consumePendingSsrLoaderIds,
|
|
233
|
+
executeLoader: () => executeLoader,
|
|
234
|
+
hasLoader: () => hasLoader,
|
|
235
|
+
isPendingSsrLoaderError: () => isPendingSsrLoaderError,
|
|
236
|
+
loader$: () => loader$,
|
|
237
|
+
markPendingSsrLoader: () => markPendingSsrLoader,
|
|
238
|
+
primeLoaderState: () => primeLoaderState,
|
|
239
|
+
registerLoader: () => registerLoader,
|
|
240
|
+
resolvePendingLoaders: () => resolvePendingLoaders
|
|
241
|
+
});
|
|
242
|
+
const LOADER_REGISTRY_KEY = Symbol.for("eclipsa.loader-registry");
|
|
243
|
+
const LOADER_CONTENT_TYPE = "application/eclipsa-loader+json";
|
|
244
|
+
const SSR_PENDING_LOADER_ERROR = Symbol.for("eclipsa.ssr-pending-loader-error");
|
|
245
|
+
const SSR_PENDING_LOADER_IDS_KEY = Symbol.for("eclipsa.ssr-pending-loader-ids");
|
|
246
|
+
const ensurePendingSsrLoaderIds = (container) => {
|
|
247
|
+
const record = container;
|
|
248
|
+
const existing = record[SSR_PENDING_LOADER_IDS_KEY];
|
|
249
|
+
if (existing instanceof Set) return existing;
|
|
250
|
+
const created = /* @__PURE__ */ new Set();
|
|
251
|
+
record[SSR_PENDING_LOADER_IDS_KEY] = created;
|
|
252
|
+
return created;
|
|
253
|
+
};
|
|
254
|
+
const markPendingSsrLoader = (container, id) => {
|
|
255
|
+
ensurePendingSsrLoaderIds(container).add(id);
|
|
256
|
+
};
|
|
257
|
+
const consumePendingSsrLoaderIds = (container) => {
|
|
258
|
+
const ids = [...ensurePendingSsrLoaderIds(container)];
|
|
259
|
+
ensurePendingSsrLoaderIds(container).clear();
|
|
260
|
+
return ids;
|
|
261
|
+
};
|
|
262
|
+
const isPendingSsrLoaderError = (error) => error === SSR_PENDING_LOADER_ERROR;
|
|
263
|
+
const getLoaderRegistry = () => {
|
|
264
|
+
const globalRecord = globalThis;
|
|
265
|
+
const existing = globalRecord[LOADER_REGISTRY_KEY];
|
|
266
|
+
if (existing instanceof Map) return existing;
|
|
267
|
+
const created = /* @__PURE__ */ new Map();
|
|
268
|
+
globalRecord[LOADER_REGISTRY_KEY] = created;
|
|
269
|
+
return created;
|
|
270
|
+
};
|
|
271
|
+
const composeMiddlewares$1 = async (c, middlewares, handler) => {
|
|
272
|
+
let index = -1;
|
|
273
|
+
const dispatch = async (nextIndex) => {
|
|
274
|
+
if (nextIndex <= index) throw new Error("Loader middleware called next() multiple times.");
|
|
275
|
+
index = nextIndex;
|
|
276
|
+
const middleware = middlewares[nextIndex];
|
|
277
|
+
if (!middleware) return handler(c);
|
|
278
|
+
let nextResult = void 0;
|
|
279
|
+
const result = await middleware(c, (async () => {
|
|
280
|
+
nextResult = await dispatch(nextIndex + 1);
|
|
281
|
+
}));
|
|
282
|
+
if (result !== void 0) return result;
|
|
283
|
+
return nextResult;
|
|
284
|
+
};
|
|
285
|
+
return dispatch(0);
|
|
286
|
+
};
|
|
287
|
+
const toSerializedLoaderError = (error) => {
|
|
288
|
+
if (error instanceof Error) return serializeValue({
|
|
289
|
+
message: error.message,
|
|
290
|
+
name: error.name
|
|
291
|
+
});
|
|
292
|
+
try {
|
|
293
|
+
return serializeValue(error);
|
|
294
|
+
} catch {
|
|
295
|
+
return serializeValue({ message: "Loader failed." });
|
|
296
|
+
}
|
|
297
|
+
};
|
|
298
|
+
const normalizeLoaderValue = (value) => {
|
|
299
|
+
if (value instanceof Response) throw new TypeError("loader$() handlers and middlewares must resolve to data, not Response.");
|
|
300
|
+
if (typeof ReadableStream !== "undefined" && value instanceof ReadableStream) throw new TypeError("loader$() does not support ReadableStream results.");
|
|
301
|
+
if (value && typeof value === "object" && Symbol.asyncIterator in value) throw new TypeError("loader$() does not support async iterable results.");
|
|
302
|
+
return value;
|
|
303
|
+
};
|
|
304
|
+
const updateLoaderSnapshot = (container, id, snapshot) => {
|
|
305
|
+
container?.loaderStates.set(id, snapshot);
|
|
306
|
+
};
|
|
307
|
+
const createHandleSignal$1 = (container, id, key, initialValue) => {
|
|
308
|
+
if (!container) throw new Error("Loader handles require an active runtime container.");
|
|
309
|
+
return {
|
|
310
|
+
detached: false,
|
|
311
|
+
signal: createDetachedRuntimeSignal(container, `$loader:${id}:${key}`, initialValue)
|
|
312
|
+
};
|
|
313
|
+
};
|
|
314
|
+
const parseJsonLoaderResponse = async (response) => {
|
|
315
|
+
const body = await response.json();
|
|
316
|
+
if (!body || typeof body !== "object" || typeof body.ok !== "boolean") throw new TypeError("Malformed loader response.");
|
|
317
|
+
if (!body.ok) throw deserializeValue(body.error);
|
|
318
|
+
return deserializeValue(body.value);
|
|
319
|
+
};
|
|
320
|
+
const invokeLoader = async (id) => {
|
|
321
|
+
return parseJsonLoaderResponse(await fetch(`/__eclipsa/loader/${encodeURIComponent(id)}`, {
|
|
322
|
+
headers: { accept: LOADER_CONTENT_TYPE },
|
|
323
|
+
method: "GET"
|
|
324
|
+
}));
|
|
325
|
+
};
|
|
326
|
+
const resolveLoader = async (id, c) => {
|
|
327
|
+
const loader = getLoaderRegistry().get(id);
|
|
328
|
+
if (!loader) throw new Error(`Unknown loader ${id}.`);
|
|
329
|
+
return normalizeLoaderValue(await composeMiddlewares$1(c, loader.middlewares, loader.handler));
|
|
330
|
+
};
|
|
331
|
+
const loader$ = (() => {
|
|
332
|
+
throw new Error("loader$() must be compiled by the Eclipsa analyzer before it can run.");
|
|
333
|
+
});
|
|
334
|
+
function registerLoader(id, middlewares, handler) {
|
|
335
|
+
getLoaderRegistry().set(id, {
|
|
336
|
+
handler,
|
|
337
|
+
id,
|
|
338
|
+
middlewares: [...middlewares]
|
|
339
|
+
});
|
|
340
|
+
}
|
|
341
|
+
const hasLoader = (id) => getLoaderRegistry().has(id);
|
|
342
|
+
const executeLoader = async (id, c) => {
|
|
343
|
+
try {
|
|
344
|
+
return new Response(JSON.stringify({
|
|
345
|
+
ok: true,
|
|
346
|
+
value: serializeValue(await resolveLoader(id, c))
|
|
347
|
+
}), { headers: { "content-type": LOADER_CONTENT_TYPE } });
|
|
348
|
+
} catch (error) {
|
|
349
|
+
return new Response(JSON.stringify({
|
|
350
|
+
error: toSerializedLoaderError(error),
|
|
351
|
+
ok: false
|
|
352
|
+
}), {
|
|
353
|
+
headers: { "content-type": LOADER_CONTENT_TYPE },
|
|
354
|
+
status: error instanceof Error && error.message.startsWith("Unknown loader ") ? 404 : 500
|
|
355
|
+
});
|
|
356
|
+
}
|
|
357
|
+
};
|
|
358
|
+
const primeLoaderState = async (container, id, c) => {
|
|
359
|
+
const value = await resolveLoader(id, c);
|
|
360
|
+
updateLoaderSnapshot(container, id, {
|
|
361
|
+
data: value,
|
|
362
|
+
error: void 0,
|
|
363
|
+
loaded: true
|
|
364
|
+
});
|
|
365
|
+
return value;
|
|
366
|
+
};
|
|
367
|
+
const resolvePendingLoaders = async (container, c) => {
|
|
368
|
+
const pendingIds = consumePendingSsrLoaderIds(container);
|
|
369
|
+
if (pendingIds.length === 0) return false;
|
|
370
|
+
await Promise.all(pendingIds.map((id) => primeLoaderState(container, id, c)));
|
|
371
|
+
return true;
|
|
372
|
+
};
|
|
373
|
+
const __eclipsaLoader = (id, middlewares, handler) => {
|
|
374
|
+
if (typeof window === "undefined") getLoaderRegistry().set(id, {
|
|
375
|
+
handler,
|
|
376
|
+
id,
|
|
377
|
+
middlewares: [...middlewares]
|
|
378
|
+
});
|
|
379
|
+
return registerLoaderHook(id, setLoaderHookMeta(() => {
|
|
380
|
+
const container = getRuntimeContainer();
|
|
381
|
+
const existing = container?.loaders.get(id);
|
|
382
|
+
if (existing) return existing;
|
|
383
|
+
const initialState = container?.loaderStates.get(id);
|
|
384
|
+
if (typeof window === "undefined" && !initialState?.loaded) {
|
|
385
|
+
if (!container) throw new Error(`loader$("${id}") was used during SSR before it was preloaded.`);
|
|
386
|
+
markPendingSsrLoader(container, id);
|
|
387
|
+
throw SSR_PENDING_LOADER_ERROR;
|
|
388
|
+
}
|
|
389
|
+
const loadingState = createHandleSignal$1(container, id, "loading", false);
|
|
390
|
+
const dataState = createHandleSignal$1(container, id, "data", initialState?.loaded ? initialState.data : void 0);
|
|
391
|
+
const errorState = createHandleSignal$1(container, id, "error", initialState?.error);
|
|
392
|
+
const isDetached = loadingState.detached || dataState.detached || errorState.detached;
|
|
393
|
+
const isLoading = loadingState.signal;
|
|
394
|
+
const data = dataState.signal;
|
|
395
|
+
const error = errorState.signal;
|
|
396
|
+
let loaded = initialState?.loaded ?? false;
|
|
397
|
+
let inFlight = null;
|
|
398
|
+
let autoRequested = false;
|
|
399
|
+
const syncSnapshot = () => {
|
|
400
|
+
updateLoaderSnapshot(container ?? null, id, {
|
|
401
|
+
data: data.value,
|
|
402
|
+
error: error.value,
|
|
403
|
+
loaded
|
|
404
|
+
});
|
|
405
|
+
};
|
|
406
|
+
const load = async () => {
|
|
407
|
+
if (inFlight) return inFlight;
|
|
408
|
+
isLoading.value = true;
|
|
409
|
+
error.value = void 0;
|
|
410
|
+
syncSnapshot();
|
|
411
|
+
inFlight = (async () => {
|
|
412
|
+
try {
|
|
413
|
+
const value = await invokeLoader(id);
|
|
414
|
+
data.value = value;
|
|
415
|
+
loaded = true;
|
|
416
|
+
return value;
|
|
417
|
+
} catch (caught) {
|
|
418
|
+
error.value = caught;
|
|
419
|
+
throw caught;
|
|
420
|
+
} finally {
|
|
421
|
+
isLoading.value = false;
|
|
422
|
+
syncSnapshot();
|
|
423
|
+
inFlight = null;
|
|
424
|
+
}
|
|
425
|
+
})();
|
|
426
|
+
return inFlight;
|
|
427
|
+
};
|
|
428
|
+
const loaderHandle = setLoaderHandleMeta({
|
|
429
|
+
get data() {
|
|
430
|
+
return data.value;
|
|
431
|
+
},
|
|
432
|
+
get error() {
|
|
433
|
+
return error.value;
|
|
434
|
+
},
|
|
435
|
+
get isLoading() {
|
|
436
|
+
return isLoading.value;
|
|
437
|
+
},
|
|
438
|
+
load
|
|
439
|
+
}, id);
|
|
440
|
+
container?.loaders.set(id, loaderHandle);
|
|
441
|
+
syncSnapshot();
|
|
442
|
+
if (typeof window !== "undefined" && !isDetached && !loaded && !autoRequested) {
|
|
443
|
+
autoRequested = true;
|
|
444
|
+
queueMicrotask(() => {
|
|
445
|
+
load().catch(() => {});
|
|
446
|
+
});
|
|
447
|
+
}
|
|
448
|
+
return loaderHandle;
|
|
449
|
+
}, id));
|
|
450
|
+
};
|
|
451
|
+
//#endregion
|
|
452
|
+
//#region core/internal.ts
|
|
453
|
+
const COMPONENT_META_KEY = Symbol.for("eclipsa.component-meta");
|
|
454
|
+
const LAZY_META_KEY = Symbol.for("eclipsa.lazy-meta");
|
|
455
|
+
const NAVIGATE_META_KEY = Symbol.for("eclipsa.navigate-meta");
|
|
456
|
+
const SIGNAL_META_KEY = Symbol.for("eclipsa.signal-meta");
|
|
457
|
+
const ACTION_HOOK_META_KEY = Symbol.for("eclipsa.action-hook-meta");
|
|
458
|
+
const ACTION_HANDLE_META_KEY = Symbol.for("eclipsa.action-handle-meta");
|
|
459
|
+
const LOADER_HOOK_META_KEY = Symbol.for("eclipsa.loader-hook-meta");
|
|
460
|
+
const LOADER_HANDLE_META_KEY = Symbol.for("eclipsa.loader-handle-meta");
|
|
461
|
+
const WATCH_META_KEY = Symbol.for("eclipsa.watch-meta");
|
|
462
|
+
const ACTION_HOOK_REGISTRY_KEY = Symbol.for("eclipsa.action-hook-registry");
|
|
463
|
+
const LOADER_HOOK_REGISTRY_KEY = Symbol.for("eclipsa.loader-hook-registry");
|
|
464
|
+
const __eclipsaComponent = (component, symbol, captures, projectionSlots) => {
|
|
465
|
+
Object.defineProperty(component, COMPONENT_META_KEY, {
|
|
466
|
+
configurable: true,
|
|
467
|
+
enumerable: false,
|
|
468
|
+
value: {
|
|
469
|
+
symbol,
|
|
470
|
+
captures,
|
|
471
|
+
...projectionSlots ? { projectionSlots } : {}
|
|
472
|
+
},
|
|
473
|
+
writable: true
|
|
474
|
+
});
|
|
475
|
+
return component;
|
|
476
|
+
};
|
|
477
|
+
const __eclipsaLazy = (symbol, fn, captures) => {
|
|
478
|
+
const wrapped = ((...args) => fn(...args));
|
|
479
|
+
Object.defineProperty(wrapped, LAZY_META_KEY, {
|
|
480
|
+
configurable: true,
|
|
481
|
+
enumerable: false,
|
|
482
|
+
value: {
|
|
483
|
+
symbol,
|
|
484
|
+
captures
|
|
485
|
+
},
|
|
486
|
+
writable: true
|
|
487
|
+
});
|
|
488
|
+
return wrapped;
|
|
489
|
+
};
|
|
490
|
+
const __eclipsaWatch = (symbol, fn, captures) => {
|
|
491
|
+
const wrapped = ((...args) => fn(...args));
|
|
492
|
+
Object.defineProperty(wrapped, WATCH_META_KEY, {
|
|
493
|
+
configurable: true,
|
|
494
|
+
enumerable: false,
|
|
495
|
+
value: {
|
|
496
|
+
symbol,
|
|
497
|
+
captures
|
|
498
|
+
},
|
|
499
|
+
writable: true
|
|
500
|
+
});
|
|
501
|
+
return wrapped;
|
|
502
|
+
};
|
|
503
|
+
const __eclipsaEvent = (eventName, symbol, captures) => ({
|
|
504
|
+
eventName,
|
|
505
|
+
symbol,
|
|
506
|
+
captures
|
|
507
|
+
});
|
|
508
|
+
const getComponentMeta = (value) => {
|
|
509
|
+
if (typeof value !== "function") return null;
|
|
510
|
+
return value[COMPONENT_META_KEY] ?? null;
|
|
511
|
+
};
|
|
512
|
+
const getLazyMeta = (value) => {
|
|
513
|
+
if (typeof value !== "function") return null;
|
|
514
|
+
return value[LAZY_META_KEY] ?? null;
|
|
515
|
+
};
|
|
516
|
+
const setNavigateMeta = (target) => {
|
|
517
|
+
Object.defineProperty(target, NAVIGATE_META_KEY, {
|
|
518
|
+
configurable: true,
|
|
519
|
+
enumerable: false,
|
|
520
|
+
value: { kind: "navigate" },
|
|
521
|
+
writable: true
|
|
522
|
+
});
|
|
523
|
+
return target;
|
|
524
|
+
};
|
|
525
|
+
const getNavigateMeta = (value) => {
|
|
526
|
+
if (typeof value !== "function") return null;
|
|
527
|
+
return value[NAVIGATE_META_KEY] ?? null;
|
|
528
|
+
};
|
|
529
|
+
const setActionHookMeta = (target, id) => {
|
|
530
|
+
Object.defineProperty(target, ACTION_HOOK_META_KEY, {
|
|
531
|
+
configurable: true,
|
|
532
|
+
enumerable: false,
|
|
533
|
+
value: {
|
|
534
|
+
id,
|
|
535
|
+
kind: "action-hook"
|
|
536
|
+
},
|
|
537
|
+
writable: true
|
|
538
|
+
});
|
|
539
|
+
return target;
|
|
540
|
+
};
|
|
541
|
+
const getActionHookMeta = (value) => {
|
|
542
|
+
if (typeof value !== "function") return null;
|
|
543
|
+
return value[ACTION_HOOK_META_KEY] ?? null;
|
|
544
|
+
};
|
|
545
|
+
const setActionHandleMeta = (target, id) => {
|
|
546
|
+
Object.defineProperty(target, ACTION_HANDLE_META_KEY, {
|
|
547
|
+
configurable: true,
|
|
548
|
+
enumerable: false,
|
|
549
|
+
value: {
|
|
550
|
+
id,
|
|
551
|
+
kind: "action"
|
|
552
|
+
},
|
|
553
|
+
writable: true
|
|
554
|
+
});
|
|
555
|
+
return target;
|
|
556
|
+
};
|
|
557
|
+
const getActionHandleMeta = (value) => {
|
|
558
|
+
if (!value || typeof value !== "object") return null;
|
|
559
|
+
return value[ACTION_HANDLE_META_KEY] ?? null;
|
|
560
|
+
};
|
|
561
|
+
const setLoaderHookMeta = (target, id) => {
|
|
562
|
+
Object.defineProperty(target, LOADER_HOOK_META_KEY, {
|
|
563
|
+
configurable: true,
|
|
564
|
+
enumerable: false,
|
|
565
|
+
value: {
|
|
566
|
+
id,
|
|
567
|
+
kind: "loader-hook"
|
|
568
|
+
},
|
|
569
|
+
writable: true
|
|
570
|
+
});
|
|
571
|
+
return target;
|
|
572
|
+
};
|
|
573
|
+
const getLoaderHookMeta = (value) => {
|
|
574
|
+
if (typeof value !== "function") return null;
|
|
575
|
+
return value[LOADER_HOOK_META_KEY] ?? null;
|
|
576
|
+
};
|
|
577
|
+
const setLoaderHandleMeta = (target, id) => {
|
|
578
|
+
Object.defineProperty(target, LOADER_HANDLE_META_KEY, {
|
|
579
|
+
configurable: true,
|
|
580
|
+
enumerable: false,
|
|
581
|
+
value: {
|
|
582
|
+
id,
|
|
583
|
+
kind: "loader"
|
|
584
|
+
},
|
|
585
|
+
writable: true
|
|
586
|
+
});
|
|
587
|
+
return target;
|
|
588
|
+
};
|
|
589
|
+
const getLoaderHandleMeta = (value) => {
|
|
590
|
+
if (!value || typeof value !== "object") return null;
|
|
591
|
+
return value[LOADER_HANDLE_META_KEY] ?? null;
|
|
592
|
+
};
|
|
593
|
+
const getActionHookRegistry = () => {
|
|
594
|
+
const globalRecord = globalThis;
|
|
595
|
+
const existing = globalRecord[ACTION_HOOK_REGISTRY_KEY];
|
|
596
|
+
if (existing instanceof Map) return existing;
|
|
597
|
+
const created = /* @__PURE__ */ new Map();
|
|
598
|
+
globalRecord[ACTION_HOOK_REGISTRY_KEY] = created;
|
|
599
|
+
return created;
|
|
600
|
+
};
|
|
601
|
+
const registerActionHook = (id, hook) => {
|
|
602
|
+
getActionHookRegistry().set(id, hook);
|
|
603
|
+
return hook;
|
|
604
|
+
};
|
|
605
|
+
const getRegisteredActionHook = (id) => getActionHookRegistry().get(id) ?? null;
|
|
606
|
+
const getRegisteredActionHookIds = () => [...getActionHookRegistry().keys()];
|
|
607
|
+
const getLoaderHookRegistry = () => {
|
|
608
|
+
const globalRecord = globalThis;
|
|
609
|
+
const existing = globalRecord[LOADER_HOOK_REGISTRY_KEY];
|
|
610
|
+
if (existing instanceof Map) return existing;
|
|
611
|
+
const created = /* @__PURE__ */ new Map();
|
|
612
|
+
globalRecord[LOADER_HOOK_REGISTRY_KEY] = created;
|
|
613
|
+
return created;
|
|
614
|
+
};
|
|
615
|
+
const registerLoaderHook = (id, hook) => {
|
|
616
|
+
getLoaderHookRegistry().set(id, hook);
|
|
617
|
+
return hook;
|
|
618
|
+
};
|
|
619
|
+
const getRegisteredLoaderHook = (id) => getLoaderHookRegistry().get(id) ?? null;
|
|
620
|
+
const getRegisteredLoaderHookIds = () => [...getLoaderHookRegistry().keys()];
|
|
621
|
+
const getWatchMeta = (value) => {
|
|
622
|
+
if (typeof value !== "function") return null;
|
|
623
|
+
return value[WATCH_META_KEY] ?? null;
|
|
624
|
+
};
|
|
625
|
+
const getEventMeta = (value) => {
|
|
626
|
+
if (value && typeof value === "object") {
|
|
627
|
+
const descriptor = value;
|
|
628
|
+
if (typeof descriptor.symbol === "string" && typeof descriptor.captures === "function") return descriptor;
|
|
629
|
+
}
|
|
630
|
+
return getLazyMeta(value);
|
|
631
|
+
};
|
|
632
|
+
const setSignalMeta = (target, meta) => {
|
|
633
|
+
Object.defineProperty(target, SIGNAL_META_KEY, {
|
|
634
|
+
configurable: true,
|
|
635
|
+
enumerable: false,
|
|
636
|
+
value: meta,
|
|
637
|
+
writable: true
|
|
638
|
+
});
|
|
639
|
+
return target;
|
|
640
|
+
};
|
|
641
|
+
const getSignalMeta = (value) => {
|
|
642
|
+
if (!value || typeof value !== "object") return null;
|
|
643
|
+
return value[SIGNAL_META_KEY] ?? null;
|
|
644
|
+
};
|
|
645
|
+
//#endregion
|
|
646
|
+
//#region core/runtime.ts
|
|
647
|
+
const CONTAINER_STACK_KEY = Symbol.for("eclipsa.container-stack");
|
|
648
|
+
const FRAME_STACK_KEY = Symbol.for("eclipsa.frame-stack");
|
|
649
|
+
const DIRTY_FLUSH_PROMISE_KEY = Symbol.for("eclipsa.dirty-flush-promise");
|
|
650
|
+
const ASYNC_SIGNAL_SNAPSHOT_CACHE_KEY = Symbol.for("eclipsa.async-signal-snapshot-cache");
|
|
651
|
+
const STANDALONE_SIGNAL_ID_KEY = Symbol.for("eclipsa.standalone-signal-id");
|
|
652
|
+
const ACTION_FORM_ATTR$1 = "data-e-action-form";
|
|
653
|
+
const ROUTER_EVENT_STATE_KEY = Symbol.for("eclipsa.router-event-state");
|
|
654
|
+
const ROUTER_CURRENT_PATH_SIGNAL_ID = "$router:path";
|
|
655
|
+
const ROUTER_IS_NAVIGATING_SIGNAL_ID = "$router:isNavigating";
|
|
656
|
+
const ROUTER_LINK_BOUND_KEY = Symbol.for("eclipsa.router-link-bound");
|
|
657
|
+
const ROUTER_LINK_PREFETCH_BOUND_KEY = Symbol.for("eclipsa.router-link-prefetch-bound");
|
|
658
|
+
const ROUTE_NOT_FOUND_KEY = Symbol.for("eclipsa.route-not-found");
|
|
659
|
+
const ROUTE_PARAMS_PROP = "__eclipsa_route_params";
|
|
660
|
+
const ROUTE_SLOT_ROUTE_KEY = Symbol.for("eclipsa.route-slot-route");
|
|
661
|
+
const RESUME_CONTAINERS_KEY = Symbol.for("eclipsa.resume-containers");
|
|
662
|
+
const RESUME_STATE_ELEMENT_ID = "eclipsa-resume";
|
|
663
|
+
const RESUME_FINAL_STATE_ELEMENT_ID = "eclipsa-resume-final";
|
|
664
|
+
const ROOT_COMPONENT_ID = "$root";
|
|
665
|
+
const SUSPENSE_COMPONENT_SYMBOL = "$suspense";
|
|
666
|
+
const ROUTE_SLOT_TYPE = "route-slot";
|
|
667
|
+
const PROJECTION_SLOT_TYPE = "projection-slot";
|
|
668
|
+
const CONTAINER_ID_KEY = Symbol.for("eclipsa.runtime-container-id");
|
|
669
|
+
const RENDER_COMPONENT_TYPE_KEY = Symbol.for("eclipsa.render-component-type");
|
|
670
|
+
const RENDER_REFERENCE_KIND = "render";
|
|
671
|
+
const REF_SIGNAL_ATTR = "data-e-ref";
|
|
672
|
+
const STREAM_STATE_KEY = "__eclipsa_stream";
|
|
673
|
+
const getDomContexts = (value) => {
|
|
674
|
+
const contexts = [];
|
|
675
|
+
if (!value || typeof value !== "object" && typeof value !== "function") return contexts;
|
|
676
|
+
if ("ownerDocument" in value) {
|
|
677
|
+
const ownerDocument = value.ownerDocument;
|
|
678
|
+
if (ownerDocument?.defaultView) contexts.push(ownerDocument.defaultView);
|
|
679
|
+
}
|
|
680
|
+
if ("defaultView" in value) {
|
|
681
|
+
const defaultView = value.defaultView;
|
|
682
|
+
if (defaultView) contexts.push(defaultView);
|
|
683
|
+
}
|
|
684
|
+
contexts.push(globalThis);
|
|
685
|
+
return contexts;
|
|
686
|
+
};
|
|
687
|
+
const isDomInstance = (value, name) => {
|
|
688
|
+
for (const context of getDomContexts(value)) {
|
|
689
|
+
const ctor = context[name];
|
|
690
|
+
if (typeof ctor === "function" && value instanceof ctor) return true;
|
|
691
|
+
}
|
|
692
|
+
return false;
|
|
693
|
+
};
|
|
694
|
+
const hasOwnerDocument = (value) => !!value && (typeof value === "object" || typeof value === "function") && "ownerDocument" in value && !!value.ownerDocument;
|
|
695
|
+
const isElementNode = (value) => isDomInstance(value, "Element") || isDomInstance(value, "HTMLElement");
|
|
696
|
+
const isHTMLElementNode = (value) => isDomInstance(value, "HTMLElement");
|
|
697
|
+
const isHTMLInputElementNode = (value) => isDomInstance(value, "HTMLInputElement");
|
|
698
|
+
const isHTMLTextAreaElementNode = (value) => isDomInstance(value, "HTMLTextAreaElement");
|
|
699
|
+
const isTextEntryElement = (value) => isHTMLInputElementNode(value) || isHTMLTextAreaElementNode(value);
|
|
700
|
+
const isHTMLAnchorElementNode = (value) => isDomInstance(value, "HTMLAnchorElement");
|
|
701
|
+
const isHTMLFormElementNode = (value) => isDomInstance(value, "HTMLFormElement");
|
|
702
|
+
const resolvedRuntimeSymbols = /* @__PURE__ */ new WeakMap();
|
|
703
|
+
const getResolvedRuntimeSymbols = (container) => {
|
|
704
|
+
const existing = resolvedRuntimeSymbols.get(container);
|
|
705
|
+
if (existing) return existing;
|
|
706
|
+
const created = /* @__PURE__ */ new Map();
|
|
707
|
+
resolvedRuntimeSymbols.set(container, created);
|
|
708
|
+
return created;
|
|
709
|
+
};
|
|
710
|
+
const isRenderObject = (value) => typeof value === "object" && value !== null && "type" in value && "props" in value;
|
|
711
|
+
const getContainerStack = () => {
|
|
712
|
+
const globalRecord = globalThis;
|
|
713
|
+
const existing = globalRecord[CONTAINER_STACK_KEY];
|
|
714
|
+
if (Array.isArray(existing)) return existing;
|
|
715
|
+
const created = [];
|
|
716
|
+
globalRecord[CONTAINER_STACK_KEY] = created;
|
|
717
|
+
return created;
|
|
718
|
+
};
|
|
719
|
+
const getFrameStack = () => {
|
|
720
|
+
const globalRecord = globalThis;
|
|
721
|
+
const existing = globalRecord[FRAME_STACK_KEY];
|
|
722
|
+
if (Array.isArray(existing)) return existing;
|
|
723
|
+
const created = [];
|
|
724
|
+
globalRecord[FRAME_STACK_KEY] = created;
|
|
725
|
+
return created;
|
|
726
|
+
};
|
|
727
|
+
const getResumeContainers = () => {
|
|
728
|
+
const globalRecord = globalThis;
|
|
729
|
+
const existing = globalRecord[RESUME_CONTAINERS_KEY];
|
|
730
|
+
if (existing instanceof Set) return existing;
|
|
731
|
+
const created = /* @__PURE__ */ new Set();
|
|
732
|
+
globalRecord[RESUME_CONTAINERS_KEY] = created;
|
|
733
|
+
return created;
|
|
734
|
+
};
|
|
735
|
+
const getCurrentContainer = () => {
|
|
736
|
+
const stack = getContainerStack();
|
|
737
|
+
return stack.length > 0 ? stack[stack.length - 1] : null;
|
|
738
|
+
};
|
|
739
|
+
const getAsyncSignalSnapshotCache = () => {
|
|
740
|
+
const globalRecord = globalThis;
|
|
741
|
+
const existing = globalRecord[ASYNC_SIGNAL_SNAPSHOT_CACHE_KEY];
|
|
742
|
+
if (existing instanceof Map) return existing;
|
|
743
|
+
const created = /* @__PURE__ */ new Map();
|
|
744
|
+
globalRecord[ASYNC_SIGNAL_SNAPSHOT_CACHE_KEY] = created;
|
|
745
|
+
return created;
|
|
746
|
+
};
|
|
747
|
+
const readAsyncSignalSnapshot = (id, container = getCurrentContainer()) => container?.asyncSignalStates.get(id) ?? container?.asyncSignalSnapshotCache.get(id) ?? getAsyncSignalSnapshotCache().get(id);
|
|
748
|
+
const writeAsyncSignalSnapshot = (id, value, container = getCurrentContainer()) => {
|
|
749
|
+
container?.asyncSignalStates.set(id, value);
|
|
750
|
+
container?.asyncSignalSnapshotCache.set(id, value);
|
|
751
|
+
if (!container) getAsyncSignalSnapshotCache().set(id, value);
|
|
752
|
+
};
|
|
753
|
+
const getCurrentFrame = () => {
|
|
754
|
+
const stack = getFrameStack();
|
|
755
|
+
return stack.length > 0 ? stack[stack.length - 1] : null;
|
|
756
|
+
};
|
|
757
|
+
const normalizeRoutePath = (pathname) => {
|
|
758
|
+
const normalizedPath = pathname.trim() || "/";
|
|
759
|
+
const withLeadingSlash = normalizedPath.startsWith("/") ? normalizedPath : `/${normalizedPath}`;
|
|
760
|
+
if (withLeadingSlash.length > 1 && withLeadingSlash.endsWith("/")) return withLeadingSlash.slice(0, -1);
|
|
761
|
+
return withLeadingSlash;
|
|
762
|
+
};
|
|
763
|
+
const EMPTY_ROUTE_PARAMS = Object.freeze({});
|
|
764
|
+
const splitRoutePath = (pathname) => normalizeRoutePath(pathname).split("/").filter(Boolean);
|
|
765
|
+
const matchRouteSegments = (segments, pathnameSegments, routeIndex = 0, pathIndex = 0, params = {}) => {
|
|
766
|
+
if (routeIndex >= segments.length) return pathIndex >= pathnameSegments.length ? params : null;
|
|
767
|
+
const segment = segments[routeIndex];
|
|
768
|
+
switch (segment.kind) {
|
|
769
|
+
case "static":
|
|
770
|
+
if (pathnameSegments[pathIndex] !== segment.value) return null;
|
|
771
|
+
return matchRouteSegments(segments, pathnameSegments, routeIndex + 1, pathIndex + 1, params);
|
|
772
|
+
case "required":
|
|
773
|
+
if (pathIndex >= pathnameSegments.length) return null;
|
|
774
|
+
return matchRouteSegments(segments, pathnameSegments, routeIndex + 1, pathIndex + 1, {
|
|
775
|
+
...params,
|
|
776
|
+
[segment.value]: pathnameSegments[pathIndex]
|
|
777
|
+
});
|
|
778
|
+
case "optional": {
|
|
779
|
+
const consumed = pathIndex < pathnameSegments.length ? matchRouteSegments(segments, pathnameSegments, routeIndex + 1, pathIndex + 1, {
|
|
780
|
+
...params,
|
|
781
|
+
[segment.value]: pathnameSegments[pathIndex]
|
|
782
|
+
}) : null;
|
|
783
|
+
if (consumed) return consumed;
|
|
784
|
+
return matchRouteSegments(segments, pathnameSegments, routeIndex + 1, pathIndex, {
|
|
785
|
+
...params,
|
|
786
|
+
[segment.value]: void 0
|
|
787
|
+
});
|
|
788
|
+
}
|
|
789
|
+
case "rest": return matchRouteSegments(segments, pathnameSegments, segments.length, pathnameSegments.length, {
|
|
790
|
+
...params,
|
|
791
|
+
[segment.value]: pathnameSegments.slice(pathIndex)
|
|
792
|
+
});
|
|
793
|
+
}
|
|
794
|
+
};
|
|
795
|
+
const matchRouteManifest = (manifest, pathname) => {
|
|
796
|
+
const normalizedPath = normalizeRoutePath(pathname);
|
|
797
|
+
const pathnameSegments = splitRoutePath(normalizedPath);
|
|
798
|
+
for (const entry of manifest) {
|
|
799
|
+
const params = matchRouteSegments(entry.segments, pathnameSegments);
|
|
800
|
+
if (params) return {
|
|
801
|
+
entry,
|
|
802
|
+
params,
|
|
803
|
+
pathname: normalizedPath
|
|
804
|
+
};
|
|
805
|
+
}
|
|
806
|
+
return null;
|
|
807
|
+
};
|
|
808
|
+
const scoreSpecialManifestEntry = (entry, pathname) => {
|
|
809
|
+
const pathnameSegments = splitRoutePath(pathname);
|
|
810
|
+
let score = 0;
|
|
811
|
+
for (let index = 0; index < entry.segments.length && index < pathnameSegments.length; index += 1) {
|
|
812
|
+
const segment = entry.segments[index];
|
|
813
|
+
const pathnameSegment = pathnameSegments[index];
|
|
814
|
+
if (segment.kind === "static") {
|
|
815
|
+
if (segment.value !== pathnameSegment) break;
|
|
816
|
+
score += 10;
|
|
817
|
+
continue;
|
|
818
|
+
}
|
|
819
|
+
score += segment.kind === "rest" ? 1 : 2;
|
|
820
|
+
if (segment.kind === "rest") break;
|
|
821
|
+
}
|
|
822
|
+
return score;
|
|
823
|
+
};
|
|
824
|
+
const findSpecialManifestEntry = (manifest, pathname, kind) => {
|
|
825
|
+
const matched = matchRouteManifest(manifest, pathname);
|
|
826
|
+
if (matched?.entry[kind]) return matched;
|
|
827
|
+
let best = null;
|
|
828
|
+
let bestScore = -1;
|
|
829
|
+
for (const entry of manifest) {
|
|
830
|
+
if (!entry[kind]) continue;
|
|
831
|
+
const score = scoreSpecialManifestEntry(entry, pathname);
|
|
832
|
+
if (score > bestScore) {
|
|
833
|
+
best = {
|
|
834
|
+
entry,
|
|
835
|
+
params: EMPTY_ROUTE_PARAMS,
|
|
836
|
+
pathname: normalizeRoutePath(pathname)
|
|
837
|
+
};
|
|
838
|
+
bestScore = score;
|
|
839
|
+
}
|
|
840
|
+
}
|
|
841
|
+
return best;
|
|
842
|
+
};
|
|
843
|
+
const routeCacheKey = (pathname, variant = "page") => `${normalizeRoutePath(pathname)}::${variant}`;
|
|
844
|
+
const routePrefetchKey = (url) => `${normalizeRoutePath(url.pathname)}${url.search}`;
|
|
845
|
+
let currentEffect = null;
|
|
846
|
+
let currentCleanupSlot = null;
|
|
847
|
+
const withoutTrackedEffect = (fn) => {
|
|
848
|
+
const previous = currentEffect;
|
|
849
|
+
currentEffect = null;
|
|
850
|
+
try {
|
|
851
|
+
return fn();
|
|
852
|
+
} finally {
|
|
853
|
+
currentEffect = previous;
|
|
854
|
+
}
|
|
855
|
+
};
|
|
856
|
+
const createCleanupSlot = () => ({ callbacks: [] });
|
|
857
|
+
const withCleanupSlot = (slot, fn) => {
|
|
858
|
+
const previous = currentCleanupSlot;
|
|
859
|
+
currentCleanupSlot = slot;
|
|
860
|
+
try {
|
|
861
|
+
return fn();
|
|
862
|
+
} finally {
|
|
863
|
+
currentCleanupSlot = previous;
|
|
864
|
+
}
|
|
865
|
+
};
|
|
866
|
+
const disposeCleanupSlot = (slot) => {
|
|
867
|
+
if (!slot || slot.callbacks.length === 0) return;
|
|
868
|
+
const callbacks = [...slot.callbacks].reverse();
|
|
869
|
+
slot.callbacks.length = 0;
|
|
870
|
+
let firstError = null;
|
|
871
|
+
const previous = currentCleanupSlot;
|
|
872
|
+
currentCleanupSlot = null;
|
|
873
|
+
try {
|
|
874
|
+
for (const callback of callbacks) try {
|
|
875
|
+
withoutTrackedEffect(callback);
|
|
876
|
+
} catch (error) {
|
|
877
|
+
firstError ??= error;
|
|
878
|
+
}
|
|
879
|
+
} finally {
|
|
880
|
+
currentCleanupSlot = previous;
|
|
881
|
+
}
|
|
882
|
+
if (firstError) throw firstError;
|
|
883
|
+
};
|
|
884
|
+
const clearEffectSignals = (effect) => {
|
|
885
|
+
for (const signal of effect.signals) signal.effects.delete(effect);
|
|
886
|
+
effect.signals.clear();
|
|
887
|
+
};
|
|
888
|
+
const collectTrackedDependencies = (effect, fn) => {
|
|
889
|
+
clearEffectSignals(effect);
|
|
890
|
+
currentEffect = effect;
|
|
891
|
+
try {
|
|
892
|
+
fn();
|
|
893
|
+
} finally {
|
|
894
|
+
currentEffect = null;
|
|
895
|
+
}
|
|
896
|
+
};
|
|
897
|
+
const trackWatchDependencies = (dependencies) => {
|
|
898
|
+
for (const dependency of dependencies) {
|
|
899
|
+
if (typeof dependency === "function") {
|
|
900
|
+
dependency();
|
|
901
|
+
continue;
|
|
902
|
+
}
|
|
903
|
+
if (!getSignalMeta(dependency)) throw new TypeError("useWatch dependencies must be signals or getter functions.");
|
|
904
|
+
dependency.value;
|
|
905
|
+
}
|
|
906
|
+
};
|
|
907
|
+
const runWatchCallback = (effect, cleanupSlot, fn, dependencies) => {
|
|
908
|
+
disposeCleanupSlot(cleanupSlot);
|
|
909
|
+
if (!dependencies) {
|
|
910
|
+
collectTrackedDependencies(effect, () => {
|
|
911
|
+
withCleanupSlot(cleanupSlot, fn);
|
|
912
|
+
});
|
|
913
|
+
return;
|
|
914
|
+
}
|
|
915
|
+
collectTrackedDependencies(effect, () => {
|
|
916
|
+
trackWatchDependencies(dependencies);
|
|
917
|
+
});
|
|
918
|
+
withCleanupSlot(cleanupSlot, fn);
|
|
919
|
+
};
|
|
920
|
+
const createLocalWatchRunner = (effect, cleanupSlot, fn, dependencies) => () => {
|
|
921
|
+
runWatchCallback(effect, cleanupSlot, fn, dependencies);
|
|
922
|
+
};
|
|
923
|
+
const isPlainObject = (value) => {
|
|
924
|
+
if (!value || typeof value !== "object") return false;
|
|
925
|
+
const proto = Object.getPrototypeOf(value);
|
|
926
|
+
return proto === Object.prototype || proto === null;
|
|
927
|
+
};
|
|
928
|
+
const isProjectionSlot = (value) => isPlainObject(value) && value.__eclipsa_type === PROJECTION_SLOT_TYPE;
|
|
929
|
+
const createProjectionSlot = (componentId, name, occurrence, source) => ({
|
|
930
|
+
__eclipsa_type: PROJECTION_SLOT_TYPE,
|
|
931
|
+
componentId,
|
|
932
|
+
name,
|
|
933
|
+
occurrence,
|
|
934
|
+
source
|
|
935
|
+
});
|
|
936
|
+
const encodeProjectionSlotName = (value) => encodeURIComponent(value);
|
|
937
|
+
const decodeProjectionSlotName = (value) => decodeURIComponent(value);
|
|
938
|
+
const createProjectionSlotMarker = (componentId, name, occurrence, kind) => `ec:s:${componentId}:${encodeProjectionSlotName(name)}:${occurrence}:${kind}`;
|
|
939
|
+
const PROJECTION_SLOT_MARKER_REGEX = /^ec:s:([^:]+):([^:]+):(\d+):(start|end)$/;
|
|
940
|
+
const parseProjectionSlotMarker = (value) => {
|
|
941
|
+
const matched = value.match(PROJECTION_SLOT_MARKER_REGEX);
|
|
942
|
+
if (!matched) return null;
|
|
943
|
+
return {
|
|
944
|
+
componentId: matched[1],
|
|
945
|
+
kind: matched[4],
|
|
946
|
+
key: `${matched[1]}:${matched[2]}:${matched[3]}`,
|
|
947
|
+
name: decodeProjectionSlotName(matched[2]),
|
|
948
|
+
occurrence: Number(matched[3])
|
|
949
|
+
};
|
|
950
|
+
};
|
|
951
|
+
const getRenderComponentTypeRef = (value) => {
|
|
952
|
+
if (typeof value !== "function") return null;
|
|
953
|
+
return value[RENDER_COMPONENT_TYPE_KEY] ?? null;
|
|
954
|
+
};
|
|
955
|
+
const createMaterializedRenderComponentType = (container, symbol, scopeId) => {
|
|
956
|
+
const component = __eclipsaComponent(((props) => {
|
|
957
|
+
const module = getResolvedRuntimeSymbols(container).get(symbol);
|
|
958
|
+
if (!module) throw new Error(`Missing preloaded render component symbol ${symbol}.`);
|
|
959
|
+
return module.default(materializeScope(container, scopeId), props);
|
|
960
|
+
}), symbol, () => materializeScope(container, scopeId));
|
|
961
|
+
Object.defineProperty(component, RENDER_COMPONENT_TYPE_KEY, {
|
|
962
|
+
configurable: true,
|
|
963
|
+
enumerable: false,
|
|
964
|
+
value: {
|
|
965
|
+
scopeId,
|
|
966
|
+
symbol
|
|
967
|
+
},
|
|
968
|
+
writable: true
|
|
969
|
+
});
|
|
970
|
+
return component;
|
|
971
|
+
};
|
|
972
|
+
const serializeRenderObjectReference = (container, value) => {
|
|
973
|
+
const evaluatedProps = evaluateProps(value.props);
|
|
974
|
+
const key = value.key ?? null;
|
|
975
|
+
const metadata = value.metadata ?? null;
|
|
976
|
+
if (typeof value.type === "string") return {
|
|
977
|
+
__eclipsa_type: "ref",
|
|
978
|
+
data: [
|
|
979
|
+
"element",
|
|
980
|
+
value.type,
|
|
981
|
+
null,
|
|
982
|
+
serializeRuntimeValue(container, evaluatedProps),
|
|
983
|
+
serializeRuntimeValue(container, key),
|
|
984
|
+
value.isStatic,
|
|
985
|
+
serializeRuntimeValue(container, metadata)
|
|
986
|
+
],
|
|
987
|
+
kind: RENDER_REFERENCE_KIND,
|
|
988
|
+
token: "jsx"
|
|
989
|
+
};
|
|
990
|
+
const meta = getComponentMeta(value.type);
|
|
991
|
+
if (!meta) throw new TypeError("Only resumable component render objects can be serialized.");
|
|
992
|
+
return {
|
|
993
|
+
__eclipsa_type: "ref",
|
|
994
|
+
data: [
|
|
995
|
+
"component",
|
|
996
|
+
meta.symbol,
|
|
997
|
+
registerScope(container, meta.captures()),
|
|
998
|
+
serializeRuntimeValue(container, evaluatedProps),
|
|
999
|
+
serializeRuntimeValue(container, key),
|
|
1000
|
+
value.isStatic,
|
|
1001
|
+
serializeRuntimeValue(container, metadata)
|
|
1002
|
+
],
|
|
1003
|
+
kind: RENDER_REFERENCE_KIND,
|
|
1004
|
+
token: "jsx"
|
|
1005
|
+
};
|
|
1006
|
+
};
|
|
1007
|
+
const deserializeRenderObjectReference = (container, data) => {
|
|
1008
|
+
if (!Array.isArray(data) || data.length !== 7) throw new TypeError("Render references require a seven-part payload.");
|
|
1009
|
+
const [variant, typeValue, scopeValue, propsValue, keyValue, isStaticValue, metadataValue] = data;
|
|
1010
|
+
if (variant !== "element" && variant !== "component") throw new TypeError(`Unsupported render reference variant "${String(variant)}".`);
|
|
1011
|
+
if (typeof isStaticValue !== "boolean") throw new TypeError("Render references require a boolean static flag.");
|
|
1012
|
+
const props = deserializeRuntimeValue(container, propsValue);
|
|
1013
|
+
const key = deserializeRuntimeValue(container, keyValue);
|
|
1014
|
+
const metadata = deserializeRuntimeValue(container, metadataValue);
|
|
1015
|
+
if (!props || typeof props !== "object") throw new TypeError("Render references require object props.");
|
|
1016
|
+
if (variant === "element") {
|
|
1017
|
+
if (typeof typeValue !== "string") throw new TypeError("Element render references require a string tag name.");
|
|
1018
|
+
return {
|
|
1019
|
+
isStatic: isStaticValue,
|
|
1020
|
+
key: key ?? void 0,
|
|
1021
|
+
metadata: metadata ?? void 0,
|
|
1022
|
+
props,
|
|
1023
|
+
type: typeValue
|
|
1024
|
+
};
|
|
1025
|
+
}
|
|
1026
|
+
if (typeof typeValue !== "string" || typeof scopeValue !== "string") throw new TypeError("Component render references require a symbol id and scope id.");
|
|
1027
|
+
return {
|
|
1028
|
+
isStatic: isStaticValue,
|
|
1029
|
+
key: key ?? void 0,
|
|
1030
|
+
metadata: metadata ?? void 0,
|
|
1031
|
+
props,
|
|
1032
|
+
type: createMaterializedRenderComponentType(container, typeValue, scopeValue)
|
|
1033
|
+
};
|
|
1034
|
+
};
|
|
1035
|
+
const preloadResumableValue = async (container, value, seen = /* @__PURE__ */ new Set()) => {
|
|
1036
|
+
if (value === null || value === void 0 || value === false) return;
|
|
1037
|
+
if (seen.has(value)) return;
|
|
1038
|
+
if (typeof value === "function") {
|
|
1039
|
+
const renderComponentRef = getRenderComponentTypeRef(value);
|
|
1040
|
+
if (!renderComponentRef) return;
|
|
1041
|
+
seen.add(value);
|
|
1042
|
+
await loadSymbol(container, renderComponentRef.symbol);
|
|
1043
|
+
for (const capturedValue of materializeScope(container, renderComponentRef.scopeId)) await preloadResumableValue(container, capturedValue, seen);
|
|
1044
|
+
return;
|
|
1045
|
+
}
|
|
1046
|
+
if (Array.isArray(value)) {
|
|
1047
|
+
seen.add(value);
|
|
1048
|
+
for (const entry of value) await preloadResumableValue(container, entry, seen);
|
|
1049
|
+
return;
|
|
1050
|
+
}
|
|
1051
|
+
if (typeof Node !== "undefined" && value instanceof Node) return;
|
|
1052
|
+
if (isProjectionSlot(value)) return;
|
|
1053
|
+
if (isRenderObject(value)) {
|
|
1054
|
+
seen.add(value);
|
|
1055
|
+
await preloadResumableValue(container, value.type, seen);
|
|
1056
|
+
await preloadResumableValue(container, evaluateProps(value.props), seen);
|
|
1057
|
+
return;
|
|
1058
|
+
}
|
|
1059
|
+
if (!isPlainObject(value)) return;
|
|
1060
|
+
seen.add(value);
|
|
1061
|
+
for (const entry of Object.values(value)) await preloadResumableValue(container, entry, seen);
|
|
1062
|
+
};
|
|
1063
|
+
const serializeRuntimeValue = (container, value) => serializeValue(value, { serializeReference(candidate) {
|
|
1064
|
+
const signalMeta = getSignalMeta(candidate);
|
|
1065
|
+
if (signalMeta) return {
|
|
1066
|
+
__eclipsa_type: "ref",
|
|
1067
|
+
kind: "signal",
|
|
1068
|
+
token: signalMeta.id
|
|
1069
|
+
};
|
|
1070
|
+
if (getNavigateMeta(candidate)) return {
|
|
1071
|
+
__eclipsa_type: "ref",
|
|
1072
|
+
kind: "navigate",
|
|
1073
|
+
token: "navigate"
|
|
1074
|
+
};
|
|
1075
|
+
const actionMeta = getActionHandleMeta(candidate);
|
|
1076
|
+
if (actionMeta) return {
|
|
1077
|
+
__eclipsa_type: "ref",
|
|
1078
|
+
kind: "action",
|
|
1079
|
+
token: actionMeta.id
|
|
1080
|
+
};
|
|
1081
|
+
const actionHookMeta = getActionHookMeta(candidate);
|
|
1082
|
+
if (actionHookMeta) return {
|
|
1083
|
+
__eclipsa_type: "ref",
|
|
1084
|
+
kind: "action-hook",
|
|
1085
|
+
token: actionHookMeta.id
|
|
1086
|
+
};
|
|
1087
|
+
const loaderMeta = getLoaderHandleMeta(candidate);
|
|
1088
|
+
if (loaderMeta) return {
|
|
1089
|
+
__eclipsa_type: "ref",
|
|
1090
|
+
kind: "loader",
|
|
1091
|
+
token: loaderMeta.id
|
|
1092
|
+
};
|
|
1093
|
+
const loaderHookMeta = getLoaderHookMeta(candidate);
|
|
1094
|
+
if (loaderHookMeta) return {
|
|
1095
|
+
__eclipsa_type: "ref",
|
|
1096
|
+
kind: "loader-hook",
|
|
1097
|
+
token: loaderHookMeta.id
|
|
1098
|
+
};
|
|
1099
|
+
if (isRouteSlot(candidate)) return {
|
|
1100
|
+
__eclipsa_type: "ref",
|
|
1101
|
+
data: serializeValue(candidate.startLayoutIndex),
|
|
1102
|
+
kind: "route-slot",
|
|
1103
|
+
token: candidate.pathname
|
|
1104
|
+
};
|
|
1105
|
+
if (isProjectionSlot(candidate)) return {
|
|
1106
|
+
__eclipsa_type: "ref",
|
|
1107
|
+
data: serializeRuntimeValue(container, candidate.source),
|
|
1108
|
+
kind: PROJECTION_SLOT_TYPE,
|
|
1109
|
+
token: JSON.stringify([
|
|
1110
|
+
candidate.componentId,
|
|
1111
|
+
candidate.name,
|
|
1112
|
+
candidate.occurrence
|
|
1113
|
+
])
|
|
1114
|
+
};
|
|
1115
|
+
if (isRenderObject(candidate)) return serializeRenderObjectReference(container, candidate);
|
|
1116
|
+
const lazyMeta = getLazyMeta(candidate);
|
|
1117
|
+
if (lazyMeta) return {
|
|
1118
|
+
__eclipsa_type: "ref",
|
|
1119
|
+
data: lazyMeta.captures().map((entry) => serializeRuntimeValue(container, entry)),
|
|
1120
|
+
kind: "symbol",
|
|
1121
|
+
token: lazyMeta.symbol
|
|
1122
|
+
};
|
|
1123
|
+
if (isElementNode(candidate)) return {
|
|
1124
|
+
__eclipsa_type: "ref",
|
|
1125
|
+
kind: "dom",
|
|
1126
|
+
token: ensureRuntimeElementId(container, candidate)
|
|
1127
|
+
};
|
|
1128
|
+
return null;
|
|
1129
|
+
} });
|
|
1130
|
+
const deserializeRuntimeValue = (container, value) => deserializeValue(value, { deserializeReference(reference) {
|
|
1131
|
+
if (reference.kind === "navigate") return ensureRouterState(container).navigate;
|
|
1132
|
+
if (reference.kind === "action") {
|
|
1133
|
+
const action = container.actions.get(reference.token);
|
|
1134
|
+
if (!action) throw new Error(`Missing action handle ${reference.token}.`);
|
|
1135
|
+
return action;
|
|
1136
|
+
}
|
|
1137
|
+
if (reference.kind === "action-hook") {
|
|
1138
|
+
const actionHook = getRegisteredActionHook(reference.token);
|
|
1139
|
+
if (!actionHook) throw new Error(`Missing action hook ${reference.token}.`);
|
|
1140
|
+
return actionHook;
|
|
1141
|
+
}
|
|
1142
|
+
if (reference.kind === "loader") {
|
|
1143
|
+
const loader = container.loaders.get(reference.token);
|
|
1144
|
+
if (!loader) throw new Error(`Missing loader handle ${reference.token}.`);
|
|
1145
|
+
return loader;
|
|
1146
|
+
}
|
|
1147
|
+
if (reference.kind === "loader-hook") {
|
|
1148
|
+
const loaderHook = getRegisteredLoaderHook(reference.token);
|
|
1149
|
+
if (!loaderHook) throw new Error(`Missing loader hook ${reference.token}.`);
|
|
1150
|
+
return loaderHook;
|
|
1151
|
+
}
|
|
1152
|
+
if (reference.kind === "route-slot") {
|
|
1153
|
+
const startLayoutIndex = reference.data === void 0 ? 0 : deserializeValue(reference.data);
|
|
1154
|
+
if (typeof startLayoutIndex !== "number" || !Number.isInteger(startLayoutIndex)) throw new TypeError("Route slot references require an integer start layout index.");
|
|
1155
|
+
return {
|
|
1156
|
+
__eclipsa_type: ROUTE_SLOT_TYPE,
|
|
1157
|
+
pathname: reference.token,
|
|
1158
|
+
startLayoutIndex
|
|
1159
|
+
};
|
|
1160
|
+
}
|
|
1161
|
+
if (reference.kind === PROJECTION_SLOT_TYPE) {
|
|
1162
|
+
let componentId = "";
|
|
1163
|
+
let name = "";
|
|
1164
|
+
let occurrence = 0;
|
|
1165
|
+
try {
|
|
1166
|
+
const parsed = JSON.parse(reference.token);
|
|
1167
|
+
if (!Array.isArray(parsed) || parsed.length !== 3 || typeof parsed[0] !== "string" || typeof parsed[1] !== "string" || typeof parsed[2] !== "number") throw new Error("invalid projection slot token");
|
|
1168
|
+
componentId = parsed[0];
|
|
1169
|
+
name = parsed[1];
|
|
1170
|
+
occurrence = parsed[2];
|
|
1171
|
+
} catch {
|
|
1172
|
+
throw new TypeError("Projection slot references require a component id, name, and occurrence.");
|
|
1173
|
+
}
|
|
1174
|
+
return createProjectionSlot(componentId, name, occurrence, deserializeRuntimeValue(container, reference.data));
|
|
1175
|
+
}
|
|
1176
|
+
if (reference.kind === "signal") {
|
|
1177
|
+
const record = container.signals.get(reference.token);
|
|
1178
|
+
if (!record) throw new Error(`Missing signal ${reference.token}.`);
|
|
1179
|
+
return record.handle;
|
|
1180
|
+
}
|
|
1181
|
+
if (reference.kind === "symbol") {
|
|
1182
|
+
if (!reference.data || !Array.isArray(reference.data)) throw new TypeError("Symbol references require an encoded scope array.");
|
|
1183
|
+
const scopeId = registerSerializedScope(container, reference.data);
|
|
1184
|
+
return materializeSymbolReference(container, reference.token, scopeId);
|
|
1185
|
+
}
|
|
1186
|
+
if (reference.kind === RENDER_REFERENCE_KIND) return deserializeRenderObjectReference(container, reference.data);
|
|
1187
|
+
if (reference.kind === "dom") {
|
|
1188
|
+
const element = findRuntimeElement(container, reference.token);
|
|
1189
|
+
if (!element) throw new Error(`Missing DOM reference ${reference.token}.`);
|
|
1190
|
+
return element;
|
|
1191
|
+
}
|
|
1192
|
+
throw new TypeError(`Unsupported runtime reference kind "${reference.kind}".`);
|
|
1193
|
+
} });
|
|
1194
|
+
const findNextNumericId = (ids, prefix) => {
|
|
1195
|
+
let nextId = 0;
|
|
1196
|
+
for (const id of ids) {
|
|
1197
|
+
if (!id.startsWith(prefix)) continue;
|
|
1198
|
+
const suffix = id.slice(prefix.length);
|
|
1199
|
+
if (!/^\d+$/.test(suffix)) continue;
|
|
1200
|
+
nextId = Math.max(nextId, Number(suffix) + 1);
|
|
1201
|
+
}
|
|
1202
|
+
return nextId;
|
|
1203
|
+
};
|
|
1204
|
+
const getRefSignalId = (value) => getSignalMeta(value)?.id ?? null;
|
|
1205
|
+
const assignRuntimeRef = (value, element, container = getCurrentContainer()) => {
|
|
1206
|
+
const signalMeta = getSignalMeta(value);
|
|
1207
|
+
if (!signalMeta) return false;
|
|
1208
|
+
const record = container?.signals.get(signalMeta.id);
|
|
1209
|
+
if (record) {
|
|
1210
|
+
record.value = element;
|
|
1211
|
+
return true;
|
|
1212
|
+
}
|
|
1213
|
+
signalMeta.set(element);
|
|
1214
|
+
return true;
|
|
1215
|
+
};
|
|
1216
|
+
const restoreSignalRefs = (container, root) => {
|
|
1217
|
+
const assignElement = (element) => {
|
|
1218
|
+
const signalId = element.getAttribute(REF_SIGNAL_ATTR);
|
|
1219
|
+
if (!signalId) return;
|
|
1220
|
+
const record = container.signals.get(signalId);
|
|
1221
|
+
if (!record) return;
|
|
1222
|
+
record.value = element;
|
|
1223
|
+
};
|
|
1224
|
+
if (isElementNode(root) && root.getAttribute(REF_SIGNAL_ATTR)) assignElement(root);
|
|
1225
|
+
if (!("querySelectorAll" in root)) return;
|
|
1226
|
+
for (const element of root.querySelectorAll(`[${REF_SIGNAL_ATTR}]`)) assignElement(element);
|
|
1227
|
+
};
|
|
1228
|
+
const evaluateProps = (props) => {
|
|
1229
|
+
const result = {};
|
|
1230
|
+
for (const [key, descriptor] of Object.entries(Object.getOwnPropertyDescriptors(props))) if (descriptor.get) result[key] = descriptor.get.call(props);
|
|
1231
|
+
else result[key] = descriptor.value;
|
|
1232
|
+
return result;
|
|
1233
|
+
};
|
|
1234
|
+
const hasProjectionSlotValue = (props, name) => Object.prototype.hasOwnProperty.call(props, name);
|
|
1235
|
+
const createRenderProps = (componentId, meta, props) => {
|
|
1236
|
+
if (!meta.projectionSlots || Object.keys(meta.projectionSlots).length === 0) return props;
|
|
1237
|
+
const nextProps = { ...props };
|
|
1238
|
+
const counters = /* @__PURE__ */ new Map();
|
|
1239
|
+
for (const [name, totalOccurrences] of Object.entries(meta.projectionSlots)) Object.defineProperty(nextProps, name, {
|
|
1240
|
+
configurable: true,
|
|
1241
|
+
enumerable: true,
|
|
1242
|
+
get() {
|
|
1243
|
+
if (!hasProjectionSlotValue(props, name)) return;
|
|
1244
|
+
const current = counters.get(name) ?? 0;
|
|
1245
|
+
counters.set(name, current + 1);
|
|
1246
|
+
return createProjectionSlot(componentId, name, totalOccurrences > 0 ? current % totalOccurrences : current, props[name]);
|
|
1247
|
+
}
|
|
1248
|
+
});
|
|
1249
|
+
return nextProps;
|
|
1250
|
+
};
|
|
1251
|
+
const preloadComponentProps = async (container, meta, props) => {
|
|
1252
|
+
if (!props || typeof props !== "object" || !meta.projectionSlots) {
|
|
1253
|
+
await preloadResumableValue(container, props);
|
|
1254
|
+
return;
|
|
1255
|
+
}
|
|
1256
|
+
const projectionNames = new Set(Object.keys(meta.projectionSlots));
|
|
1257
|
+
const entries = {};
|
|
1258
|
+
for (const [key, value] of Object.entries(props)) {
|
|
1259
|
+
if (projectionNames.has(key)) continue;
|
|
1260
|
+
entries[key] = value;
|
|
1261
|
+
}
|
|
1262
|
+
await preloadResumableValue(container, entries);
|
|
1263
|
+
};
|
|
1264
|
+
const createContainer = (symbols, doc, asyncSignalSnapshotCache) => ({
|
|
1265
|
+
actions: /* @__PURE__ */ new Map(),
|
|
1266
|
+
actionStates: /* @__PURE__ */ new Map(),
|
|
1267
|
+
asyncSignalStates: /* @__PURE__ */ new Map(),
|
|
1268
|
+
asyncSignalSnapshotCache: asyncSignalSnapshotCache ?? /* @__PURE__ */ new Map(),
|
|
1269
|
+
components: /* @__PURE__ */ new Map(),
|
|
1270
|
+
dirty: /* @__PURE__ */ new Set(),
|
|
1271
|
+
doc,
|
|
1272
|
+
id: `rt${globalThis[CONTAINER_ID_KEY] = (globalThis[CONTAINER_ID_KEY] ?? 0) + 1}`,
|
|
1273
|
+
imports: /* @__PURE__ */ new Map(),
|
|
1274
|
+
loaderStates: /* @__PURE__ */ new Map(),
|
|
1275
|
+
loaders: /* @__PURE__ */ new Map(),
|
|
1276
|
+
nextComponentId: 0,
|
|
1277
|
+
nextElementId: 0,
|
|
1278
|
+
nextScopeId: 0,
|
|
1279
|
+
nextSignalId: 0,
|
|
1280
|
+
pendingSuspensePromises: /* @__PURE__ */ new Set(),
|
|
1281
|
+
rootChildCursor: 0,
|
|
1282
|
+
rootElement: doc?.body,
|
|
1283
|
+
router: null,
|
|
1284
|
+
scopes: /* @__PURE__ */ new Map(),
|
|
1285
|
+
signals: /* @__PURE__ */ new Map(),
|
|
1286
|
+
symbols: new Map(Object.entries(symbols)),
|
|
1287
|
+
visibilityCheckQueued: false,
|
|
1288
|
+
visibilityListenersCleanup: null,
|
|
1289
|
+
visibles: /* @__PURE__ */ new Map(),
|
|
1290
|
+
watches: /* @__PURE__ */ new Map()
|
|
1291
|
+
});
|
|
1292
|
+
const registerResumeContainer = (container) => {
|
|
1293
|
+
const containers = getResumeContainers();
|
|
1294
|
+
containers.add(container);
|
|
1295
|
+
return () => {
|
|
1296
|
+
containers.delete(container);
|
|
1297
|
+
};
|
|
1298
|
+
};
|
|
1299
|
+
const createSignalHandle = (record, container) => {
|
|
1300
|
+
const handle = {};
|
|
1301
|
+
Object.defineProperty(handle, "value", {
|
|
1302
|
+
configurable: true,
|
|
1303
|
+
enumerable: true,
|
|
1304
|
+
get() {
|
|
1305
|
+
recordSignalRead(record);
|
|
1306
|
+
return record.value;
|
|
1307
|
+
},
|
|
1308
|
+
set(value) {
|
|
1309
|
+
record.value = value;
|
|
1310
|
+
notifySignalWrite(container, record);
|
|
1311
|
+
}
|
|
1312
|
+
});
|
|
1313
|
+
setSignalMeta(handle, {
|
|
1314
|
+
get: () => record.value,
|
|
1315
|
+
id: record.id,
|
|
1316
|
+
set: (value) => {
|
|
1317
|
+
record.value = value;
|
|
1318
|
+
notifySignalWrite(container, record);
|
|
1319
|
+
}
|
|
1320
|
+
});
|
|
1321
|
+
return handle;
|
|
1322
|
+
};
|
|
1323
|
+
const ensureSignalRecord = (container, id, initialValue) => {
|
|
1324
|
+
if (!container) {
|
|
1325
|
+
const record = {
|
|
1326
|
+
effects: /* @__PURE__ */ new Set(),
|
|
1327
|
+
handle: void 0,
|
|
1328
|
+
id,
|
|
1329
|
+
subscribers: /* @__PURE__ */ new Set(),
|
|
1330
|
+
value: initialValue
|
|
1331
|
+
};
|
|
1332
|
+
record.handle = createSignalHandle(record, null);
|
|
1333
|
+
return record;
|
|
1334
|
+
}
|
|
1335
|
+
const existing = container.signals.get(id);
|
|
1336
|
+
if (existing) return existing;
|
|
1337
|
+
const record = {
|
|
1338
|
+
effects: /* @__PURE__ */ new Set(),
|
|
1339
|
+
handle: void 0,
|
|
1340
|
+
id,
|
|
1341
|
+
subscribers: /* @__PURE__ */ new Set(),
|
|
1342
|
+
value: initialValue
|
|
1343
|
+
};
|
|
1344
|
+
record.handle = createSignalHandle(record, container);
|
|
1345
|
+
container.signals.set(id, record);
|
|
1346
|
+
return record;
|
|
1347
|
+
};
|
|
1348
|
+
const isRouterSignalId = (id) => id === ROUTER_CURRENT_PATH_SIGNAL_ID || id === ROUTER_IS_NAVIGATING_SIGNAL_ID;
|
|
1349
|
+
const createStandaloneNavigate = () => {
|
|
1350
|
+
const navigate = (async (href, options) => {
|
|
1351
|
+
if (typeof window === "undefined") return;
|
|
1352
|
+
const url = new URL(href, window.location.href);
|
|
1353
|
+
if (options?.replace) {
|
|
1354
|
+
window.location.replace(url.href);
|
|
1355
|
+
return;
|
|
1356
|
+
}
|
|
1357
|
+
window.location.assign(url.href);
|
|
1358
|
+
});
|
|
1359
|
+
Object.defineProperty(navigate, "isNavigating", {
|
|
1360
|
+
configurable: true,
|
|
1361
|
+
enumerable: true,
|
|
1362
|
+
get() {
|
|
1363
|
+
return false;
|
|
1364
|
+
}
|
|
1365
|
+
});
|
|
1366
|
+
return setNavigateMeta(navigate);
|
|
1367
|
+
};
|
|
1368
|
+
const recordSignalRead = (record) => {
|
|
1369
|
+
if (currentEffect) {
|
|
1370
|
+
currentEffect.signals.add(record);
|
|
1371
|
+
record.effects.add(currentEffect);
|
|
1372
|
+
}
|
|
1373
|
+
const frame = getCurrentFrame();
|
|
1374
|
+
if (!frame) return;
|
|
1375
|
+
record.subscribers.add(frame.component.id);
|
|
1376
|
+
};
|
|
1377
|
+
const notifySignalWrite = (container, record) => {
|
|
1378
|
+
for (const effect of [...record.effects]) effect.fn();
|
|
1379
|
+
if (!container) return;
|
|
1380
|
+
for (const componentId of record.subscribers) {
|
|
1381
|
+
if (container.components.get(componentId)?.active) continue;
|
|
1382
|
+
container.dirty.add(componentId);
|
|
1383
|
+
}
|
|
1384
|
+
};
|
|
1385
|
+
const ensureRouterState = (container, manifest) => {
|
|
1386
|
+
if (container.router) {
|
|
1387
|
+
if (manifest) container.router.manifest = [...manifest];
|
|
1388
|
+
return container.router;
|
|
1389
|
+
}
|
|
1390
|
+
const currentPath = ensureSignalRecord(container, ROUTER_CURRENT_PATH_SIGNAL_ID, normalizeRoutePath(container.doc?.location.pathname ?? "/")).handle;
|
|
1391
|
+
const isNavigating = ensureSignalRecord(container, ROUTER_IS_NAVIGATING_SIGNAL_ID, false).handle;
|
|
1392
|
+
const router = {
|
|
1393
|
+
currentPath,
|
|
1394
|
+
currentRoute: null,
|
|
1395
|
+
defaultTitle: container.doc?.title ?? "",
|
|
1396
|
+
isNavigating,
|
|
1397
|
+
loadedRoutes: /* @__PURE__ */ new Map(),
|
|
1398
|
+
manifest: [],
|
|
1399
|
+
navigate: void 0,
|
|
1400
|
+
prefetchedLoaders: /* @__PURE__ */ new Map(),
|
|
1401
|
+
routePrefetches: /* @__PURE__ */ new Map(),
|
|
1402
|
+
sequence: 0
|
|
1403
|
+
};
|
|
1404
|
+
container.router = router;
|
|
1405
|
+
router.navigate = setNavigateMeta((async (href, options) => {
|
|
1406
|
+
await navigateContainer(container, href, { mode: options?.replace ? "replace" : "push" });
|
|
1407
|
+
}));
|
|
1408
|
+
Object.defineProperty(router.navigate, "isNavigating", {
|
|
1409
|
+
configurable: true,
|
|
1410
|
+
enumerable: true,
|
|
1411
|
+
get() {
|
|
1412
|
+
return ensureRouterState(container).isNavigating.value;
|
|
1413
|
+
}
|
|
1414
|
+
});
|
|
1415
|
+
if (manifest) router.manifest = [...manifest];
|
|
1416
|
+
return router;
|
|
1417
|
+
};
|
|
1418
|
+
const pushContainer = (container, fn) => {
|
|
1419
|
+
const stack = getContainerStack();
|
|
1420
|
+
stack.push(container);
|
|
1421
|
+
try {
|
|
1422
|
+
return fn();
|
|
1423
|
+
} finally {
|
|
1424
|
+
stack.pop();
|
|
1425
|
+
}
|
|
1426
|
+
};
|
|
1427
|
+
const withRuntimeContainer = pushContainer;
|
|
1428
|
+
const pushFrame = (frame, fn) => {
|
|
1429
|
+
const stack = getFrameStack();
|
|
1430
|
+
stack.push(frame);
|
|
1431
|
+
try {
|
|
1432
|
+
return fn();
|
|
1433
|
+
} finally {
|
|
1434
|
+
stack.pop();
|
|
1435
|
+
}
|
|
1436
|
+
};
|
|
1437
|
+
const allocateScopeId = (container) => `sc${container.nextScopeId++}`;
|
|
1438
|
+
const ensureRuntimeElementId = (container, element) => {
|
|
1439
|
+
const existingId = element.getAttribute("data-eid");
|
|
1440
|
+
if (existingId) return existingId;
|
|
1441
|
+
const nextId = `e${container.nextElementId++}`;
|
|
1442
|
+
element.setAttribute("data-eid", nextId);
|
|
1443
|
+
return nextId;
|
|
1444
|
+
};
|
|
1445
|
+
const findRuntimeElement = (container, elementId) => {
|
|
1446
|
+
const root = container.rootElement ?? container.doc?.body;
|
|
1447
|
+
if (!root) return null;
|
|
1448
|
+
if (root.getAttribute("data-eid") === elementId) return root;
|
|
1449
|
+
return root.querySelector(`[data-eid="${elementId}"]`);
|
|
1450
|
+
};
|
|
1451
|
+
const registerScope = (container, values) => {
|
|
1452
|
+
const id = allocateScopeId(container);
|
|
1453
|
+
container.scopes.set(id, values.map((value) => serializeRuntimeValue(container, value)));
|
|
1454
|
+
return id;
|
|
1455
|
+
};
|
|
1456
|
+
const registerSerializedScope = (container, values) => {
|
|
1457
|
+
const id = allocateScopeId(container);
|
|
1458
|
+
container.scopes.set(id, [...values]);
|
|
1459
|
+
return id;
|
|
1460
|
+
};
|
|
1461
|
+
const materializeSymbolReference = (container, symbolId, scopeId) => {
|
|
1462
|
+
const fn = (...args) => {
|
|
1463
|
+
loadSymbol(container, symbolId).then((module) => module.default(materializeScope(container, scopeId), ...args));
|
|
1464
|
+
};
|
|
1465
|
+
Object.defineProperty(fn, "name", {
|
|
1466
|
+
configurable: true,
|
|
1467
|
+
value: `eclipsa$${symbolId}`
|
|
1468
|
+
});
|
|
1469
|
+
return fn;
|
|
1470
|
+
};
|
|
1471
|
+
const materializeScope = (container, scopeId) => {
|
|
1472
|
+
const slots = container.scopes.get(scopeId);
|
|
1473
|
+
if (!slots) throw new Error(`Missing scope ${scopeId}.`);
|
|
1474
|
+
return slots.map((slot) => deserializeRuntimeValue(container, slot));
|
|
1475
|
+
};
|
|
1476
|
+
const createFrame = (container, component, mode, options) => ({
|
|
1477
|
+
childCursor: 0,
|
|
1478
|
+
component,
|
|
1479
|
+
container,
|
|
1480
|
+
mountCallbacks: [],
|
|
1481
|
+
mode,
|
|
1482
|
+
projectionState: {
|
|
1483
|
+
counters: /* @__PURE__ */ new Map(),
|
|
1484
|
+
reuseExistingDom: options?.reuseExistingDom ?? false
|
|
1485
|
+
},
|
|
1486
|
+
signalCursor: 0,
|
|
1487
|
+
visibleCursor: 0,
|
|
1488
|
+
visitedDescendants: /* @__PURE__ */ new Set(),
|
|
1489
|
+
watchCursor: 0
|
|
1490
|
+
});
|
|
1491
|
+
const createComponentId = (container, parentId, childIndex) => {
|
|
1492
|
+
if (!parentId || parentId === ROOT_COMPONENT_ID) return `c${childIndex}`;
|
|
1493
|
+
return `${parentId}.${childIndex}`;
|
|
1494
|
+
};
|
|
1495
|
+
const getOrCreateComponentState = (container, id, symbol, parentId) => {
|
|
1496
|
+
const existing = container.components.get(id);
|
|
1497
|
+
if (existing) {
|
|
1498
|
+
existing.parentId = parentId;
|
|
1499
|
+
existing.symbol = symbol;
|
|
1500
|
+
return existing;
|
|
1501
|
+
}
|
|
1502
|
+
const component = {
|
|
1503
|
+
active: false,
|
|
1504
|
+
didMount: false,
|
|
1505
|
+
id,
|
|
1506
|
+
mountCleanupSlots: [],
|
|
1507
|
+
parentId,
|
|
1508
|
+
props: {},
|
|
1509
|
+
projectionSlots: null,
|
|
1510
|
+
reuseExistingDomOnActivate: true,
|
|
1511
|
+
scopeId: registerScope(container, []),
|
|
1512
|
+
signalIds: [],
|
|
1513
|
+
symbol,
|
|
1514
|
+
suspensePromise: null,
|
|
1515
|
+
visibleCount: 0,
|
|
1516
|
+
watchCount: 0
|
|
1517
|
+
};
|
|
1518
|
+
container.components.set(id, component);
|
|
1519
|
+
return component;
|
|
1520
|
+
};
|
|
1521
|
+
const resetComponentForSymbolChange = (container, component, meta) => {
|
|
1522
|
+
disposeComponentMountCleanups(component);
|
|
1523
|
+
component.didMount = false;
|
|
1524
|
+
component.projectionSlots = meta.projectionSlots ?? null;
|
|
1525
|
+
component.scopeId = registerScope(container, meta.captures());
|
|
1526
|
+
component.signalIds = [];
|
|
1527
|
+
component.suspensePromise = null;
|
|
1528
|
+
pruneComponentVisibles(container, component, 0);
|
|
1529
|
+
pruneComponentWatches(container, component, 0);
|
|
1530
|
+
};
|
|
1531
|
+
const createWatchId = (componentId, watchIndex) => `${componentId}:w${watchIndex}`;
|
|
1532
|
+
const createVisibleId = (componentId, visibleIndex) => `${componentId}:v${visibleIndex}`;
|
|
1533
|
+
const getOrCreateWatchState = (container, id, componentId) => {
|
|
1534
|
+
const existing = container.watches.get(id);
|
|
1535
|
+
if (existing) {
|
|
1536
|
+
existing.componentId = componentId;
|
|
1537
|
+
return existing;
|
|
1538
|
+
}
|
|
1539
|
+
const effect = {
|
|
1540
|
+
fn() {},
|
|
1541
|
+
signals: /* @__PURE__ */ new Set()
|
|
1542
|
+
};
|
|
1543
|
+
const watch = {
|
|
1544
|
+
cleanupSlot: createCleanupSlot(),
|
|
1545
|
+
componentId,
|
|
1546
|
+
effect,
|
|
1547
|
+
id,
|
|
1548
|
+
mode: "dynamic",
|
|
1549
|
+
pending: null,
|
|
1550
|
+
run: null,
|
|
1551
|
+
scopeId: registerScope(container, []),
|
|
1552
|
+
symbol: "",
|
|
1553
|
+
track: null
|
|
1554
|
+
};
|
|
1555
|
+
effect.fn = () => {
|
|
1556
|
+
if (watch.run) {
|
|
1557
|
+
watch.run();
|
|
1558
|
+
return;
|
|
1559
|
+
}
|
|
1560
|
+
if (!container.doc) return;
|
|
1561
|
+
const queued = (watch.pending ?? Promise.resolve()).then(async () => {
|
|
1562
|
+
disposeCleanupSlot(watch.cleanupSlot);
|
|
1563
|
+
const module = await loadSymbol(container, watch.symbol);
|
|
1564
|
+
const scope = materializeScope(container, watch.scopeId);
|
|
1565
|
+
await withClientContainer(container, () => {
|
|
1566
|
+
if (watch.mode === "dynamic") {
|
|
1567
|
+
collectTrackedDependencies(effect, () => {
|
|
1568
|
+
withCleanupSlot(watch.cleanupSlot, () => {
|
|
1569
|
+
module.default(scope);
|
|
1570
|
+
});
|
|
1571
|
+
});
|
|
1572
|
+
return;
|
|
1573
|
+
}
|
|
1574
|
+
collectTrackedDependencies(effect, () => {
|
|
1575
|
+
module.default(scope, "track");
|
|
1576
|
+
});
|
|
1577
|
+
withCleanupSlot(watch.cleanupSlot, () => {
|
|
1578
|
+
module.default(scope, "run");
|
|
1579
|
+
});
|
|
1580
|
+
});
|
|
1581
|
+
await flushDirtyComponents(container);
|
|
1582
|
+
}).finally(() => {
|
|
1583
|
+
if (watch.pending === queued) watch.pending = null;
|
|
1584
|
+
});
|
|
1585
|
+
watch.pending = queued;
|
|
1586
|
+
};
|
|
1587
|
+
container.watches.set(id, watch);
|
|
1588
|
+
return watch;
|
|
1589
|
+
};
|
|
1590
|
+
const getOrCreateVisibleState = (container, id, componentId) => {
|
|
1591
|
+
const existing = container.visibles.get(id);
|
|
1592
|
+
if (existing) {
|
|
1593
|
+
existing.componentId = componentId;
|
|
1594
|
+
return existing;
|
|
1595
|
+
}
|
|
1596
|
+
const visible = {
|
|
1597
|
+
cleanupSlot: createCleanupSlot(),
|
|
1598
|
+
componentId,
|
|
1599
|
+
done: false,
|
|
1600
|
+
id,
|
|
1601
|
+
pending: null,
|
|
1602
|
+
run: null,
|
|
1603
|
+
scopeId: registerScope(container, []),
|
|
1604
|
+
symbol: ""
|
|
1605
|
+
};
|
|
1606
|
+
container.visibles.set(id, visible);
|
|
1607
|
+
return visible;
|
|
1608
|
+
};
|
|
1609
|
+
const clearComponentSubscriptions = (container, componentId) => {
|
|
1610
|
+
for (const record of container.signals.values()) record.subscribers.delete(componentId);
|
|
1611
|
+
};
|
|
1612
|
+
const disposeComponentMountCleanups = (component) => {
|
|
1613
|
+
const cleanupSlots = [...component.mountCleanupSlots].reverse();
|
|
1614
|
+
component.mountCleanupSlots = [];
|
|
1615
|
+
for (const cleanupSlot of cleanupSlots) disposeCleanupSlot(cleanupSlot);
|
|
1616
|
+
};
|
|
1617
|
+
const removeWatchState = (container, watchId) => {
|
|
1618
|
+
const watch = container.watches.get(watchId);
|
|
1619
|
+
if (!watch) return;
|
|
1620
|
+
disposeCleanupSlot(watch.cleanupSlot);
|
|
1621
|
+
clearEffectSignals(watch.effect);
|
|
1622
|
+
container.watches.delete(watchId);
|
|
1623
|
+
};
|
|
1624
|
+
const removeVisibleState = (container, visibleId) => {
|
|
1625
|
+
const visible = container.visibles.get(visibleId);
|
|
1626
|
+
if (visible) disposeCleanupSlot(visible.cleanupSlot);
|
|
1627
|
+
container.visibles.delete(visibleId);
|
|
1628
|
+
};
|
|
1629
|
+
const resetVisibleState = (visible) => {
|
|
1630
|
+
disposeCleanupSlot(visible.cleanupSlot);
|
|
1631
|
+
visible.cleanupSlot = createCleanupSlot();
|
|
1632
|
+
visible.done = false;
|
|
1633
|
+
visible.pending = null;
|
|
1634
|
+
};
|
|
1635
|
+
const resetComponentVisibleStates = (container, componentId) => {
|
|
1636
|
+
const targetIds = new Set([componentId, ...collectDescendantIds(container, componentId)]);
|
|
1637
|
+
for (const visible of container.visibles.values()) {
|
|
1638
|
+
if (!targetIds.has(visible.componentId)) continue;
|
|
1639
|
+
resetVisibleState(visible);
|
|
1640
|
+
}
|
|
1641
|
+
};
|
|
1642
|
+
const pruneComponentWatches = (container, component, nextCount) => {
|
|
1643
|
+
for (let index = nextCount; index < component.watchCount; index++) removeWatchState(container, createWatchId(component.id, index));
|
|
1644
|
+
component.watchCount = nextCount;
|
|
1645
|
+
};
|
|
1646
|
+
const pruneComponentVisibles = (container, component, nextCount) => {
|
|
1647
|
+
for (let index = nextCount; index < component.visibleCount; index++) removeVisibleState(container, createVisibleId(component.id, index));
|
|
1648
|
+
component.visibleCount = nextCount;
|
|
1649
|
+
};
|
|
1650
|
+
const isDescendantOf = (parentId, candidateId) => candidateId.startsWith(`${parentId}.`);
|
|
1651
|
+
const collectDescendantIds = (container, componentId) => [...container.components.keys()].filter((candidate) => isDescendantOf(componentId, candidate));
|
|
1652
|
+
const pruneRemovedComponents = (container, componentId, keep) => {
|
|
1653
|
+
for (const descendantId of collectDescendantIds(container, componentId)) {
|
|
1654
|
+
if (keep.has(descendantId)) continue;
|
|
1655
|
+
clearComponentSubscriptions(container, descendantId);
|
|
1656
|
+
const descendant = container.components.get(descendantId);
|
|
1657
|
+
if (descendant) {
|
|
1658
|
+
disposeComponentMountCleanups(descendant);
|
|
1659
|
+
pruneComponentVisibles(container, descendant, 0);
|
|
1660
|
+
pruneComponentWatches(container, descendant, 0);
|
|
1661
|
+
}
|
|
1662
|
+
container.components.delete(descendantId);
|
|
1663
|
+
}
|
|
1664
|
+
};
|
|
1665
|
+
const scheduleMicrotask = (fn) => {
|
|
1666
|
+
if (typeof queueMicrotask === "function") {
|
|
1667
|
+
queueMicrotask(fn);
|
|
1668
|
+
return;
|
|
1669
|
+
}
|
|
1670
|
+
Promise.resolve().then(fn);
|
|
1671
|
+
};
|
|
1672
|
+
const scheduleVisibleCallbacksCheck = (container) => {
|
|
1673
|
+
if (container.visibilityCheckQueued || container.visibles.size === 0 || !container.doc) return;
|
|
1674
|
+
ensureVisibilityListeners(container);
|
|
1675
|
+
container.visibilityCheckQueued = true;
|
|
1676
|
+
const run = () => {
|
|
1677
|
+
container.visibilityCheckQueued = false;
|
|
1678
|
+
flushVisibleCallbacks(container);
|
|
1679
|
+
};
|
|
1680
|
+
const view = container.doc.defaultView;
|
|
1681
|
+
if (view && typeof view.requestAnimationFrame === "function") {
|
|
1682
|
+
view.requestAnimationFrame(() => run());
|
|
1683
|
+
return;
|
|
1684
|
+
}
|
|
1685
|
+
scheduleMicrotask(run);
|
|
1686
|
+
};
|
|
1687
|
+
const rectIntersectsViewport = (view, rect) => {
|
|
1688
|
+
const viewportHeight = view.innerHeight ?? 0;
|
|
1689
|
+
const viewportWidth = view.innerWidth ?? 0;
|
|
1690
|
+
return rect.bottom > 0 && rect.right > 0 && rect.top < viewportHeight && rect.left < viewportWidth;
|
|
1691
|
+
};
|
|
1692
|
+
const isBoundaryVisible = (doc, start, end) => {
|
|
1693
|
+
const view = doc.defaultView;
|
|
1694
|
+
if (typeof doc.createRange !== "function" || !view) return false;
|
|
1695
|
+
const range = doc.createRange();
|
|
1696
|
+
range.setStartAfter(start);
|
|
1697
|
+
range.setEndBefore(end);
|
|
1698
|
+
const rects = range.getClientRects();
|
|
1699
|
+
if (rects.length === 0) return rectIntersectsViewport(view, range.getBoundingClientRect());
|
|
1700
|
+
for (let index = 0; index < rects.length; index++) {
|
|
1701
|
+
const rect = rects[index];
|
|
1702
|
+
if (rectIntersectsViewport(view, rect)) return true;
|
|
1703
|
+
}
|
|
1704
|
+
return false;
|
|
1705
|
+
};
|
|
1706
|
+
const runVisibleCallback = async (container, visible) => {
|
|
1707
|
+
if (visible.done) return;
|
|
1708
|
+
visible.done = true;
|
|
1709
|
+
const pending = Promise.resolve().then(async () => {
|
|
1710
|
+
if (visible.run) await withClientContainer(container, async () => {
|
|
1711
|
+
await withCleanupSlot(visible.cleanupSlot, () => visible.run?.());
|
|
1712
|
+
});
|
|
1713
|
+
else {
|
|
1714
|
+
const module = await loadSymbol(container, visible.symbol);
|
|
1715
|
+
await withClientContainer(container, async () => {
|
|
1716
|
+
await withCleanupSlot(visible.cleanupSlot, () => module.default(materializeScope(container, visible.scopeId)));
|
|
1717
|
+
});
|
|
1718
|
+
}
|
|
1719
|
+
await flushDirtyComponents(container);
|
|
1720
|
+
scheduleVisibleCallbacksCheck(container);
|
|
1721
|
+
}).finally(() => {
|
|
1722
|
+
if (visible.pending === pending) visible.pending = null;
|
|
1723
|
+
});
|
|
1724
|
+
visible.pending = pending;
|
|
1725
|
+
await pending;
|
|
1726
|
+
};
|
|
1727
|
+
const flushVisibleCallbacks = async (container) => {
|
|
1728
|
+
if (!container.doc || container.visibles.size === 0) return;
|
|
1729
|
+
for (const visible of [...container.visibles.values()]) {
|
|
1730
|
+
if (visible.done || visible.pending) continue;
|
|
1731
|
+
const component = container.components.get(visible.componentId);
|
|
1732
|
+
if (!component?.start || !component.end) continue;
|
|
1733
|
+
if (!isBoundaryVisible(container.doc, component.start, component.end)) continue;
|
|
1734
|
+
await runVisibleCallback(container, visible);
|
|
1735
|
+
}
|
|
1736
|
+
};
|
|
1737
|
+
const ensureVisibilityListeners = (container) => {
|
|
1738
|
+
if (container.visibilityListenersCleanup || !container.doc) return;
|
|
1739
|
+
const doc = container.doc;
|
|
1740
|
+
const onVisibilityChange = () => {
|
|
1741
|
+
scheduleVisibleCallbacksCheck(container);
|
|
1742
|
+
};
|
|
1743
|
+
doc.addEventListener("scroll", onVisibilityChange, true);
|
|
1744
|
+
doc.defaultView?.addEventListener("resize", onVisibilityChange);
|
|
1745
|
+
container.visibilityListenersCleanup = () => {
|
|
1746
|
+
doc.removeEventListener("scroll", onVisibilityChange, true);
|
|
1747
|
+
doc.defaultView?.removeEventListener("resize", onVisibilityChange);
|
|
1748
|
+
container.visibilityListenersCleanup = null;
|
|
1749
|
+
};
|
|
1750
|
+
};
|
|
1751
|
+
const scheduleMountCallbacks = (container, component, callbacks) => {
|
|
1752
|
+
if (component.didMount || callbacks.length === 0) return;
|
|
1753
|
+
component.didMount = true;
|
|
1754
|
+
scheduleMicrotask(() => {
|
|
1755
|
+
withClientContainer(container, () => {
|
|
1756
|
+
for (const callback of callbacks) {
|
|
1757
|
+
const cleanupSlot = createCleanupSlot();
|
|
1758
|
+
component.mountCleanupSlots.push(cleanupSlot);
|
|
1759
|
+
withCleanupSlot(cleanupSlot, callback);
|
|
1760
|
+
}
|
|
1761
|
+
}).then(() => flushDirtyComponents(container)).then(() => {
|
|
1762
|
+
scheduleVisibleCallbacksCheck(container);
|
|
1763
|
+
});
|
|
1764
|
+
});
|
|
1765
|
+
};
|
|
1766
|
+
const collectProjectionSlotRanges = (roots) => {
|
|
1767
|
+
const starts = /* @__PURE__ */ new Map();
|
|
1768
|
+
const ranges = /* @__PURE__ */ new Map();
|
|
1769
|
+
const visit = (node) => {
|
|
1770
|
+
if (typeof Comment !== "undefined" ? node instanceof Comment : node.nodeType === 8) {
|
|
1771
|
+
const commentNode = node;
|
|
1772
|
+
const marker = parseProjectionSlotMarker(commentNode.data);
|
|
1773
|
+
if (marker) if (marker.kind === "start") starts.set(marker.key, commentNode);
|
|
1774
|
+
else {
|
|
1775
|
+
const startNode = starts.get(marker.key);
|
|
1776
|
+
if (startNode) ranges.set(marker.key, {
|
|
1777
|
+
end: commentNode,
|
|
1778
|
+
start: startNode
|
|
1779
|
+
});
|
|
1780
|
+
}
|
|
1781
|
+
}
|
|
1782
|
+
for (const child of Array.from(node.childNodes ?? [])) visit(child);
|
|
1783
|
+
};
|
|
1784
|
+
for (const root of roots) visit(root);
|
|
1785
|
+
return ranges;
|
|
1786
|
+
};
|
|
1787
|
+
const preserveProjectionSlotContents = (start, end, nodes) => {
|
|
1788
|
+
const currentRanges = collectProjectionSlotRanges(getBoundaryChildren(start, end));
|
|
1789
|
+
const nextRanges = collectProjectionSlotRanges(nodes);
|
|
1790
|
+
for (const [key, nextRange] of nextRanges) {
|
|
1791
|
+
const currentRange = currentRanges.get(key);
|
|
1792
|
+
if (!currentRange) continue;
|
|
1793
|
+
let cursor = currentRange.start.nextSibling;
|
|
1794
|
+
while (cursor && cursor !== currentRange.end) {
|
|
1795
|
+
const nextSibling = cursor.nextSibling;
|
|
1796
|
+
nextRange.end.parentNode?.insertBefore(cursor, nextRange.end);
|
|
1797
|
+
cursor = nextSibling;
|
|
1798
|
+
}
|
|
1799
|
+
}
|
|
1800
|
+
};
|
|
1801
|
+
const replaceBoundaryContents = (start, end, nodes, options) => {
|
|
1802
|
+
if (options?.preserveProjectionSlots ?? true) preserveProjectionSlotContents(start, end, nodes);
|
|
1803
|
+
let cursor = start.nextSibling;
|
|
1804
|
+
while (cursor && cursor !== end) {
|
|
1805
|
+
const next = cursor.nextSibling;
|
|
1806
|
+
cursor.remove();
|
|
1807
|
+
cursor = next;
|
|
1808
|
+
}
|
|
1809
|
+
for (const node of nodes) end.parentNode?.insertBefore(node, end);
|
|
1810
|
+
};
|
|
1811
|
+
const getBoundaryChildren = (start, end) => {
|
|
1812
|
+
const nodes = [];
|
|
1813
|
+
let cursor = start.nextSibling;
|
|
1814
|
+
while (cursor && cursor !== end) {
|
|
1815
|
+
nodes.push(cursor);
|
|
1816
|
+
cursor = cursor.nextSibling;
|
|
1817
|
+
}
|
|
1818
|
+
return nodes;
|
|
1819
|
+
};
|
|
1820
|
+
const getNodePath = (root, target) => {
|
|
1821
|
+
if (root === target) return [];
|
|
1822
|
+
const path = [];
|
|
1823
|
+
let cursor = target;
|
|
1824
|
+
while (cursor && cursor !== root) {
|
|
1825
|
+
const parent = cursor.parentNode;
|
|
1826
|
+
if (!parent) return null;
|
|
1827
|
+
const index = Array.prototype.indexOf.call(parent.childNodes, cursor);
|
|
1828
|
+
if (index < 0) return null;
|
|
1829
|
+
path.unshift(index);
|
|
1830
|
+
cursor = parent;
|
|
1831
|
+
}
|
|
1832
|
+
return cursor === root ? path : null;
|
|
1833
|
+
};
|
|
1834
|
+
const getNodeByPath = (root, path) => {
|
|
1835
|
+
let cursor = root;
|
|
1836
|
+
for (const index of path) {
|
|
1837
|
+
cursor = cursor?.childNodes.item(index) ?? null;
|
|
1838
|
+
if (!cursor) return null;
|
|
1839
|
+
}
|
|
1840
|
+
return cursor;
|
|
1841
|
+
};
|
|
1842
|
+
const getElementPath = (root, target) => {
|
|
1843
|
+
if (root === target) return [];
|
|
1844
|
+
const path = [];
|
|
1845
|
+
let cursor = target;
|
|
1846
|
+
while (cursor && cursor !== root) {
|
|
1847
|
+
const parent = cursor.parentElement;
|
|
1848
|
+
if (!parent) return null;
|
|
1849
|
+
const index = Array.prototype.indexOf.call(parent.children, cursor);
|
|
1850
|
+
if (index < 0) return null;
|
|
1851
|
+
path.unshift(index);
|
|
1852
|
+
cursor = parent;
|
|
1853
|
+
}
|
|
1854
|
+
return cursor === root ? path : null;
|
|
1855
|
+
};
|
|
1856
|
+
const getElementByPath = (root, path) => {
|
|
1857
|
+
let cursor = root;
|
|
1858
|
+
for (const index of path) {
|
|
1859
|
+
cursor = cursor?.children.item(index) ?? null;
|
|
1860
|
+
if (!cursor) return null;
|
|
1861
|
+
}
|
|
1862
|
+
return cursor;
|
|
1863
|
+
};
|
|
1864
|
+
const captureBoundaryFocus = (doc, start, end) => {
|
|
1865
|
+
const activeElement = doc.activeElement;
|
|
1866
|
+
if (!isHTMLElementNode(activeElement)) return null;
|
|
1867
|
+
const topLevelNodes = getBoundaryChildren(start, end);
|
|
1868
|
+
for (let i = 0; i < topLevelNodes.length; i++) {
|
|
1869
|
+
const candidate = topLevelNodes[i];
|
|
1870
|
+
if (candidate !== activeElement && (!isElementNode(candidate) || !candidate.contains(activeElement))) continue;
|
|
1871
|
+
const innerPath = getNodePath(candidate, activeElement);
|
|
1872
|
+
if (!innerPath) continue;
|
|
1873
|
+
return {
|
|
1874
|
+
path: [i, ...innerPath],
|
|
1875
|
+
selectionDirection: isTextEntryElement(activeElement) ? activeElement.selectionDirection : null,
|
|
1876
|
+
selectionEnd: isTextEntryElement(activeElement) ? activeElement.selectionEnd : null,
|
|
1877
|
+
selectionStart: isTextEntryElement(activeElement) ? activeElement.selectionStart : null
|
|
1878
|
+
};
|
|
1879
|
+
}
|
|
1880
|
+
return null;
|
|
1881
|
+
};
|
|
1882
|
+
const restoreBoundaryFocus = (doc, start, end, snapshot) => {
|
|
1883
|
+
if (!snapshot) return;
|
|
1884
|
+
const [topLevelIndex, ...innerPath] = snapshot.path;
|
|
1885
|
+
const root = getBoundaryChildren(start, end)[topLevelIndex];
|
|
1886
|
+
if (!root) return;
|
|
1887
|
+
const nextActive = innerPath.length > 0 ? getNodeByPath(root, innerPath) : root;
|
|
1888
|
+
if (!isHTMLElementNode(nextActive)) return;
|
|
1889
|
+
restoreFocusTarget(doc, nextActive, snapshot);
|
|
1890
|
+
};
|
|
1891
|
+
const restoreFocusTarget = (doc, nextActive, snapshot) => {
|
|
1892
|
+
const restore = () => {
|
|
1893
|
+
if (!nextActive.isConnected) return false;
|
|
1894
|
+
nextActive.focus({ preventScroll: true });
|
|
1895
|
+
if (isTextEntryElement(nextActive) && snapshot.selectionStart !== null && snapshot.selectionStart !== void 0) nextActive.setSelectionRange(snapshot.selectionStart, snapshot.selectionEnd ?? snapshot.selectionStart, snapshot.selectionDirection ?? void 0);
|
|
1896
|
+
return doc.activeElement === nextActive;
|
|
1897
|
+
};
|
|
1898
|
+
if (restore()) return;
|
|
1899
|
+
const win = doc.defaultView;
|
|
1900
|
+
if (!win) return;
|
|
1901
|
+
let remainingAttempts = 3;
|
|
1902
|
+
const retry = () => {
|
|
1903
|
+
if (remainingAttempts <= 0) return;
|
|
1904
|
+
remainingAttempts--;
|
|
1905
|
+
const run = () => {
|
|
1906
|
+
if (restore()) return;
|
|
1907
|
+
retry();
|
|
1908
|
+
};
|
|
1909
|
+
if (typeof win.requestAnimationFrame === "function") {
|
|
1910
|
+
win.requestAnimationFrame(() => run());
|
|
1911
|
+
return;
|
|
1912
|
+
}
|
|
1913
|
+
win.setTimeout(run, 16);
|
|
1914
|
+
};
|
|
1915
|
+
retry();
|
|
1916
|
+
};
|
|
1917
|
+
const captureDocumentFocus = (doc, focusSource) => {
|
|
1918
|
+
const candidate = isHTMLElementNode(focusSource) ? focusSource : isHTMLElementNode(doc.activeElement) ? doc.activeElement : null;
|
|
1919
|
+
if (!candidate) return null;
|
|
1920
|
+
const path = getElementPath(doc.body, candidate);
|
|
1921
|
+
if (!path) return null;
|
|
1922
|
+
return {
|
|
1923
|
+
path,
|
|
1924
|
+
selectionDirection: isTextEntryElement(candidate) ? candidate.selectionDirection : null,
|
|
1925
|
+
selectionEnd: isTextEntryElement(candidate) ? candidate.selectionEnd : null,
|
|
1926
|
+
selectionStart: isTextEntryElement(candidate) ? candidate.selectionStart : null
|
|
1927
|
+
};
|
|
1928
|
+
};
|
|
1929
|
+
const capturePendingFocusRestore = (container, focusSource) => {
|
|
1930
|
+
if (!container.doc) return null;
|
|
1931
|
+
const snapshot = captureDocumentFocus(container.doc, focusSource);
|
|
1932
|
+
if (!snapshot) return null;
|
|
1933
|
+
return { snapshot };
|
|
1934
|
+
};
|
|
1935
|
+
const restorePendingFocus = (container, pending) => {
|
|
1936
|
+
if (!pending || !container.doc) return;
|
|
1937
|
+
const nextActive = getElementByPath(container.doc.body, pending.snapshot.path);
|
|
1938
|
+
if (!isHTMLElementNode(nextActive)) return;
|
|
1939
|
+
restoreFocusTarget(container.doc, nextActive, pending.snapshot);
|
|
1940
|
+
};
|
|
1941
|
+
const EVENT_PROP_REGEX = /^on([A-Z].+)\$$/;
|
|
1942
|
+
const DANGEROUSLY_SET_INNER_HTML_PROP = "dangerouslySetInnerHTML";
|
|
1943
|
+
const resolveDangerouslySetInnerHTML = (value) => value === false || value === void 0 || value === null ? null : String(value);
|
|
1944
|
+
const toEventName = (propName) => {
|
|
1945
|
+
const matched = propName.match(EVENT_PROP_REGEX);
|
|
1946
|
+
if (!matched) return null;
|
|
1947
|
+
const [first, ...rest] = matched[1];
|
|
1948
|
+
return `${first.toLowerCase()}${rest.join("")}`;
|
|
1949
|
+
};
|
|
1950
|
+
const TEXT_ESCAPE_REGEX = /[&<>]/;
|
|
1951
|
+
const ATTR_ESCAPE_REGEX = /[&<>'"]/;
|
|
1952
|
+
const escapeString = (value, mode) => {
|
|
1953
|
+
const escapePattern = mode === "attr" ? ATTR_ESCAPE_REGEX : TEXT_ESCAPE_REGEX;
|
|
1954
|
+
const firstMatch = value.search(escapePattern);
|
|
1955
|
+
if (firstMatch < 0) return value;
|
|
1956
|
+
let output = "";
|
|
1957
|
+
let lastIndex = 0;
|
|
1958
|
+
for (let index = firstMatch; index < value.length; index += 1) {
|
|
1959
|
+
let escaped = null;
|
|
1960
|
+
switch (value.charCodeAt(index)) {
|
|
1961
|
+
case 34:
|
|
1962
|
+
escaped = mode === "attr" ? """ : null;
|
|
1963
|
+
break;
|
|
1964
|
+
case 38:
|
|
1965
|
+
escaped = "&";
|
|
1966
|
+
break;
|
|
1967
|
+
case 39:
|
|
1968
|
+
escaped = mode === "attr" ? "'" : null;
|
|
1969
|
+
break;
|
|
1970
|
+
case 60:
|
|
1971
|
+
escaped = "<";
|
|
1972
|
+
break;
|
|
1973
|
+
case 62:
|
|
1974
|
+
escaped = ">";
|
|
1975
|
+
break;
|
|
1976
|
+
}
|
|
1977
|
+
if (!escaped) continue;
|
|
1978
|
+
output += value.slice(lastIndex, index);
|
|
1979
|
+
output += escaped;
|
|
1980
|
+
lastIndex = index + 1;
|
|
1981
|
+
}
|
|
1982
|
+
return output + value.slice(lastIndex);
|
|
1983
|
+
};
|
|
1984
|
+
const escapeText = (value) => escapeString(value, "text");
|
|
1985
|
+
const escapeAttr = (value) => escapeString(value, "attr");
|
|
1986
|
+
const renderSSRAttr = (name, value) => {
|
|
1987
|
+
if (value === false || value === void 0 || value === null) return "";
|
|
1988
|
+
if (value === true) return ` ${name}`;
|
|
1989
|
+
return ` ${name}="${escapeAttr(String(value))}"`;
|
|
1990
|
+
};
|
|
1991
|
+
const renderStringArray = (values) => {
|
|
1992
|
+
let output = "";
|
|
1993
|
+
for (let index = 0; index < values.length; index += 1) output += renderStringNode(values[index]);
|
|
1994
|
+
return output;
|
|
1995
|
+
};
|
|
1996
|
+
const renderSSRTemplateValue = (value) => {
|
|
1997
|
+
if (value === false || value === null || value === void 0) return "";
|
|
1998
|
+
if (Array.isArray(value)) return renderStringArray(value);
|
|
1999
|
+
if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") return escapeText(String(value));
|
|
2000
|
+
if (isSSRTemplate(value)) return renderSSRTemplateNode(value);
|
|
2001
|
+
if (isProjectionSlot(value)) return renderProjectionSlotToString(value);
|
|
2002
|
+
if (isRouteSlot(value)) {
|
|
2003
|
+
const routeElement = resolveRouteSlot(getCurrentContainer(), value);
|
|
2004
|
+
return routeElement ? renderStringNode(routeElement) : "";
|
|
2005
|
+
}
|
|
2006
|
+
return renderStringNode(value);
|
|
2007
|
+
};
|
|
2008
|
+
const renderSSRTemplateNode = (template) => {
|
|
2009
|
+
let output = template.strings[0] ?? "";
|
|
2010
|
+
for (let index = 0; index < template.values.length; index += 1) {
|
|
2011
|
+
const value = template.values[index];
|
|
2012
|
+
output += isSSRAttrValue(value) ? renderSSRAttr(value.name, value.value) : renderSSRTemplateValue(value);
|
|
2013
|
+
output += template.strings[index + 1] ?? "";
|
|
2014
|
+
}
|
|
2015
|
+
return output;
|
|
2016
|
+
};
|
|
2017
|
+
const resolveRenderable = (value) => {
|
|
2018
|
+
let current = value;
|
|
2019
|
+
while (typeof current === "function" && !getLazyMeta(current) && !getComponentMeta(current)) current = current();
|
|
2020
|
+
return current;
|
|
2021
|
+
};
|
|
2022
|
+
const nextComponentPosition = (container) => {
|
|
2023
|
+
const frame = getCurrentFrame();
|
|
2024
|
+
if (!frame) return {
|
|
2025
|
+
childIndex: container.rootChildCursor++,
|
|
2026
|
+
parentId: ROOT_COMPONENT_ID
|
|
2027
|
+
};
|
|
2028
|
+
return {
|
|
2029
|
+
childIndex: frame.childCursor++,
|
|
2030
|
+
parentId: frame.component.id
|
|
2031
|
+
};
|
|
2032
|
+
};
|
|
2033
|
+
const registerEventBinding = (container, descriptor) => {
|
|
2034
|
+
const scopeId = registerScope(container, descriptor.captures());
|
|
2035
|
+
return `${descriptor.symbol}:${scopeId}`;
|
|
2036
|
+
};
|
|
2037
|
+
const bindRuntimeEvent = (element, eventName, value) => {
|
|
2038
|
+
const descriptor = getEventMeta(value);
|
|
2039
|
+
if (!descriptor) return false;
|
|
2040
|
+
const container = getCurrentContainer();
|
|
2041
|
+
if (!container) return false;
|
|
2042
|
+
element.setAttribute("data-eid", `e${container.nextElementId++}`);
|
|
2043
|
+
element.setAttribute(`data-e-on${eventName}`, registerEventBinding(container, descriptor));
|
|
2044
|
+
return true;
|
|
2045
|
+
};
|
|
2046
|
+
const renderProjectionSlotToString = (slot) => {
|
|
2047
|
+
const frame = getCurrentFrame();
|
|
2048
|
+
const start = createProjectionSlotMarker(slot.componentId, slot.name, slot.occurrence, "start");
|
|
2049
|
+
const end = createProjectionSlotMarker(slot.componentId, slot.name, slot.occurrence, "end");
|
|
2050
|
+
if (frame?.component.id === slot.componentId && frame.projectionState.reuseExistingDom) return `<!--${start}--><!--${end}-->`;
|
|
2051
|
+
return `<!--${start}-->${renderStringNode(slot.source)}<!--${end}-->`;
|
|
2052
|
+
};
|
|
2053
|
+
const renderProjectionSlotToNodes = (slot, container) => {
|
|
2054
|
+
if (!container.doc) throw new Error("Client rendering requires a document.");
|
|
2055
|
+
const frame = getCurrentFrame();
|
|
2056
|
+
const start = container.doc.createComment(createProjectionSlotMarker(slot.componentId, slot.name, slot.occurrence, "start"));
|
|
2057
|
+
const end = container.doc.createComment(createProjectionSlotMarker(slot.componentId, slot.name, slot.occurrence, "end"));
|
|
2058
|
+
if (frame?.component.id === slot.componentId && frame.projectionState.reuseExistingDom) return [start, end];
|
|
2059
|
+
return [
|
|
2060
|
+
start,
|
|
2061
|
+
...renderClientInsertable(slot.source, container),
|
|
2062
|
+
end
|
|
2063
|
+
];
|
|
2064
|
+
};
|
|
2065
|
+
const renderStringNode = (inputElementLike) => {
|
|
2066
|
+
if (Array.isArray(inputElementLike)) return renderStringArray(inputElementLike);
|
|
2067
|
+
const resolved = resolveRenderable(inputElementLike);
|
|
2068
|
+
if (resolved === false || resolved === null || resolved === void 0) return "";
|
|
2069
|
+
if (Array.isArray(resolved)) return renderStringNode(resolved);
|
|
2070
|
+
if (typeof resolved === "string" || typeof resolved === "number" || typeof resolved === "boolean") return escapeText(String(resolved));
|
|
2071
|
+
if (isSSRTemplate(resolved)) return renderSSRTemplateNode(resolved);
|
|
2072
|
+
if (isProjectionSlot(resolved)) return renderProjectionSlotToString(resolved);
|
|
2073
|
+
if (isRouteSlot(resolved)) {
|
|
2074
|
+
const routeElement = resolveRouteSlot(getCurrentContainer(), resolved);
|
|
2075
|
+
return routeElement ? renderStringNode(routeElement) : "";
|
|
2076
|
+
}
|
|
2077
|
+
if (!isRenderObject(resolved)) return "";
|
|
2078
|
+
if (isSuspenseType(resolved.type)) return renderSuspenseComponentToString(resolved.props);
|
|
2079
|
+
if (typeof resolved.type === "function") {
|
|
2080
|
+
const container = getCurrentContainer();
|
|
2081
|
+
const componentFn = resolved.type;
|
|
2082
|
+
const meta = getComponentMeta(componentFn);
|
|
2083
|
+
if (!meta || !container) return renderStringNode(componentFn(resolved.props));
|
|
2084
|
+
const evaluatedProps = evaluateProps(resolved.props);
|
|
2085
|
+
const position = nextComponentPosition(container);
|
|
2086
|
+
const componentId = createComponentId(container, position.parentId, position.childIndex);
|
|
2087
|
+
const component = getOrCreateComponentState(container, componentId, meta.symbol, position.parentId);
|
|
2088
|
+
component.scopeId = registerScope(container, meta.captures());
|
|
2089
|
+
component.props = evaluatedProps;
|
|
2090
|
+
component.projectionSlots = meta.projectionSlots ?? null;
|
|
2091
|
+
const frame = createFrame(container, component, "ssr");
|
|
2092
|
+
clearComponentSubscriptions(container, component.id);
|
|
2093
|
+
const renderProps = createRenderProps(componentId, meta, evaluatedProps);
|
|
2094
|
+
const body = pushFrame(frame, () => renderStringNode(componentFn(renderProps)));
|
|
2095
|
+
pruneComponentVisibles(container, component, frame.visibleCursor);
|
|
2096
|
+
pruneComponentWatches(container, component, frame.watchCursor);
|
|
2097
|
+
return `<!--ec:c:${componentId}:start-->${body}<!--ec:c:${componentId}:end-->`;
|
|
2098
|
+
}
|
|
2099
|
+
const attrParts = [];
|
|
2100
|
+
const container = getCurrentContainer();
|
|
2101
|
+
let hasInnerHTML = false;
|
|
2102
|
+
let innerHTML = null;
|
|
2103
|
+
for (const name in resolved.props) {
|
|
2104
|
+
if (!Object.hasOwn(resolved.props, name)) continue;
|
|
2105
|
+
if (name === "children") continue;
|
|
2106
|
+
const eventName = toEventName(name);
|
|
2107
|
+
const value = resolved.props[name];
|
|
2108
|
+
if (eventName) {
|
|
2109
|
+
const eventMeta = getEventMeta(value);
|
|
2110
|
+
if (!eventMeta || !container) continue;
|
|
2111
|
+
attrParts.push(`data-eid="e${container.nextElementId++}"`);
|
|
2112
|
+
attrParts.push(`data-e-on${eventName}="${escapeAttr(registerEventBinding(container, eventMeta))}"`);
|
|
2113
|
+
continue;
|
|
2114
|
+
}
|
|
2115
|
+
if (name === "ref") {
|
|
2116
|
+
const signalId = getRefSignalId(value);
|
|
2117
|
+
if (signalId) attrParts.push(`${REF_SIGNAL_ATTR}="${escapeAttr(signalId)}"`);
|
|
2118
|
+
continue;
|
|
2119
|
+
}
|
|
2120
|
+
if (name === DANGEROUSLY_SET_INNER_HTML_PROP) {
|
|
2121
|
+
hasInnerHTML = true;
|
|
2122
|
+
innerHTML = resolveDangerouslySetInnerHTML(value);
|
|
2123
|
+
continue;
|
|
2124
|
+
}
|
|
2125
|
+
if (value === false || value === void 0 || value === null) continue;
|
|
2126
|
+
if (resolved.type === "body" && name === "data-e-resume") continue;
|
|
2127
|
+
if (value === true) {
|
|
2128
|
+
attrParts.push(name);
|
|
2129
|
+
continue;
|
|
2130
|
+
}
|
|
2131
|
+
attrParts.push(`${name}="${escapeAttr(String(value))}"`);
|
|
2132
|
+
}
|
|
2133
|
+
if (resolved.type === "body" && container) attrParts.push("data-e-resume=\"paused\"");
|
|
2134
|
+
let childrenText = innerHTML ?? "";
|
|
2135
|
+
if (!hasInnerHTML) {
|
|
2136
|
+
const children = resolved.props.children;
|
|
2137
|
+
if (Array.isArray(children)) for (const child of children) childrenText += renderStringNode(child);
|
|
2138
|
+
else childrenText += renderStringNode(children);
|
|
2139
|
+
}
|
|
2140
|
+
if (resolved.type === "__ECLIPSA_FRAGMENT") return childrenText;
|
|
2141
|
+
return `<${resolved.type}${attrParts.length > 0 ? ` ${attrParts.join(" ")}` : ""}>${childrenText}</${resolved.type}>`;
|
|
2142
|
+
};
|
|
2143
|
+
const createElementNode = (doc, tagName) => doc.createElement(tagName);
|
|
2144
|
+
const renderComponentToNodes = (componentFn, props, container, mode) => {
|
|
2145
|
+
if (!container.doc) throw new Error("Client rendering requires a document.");
|
|
2146
|
+
const meta = getComponentMeta(componentFn);
|
|
2147
|
+
if (!meta) return renderClientNodes(componentFn(props), container);
|
|
2148
|
+
const position = nextComponentPosition(container);
|
|
2149
|
+
const componentId = createComponentId(container, position.parentId, position.childIndex);
|
|
2150
|
+
const existing = container.components.get(componentId);
|
|
2151
|
+
const symbolChanged = !!existing && existing.symbol !== meta.symbol;
|
|
2152
|
+
const component = getOrCreateComponentState(container, componentId, meta.symbol, position.parentId);
|
|
2153
|
+
component.active = mode === "client";
|
|
2154
|
+
if (!existing || symbolChanged) resetComponentForSymbolChange(container, component, meta);
|
|
2155
|
+
component.props = props;
|
|
2156
|
+
component.projectionSlots = meta.projectionSlots ?? null;
|
|
2157
|
+
const frame = createFrame(container, component, mode);
|
|
2158
|
+
clearComponentSubscriptions(container, componentId);
|
|
2159
|
+
const oldDescendants = collectDescendantIds(container, componentId);
|
|
2160
|
+
const start = container.doc.createComment(`ec:c:${componentId}:start`);
|
|
2161
|
+
const end = container.doc.createComment(`ec:c:${componentId}:end`);
|
|
2162
|
+
component.start = start;
|
|
2163
|
+
component.end = end;
|
|
2164
|
+
const renderProps = createRenderProps(componentId, meta, props);
|
|
2165
|
+
const rendered = pushFrame(frame, () => toMountedNodes(componentFn(renderProps), container));
|
|
2166
|
+
pruneComponentVisibles(container, component, frame.visibleCursor);
|
|
2167
|
+
pruneComponentWatches(container, component, frame.watchCursor);
|
|
2168
|
+
pruneRemovedComponents(container, componentId, frame.visitedDescendants);
|
|
2169
|
+
for (const descendantId of oldDescendants) {
|
|
2170
|
+
if (frame.visitedDescendants.has(descendantId)) continue;
|
|
2171
|
+
clearComponentSubscriptions(container, descendantId);
|
|
2172
|
+
}
|
|
2173
|
+
const currentFrame = getCurrentFrame();
|
|
2174
|
+
if (currentFrame) {
|
|
2175
|
+
currentFrame.visitedDescendants.add(componentId);
|
|
2176
|
+
for (const descendantId of frame.visitedDescendants) currentFrame.visitedDescendants.add(descendantId);
|
|
2177
|
+
}
|
|
2178
|
+
scheduleMountCallbacks(container, component, frame.mountCallbacks);
|
|
2179
|
+
scheduleVisibleCallbacksCheck(container);
|
|
2180
|
+
return [
|
|
2181
|
+
start,
|
|
2182
|
+
...rendered,
|
|
2183
|
+
end
|
|
2184
|
+
];
|
|
2185
|
+
};
|
|
2186
|
+
const applyElementProp = (element, name, value, container) => {
|
|
2187
|
+
const eventName = toEventName(name);
|
|
2188
|
+
if (eventName) {
|
|
2189
|
+
const eventMeta = getEventMeta(value);
|
|
2190
|
+
if (!eventMeta) return;
|
|
2191
|
+
element.setAttribute("data-eid", `e${container.nextElementId++}`);
|
|
2192
|
+
element.setAttribute(`data-e-on${eventName}`, registerEventBinding(container, eventMeta));
|
|
2193
|
+
return;
|
|
2194
|
+
}
|
|
2195
|
+
if (name === "ref") {
|
|
2196
|
+
assignRuntimeRef(value, element, container);
|
|
2197
|
+
return;
|
|
2198
|
+
}
|
|
2199
|
+
if (name === DANGEROUSLY_SET_INNER_HTML_PROP) {
|
|
2200
|
+
const html = resolveDangerouslySetInnerHTML(value);
|
|
2201
|
+
if (html !== null) element.innerHTML = html;
|
|
2202
|
+
return;
|
|
2203
|
+
}
|
|
2204
|
+
if (value === false || value === void 0 || value === null) return;
|
|
2205
|
+
if (name === "class") {
|
|
2206
|
+
element.className = String(value);
|
|
2207
|
+
return;
|
|
2208
|
+
}
|
|
2209
|
+
if (name === "style" && isPlainObject(value)) {
|
|
2210
|
+
element.setAttribute("style", Object.entries(value).map(([styleName, styleValue]) => `${styleName}: ${styleValue}`).join("; "));
|
|
2211
|
+
return;
|
|
2212
|
+
}
|
|
2213
|
+
if (name === "value" && "value" in element) element.value = String(value);
|
|
2214
|
+
if (name === "checked" && isHTMLInputElementNode(element)) element.checked = Boolean(value);
|
|
2215
|
+
if (value === true) {
|
|
2216
|
+
element.setAttribute(name, "");
|
|
2217
|
+
return;
|
|
2218
|
+
}
|
|
2219
|
+
element.setAttribute(name, String(value));
|
|
2220
|
+
};
|
|
2221
|
+
const renderClientNodes = (inputElementLike, container) => {
|
|
2222
|
+
if (!container.doc) throw new Error("Client rendering requires a document.");
|
|
2223
|
+
if (Array.isArray(inputElementLike)) return inputElementLike.flatMap((entry) => renderClientNodes(entry, container));
|
|
2224
|
+
const resolved = resolveRenderable(inputElementLike);
|
|
2225
|
+
if (resolved === false || resolved === null || resolved === void 0) return [];
|
|
2226
|
+
if (Array.isArray(resolved)) return renderClientNodes(resolved, container);
|
|
2227
|
+
if (typeof resolved === "string" || typeof resolved === "number" || typeof resolved === "boolean") return [container.doc.createTextNode(String(resolved))];
|
|
2228
|
+
if (typeof Node !== "undefined" && resolved instanceof Node) return [resolved];
|
|
2229
|
+
if (isProjectionSlot(resolved)) return renderProjectionSlotToNodes(resolved, container);
|
|
2230
|
+
if (isRouteSlot(resolved)) {
|
|
2231
|
+
const routeElement = resolveRouteSlot(container, resolved);
|
|
2232
|
+
return routeElement ? renderClientNodes(routeElement, container) : [];
|
|
2233
|
+
}
|
|
2234
|
+
if (!isRenderObject(resolved)) return [];
|
|
2235
|
+
if (isSuspenseType(resolved.type)) return renderSuspenseComponentToNodes(resolved.props, container, "client");
|
|
2236
|
+
if (typeof resolved.type === "function") {
|
|
2237
|
+
const evaluatedProps = evaluateProps(resolved.props);
|
|
2238
|
+
const componentFn = resolved.type;
|
|
2239
|
+
if (getComponentMeta(resolved.type)) return withoutTrackedEffect(() => renderComponentToNodes(componentFn, evaluatedProps, container, "client"));
|
|
2240
|
+
return renderComponentToNodes(componentFn, evaluatedProps, container, "client");
|
|
2241
|
+
}
|
|
2242
|
+
if (resolved.type === "__ECLIPSA_FRAGMENT") {
|
|
2243
|
+
const children = resolved.props.children;
|
|
2244
|
+
return Array.isArray(children) ? children.flatMap((child) => renderClientNodes(child, container)) : renderClientNodes(children, container);
|
|
2245
|
+
}
|
|
2246
|
+
const element = createElementNode(container.doc, resolved.type);
|
|
2247
|
+
let hasInnerHTML = false;
|
|
2248
|
+
for (const [name, descriptor] of Object.entries(Object.getOwnPropertyDescriptors(resolved.props))) {
|
|
2249
|
+
if (name === "children") continue;
|
|
2250
|
+
const value = descriptor.get ? descriptor.get.call(resolved.props) : descriptor.value;
|
|
2251
|
+
if (resolved.type === "body" && name === "data-e-resume") continue;
|
|
2252
|
+
if (name === DANGEROUSLY_SET_INNER_HTML_PROP) hasInnerHTML = true;
|
|
2253
|
+
applyElementProp(element, name, value, container);
|
|
2254
|
+
}
|
|
2255
|
+
if (hasInnerHTML) return [element];
|
|
2256
|
+
const children = resolved.props.children;
|
|
2257
|
+
const childNodes = Array.isArray(children) ? children.flatMap((child) => renderClientNodes(child, container)) : renderClientNodes(children, container);
|
|
2258
|
+
for (const child of childNodes) element.appendChild(child);
|
|
2259
|
+
return [element];
|
|
2260
|
+
};
|
|
2261
|
+
const scanComponentBoundaries = (root) => {
|
|
2262
|
+
const walker = root.ownerDocument.createTreeWalker(root, NodeFilter.SHOW_COMMENT);
|
|
2263
|
+
const boundaries = /* @__PURE__ */ new Map();
|
|
2264
|
+
while (walker.nextNode()) {
|
|
2265
|
+
const node = walker.currentNode;
|
|
2266
|
+
if (!(node instanceof Comment)) continue;
|
|
2267
|
+
const matched = node.data.match(/^ec:c:(.+):(start|end)$/);
|
|
2268
|
+
if (!matched) continue;
|
|
2269
|
+
const [, id, edge] = matched;
|
|
2270
|
+
const boundary = boundaries.get(id) ?? {};
|
|
2271
|
+
if (edge === "start") boundary.start = node;
|
|
2272
|
+
else boundary.end = node;
|
|
2273
|
+
boundaries.set(id, boundary);
|
|
2274
|
+
}
|
|
2275
|
+
return boundaries;
|
|
2276
|
+
};
|
|
2277
|
+
const loadSymbol = async (container, symbolId) => {
|
|
2278
|
+
const resolved = getResolvedRuntimeSymbols(container).get(symbolId);
|
|
2279
|
+
if (resolved) return resolved;
|
|
2280
|
+
const existing = container.imports.get(symbolId);
|
|
2281
|
+
if (existing) {
|
|
2282
|
+
const module = await existing;
|
|
2283
|
+
getResolvedRuntimeSymbols(container).set(symbolId, module);
|
|
2284
|
+
return module;
|
|
2285
|
+
}
|
|
2286
|
+
const url = container.symbols.get(symbolId);
|
|
2287
|
+
if (!url) throw new Error(`Missing symbol URL for ${symbolId}.`);
|
|
2288
|
+
const loaded = import(
|
|
2289
|
+
/* @vite-ignore */
|
|
2290
|
+
url
|
|
2291
|
+
).then((module) => {
|
|
2292
|
+
getResolvedRuntimeSymbols(container).set(symbolId, module);
|
|
2293
|
+
return module;
|
|
2294
|
+
});
|
|
2295
|
+
container.imports.set(symbolId, loaded);
|
|
2296
|
+
return loaded;
|
|
2297
|
+
};
|
|
2298
|
+
const toMountedNodes = (value, container) => {
|
|
2299
|
+
if (!container.doc) throw new Error("Client rendering requires a document.");
|
|
2300
|
+
let resolved = value;
|
|
2301
|
+
while (typeof resolved === "function") resolved = resolved();
|
|
2302
|
+
if (Array.isArray(resolved)) return resolved.flatMap((entry) => toMountedNodes(entry, container));
|
|
2303
|
+
if (resolved === null || resolved === void 0 || resolved === false) return [container.doc.createComment("eclipsa-empty")];
|
|
2304
|
+
if (resolved instanceof Node) return [resolved];
|
|
2305
|
+
if (typeof resolved === "string" || typeof resolved === "number" || typeof resolved === "boolean") return [container.doc.createTextNode(String(resolved))];
|
|
2306
|
+
return renderClientNodes(resolved, container);
|
|
2307
|
+
};
|
|
2308
|
+
const getRuntimeContainer = () => getCurrentContainer();
|
|
2309
|
+
const renderClientInsertable = (value, container = getCurrentContainer()) => {
|
|
2310
|
+
const doc = container?.doc ?? (typeof document !== "undefined" ? document : null);
|
|
2311
|
+
if (!doc) return [];
|
|
2312
|
+
if (Array.isArray(value)) return value.flatMap((entry) => renderClientInsertable(entry, container));
|
|
2313
|
+
let resolved = value;
|
|
2314
|
+
while (typeof resolved === "function") resolved = resolved();
|
|
2315
|
+
if (resolved === null || resolved === void 0 || resolved === false) return [doc.createComment("eclipsa-empty")];
|
|
2316
|
+
if (resolved instanceof Node) return [resolved];
|
|
2317
|
+
if (typeof resolved === "string" || typeof resolved === "number" || typeof resolved === "boolean") return [doc.createTextNode(String(resolved))];
|
|
2318
|
+
if (isProjectionSlot(resolved)) return container ? renderProjectionSlotToNodes(resolved, container) : renderClientInsertable(resolved.source, container);
|
|
2319
|
+
if (isRouteSlot(resolved)) {
|
|
2320
|
+
const routeElement = resolveRouteSlot(container, resolved);
|
|
2321
|
+
return routeElement ? renderClientInsertable(routeElement, container) : [doc.createComment("eclipsa-empty")];
|
|
2322
|
+
}
|
|
2323
|
+
if (container) return renderClientNodes(resolved, container);
|
|
2324
|
+
return [doc.createTextNode(String(resolved))];
|
|
2325
|
+
};
|
|
2326
|
+
const resetContainerForRouteRender = (container) => {
|
|
2327
|
+
for (const component of container.components.values()) {
|
|
2328
|
+
disposeComponentMountCleanups(component);
|
|
2329
|
+
pruneComponentVisibles(container, component, 0);
|
|
2330
|
+
pruneComponentWatches(container, component, 0);
|
|
2331
|
+
}
|
|
2332
|
+
for (const watch of container.watches.values()) clearEffectSignals(watch.effect);
|
|
2333
|
+
container.components.clear();
|
|
2334
|
+
container.dirty.clear();
|
|
2335
|
+
container.nextElementId = 0;
|
|
2336
|
+
container.nextScopeId = 0;
|
|
2337
|
+
container.nextSignalId = 0;
|
|
2338
|
+
container.rootChildCursor = 0;
|
|
2339
|
+
container.scopes.clear();
|
|
2340
|
+
container.visibles.clear();
|
|
2341
|
+
container.watches.clear();
|
|
2342
|
+
for (const [id, record] of [...container.signals.entries()]) {
|
|
2343
|
+
for (const effect of [...record.effects]) clearEffectSignals(effect);
|
|
2344
|
+
record.effects.clear();
|
|
2345
|
+
record.subscribers.clear();
|
|
2346
|
+
if (!isRouterSignalId(id)) container.signals.delete(id);
|
|
2347
|
+
}
|
|
2348
|
+
};
|
|
2349
|
+
const resolveRouteComponentSymbol = (Page, fallback) => getComponentMeta(Page)?.symbol ?? fallback;
|
|
2350
|
+
const isRouteSlot = (value) => isPlainObject(value) && value.__eclipsa_type === ROUTE_SLOT_TYPE;
|
|
2351
|
+
const createRouteSlot = (route, startLayoutIndex) => {
|
|
2352
|
+
const slot = {
|
|
2353
|
+
__eclipsa_type: ROUTE_SLOT_TYPE,
|
|
2354
|
+
pathname: route.pathname,
|
|
2355
|
+
startLayoutIndex
|
|
2356
|
+
};
|
|
2357
|
+
Object.defineProperty(slot, ROUTE_SLOT_ROUTE_KEY, {
|
|
2358
|
+
configurable: true,
|
|
2359
|
+
enumerable: false,
|
|
2360
|
+
value: route,
|
|
2361
|
+
writable: true
|
|
2362
|
+
});
|
|
2363
|
+
return slot;
|
|
2364
|
+
};
|
|
2365
|
+
const resolveRouteSlot = (container, slot) => {
|
|
2366
|
+
const route = slot[ROUTE_SLOT_ROUTE_KEY] ?? container?.router?.loadedRoutes.get(routeCacheKey(slot.pathname, "page"));
|
|
2367
|
+
if (!route) return null;
|
|
2368
|
+
return createRouteElement(route, slot.startLayoutIndex);
|
|
2369
|
+
};
|
|
2370
|
+
const createRouteElement = (route, startLayoutIndex = 0) => {
|
|
2371
|
+
const createRouteProps = (props) => {
|
|
2372
|
+
const nextProps = { ...props };
|
|
2373
|
+
Object.defineProperty(nextProps, ROUTE_PARAMS_PROP, {
|
|
2374
|
+
configurable: true,
|
|
2375
|
+
enumerable: false,
|
|
2376
|
+
value: route.params,
|
|
2377
|
+
writable: true
|
|
2378
|
+
});
|
|
2379
|
+
return nextProps;
|
|
2380
|
+
};
|
|
2381
|
+
if (startLayoutIndex >= route.layouts.length) return jsxDEV(route.page.renderer, createRouteProps({}), null, false, {});
|
|
2382
|
+
let children = null;
|
|
2383
|
+
for (let index = route.layouts.length - 1; index >= startLayoutIndex; index -= 1) {
|
|
2384
|
+
const layout = route.layouts[index];
|
|
2385
|
+
children = jsxDEV(layout.renderer, createRouteProps({ children: createRouteSlot(route, index + 1) }), null, false, {});
|
|
2386
|
+
}
|
|
2387
|
+
return children;
|
|
2388
|
+
};
|
|
2389
|
+
const trackSuspenseBoundaryPromise = (container, componentId, promise) => {
|
|
2390
|
+
const component = container.components.get(componentId);
|
|
2391
|
+
if (!component) return;
|
|
2392
|
+
component.suspensePromise = promise;
|
|
2393
|
+
promise.finally(() => {
|
|
2394
|
+
const current = container.components.get(componentId);
|
|
2395
|
+
if (!current || current.suspensePromise !== promise) return;
|
|
2396
|
+
current.suspensePromise = null;
|
|
2397
|
+
current.active = false;
|
|
2398
|
+
container.dirty.add(componentId);
|
|
2399
|
+
flushDirtyComponents(container);
|
|
2400
|
+
});
|
|
2401
|
+
};
|
|
2402
|
+
const renderSuspenseContentToString = (props, container, componentId) => {
|
|
2403
|
+
try {
|
|
2404
|
+
return renderStringNode(typeof props.children === "function" ? props.children() : props.children ?? null);
|
|
2405
|
+
} catch (error) {
|
|
2406
|
+
if (!isPendingSignalError(error)) throw error;
|
|
2407
|
+
container.pendingSuspensePromises.add(error.promise);
|
|
2408
|
+
const component = container.components.get(componentId);
|
|
2409
|
+
if (component) component.suspensePromise = error.promise;
|
|
2410
|
+
return renderStringNode(props.fallback ?? null);
|
|
2411
|
+
}
|
|
2412
|
+
};
|
|
2413
|
+
const collectPendingSuspenseBoundaryIds = (container) => [...container.components.values()].filter((component) => component.symbol === SUSPENSE_COMPONENT_SYMBOL && !!component.suspensePromise).map((component) => component.id).sort((left, right) => left.split(".").length - right.split(".").length);
|
|
2414
|
+
const renderSuspenseContentToNodes = (props, container, componentId) => {
|
|
2415
|
+
try {
|
|
2416
|
+
return toMountedNodes(typeof props.children === "function" ? props.children() : props.children ?? null, container);
|
|
2417
|
+
} catch (error) {
|
|
2418
|
+
if (!isPendingSignalError(error)) throw error;
|
|
2419
|
+
trackSuspenseBoundaryPromise(container, componentId, error.promise);
|
|
2420
|
+
return toMountedNodes(props.fallback ?? null, container);
|
|
2421
|
+
}
|
|
2422
|
+
};
|
|
2423
|
+
const renderSuspenseComponentToString = (props) => {
|
|
2424
|
+
const container = getCurrentContainer();
|
|
2425
|
+
if (!container) return renderStringNode(props.children ?? null);
|
|
2426
|
+
if (!getCurrentFrame()) return renderSuspenseContentToString(props, container, ROOT_COMPONENT_ID);
|
|
2427
|
+
const position = nextComponentPosition(container);
|
|
2428
|
+
const componentId = createComponentId(container, position.parentId, position.childIndex);
|
|
2429
|
+
const component = getOrCreateComponentState(container, componentId, SUSPENSE_COMPONENT_SYMBOL, position.parentId);
|
|
2430
|
+
component.props = {
|
|
2431
|
+
children: props.children,
|
|
2432
|
+
fallback: props.fallback
|
|
2433
|
+
};
|
|
2434
|
+
component.projectionSlots = null;
|
|
2435
|
+
const frame = createFrame(container, component, "ssr");
|
|
2436
|
+
clearComponentSubscriptions(container, component.id);
|
|
2437
|
+
const body = pushFrame(frame, () => renderSuspenseContentToString(component.props, container, componentId));
|
|
2438
|
+
pruneComponentVisibles(container, component, frame.visibleCursor);
|
|
2439
|
+
pruneComponentWatches(container, component, frame.watchCursor);
|
|
2440
|
+
return `<!--ec:c:${componentId}:start-->${body}<!--ec:c:${componentId}:end-->`;
|
|
2441
|
+
};
|
|
2442
|
+
const renderSuspenseComponentToNodes = (props, container, mode) => {
|
|
2443
|
+
const parentFrame = getCurrentFrame();
|
|
2444
|
+
if (!parentFrame) return renderSuspenseContentToNodes(props, container, ROOT_COMPONENT_ID);
|
|
2445
|
+
const position = nextComponentPosition(container);
|
|
2446
|
+
const componentId = createComponentId(container, position.parentId, position.childIndex);
|
|
2447
|
+
const component = getOrCreateComponentState(container, componentId, SUSPENSE_COMPONENT_SYMBOL, position.parentId);
|
|
2448
|
+
component.props = {
|
|
2449
|
+
children: props.children,
|
|
2450
|
+
fallback: props.fallback
|
|
2451
|
+
};
|
|
2452
|
+
component.projectionSlots = null;
|
|
2453
|
+
component.active = true;
|
|
2454
|
+
component.start = void 0;
|
|
2455
|
+
component.end = void 0;
|
|
2456
|
+
const frame = createFrame(container, component, mode);
|
|
2457
|
+
clearComponentSubscriptions(container, component.id);
|
|
2458
|
+
const bodyNodes = pushFrame(frame, () => renderSuspenseContentToNodes(component.props, container, componentId));
|
|
2459
|
+
pruneComponentVisibles(container, component, frame.visibleCursor);
|
|
2460
|
+
pruneComponentWatches(container, component, frame.watchCursor);
|
|
2461
|
+
parentFrame.visitedDescendants.add(componentId);
|
|
2462
|
+
scheduleMountCallbacks(container, component, frame.mountCallbacks);
|
|
2463
|
+
scheduleVisibleCallbacksCheck(container);
|
|
2464
|
+
if (!container.doc) return bodyNodes;
|
|
2465
|
+
const start = container.doc.createComment(`ec:c:${componentId}:start`);
|
|
2466
|
+
const end = container.doc.createComment(`ec:c:${componentId}:end`);
|
|
2467
|
+
component.start = start;
|
|
2468
|
+
component.end = end;
|
|
2469
|
+
return [
|
|
2470
|
+
start,
|
|
2471
|
+
...bodyNodes,
|
|
2472
|
+
end
|
|
2473
|
+
];
|
|
2474
|
+
};
|
|
2475
|
+
const renderRouteIntoRoot = (container, Page, routeKey) => {
|
|
2476
|
+
if (!container.doc || !container.rootElement) throw new Error("Client route rendering requires a document root.");
|
|
2477
|
+
resetContainerForRouteRender(container);
|
|
2478
|
+
const rootComponent = getOrCreateComponentState(container, "c0", resolveRouteComponentSymbol(Page, routeKey), ROOT_COMPONENT_ID);
|
|
2479
|
+
rootComponent.active = true;
|
|
2480
|
+
rootComponent.didMount = false;
|
|
2481
|
+
rootComponent.end = void 0;
|
|
2482
|
+
rootComponent.mountCleanupSlots = [];
|
|
2483
|
+
rootComponent.props = {};
|
|
2484
|
+
rootComponent.scopeId = registerScope(container, getComponentMeta(Page)?.captures() ?? []);
|
|
2485
|
+
rootComponent.signalIds = [];
|
|
2486
|
+
rootComponent.start = void 0;
|
|
2487
|
+
rootComponent.watchCount = 0;
|
|
2488
|
+
rootComponent.visibleCount = 0;
|
|
2489
|
+
clearComponentSubscriptions(container, rootComponent.id);
|
|
2490
|
+
const frame = createFrame(container, rootComponent, "client");
|
|
2491
|
+
const nodes = pushContainer(container, () => pushFrame(frame, () => {
|
|
2492
|
+
return toMountedNodes(Page({}), container);
|
|
2493
|
+
}));
|
|
2494
|
+
pruneComponentVisibles(container, rootComponent, frame.visibleCursor);
|
|
2495
|
+
pruneComponentWatches(container, rootComponent, frame.watchCursor);
|
|
2496
|
+
scheduleMountCallbacks(container, rootComponent, frame.mountCallbacks);
|
|
2497
|
+
const root = container.rootElement;
|
|
2498
|
+
while (root.firstChild) root.firstChild.remove();
|
|
2499
|
+
const start = container.doc.createComment("ec:c:c0:start");
|
|
2500
|
+
const end = container.doc.createComment("ec:c:c0:end");
|
|
2501
|
+
root.appendChild(start);
|
|
2502
|
+
for (const node of nodes) root.appendChild(node);
|
|
2503
|
+
root.appendChild(end);
|
|
2504
|
+
rootComponent.start = start;
|
|
2505
|
+
rootComponent.end = end;
|
|
2506
|
+
bindRouterLinks(container, root);
|
|
2507
|
+
scheduleVisibleCallbacksCheck(container);
|
|
2508
|
+
};
|
|
2509
|
+
const loadRouteModule = async (url) => {
|
|
2510
|
+
const module = await import(
|
|
2511
|
+
/* @vite-ignore */
|
|
2512
|
+
url
|
|
2513
|
+
);
|
|
2514
|
+
if (typeof module.default !== "function") throw new TypeError(`Route module ${url} does not export a default component.`);
|
|
2515
|
+
return {
|
|
2516
|
+
metadata: module.metadata ?? null,
|
|
2517
|
+
renderer: module.default,
|
|
2518
|
+
symbol: getComponentMeta(module.default)?.symbol ?? null,
|
|
2519
|
+
url
|
|
2520
|
+
};
|
|
2521
|
+
};
|
|
2522
|
+
const createManagedHeadElement = (doc, tagName) => {
|
|
2523
|
+
const element = doc.createElement(tagName);
|
|
2524
|
+
element.setAttribute(ROUTE_METADATA_HEAD_ATTR, "");
|
|
2525
|
+
return element;
|
|
2526
|
+
};
|
|
2527
|
+
const applyRouteMetadata = (doc, route, url, defaultTitle) => {
|
|
2528
|
+
for (const element of [...doc.head.querySelectorAll(`[${ROUTE_METADATA_HEAD_ATTR}]`)]) element.remove();
|
|
2529
|
+
const metadata = composeRouteMetadata([...route.layouts.map((layout) => layout.metadata ?? null), route.page.metadata ?? null], {
|
|
2530
|
+
params: route.params,
|
|
2531
|
+
url
|
|
2532
|
+
});
|
|
2533
|
+
doc.title = metadata?.title ?? defaultTitle;
|
|
2534
|
+
const appendMeta = (name, content, attr) => {
|
|
2535
|
+
const meta = createManagedHeadElement(doc, "meta");
|
|
2536
|
+
meta.setAttribute(attr, name);
|
|
2537
|
+
meta.setAttribute("content", content);
|
|
2538
|
+
doc.head.appendChild(meta);
|
|
2539
|
+
};
|
|
2540
|
+
if (metadata?.description) appendMeta("description", metadata.description, "name");
|
|
2541
|
+
if (metadata?.canonical) {
|
|
2542
|
+
const link = createManagedHeadElement(doc, "link");
|
|
2543
|
+
link.setAttribute("href", metadata.canonical);
|
|
2544
|
+
link.setAttribute("rel", "canonical");
|
|
2545
|
+
doc.head.appendChild(link);
|
|
2546
|
+
}
|
|
2547
|
+
if (metadata?.openGraph?.title) appendMeta("og:title", metadata.openGraph.title, "property");
|
|
2548
|
+
if (metadata?.openGraph?.description) appendMeta("og:description", metadata.openGraph.description, "property");
|
|
2549
|
+
if (metadata?.openGraph?.type) appendMeta("og:type", metadata.openGraph.type, "property");
|
|
2550
|
+
if (metadata?.openGraph?.url) appendMeta("og:url", metadata.openGraph.url, "property");
|
|
2551
|
+
for (const image of metadata?.openGraph?.images ?? []) appendMeta("og:image", image, "property");
|
|
2552
|
+
if (metadata?.twitter?.card) appendMeta("twitter:card", metadata.twitter.card, "name");
|
|
2553
|
+
if (metadata?.twitter?.title) appendMeta("twitter:title", metadata.twitter.title, "name");
|
|
2554
|
+
if (metadata?.twitter?.description) appendMeta("twitter:description", metadata.twitter.description, "name");
|
|
2555
|
+
for (const image of metadata?.twitter?.images ?? []) appendMeta("twitter:image", image, "name");
|
|
2556
|
+
};
|
|
2557
|
+
const loadResolvedRoute = async (container, matched, variant = "page") => {
|
|
2558
|
+
const router = ensureRouterState(container);
|
|
2559
|
+
const normalizedPath = normalizeRoutePath(matched.pathname);
|
|
2560
|
+
const cacheKey = routeCacheKey(normalizedPath, variant);
|
|
2561
|
+
const existing = router.loadedRoutes.get(cacheKey);
|
|
2562
|
+
if (existing) return existing;
|
|
2563
|
+
const moduleUrl = variant === "page" ? matched.entry.page : variant === "loading" ? matched.entry.loading : variant === "error" ? matched.entry.error : matched.entry.notFound;
|
|
2564
|
+
if (!moduleUrl) return null;
|
|
2565
|
+
const [page, ...layouts] = await Promise.all([loadRouteModule(moduleUrl), ...matched.entry.layouts.map((layoutUrl) => loadRouteModule(layoutUrl))]);
|
|
2566
|
+
let route;
|
|
2567
|
+
route = {
|
|
2568
|
+
entry: matched.entry,
|
|
2569
|
+
layouts,
|
|
2570
|
+
params: matched.params,
|
|
2571
|
+
pathname: normalizedPath,
|
|
2572
|
+
page,
|
|
2573
|
+
render: () => createRouteElement(route)
|
|
2574
|
+
};
|
|
2575
|
+
router.loadedRoutes.set(cacheKey, route);
|
|
2576
|
+
return route;
|
|
2577
|
+
};
|
|
2578
|
+
const loadResolvedRouteFromSpecial = async (container, pathname, kind) => {
|
|
2579
|
+
const matched = findSpecialManifestEntry(ensureRouterState(container).manifest, pathname, kind);
|
|
2580
|
+
if (!matched) return null;
|
|
2581
|
+
return loadResolvedRoute(container, matched, kind === "error" ? "error" : "not-found");
|
|
2582
|
+
};
|
|
2583
|
+
const loadRouteComponent = async (container, pathname) => {
|
|
2584
|
+
const matched = matchRouteManifest(ensureRouterState(container).manifest, pathname);
|
|
2585
|
+
if (!matched || !matched.entry.page) return null;
|
|
2586
|
+
return loadResolvedRoute(container, matched, "page");
|
|
2587
|
+
};
|
|
2588
|
+
const findRouteComponentChain = (container, symbols, parentId = null) => {
|
|
2589
|
+
if (symbols.length === 0) return [];
|
|
2590
|
+
const [symbol, ...rest] = symbols;
|
|
2591
|
+
const candidates = [...container.components.values()].filter((component) => {
|
|
2592
|
+
if (!component.active || component.symbol !== symbol) return false;
|
|
2593
|
+
if (parentId === null) return true;
|
|
2594
|
+
return isDescendantOf(parentId, component.id);
|
|
2595
|
+
}).sort((left, right) => left.id.split(".").length - right.id.split(".").length);
|
|
2596
|
+
for (const candidate of candidates) {
|
|
2597
|
+
const remainder = findRouteComponentChain(container, rest, candidate.id);
|
|
2598
|
+
if (!remainder) continue;
|
|
2599
|
+
return [candidate.id, ...remainder];
|
|
2600
|
+
}
|
|
2601
|
+
return null;
|
|
2602
|
+
};
|
|
2603
|
+
const collectSharedLayoutBoundaryIds = (container, route) => {
|
|
2604
|
+
if (route.layouts.length === 0 || route.layouts.some((layout) => !layout.symbol)) return null;
|
|
2605
|
+
const symbols = route.layouts.map((layout) => layout.symbol);
|
|
2606
|
+
if (route.page.symbol) {
|
|
2607
|
+
const chain = findRouteComponentChain(container, [...symbols, route.page.symbol]);
|
|
2608
|
+
if (chain) return chain.slice(0, route.layouts.length);
|
|
2609
|
+
}
|
|
2610
|
+
return findRouteComponentChain(container, symbols);
|
|
2611
|
+
};
|
|
2612
|
+
const countSharedLayouts = (current, next) => {
|
|
2613
|
+
if (!current) return 0;
|
|
2614
|
+
let count = 0;
|
|
2615
|
+
const limit = Math.min(current.layouts.length, next.layouts.length);
|
|
2616
|
+
while (count < limit && current.layouts[count].url === next.layouts[count].url) count += 1;
|
|
2617
|
+
return count;
|
|
2618
|
+
};
|
|
2619
|
+
const updateSharedLayoutBoundary = async (container, current, next, sharedLayoutCount) => {
|
|
2620
|
+
if (sharedLayoutCount <= 0) return false;
|
|
2621
|
+
const boundaryIds = collectSharedLayoutBoundaryIds(container, current);
|
|
2622
|
+
if (!boundaryIds || boundaryIds.length < sharedLayoutCount) return false;
|
|
2623
|
+
const boundaryId = boundaryIds[sharedLayoutCount - 1];
|
|
2624
|
+
const boundary = container.components.get(boundaryId);
|
|
2625
|
+
if (!boundary) return false;
|
|
2626
|
+
boundary.props = { children: createRouteElement(next, sharedLayoutCount) };
|
|
2627
|
+
boundary.active = false;
|
|
2628
|
+
boundary.reuseExistingDomOnActivate = false;
|
|
2629
|
+
container.dirty.add(boundaryId);
|
|
2630
|
+
await flushDirtyComponents(container);
|
|
2631
|
+
return true;
|
|
2632
|
+
};
|
|
2633
|
+
const commitBrowserNavigation = (doc, url, mode) => {
|
|
2634
|
+
if (!doc.defaultView || mode === "pop") return;
|
|
2635
|
+
if (mode === "replace") {
|
|
2636
|
+
doc.defaultView.history.replaceState(null, "", url.href);
|
|
2637
|
+
return;
|
|
2638
|
+
}
|
|
2639
|
+
doc.defaultView.history.pushState(null, "", url.href);
|
|
2640
|
+
};
|
|
2641
|
+
const fallbackDocumentNavigation = (doc, url, mode) => {
|
|
2642
|
+
if (!doc.defaultView) return;
|
|
2643
|
+
if (mode === "replace") {
|
|
2644
|
+
doc.defaultView.location.replace(url.href);
|
|
2645
|
+
return;
|
|
2646
|
+
}
|
|
2647
|
+
if (mode === "pop") {
|
|
2648
|
+
doc.defaultView.location.assign(url.href);
|
|
2649
|
+
return;
|
|
2650
|
+
}
|
|
2651
|
+
doc.defaultView.location.assign(url.href);
|
|
2652
|
+
};
|
|
2653
|
+
const parseResumePayloadFromHtml = (html) => {
|
|
2654
|
+
if (typeof DOMParser === "undefined") return null;
|
|
2655
|
+
const parsed = new DOMParser().parseFromString(html, "text/html");
|
|
2656
|
+
const payloadNode = parsed.getElementById("eclipsa-resume-final") ?? parsed.getElementById("eclipsa-resume");
|
|
2657
|
+
if (!payloadNode?.textContent) return null;
|
|
2658
|
+
return JSON.parse(payloadNode.textContent);
|
|
2659
|
+
};
|
|
2660
|
+
const cachePrefetchedLoaders = (container, url, payload) => {
|
|
2661
|
+
if (!payload) return;
|
|
2662
|
+
const router = ensureRouterState(container);
|
|
2663
|
+
const snapshots = /* @__PURE__ */ new Map();
|
|
2664
|
+
for (const [id, loaderPayload] of Object.entries(payload.loaders ?? {})) snapshots.set(id, {
|
|
2665
|
+
data: deserializeRuntimeValue(container, loaderPayload.data),
|
|
2666
|
+
error: deserializeRuntimeValue(container, loaderPayload.error),
|
|
2667
|
+
loaded: loaderPayload.loaded
|
|
2668
|
+
});
|
|
2669
|
+
router.prefetchedLoaders.set(routePrefetchKey(url), snapshots);
|
|
2670
|
+
};
|
|
2671
|
+
const applyPrefetchedLoaders = (container, url) => {
|
|
2672
|
+
const prefetchedLoaders = ensureRouterState(container).prefetchedLoaders.get(routePrefetchKey(url));
|
|
2673
|
+
if (!prefetchedLoaders) return;
|
|
2674
|
+
for (const [id, snapshot] of prefetchedLoaders) container.loaderStates.set(id, {
|
|
2675
|
+
data: snapshot.data,
|
|
2676
|
+
error: snapshot.error,
|
|
2677
|
+
loaded: snapshot.loaded
|
|
2678
|
+
});
|
|
2679
|
+
};
|
|
2680
|
+
const requestRoutePreflight = async (href) => {
|
|
2681
|
+
try {
|
|
2682
|
+
const requestUrl = new URL(href, typeof window === "undefined" ? "http://localhost" : window.location.href);
|
|
2683
|
+
const response = await fetch(requestUrl.href, { headers: { [ROUTE_PREFLIGHT_REQUEST_HEADER]: "1" } });
|
|
2684
|
+
if (response.status < 200 || response.status >= 300) return {
|
|
2685
|
+
document: true,
|
|
2686
|
+
ok: false
|
|
2687
|
+
};
|
|
2688
|
+
const finalUrl = new URL(response.url || requestUrl.href, requestUrl.href);
|
|
2689
|
+
if (finalUrl.origin !== requestUrl.origin || finalUrl.pathname !== requestUrl.pathname || finalUrl.search !== requestUrl.search) return {
|
|
2690
|
+
location: finalUrl.href,
|
|
2691
|
+
ok: false
|
|
2692
|
+
};
|
|
2693
|
+
return { ok: true };
|
|
2694
|
+
} catch {
|
|
2695
|
+
return {
|
|
2696
|
+
document: true,
|
|
2697
|
+
ok: false
|
|
2698
|
+
};
|
|
2699
|
+
}
|
|
2700
|
+
};
|
|
2701
|
+
const prefetchResolvedRouteModules = async (container, pathname, finalUrl) => {
|
|
2702
|
+
const router = ensureRouterState(container);
|
|
2703
|
+
const matched = matchRouteManifest(router.manifest, pathname);
|
|
2704
|
+
if (matched?.entry.page) {
|
|
2705
|
+
await loadResolvedRoute(container, matched);
|
|
2706
|
+
return;
|
|
2707
|
+
}
|
|
2708
|
+
const notFoundMatched = findSpecialManifestEntry(router.manifest, pathname, "notFound");
|
|
2709
|
+
if (notFoundMatched?.entry.notFound) await loadResolvedRoute(container, notFoundMatched, "not-found");
|
|
2710
|
+
if (finalUrl.pathname !== pathname) {
|
|
2711
|
+
const redirectedPath = normalizeRoutePath(finalUrl.pathname);
|
|
2712
|
+
const redirectedMatched = matchRouteManifest(router.manifest, redirectedPath);
|
|
2713
|
+
if (redirectedMatched?.entry.page) await loadResolvedRoute(container, redirectedMatched);
|
|
2714
|
+
}
|
|
2715
|
+
};
|
|
2716
|
+
const prefetchRoute = async (container, href) => {
|
|
2717
|
+
const doc = container.doc;
|
|
2718
|
+
if (!doc) return;
|
|
2719
|
+
const requestUrl = new URL(href, doc.location.href);
|
|
2720
|
+
if (requestUrl.origin !== doc.location.origin) return;
|
|
2721
|
+
const key = routePrefetchKey(requestUrl);
|
|
2722
|
+
const router = ensureRouterState(container);
|
|
2723
|
+
const existing = router.routePrefetches.get(key);
|
|
2724
|
+
if (existing) {
|
|
2725
|
+
await existing;
|
|
2726
|
+
return;
|
|
2727
|
+
}
|
|
2728
|
+
const pathname = normalizeRoutePath(requestUrl.pathname);
|
|
2729
|
+
const matched = matchRouteManifest(router.manifest, pathname);
|
|
2730
|
+
const specialRoute = !matched ? findSpecialManifestEntry(router.manifest, pathname, "notFound") : null;
|
|
2731
|
+
if (!matched?.entry.page && !specialRoute?.entry.notFound) return;
|
|
2732
|
+
const prefetchPromise = (async () => {
|
|
2733
|
+
try {
|
|
2734
|
+
const response = await fetch(requestUrl.href);
|
|
2735
|
+
if (response.status < 200 || response.status >= 300) return;
|
|
2736
|
+
const finalUrl = new URL(response.url || requestUrl.href, requestUrl.href);
|
|
2737
|
+
if (finalUrl.origin !== requestUrl.origin) return;
|
|
2738
|
+
const html = (response.headers.get("content-type") ?? "").includes("text/html") ? await response.text() : "";
|
|
2739
|
+
await prefetchResolvedRouteModules(container, normalizeRoutePath(finalUrl.pathname), finalUrl);
|
|
2740
|
+
cachePrefetchedLoaders(container, new URL(`${finalUrl.pathname}${finalUrl.search}`, requestUrl.origin), html ? parseResumePayloadFromHtml(html) : null);
|
|
2741
|
+
} catch {
|
|
2742
|
+
return;
|
|
2743
|
+
}
|
|
2744
|
+
})();
|
|
2745
|
+
router.routePrefetches.set(key, prefetchPromise);
|
|
2746
|
+
await prefetchPromise;
|
|
2747
|
+
};
|
|
2748
|
+
const navigateContainer = async (container, href, options) => {
|
|
2749
|
+
const doc = container.doc;
|
|
2750
|
+
if (!doc) return;
|
|
2751
|
+
const mode = options?.mode ?? "push";
|
|
2752
|
+
const redirectDepth = options?.redirectDepth ?? 0;
|
|
2753
|
+
const url = new URL(href, doc.location.href);
|
|
2754
|
+
if (url.origin !== doc.location.origin) {
|
|
2755
|
+
fallbackDocumentNavigation(doc, url, mode);
|
|
2756
|
+
return;
|
|
2757
|
+
}
|
|
2758
|
+
const pathname = normalizeRoutePath(url.pathname);
|
|
2759
|
+
const router = ensureRouterState(container);
|
|
2760
|
+
const matched = matchRouteManifest(router.manifest, pathname);
|
|
2761
|
+
const specialPreflightTarget = !matched ? findSpecialManifestEntry(router.manifest, pathname, "notFound") : null;
|
|
2762
|
+
const currentHref = `${doc.location.pathname}${doc.location.search}${doc.location.hash}`;
|
|
2763
|
+
const nextHref = `${url.pathname}${url.search}${url.hash}`;
|
|
2764
|
+
if (nextHref === currentHref) return;
|
|
2765
|
+
if (matched?.entry.page && matched.entry.hasMiddleware || !!specialPreflightTarget?.entry.notFound && specialPreflightTarget.entry.hasMiddleware) {
|
|
2766
|
+
const preflight = await requestRoutePreflight(url.href);
|
|
2767
|
+
if (!preflight.ok) {
|
|
2768
|
+
if ("location" in preflight) {
|
|
2769
|
+
if (redirectDepth >= 8) {
|
|
2770
|
+
fallbackDocumentNavigation(doc, new URL(preflight.location, doc.location.href), mode);
|
|
2771
|
+
return;
|
|
2772
|
+
}
|
|
2773
|
+
const redirectUrl = new URL(preflight.location, doc.location.href);
|
|
2774
|
+
if (redirectUrl.origin !== doc.location.origin) {
|
|
2775
|
+
fallbackDocumentNavigation(doc, redirectUrl, mode);
|
|
2776
|
+
return;
|
|
2777
|
+
}
|
|
2778
|
+
await navigateContainer(container, redirectUrl.href, {
|
|
2779
|
+
mode,
|
|
2780
|
+
redirectDepth: redirectDepth + 1
|
|
2781
|
+
});
|
|
2782
|
+
return;
|
|
2783
|
+
}
|
|
2784
|
+
fallbackDocumentNavigation(doc, url, mode);
|
|
2785
|
+
return;
|
|
2786
|
+
}
|
|
2787
|
+
}
|
|
2788
|
+
if (!matched || !matched.entry.page) {
|
|
2789
|
+
const notFoundRoute = !matched ? await loadResolvedRouteFromSpecial(container, pathname, "notFound") : null;
|
|
2790
|
+
if (notFoundRoute) {
|
|
2791
|
+
renderRouteIntoRoot(container, notFoundRoute.render, `route:${pathname}:not-found`);
|
|
2792
|
+
router.currentRoute = notFoundRoute;
|
|
2793
|
+
applyRouteMetadata(doc, notFoundRoute, url, router.defaultTitle);
|
|
2794
|
+
commitBrowserNavigation(doc, url, mode);
|
|
2795
|
+
router.currentPath.value = pathname;
|
|
2796
|
+
return;
|
|
2797
|
+
}
|
|
2798
|
+
fallbackDocumentNavigation(doc, url, mode);
|
|
2799
|
+
return;
|
|
2800
|
+
}
|
|
2801
|
+
if (pathname === router.currentPath.value) {
|
|
2802
|
+
if (nextHref !== currentHref) commitBrowserNavigation(doc, url, mode);
|
|
2803
|
+
return;
|
|
2804
|
+
}
|
|
2805
|
+
const pendingPrefetch = router.routePrefetches.get(routePrefetchKey(url));
|
|
2806
|
+
if (pendingPrefetch) await pendingPrefetch;
|
|
2807
|
+
applyPrefetchedLoaders(container, url);
|
|
2808
|
+
const sequence = ++router.sequence;
|
|
2809
|
+
router.isNavigating.value = true;
|
|
2810
|
+
try {
|
|
2811
|
+
const nextRoutePromise = loadResolvedRoute(container, matched);
|
|
2812
|
+
if (matched.entry.loading) {
|
|
2813
|
+
let settled = false;
|
|
2814
|
+
nextRoutePromise.finally(() => {
|
|
2815
|
+
settled = true;
|
|
2816
|
+
});
|
|
2817
|
+
await Promise.resolve();
|
|
2818
|
+
if (!settled) {
|
|
2819
|
+
const loadingRoute = await loadResolvedRoute(container, matched, "loading");
|
|
2820
|
+
if (loadingRoute) {
|
|
2821
|
+
renderRouteIntoRoot(container, loadingRoute.render, `route:${pathname}:loading`);
|
|
2822
|
+
router.currentRoute = loadingRoute;
|
|
2823
|
+
}
|
|
2824
|
+
}
|
|
2825
|
+
}
|
|
2826
|
+
const [currentRoute, nextRoute] = await Promise.all([router.currentRoute ? Promise.resolve(router.currentRoute) : loadRouteComponent(container, router.currentPath.value), nextRoutePromise]);
|
|
2827
|
+
if (!nextRoute) {
|
|
2828
|
+
fallbackDocumentNavigation(doc, url, mode);
|
|
2829
|
+
return;
|
|
2830
|
+
}
|
|
2831
|
+
if (sequence !== router.sequence) return;
|
|
2832
|
+
const sharedLayoutCount = countSharedLayouts(currentRoute, nextRoute);
|
|
2833
|
+
if (!(currentRoute && sharedLayoutCount > 0 ? await updateSharedLayoutBoundary(container, currentRoute, nextRoute, sharedLayoutCount) : false)) renderRouteIntoRoot(container, nextRoute.render, `route:${pathname}`);
|
|
2834
|
+
router.currentRoute = nextRoute;
|
|
2835
|
+
applyRouteMetadata(doc, nextRoute, url, router.defaultTitle);
|
|
2836
|
+
commitBrowserNavigation(doc, url, mode);
|
|
2837
|
+
router.currentPath.value = pathname;
|
|
2838
|
+
} catch (error) {
|
|
2839
|
+
if (sequence === router.sequence) {
|
|
2840
|
+
const fallbackRoute = isRouteNotFoundError(error) ? await loadResolvedRouteFromSpecial(container, pathname, "notFound") : await loadResolvedRoute(container, matched, "error");
|
|
2841
|
+
if (fallbackRoute) {
|
|
2842
|
+
renderRouteIntoRoot(container, fallbackRoute.render, `route:${pathname}:${isRouteNotFoundError(error) ? "not-found" : "error"}`);
|
|
2843
|
+
router.currentRoute = fallbackRoute;
|
|
2844
|
+
applyRouteMetadata(doc, fallbackRoute, url, router.defaultTitle);
|
|
2845
|
+
commitBrowserNavigation(doc, url, mode);
|
|
2846
|
+
router.currentPath.value = pathname;
|
|
2847
|
+
return;
|
|
2848
|
+
}
|
|
2849
|
+
fallbackDocumentNavigation(doc, url, mode);
|
|
2850
|
+
}
|
|
2851
|
+
} finally {
|
|
2852
|
+
if (sequence === router.sequence) router.isNavigating.value = false;
|
|
2853
|
+
}
|
|
2854
|
+
};
|
|
2855
|
+
const renderClientComponent = (componentFn, props) => {
|
|
2856
|
+
const container = getCurrentContainer();
|
|
2857
|
+
const parentFrame = getCurrentFrame();
|
|
2858
|
+
const meta = getComponentMeta(componentFn);
|
|
2859
|
+
if (!container || !parentFrame || !meta) return componentFn(props);
|
|
2860
|
+
const position = nextComponentPosition(container);
|
|
2861
|
+
const componentId = createComponentId(container, position.parentId, position.childIndex);
|
|
2862
|
+
const existing = container.components.get(componentId);
|
|
2863
|
+
const symbolChanged = !!existing && existing.symbol !== meta.symbol;
|
|
2864
|
+
const component = getOrCreateComponentState(container, componentId, meta.symbol, position.parentId);
|
|
2865
|
+
component.props = props && typeof props === "object" ? evaluateProps(props) : props;
|
|
2866
|
+
component.projectionSlots = meta.projectionSlots ?? null;
|
|
2867
|
+
if (!existing || symbolChanged) resetComponentForSymbolChange(container, component, meta);
|
|
2868
|
+
component.active = true;
|
|
2869
|
+
component.start = void 0;
|
|
2870
|
+
component.end = void 0;
|
|
2871
|
+
const frame = createFrame(container, component, "client");
|
|
2872
|
+
const oldDescendants = collectDescendantIds(container, componentId);
|
|
2873
|
+
clearComponentSubscriptions(container, componentId);
|
|
2874
|
+
const renderProps = component.props && typeof component.props === "object" ? createRenderProps(componentId, meta, component.props) : component.props;
|
|
2875
|
+
const rendered = pushFrame(frame, () => componentFn(renderProps));
|
|
2876
|
+
pruneComponentVisibles(container, component, frame.visibleCursor);
|
|
2877
|
+
pruneComponentWatches(container, component, frame.watchCursor);
|
|
2878
|
+
pruneRemovedComponents(container, componentId, frame.visitedDescendants);
|
|
2879
|
+
for (const descendantId of oldDescendants) {
|
|
2880
|
+
if (frame.visitedDescendants.has(descendantId)) continue;
|
|
2881
|
+
clearComponentSubscriptions(container, descendantId);
|
|
2882
|
+
}
|
|
2883
|
+
parentFrame.visitedDescendants.add(componentId);
|
|
2884
|
+
for (const descendantId of frame.visitedDescendants) parentFrame.visitedDescendants.add(descendantId);
|
|
2885
|
+
scheduleMountCallbacks(container, component, frame.mountCallbacks);
|
|
2886
|
+
scheduleVisibleCallbacksCheck(container);
|
|
2887
|
+
return rendered;
|
|
2888
|
+
};
|
|
2889
|
+
const activateComponent = async (container, componentId) => {
|
|
2890
|
+
const component = container.components.get(componentId);
|
|
2891
|
+
if (!component?.start || !component.end || component.active) return;
|
|
2892
|
+
if (component.symbol === SUSPENSE_COMPONENT_SYMBOL) {
|
|
2893
|
+
clearComponentSubscriptions(container, componentId);
|
|
2894
|
+
const oldDescendants = collectDescendantIds(container, componentId);
|
|
2895
|
+
const frame = createFrame(container, component, "client", { reuseExistingDom: component.reuseExistingDomOnActivate ?? true });
|
|
2896
|
+
component.reuseExistingDomOnActivate = true;
|
|
2897
|
+
const focusSnapshot = captureBoundaryFocus(container.doc, component.start, component.end);
|
|
2898
|
+
const nodes = pushContainer(container, () => pushFrame(frame, () => renderSuspenseContentToNodes(component.props, container, componentId)));
|
|
2899
|
+
pruneComponentVisibles(container, component, frame.visibleCursor);
|
|
2900
|
+
pruneComponentWatches(container, component, frame.watchCursor);
|
|
2901
|
+
replaceBoundaryContents(component.start, component.end, nodes, { preserveProjectionSlots: frame.projectionState.reuseExistingDom });
|
|
2902
|
+
restoreBoundaryFocus(container.doc, component.start, component.end, focusSnapshot);
|
|
2903
|
+
component.active = true;
|
|
2904
|
+
pruneRemovedComponents(container, componentId, frame.visitedDescendants);
|
|
2905
|
+
for (const descendantId of oldDescendants) {
|
|
2906
|
+
if (frame.visitedDescendants.has(descendantId)) continue;
|
|
2907
|
+
clearComponentSubscriptions(container, descendantId);
|
|
2908
|
+
}
|
|
2909
|
+
return;
|
|
2910
|
+
}
|
|
2911
|
+
clearComponentSubscriptions(container, componentId);
|
|
2912
|
+
const oldDescendants = collectDescendantIds(container, componentId);
|
|
2913
|
+
const scope = materializeScope(container, component.scopeId);
|
|
2914
|
+
await preloadResumableValue(container, scope);
|
|
2915
|
+
const module = await loadSymbol(container, component.symbol);
|
|
2916
|
+
await preloadComponentProps(container, {
|
|
2917
|
+
captures: () => [],
|
|
2918
|
+
projectionSlots: component.projectionSlots ?? void 0,
|
|
2919
|
+
symbol: component.symbol
|
|
2920
|
+
}, component.props);
|
|
2921
|
+
const frame = createFrame(container, component, "client", { reuseExistingDom: component.reuseExistingDomOnActivate ?? true });
|
|
2922
|
+
component.reuseExistingDomOnActivate = true;
|
|
2923
|
+
const focusSnapshot = captureBoundaryFocus(container.doc, component.start, component.end);
|
|
2924
|
+
const nodes = pushContainer(container, () => pushFrame(frame, () => {
|
|
2925
|
+
const renderProps = component.props && typeof component.props === "object" ? createRenderProps(component.id, {
|
|
2926
|
+
captures: () => [],
|
|
2927
|
+
projectionSlots: component.projectionSlots ?? void 0,
|
|
2928
|
+
symbol: component.symbol
|
|
2929
|
+
}, component.props) : component.props;
|
|
2930
|
+
return toMountedNodes(module.default(scope, renderProps), container);
|
|
2931
|
+
}));
|
|
2932
|
+
pruneComponentVisibles(container, component, frame.visibleCursor);
|
|
2933
|
+
pruneComponentWatches(container, component, frame.watchCursor);
|
|
2934
|
+
replaceBoundaryContents(component.start, component.end, nodes, { preserveProjectionSlots: frame.projectionState.reuseExistingDom });
|
|
2935
|
+
if (component.start.parentNode && "querySelectorAll" in component.start.parentNode) bindRouterLinks(container, component.start.parentNode);
|
|
2936
|
+
restoreBoundaryFocus(container.doc, component.start, component.end, focusSnapshot);
|
|
2937
|
+
component.active = true;
|
|
2938
|
+
for (const descendantId of frame.visitedDescendants) {
|
|
2939
|
+
const descendant = container.components.get(descendantId);
|
|
2940
|
+
if (!descendant) continue;
|
|
2941
|
+
descendant.active = true;
|
|
2942
|
+
descendant.start = void 0;
|
|
2943
|
+
descendant.end = void 0;
|
|
2944
|
+
}
|
|
2945
|
+
pruneRemovedComponents(container, componentId, frame.visitedDescendants);
|
|
2946
|
+
for (const descendantId of oldDescendants) {
|
|
2947
|
+
if (frame.visitedDescendants.has(descendantId)) continue;
|
|
2948
|
+
clearComponentSubscriptions(container, descendantId);
|
|
2949
|
+
}
|
|
2950
|
+
clearComponentSubscriptions(container, componentId);
|
|
2951
|
+
scheduleMountCallbacks(container, component, frame.mountCallbacks);
|
|
2952
|
+
scheduleVisibleCallbacksCheck(container);
|
|
2953
|
+
};
|
|
2954
|
+
const sortDirtyComponents = (ids) => [...ids].sort((a, b) => a.split(".").length - b.split(".").length);
|
|
2955
|
+
const parseSymbolIdFromUrl = (url) => {
|
|
2956
|
+
return new URL(url, "http://localhost").searchParams.get("eclipsa-symbol");
|
|
2957
|
+
};
|
|
2958
|
+
const findNearestMountedBoundary = (container, componentId) => {
|
|
2959
|
+
let currentId = componentId;
|
|
2960
|
+
while (currentId && currentId !== ROOT_COMPONENT_ID) {
|
|
2961
|
+
const component = container.components.get(currentId);
|
|
2962
|
+
if (!component) return null;
|
|
2963
|
+
if (component.start && component.end) return component.id;
|
|
2964
|
+
currentId = component.parentId;
|
|
2965
|
+
}
|
|
2966
|
+
return null;
|
|
2967
|
+
};
|
|
2968
|
+
const collectResumeHmrBoundaryIds = (container, symbolIds) => {
|
|
2969
|
+
const targetSymbols = new Set(symbolIds);
|
|
2970
|
+
const pendingBoundaries = /* @__PURE__ */ new Set();
|
|
2971
|
+
for (const component of container.components.values()) {
|
|
2972
|
+
if (!targetSymbols.has(component.symbol)) continue;
|
|
2973
|
+
const boundaryId = findNearestMountedBoundary(container, component.id);
|
|
2974
|
+
if (!boundaryId) {
|
|
2975
|
+
if (component.active) return null;
|
|
2976
|
+
continue;
|
|
2977
|
+
}
|
|
2978
|
+
pendingBoundaries.add(boundaryId);
|
|
2979
|
+
}
|
|
2980
|
+
const result = [];
|
|
2981
|
+
for (const boundaryId of sortDirtyComponents(pendingBoundaries)) {
|
|
2982
|
+
if (result.some((parentId) => boundaryId === parentId || isDescendantOf(parentId, boundaryId))) continue;
|
|
2983
|
+
result.push(boundaryId);
|
|
2984
|
+
}
|
|
2985
|
+
return result;
|
|
2986
|
+
};
|
|
2987
|
+
const applyResumeHmrSymbolReplacements = (container, replacements) => {
|
|
2988
|
+
for (const [oldSymbolId, url] of Object.entries(replacements)) {
|
|
2989
|
+
const currentUrl = container.symbols.get(oldSymbolId);
|
|
2990
|
+
const affectedIds = new Set([oldSymbolId]);
|
|
2991
|
+
if (currentUrl) {
|
|
2992
|
+
for (const [candidateId, candidateUrl] of container.symbols.entries()) if (candidateUrl === currentUrl) affectedIds.add(candidateId);
|
|
2993
|
+
}
|
|
2994
|
+
for (const affectedId of affectedIds) {
|
|
2995
|
+
container.symbols.set(affectedId, url);
|
|
2996
|
+
container.imports.delete(affectedId);
|
|
2997
|
+
}
|
|
2998
|
+
const nextSymbolId = parseSymbolIdFromUrl(url);
|
|
2999
|
+
if (!nextSymbolId) continue;
|
|
3000
|
+
for (const component of container.components.values()) if (affectedIds.has(component.symbol)) component.symbol = nextSymbolId;
|
|
3001
|
+
for (const watch of container.watches.values()) if (affectedIds.has(watch.symbol)) watch.symbol = nextSymbolId;
|
|
3002
|
+
for (const visible of container.visibles.values()) if (affectedIds.has(visible.symbol)) visible.symbol = nextSymbolId;
|
|
3003
|
+
container.symbols.set(nextSymbolId, url);
|
|
3004
|
+
container.imports.delete(nextSymbolId);
|
|
3005
|
+
}
|
|
3006
|
+
};
|
|
3007
|
+
const applyResumeHmrUpdate = async (container, payload) => {
|
|
3008
|
+
if (payload.fullReload) return "reload";
|
|
3009
|
+
const boundaryIds = collectResumeHmrBoundaryIds(container, [...payload.rerenderComponentSymbols, ...payload.rerenderOwnerSymbols]);
|
|
3010
|
+
if (boundaryIds === null) return "reload";
|
|
3011
|
+
applyResumeHmrSymbolReplacements(container, payload.symbolUrlReplacements);
|
|
3012
|
+
for (const boundaryId of boundaryIds) {
|
|
3013
|
+
const component = container.components.get(boundaryId);
|
|
3014
|
+
if (!component) continue;
|
|
3015
|
+
resetComponentVisibleStates(container, boundaryId);
|
|
3016
|
+
component.active = false;
|
|
3017
|
+
container.dirty.add(boundaryId);
|
|
3018
|
+
}
|
|
3019
|
+
if (container.dirty.size > 0) await flushDirtyComponents(container);
|
|
3020
|
+
return "updated";
|
|
3021
|
+
};
|
|
3022
|
+
const applyResumeHmrUpdateToRegisteredContainers = async (payload) => {
|
|
3023
|
+
for (const container of getResumeContainers()) if (await applyResumeHmrUpdate(container, payload) === "reload") return "reload";
|
|
3024
|
+
return "updated";
|
|
3025
|
+
};
|
|
3026
|
+
const flushDirtyComponents = async (container) => {
|
|
3027
|
+
const globalRecord = globalThis;
|
|
3028
|
+
const existing = globalRecord[DIRTY_FLUSH_PROMISE_KEY];
|
|
3029
|
+
if (existing instanceof Promise) {
|
|
3030
|
+
await existing;
|
|
3031
|
+
return;
|
|
3032
|
+
}
|
|
3033
|
+
const flushing = (async () => {
|
|
3034
|
+
while (container.dirty.size > 0) {
|
|
3035
|
+
const batch = sortDirtyComponents(container.dirty);
|
|
3036
|
+
container.dirty.clear();
|
|
3037
|
+
const rerendered = /* @__PURE__ */ new Set();
|
|
3038
|
+
for (const componentId of batch) {
|
|
3039
|
+
if ([...rerendered].some((parentId) => componentId === parentId || isDescendantOf(parentId, componentId))) continue;
|
|
3040
|
+
if (container.components.get(componentId)?.active) continue;
|
|
3041
|
+
await activateComponent(container, componentId);
|
|
3042
|
+
rerendered.add(componentId);
|
|
3043
|
+
}
|
|
3044
|
+
}
|
|
3045
|
+
})();
|
|
3046
|
+
globalRecord[DIRTY_FLUSH_PROMISE_KEY] = flushing;
|
|
3047
|
+
try {
|
|
3048
|
+
await flushing;
|
|
3049
|
+
} finally {
|
|
3050
|
+
delete globalRecord[DIRTY_FLUSH_PROMISE_KEY];
|
|
3051
|
+
}
|
|
3052
|
+
};
|
|
3053
|
+
const beginSSRContainer = (symbols, render) => {
|
|
3054
|
+
const container = createContainer(symbols);
|
|
3055
|
+
const rootFrame = createFrame(container, {
|
|
3056
|
+
active: false,
|
|
3057
|
+
didMount: false,
|
|
3058
|
+
id: ROOT_COMPONENT_ID,
|
|
3059
|
+
mountCleanupSlots: [],
|
|
3060
|
+
parentId: null,
|
|
3061
|
+
props: {},
|
|
3062
|
+
projectionSlots: null,
|
|
3063
|
+
scopeId: registerScope(container, []),
|
|
3064
|
+
signalIds: [],
|
|
3065
|
+
symbol: ROOT_COMPONENT_ID,
|
|
3066
|
+
suspensePromise: null,
|
|
3067
|
+
visibleCount: 0,
|
|
3068
|
+
watchCount: 0
|
|
3069
|
+
}, "ssr");
|
|
3070
|
+
return {
|
|
3071
|
+
container,
|
|
3072
|
+
result: pushContainer(container, () => pushFrame(rootFrame, render))
|
|
3073
|
+
};
|
|
3074
|
+
};
|
|
3075
|
+
const beginAsyncSSRContainer = async (symbols, render, prepare, options) => {
|
|
3076
|
+
const container = createContainer(symbols, void 0, options?.asyncSignalSnapshotCache);
|
|
3077
|
+
const rootFrame = createFrame(container, {
|
|
3078
|
+
active: false,
|
|
3079
|
+
didMount: false,
|
|
3080
|
+
id: ROOT_COMPONENT_ID,
|
|
3081
|
+
mountCleanupSlots: [],
|
|
3082
|
+
parentId: null,
|
|
3083
|
+
props: {},
|
|
3084
|
+
projectionSlots: null,
|
|
3085
|
+
scopeId: registerScope(container, []),
|
|
3086
|
+
signalIds: [],
|
|
3087
|
+
symbol: ROOT_COMPONENT_ID,
|
|
3088
|
+
suspensePromise: null,
|
|
3089
|
+
visibleCount: 0,
|
|
3090
|
+
watchCount: 0
|
|
3091
|
+
}, "ssr");
|
|
3092
|
+
await prepare?.(container);
|
|
3093
|
+
return {
|
|
3094
|
+
container,
|
|
3095
|
+
result: pushContainer(container, () => pushFrame(rootFrame, render))
|
|
3096
|
+
};
|
|
3097
|
+
};
|
|
3098
|
+
const createResumePayload = (container, componentIds) => {
|
|
3099
|
+
const keepComponents = componentIds ? new Set(componentIds) : null;
|
|
3100
|
+
const componentEntries = [...container.components.entries()].filter(([id]) => !keepComponents || keepComponents.has(id));
|
|
3101
|
+
const keepSignals = new Set(componentEntries.flatMap(([, component]) => component.signalIds));
|
|
3102
|
+
return {
|
|
3103
|
+
actions: Object.fromEntries([...container.actionStates.entries()].map(([id, action]) => [id, {
|
|
3104
|
+
error: serializeRuntimeValue(container, action.error),
|
|
3105
|
+
input: serializeRuntimeValue(container, action.input),
|
|
3106
|
+
result: serializeRuntimeValue(container, action.result)
|
|
3107
|
+
}])),
|
|
3108
|
+
components: Object.fromEntries(componentEntries.map(([id, component]) => [id, {
|
|
3109
|
+
props: serializeRuntimeValue(container, component.props),
|
|
3110
|
+
...component.projectionSlots ? { projectionSlots: { ...component.projectionSlots } } : {},
|
|
3111
|
+
scope: component.scopeId,
|
|
3112
|
+
signalIds: [...component.signalIds],
|
|
3113
|
+
symbol: component.symbol,
|
|
3114
|
+
visibleCount: component.visibleCount,
|
|
3115
|
+
watchCount: component.watchCount
|
|
3116
|
+
}])),
|
|
3117
|
+
loaders: Object.fromEntries([...container.loaderStates.entries()].map(([id, loader]) => [id, {
|
|
3118
|
+
data: serializeRuntimeValue(container, loader.data),
|
|
3119
|
+
error: serializeRuntimeValue(container, loader.error),
|
|
3120
|
+
loaded: loader.loaded
|
|
3121
|
+
}])),
|
|
3122
|
+
scopes: Object.fromEntries(container.scopes.entries()),
|
|
3123
|
+
signals: Object.fromEntries([...container.signals.entries()].filter(([id]) => !keepComponents || keepSignals.has(id)).map(([id, record]) => [id, serializeRuntimeValue(container, record.value)])),
|
|
3124
|
+
subscriptions: Object.fromEntries([...container.signals.entries()].filter(([id]) => !keepComponents || keepSignals.has(id)).map(([id, record]) => [id, [...record.subscribers].filter((componentId) => !keepComponents || keepComponents.has(componentId))])),
|
|
3125
|
+
symbols: Object.fromEntries(container.symbols.entries()),
|
|
3126
|
+
visibles: Object.fromEntries([...container.visibles.entries()].filter(([, visible]) => !keepComponents || keepComponents.has(visible.componentId)).map(([id, visible]) => [id, {
|
|
3127
|
+
componentId: visible.componentId,
|
|
3128
|
+
scope: visible.scopeId,
|
|
3129
|
+
symbol: visible.symbol
|
|
3130
|
+
}])),
|
|
3131
|
+
watches: Object.fromEntries([...container.watches.entries()].filter(([, watch]) => !keepComponents || keepComponents.has(watch.componentId)).map(([id, watch]) => [id, {
|
|
3132
|
+
componentId: watch.componentId,
|
|
3133
|
+
mode: watch.mode,
|
|
3134
|
+
scope: watch.scopeId,
|
|
3135
|
+
signals: [...watch.effect.signals].map((signal) => signal.id),
|
|
3136
|
+
symbol: watch.symbol
|
|
3137
|
+
}]))
|
|
3138
|
+
};
|
|
3139
|
+
};
|
|
3140
|
+
const toResumePayload = (container) => createResumePayload(container);
|
|
3141
|
+
const toResumePayloadSubset = (container, componentIds) => createResumePayload(container, componentIds);
|
|
3142
|
+
const bindComponentBoundaries = (container, root) => {
|
|
3143
|
+
if (!hasOwnerDocument(root)) return;
|
|
3144
|
+
for (const [id, boundary] of scanComponentBoundaries(root)) {
|
|
3145
|
+
const component = container.components.get(id);
|
|
3146
|
+
if (!component) continue;
|
|
3147
|
+
component.start = boundary.start;
|
|
3148
|
+
component.end = boundary.end;
|
|
3149
|
+
}
|
|
3150
|
+
};
|
|
3151
|
+
const mergeResumePayload = (container, payload) => {
|
|
3152
|
+
for (const [id, actionPayload] of Object.entries(payload.actions ?? {})) container.actionStates.set(id, {
|
|
3153
|
+
error: deserializeRuntimeValue(container, actionPayload.error),
|
|
3154
|
+
input: deserializeRuntimeValue(container, actionPayload.input),
|
|
3155
|
+
result: deserializeRuntimeValue(container, actionPayload.result)
|
|
3156
|
+
});
|
|
3157
|
+
for (const [id, encodedValue] of Object.entries(payload.signals)) {
|
|
3158
|
+
const decodedValue = deserializeRuntimeValue(container, encodedValue);
|
|
3159
|
+
const record = ensureSignalRecord(container, id, decodedValue);
|
|
3160
|
+
record.value = decodedValue;
|
|
3161
|
+
}
|
|
3162
|
+
for (const [id, loaderPayload] of Object.entries(payload.loaders ?? {})) container.loaderStates.set(id, {
|
|
3163
|
+
data: deserializeRuntimeValue(container, loaderPayload.data),
|
|
3164
|
+
error: deserializeRuntimeValue(container, loaderPayload.error),
|
|
3165
|
+
loaded: loaderPayload.loaded
|
|
3166
|
+
});
|
|
3167
|
+
for (const [id, slots] of Object.entries(payload.scopes)) container.scopes.set(id, slots);
|
|
3168
|
+
container.nextScopeId = findNextNumericId(container.scopes.keys(), "sc");
|
|
3169
|
+
for (const [id, componentPayload] of Object.entries(payload.components)) container.components.set(id, {
|
|
3170
|
+
active: false,
|
|
3171
|
+
didMount: false,
|
|
3172
|
+
id,
|
|
3173
|
+
mountCleanupSlots: [],
|
|
3174
|
+
parentId: id.includes(".") ? id.slice(0, id.lastIndexOf(".")) : ROOT_COMPONENT_ID,
|
|
3175
|
+
props: deserializeRuntimeValue(container, componentPayload.props),
|
|
3176
|
+
projectionSlots: componentPayload.projectionSlots ? { ...componentPayload.projectionSlots } : null,
|
|
3177
|
+
scopeId: componentPayload.scope,
|
|
3178
|
+
signalIds: [...componentPayload.signalIds],
|
|
3179
|
+
symbol: componentPayload.symbol,
|
|
3180
|
+
suspensePromise: null,
|
|
3181
|
+
visibleCount: componentPayload.visibleCount ?? 0,
|
|
3182
|
+
watchCount: componentPayload.watchCount
|
|
3183
|
+
});
|
|
3184
|
+
for (const [signalId, subscribers] of Object.entries(payload.subscriptions)) {
|
|
3185
|
+
const record = container.signals.get(signalId);
|
|
3186
|
+
if (!record) continue;
|
|
3187
|
+
record.subscribers = new Set(subscribers);
|
|
3188
|
+
}
|
|
3189
|
+
container.nextSignalId = findNextNumericId(container.signals.keys(), "s");
|
|
3190
|
+
for (const [id, watchPayload] of Object.entries(payload.watches)) {
|
|
3191
|
+
const watch = getOrCreateWatchState(container, id, watchPayload.componentId);
|
|
3192
|
+
watch.mode = watchPayload.mode;
|
|
3193
|
+
watch.scopeId = watchPayload.scope;
|
|
3194
|
+
watch.symbol = watchPayload.symbol;
|
|
3195
|
+
watch.track = null;
|
|
3196
|
+
watch.run = null;
|
|
3197
|
+
clearEffectSignals(watch.effect);
|
|
3198
|
+
for (const signalId of watchPayload.signals) {
|
|
3199
|
+
const record = container.signals.get(signalId);
|
|
3200
|
+
if (!record) continue;
|
|
3201
|
+
watch.effect.signals.add(record);
|
|
3202
|
+
record.effects.add(watch.effect);
|
|
3203
|
+
}
|
|
3204
|
+
}
|
|
3205
|
+
for (const [id, visiblePayload] of Object.entries(payload.visibles ?? {})) {
|
|
3206
|
+
const visible = getOrCreateVisibleState(container, id, visiblePayload.componentId);
|
|
3207
|
+
visible.done = false;
|
|
3208
|
+
visible.pending = null;
|
|
3209
|
+
visible.run = null;
|
|
3210
|
+
visible.scopeId = visiblePayload.scope;
|
|
3211
|
+
visible.symbol = visiblePayload.symbol;
|
|
3212
|
+
}
|
|
3213
|
+
};
|
|
3214
|
+
const pruneStreamedBoundaryDescendants = (container, boundaryId, payload) => {
|
|
3215
|
+
pruneRemovedComponents(container, boundaryId, new Set(Object.keys(payload.components).filter((candidate) => isDescendantOf(boundaryId, candidate))));
|
|
3216
|
+
};
|
|
3217
|
+
const applyStreamedSuspenseBoundary = (container, chunk) => {
|
|
3218
|
+
const doc = container.doc;
|
|
3219
|
+
if (!doc) return;
|
|
3220
|
+
const component = container.components.get(chunk.boundaryId);
|
|
3221
|
+
if (!component?.start || !component.end) return;
|
|
3222
|
+
const template = doc.getElementById(chunk.templateId);
|
|
3223
|
+
const payloadScript = doc.getElementById(chunk.payloadScriptId);
|
|
3224
|
+
if (!(template instanceof HTMLTemplateElement) || !payloadScript?.textContent) return;
|
|
3225
|
+
const payload = JSON.parse(payloadScript.textContent);
|
|
3226
|
+
const focusSnapshot = captureBoundaryFocus(doc, component.start, component.end);
|
|
3227
|
+
const nodes = [...template.content.cloneNode(true).childNodes];
|
|
3228
|
+
replaceBoundaryContents(component.start, component.end, nodes);
|
|
3229
|
+
pruneStreamedBoundaryDescendants(container, chunk.boundaryId, payload);
|
|
3230
|
+
mergeResumePayload(container, payload);
|
|
3231
|
+
component.suspensePromise = null;
|
|
3232
|
+
const parentRoot = component.start.parentNode;
|
|
3233
|
+
if (parentRoot) {
|
|
3234
|
+
bindComponentBoundaries(container, parentRoot);
|
|
3235
|
+
restoreSignalRefs(container, parentRoot);
|
|
3236
|
+
if ("querySelectorAll" in parentRoot) bindRouterLinks(container, parentRoot);
|
|
3237
|
+
}
|
|
3238
|
+
restoreBoundaryFocus(doc, component.start, component.end, focusSnapshot);
|
|
3239
|
+
scheduleVisibleCallbacksCheck(container);
|
|
3240
|
+
template.remove();
|
|
3241
|
+
payloadScript.remove();
|
|
3242
|
+
};
|
|
3243
|
+
const getStreamState = () => {
|
|
3244
|
+
const globalRecord = globalThis;
|
|
3245
|
+
const existing = globalRecord[STREAM_STATE_KEY];
|
|
3246
|
+
if (existing && typeof existing === "object" && "enqueue" in existing && "pending" in existing) return existing;
|
|
3247
|
+
const created = {
|
|
3248
|
+
enqueue(chunk) {
|
|
3249
|
+
created.pending.push(chunk);
|
|
3250
|
+
created.process?.();
|
|
3251
|
+
},
|
|
3252
|
+
pending: [],
|
|
3253
|
+
processing: null
|
|
3254
|
+
};
|
|
3255
|
+
globalRecord[STREAM_STATE_KEY] = created;
|
|
3256
|
+
return created;
|
|
3257
|
+
};
|
|
3258
|
+
const getStreamingResumeBootstrapScriptContent = () => "(()=>{const key=\"__eclipsa_stream\";const state=window[key]??={pending:[]};state.enqueue=state.enqueue??function(chunk){this.pending.push(chunk);if(this.process){void this.process();}};})();";
|
|
3259
|
+
const installStreamedSuspenseController = (container) => {
|
|
3260
|
+
const state = getStreamState();
|
|
3261
|
+
state.process = async () => {
|
|
3262
|
+
if (state.processing) {
|
|
3263
|
+
await state.processing;
|
|
3264
|
+
return;
|
|
3265
|
+
}
|
|
3266
|
+
state.processing = (async () => {
|
|
3267
|
+
while (state.pending.length > 0) {
|
|
3268
|
+
const chunk = state.pending.shift();
|
|
3269
|
+
if (!chunk) continue;
|
|
3270
|
+
applyStreamedSuspenseBoundary(container, chunk);
|
|
3271
|
+
}
|
|
3272
|
+
})();
|
|
3273
|
+
try {
|
|
3274
|
+
await state.processing;
|
|
3275
|
+
} finally {
|
|
3276
|
+
state.processing = null;
|
|
3277
|
+
}
|
|
3278
|
+
};
|
|
3279
|
+
if (state.pending.length > 0) state.process();
|
|
3280
|
+
};
|
|
3281
|
+
const createResumeContainer = (source, payload, options) => {
|
|
3282
|
+
const doc = source instanceof Document ? source : source.ownerDocument;
|
|
3283
|
+
const root = source instanceof Document ? doc.body : source;
|
|
3284
|
+
const container = createContainer(payload.symbols, doc);
|
|
3285
|
+
container.rootElement = root;
|
|
3286
|
+
ensureRouterState(container, options?.routeManifest);
|
|
3287
|
+
mergeResumePayload(container, payload);
|
|
3288
|
+
bindComponentBoundaries(container, root);
|
|
3289
|
+
restoreSignalRefs(container, root);
|
|
3290
|
+
restoreRegisteredRpcHandles(container);
|
|
3291
|
+
const router = ensureRouterState(container);
|
|
3292
|
+
router.currentPath.value = normalizeRoutePath(doc.location.pathname);
|
|
3293
|
+
router.isNavigating.value = false;
|
|
3294
|
+
scheduleVisibleCallbacksCheck(container);
|
|
3295
|
+
return container;
|
|
3296
|
+
};
|
|
3297
|
+
const restoreRegisteredRpcHandles = (container) => {
|
|
3298
|
+
withRuntimeContainer(container, () => {
|
|
3299
|
+
for (const id of getRegisteredActionHookIds()) getRegisteredActionHook(id)?.();
|
|
3300
|
+
for (const id of getRegisteredLoaderHookIds()) getRegisteredLoaderHook(id)?.();
|
|
3301
|
+
});
|
|
3302
|
+
};
|
|
3303
|
+
const primeRouteModules = async (container) => {
|
|
3304
|
+
const router = ensureRouterState(container);
|
|
3305
|
+
const currentRoute = await loadRouteComponent(container, router.currentPath.value);
|
|
3306
|
+
if (currentRoute) {
|
|
3307
|
+
router.currentRoute = currentRoute;
|
|
3308
|
+
if (container.doc) applyRouteMetadata(container.doc, currentRoute, new URL(container.doc.location.href), router.defaultTitle);
|
|
3309
|
+
}
|
|
3310
|
+
};
|
|
3311
|
+
const getRouterEventState = (event) => {
|
|
3312
|
+
const eventRecord = event;
|
|
3313
|
+
const existing = eventRecord[ROUTER_EVENT_STATE_KEY];
|
|
3314
|
+
if (existing) return existing;
|
|
3315
|
+
const originalPreventDefault = event.preventDefault.bind(event);
|
|
3316
|
+
const state = {
|
|
3317
|
+
originalPreventDefault,
|
|
3318
|
+
routerPrevented: false,
|
|
3319
|
+
userPrevented: false
|
|
3320
|
+
};
|
|
3321
|
+
eventRecord.preventDefault = () => {
|
|
3322
|
+
if (state.routerPrevented) state.userPrevented = true;
|
|
3323
|
+
originalPreventDefault();
|
|
3324
|
+
};
|
|
3325
|
+
eventRecord[ROUTER_EVENT_STATE_KEY] = state;
|
|
3326
|
+
return state;
|
|
3327
|
+
};
|
|
3328
|
+
const findInteractiveTarget = (target, eventName) => {
|
|
3329
|
+
let element = isElementNode(target) ? target : target instanceof Node ? target.parentElement : null;
|
|
3330
|
+
while (element) {
|
|
3331
|
+
if (element.hasAttribute(`data-e-on${eventName}`)) return element;
|
|
3332
|
+
element = element.parentElement;
|
|
3333
|
+
}
|
|
3334
|
+
return null;
|
|
3335
|
+
};
|
|
3336
|
+
const getPendingLinkNavigationForLink = (container, event, link) => {
|
|
3337
|
+
if (!(event instanceof MouseEvent)) return null;
|
|
3338
|
+
if (event.defaultPrevented) return null;
|
|
3339
|
+
if (event.button !== 0 || event.metaKey || event.ctrlKey || event.shiftKey || event.altKey) return null;
|
|
3340
|
+
if (link.hasAttribute("download")) return null;
|
|
3341
|
+
if (link.target && link.target !== "_self") return null;
|
|
3342
|
+
const href = link.getAttribute("href");
|
|
3343
|
+
if (!href || !container.doc) return null;
|
|
3344
|
+
const url = new URL(href, container.doc.location.href);
|
|
3345
|
+
const pathname = normalizeRoutePath(url.pathname);
|
|
3346
|
+
if (url.origin !== container.doc.location.origin) return null;
|
|
3347
|
+
if (!matchRouteManifest(ensureRouterState(container).manifest, pathname)?.entry.page) return null;
|
|
3348
|
+
const state = getRouterEventState(event);
|
|
3349
|
+
state.routerPrevented = true;
|
|
3350
|
+
state.originalPreventDefault();
|
|
3351
|
+
return {
|
|
3352
|
+
href: url.href,
|
|
3353
|
+
replace: link.getAttribute(ROUTE_REPLACE_ATTR) === "true",
|
|
3354
|
+
state
|
|
3355
|
+
};
|
|
3356
|
+
};
|
|
3357
|
+
const getLinkPrefetchMode = (link) => {
|
|
3358
|
+
const value = link.getAttribute(ROUTE_PREFETCH_ATTR);
|
|
3359
|
+
if (value === "hover" || value === "focus" || value === "intent" || value === "none") return value;
|
|
3360
|
+
return "intent";
|
|
3361
|
+
};
|
|
3362
|
+
const bindRouterLinkPrefetch = (container, link) => {
|
|
3363
|
+
const boundLink = link;
|
|
3364
|
+
if (boundLink[ROUTER_LINK_PREFETCH_BOUND_KEY]) return;
|
|
3365
|
+
boundLink[ROUTER_LINK_PREFETCH_BOUND_KEY] = true;
|
|
3366
|
+
const mode = getLinkPrefetchMode(link);
|
|
3367
|
+
if (mode === "none") return;
|
|
3368
|
+
const runPrefetch = () => {
|
|
3369
|
+
const href = link.getAttribute("href");
|
|
3370
|
+
if (!href) return;
|
|
3371
|
+
prefetchRoute(container, href);
|
|
3372
|
+
};
|
|
3373
|
+
if (mode === "hover" || mode === "intent") link.addEventListener("mouseenter", runPrefetch);
|
|
3374
|
+
if (mode === "focus" || mode === "intent") link.addEventListener("focus", runPrefetch);
|
|
3375
|
+
};
|
|
3376
|
+
const bindRouterLink = (container, link) => {
|
|
3377
|
+
const boundLink = link;
|
|
3378
|
+
if (boundLink[ROUTER_LINK_BOUND_KEY]) return;
|
|
3379
|
+
boundLink[ROUTER_LINK_BOUND_KEY] = true;
|
|
3380
|
+
link.addEventListener("click", (event) => {
|
|
3381
|
+
const pendingLink = getPendingLinkNavigationForLink(container, event, link);
|
|
3382
|
+
if (!pendingLink || pendingLink.state.userPrevented) return;
|
|
3383
|
+
navigateContainer(container, pendingLink.href, { mode: pendingLink.replace ? "replace" : "push" });
|
|
3384
|
+
});
|
|
3385
|
+
};
|
|
3386
|
+
const bindRouterLinks = (container, root) => {
|
|
3387
|
+
const links = root.querySelectorAll(`a[${ROUTE_LINK_ATTR}]`);
|
|
3388
|
+
for (const link of links) {
|
|
3389
|
+
if (!isHTMLAnchorElementNode(link)) continue;
|
|
3390
|
+
bindRouterLinkPrefetch(container, link);
|
|
3391
|
+
bindRouterLink(container, link);
|
|
3392
|
+
}
|
|
3393
|
+
};
|
|
3394
|
+
const parseBinding = (value) => {
|
|
3395
|
+
const separatorIndex = value.indexOf(":");
|
|
3396
|
+
if (separatorIndex < 0) throw new Error(`Invalid binding ${value}.`);
|
|
3397
|
+
return {
|
|
3398
|
+
symbolId: value.slice(0, separatorIndex),
|
|
3399
|
+
scopeId: value.slice(separatorIndex + 1)
|
|
3400
|
+
};
|
|
3401
|
+
};
|
|
3402
|
+
const withClientContainer = async (container, fn) => pushContainer(container, () => Promise.resolve(fn()));
|
|
3403
|
+
const createDelegatedEvent = (event, currentTarget) => Object.create(event, { currentTarget: { value: currentTarget } });
|
|
3404
|
+
const dispatchResumeEvent = async (container, event) => {
|
|
3405
|
+
const interactiveTarget = findInteractiveTarget(event.target, event.type);
|
|
3406
|
+
if (!interactiveTarget) return;
|
|
3407
|
+
const pendingFocus = capturePendingFocusRestore(container, event.target);
|
|
3408
|
+
const binding = interactiveTarget.getAttribute(`data-e-on${event.type}`);
|
|
3409
|
+
if (!binding) return;
|
|
3410
|
+
const { scopeId, symbolId } = parseBinding(binding);
|
|
3411
|
+
const module = await loadSymbol(container, symbolId);
|
|
3412
|
+
await withClientContainer(container, async () => {
|
|
3413
|
+
await module.default(materializeScope(container, scopeId), createDelegatedEvent(event, interactiveTarget));
|
|
3414
|
+
});
|
|
3415
|
+
await flushDirtyComponents(container);
|
|
3416
|
+
restorePendingFocus(container, pendingFocus);
|
|
3417
|
+
};
|
|
3418
|
+
const dispatchDocumentEvent = async (container, event) => {
|
|
3419
|
+
await dispatchResumeEvent(container, event);
|
|
3420
|
+
if (event.type === "submit" && !event.defaultPrevented && isHTMLFormElementNode(event.target)) {
|
|
3421
|
+
const actionId = event.target.getAttribute(ACTION_FORM_ATTR$1);
|
|
3422
|
+
if (actionId) {
|
|
3423
|
+
const handle = container.actions.get(actionId);
|
|
3424
|
+
if (handle) {
|
|
3425
|
+
event.preventDefault();
|
|
3426
|
+
const submitter = typeof SubmitEvent !== "undefined" && event instanceof SubmitEvent ? event.submitter ?? void 0 : void 0;
|
|
3427
|
+
const formData = isHTMLElementNode(submitter) ? new FormData(event.target, submitter) : new FormData(event.target);
|
|
3428
|
+
await handle.action(formData);
|
|
3429
|
+
await flushDirtyComponents(container);
|
|
3430
|
+
}
|
|
3431
|
+
}
|
|
3432
|
+
}
|
|
3433
|
+
};
|
|
3434
|
+
const installResumeListeners = (container) => {
|
|
3435
|
+
const doc = container.doc;
|
|
3436
|
+
if (!doc) return () => {};
|
|
3437
|
+
bindRouterLinks(container, doc);
|
|
3438
|
+
installStreamedSuspenseController(container);
|
|
3439
|
+
ensureVisibilityListeners(container);
|
|
3440
|
+
scheduleVisibleCallbacksCheck(container);
|
|
3441
|
+
const listeners = [
|
|
3442
|
+
"click",
|
|
3443
|
+
"input",
|
|
3444
|
+
"change",
|
|
3445
|
+
"submit"
|
|
3446
|
+
];
|
|
3447
|
+
const onEvent = (event) => {
|
|
3448
|
+
dispatchDocumentEvent(container, event);
|
|
3449
|
+
};
|
|
3450
|
+
const onPopState = () => {
|
|
3451
|
+
navigateContainer(container, doc.location.href, { mode: "pop" });
|
|
3452
|
+
};
|
|
3453
|
+
for (const eventName of listeners) doc.addEventListener(eventName, onEvent, true);
|
|
3454
|
+
doc.defaultView?.addEventListener("popstate", onPopState);
|
|
3455
|
+
return () => {
|
|
3456
|
+
for (const eventName of listeners) doc.removeEventListener(eventName, onEvent, true);
|
|
3457
|
+
doc.defaultView?.removeEventListener("popstate", onPopState);
|
|
3458
|
+
container.visibilityListenersCleanup?.();
|
|
3459
|
+
};
|
|
3460
|
+
};
|
|
3461
|
+
const renderString = (inputElementLike) => renderStringNode(inputElementLike);
|
|
3462
|
+
const createStandaloneRuntimeSignal = (fallback) => {
|
|
3463
|
+
const globalRecord = globalThis;
|
|
3464
|
+
const nextId = (globalRecord[STANDALONE_SIGNAL_ID_KEY] ?? 0) + 1;
|
|
3465
|
+
globalRecord[STANDALONE_SIGNAL_ID_KEY] = nextId;
|
|
3466
|
+
return ensureSignalRecord(null, `$standalone:${nextId}`, fallback).handle;
|
|
3467
|
+
};
|
|
3468
|
+
const useRuntimeSignal = (fallback) => {
|
|
3469
|
+
const container = getCurrentContainer();
|
|
3470
|
+
const frame = getCurrentFrame();
|
|
3471
|
+
if (!container || !frame || frame.component.id === ROOT_COMPONENT_ID) throw new Error("useSignal() can only be used while rendering a component.");
|
|
3472
|
+
const signalIndex = frame.signalCursor++;
|
|
3473
|
+
const existingId = frame.component.signalIds[signalIndex];
|
|
3474
|
+
const signalId = existingId ?? `s${container.nextSignalId++}`;
|
|
3475
|
+
if (!existingId) frame.component.signalIds.push(signalId);
|
|
3476
|
+
const record = ensureSignalRecord(container, signalId, fallback);
|
|
3477
|
+
recordSignalRead(record);
|
|
3478
|
+
return record.handle;
|
|
3479
|
+
};
|
|
3480
|
+
const createDetachedRuntimeSignal = (container, id, fallback) => ensureSignalRecord(container, id, fallback).handle;
|
|
3481
|
+
const getRuntimeSignalId = (value) => getSignalMeta(value)?.id ?? null;
|
|
3482
|
+
const useRuntimeNavigate = () => {
|
|
3483
|
+
const container = getCurrentContainer();
|
|
3484
|
+
if (!container) return createStandaloneNavigate();
|
|
3485
|
+
return ensureRouterState(container).navigate;
|
|
3486
|
+
};
|
|
3487
|
+
const useRuntimeRouteParams = () => {
|
|
3488
|
+
const frame = getCurrentFrame();
|
|
3489
|
+
if (frame?.component.props && typeof frame.component.props === "object") {
|
|
3490
|
+
const candidate = frame.component.props[ROUTE_PARAMS_PROP];
|
|
3491
|
+
if (candidate && typeof candidate === "object") return candidate;
|
|
3492
|
+
}
|
|
3493
|
+
return getCurrentContainer()?.router?.currentRoute?.params ?? EMPTY_ROUTE_PARAMS;
|
|
3494
|
+
};
|
|
3495
|
+
const notFound = () => {
|
|
3496
|
+
throw {
|
|
3497
|
+
__eclipsa_not_found__: true,
|
|
3498
|
+
[ROUTE_NOT_FOUND_KEY]: true
|
|
3499
|
+
};
|
|
3500
|
+
};
|
|
3501
|
+
const isRouteNotFoundError = (error) => !!error && typeof error === "object" && (error.__eclipsa_not_found__ === true || error[ROUTE_NOT_FOUND_KEY] === true);
|
|
3502
|
+
const createEffect = (fn) => {
|
|
3503
|
+
const effect = {
|
|
3504
|
+
fn() {
|
|
3505
|
+
collectTrackedDependencies(effect, fn);
|
|
3506
|
+
},
|
|
3507
|
+
signals: /* @__PURE__ */ new Set()
|
|
3508
|
+
};
|
|
3509
|
+
effect.fn();
|
|
3510
|
+
};
|
|
3511
|
+
const createOnCleanup = (fn) => {
|
|
3512
|
+
if (!currentCleanupSlot) throw new Error("onCleanup() can only be used while running onMount(), onVisible(), or useWatch() callbacks.");
|
|
3513
|
+
currentCleanupSlot.callbacks.push(fn);
|
|
3514
|
+
};
|
|
3515
|
+
const createOnMount = (fn) => {
|
|
3516
|
+
const frame = getCurrentFrame();
|
|
3517
|
+
if (!frame || frame.component.id === ROOT_COMPONENT_ID || frame.mode !== "client") return;
|
|
3518
|
+
frame.mountCallbacks.push(fn);
|
|
3519
|
+
};
|
|
3520
|
+
const createOnVisible = (fn) => {
|
|
3521
|
+
const container = getCurrentContainer();
|
|
3522
|
+
const frame = getCurrentFrame();
|
|
3523
|
+
const lazyMeta = getLazyMeta(fn);
|
|
3524
|
+
if (!container || !frame || frame.component.id === ROOT_COMPONENT_ID) return;
|
|
3525
|
+
if (!lazyMeta && frame.mode === "ssr") return;
|
|
3526
|
+
const visibleIndex = frame.visibleCursor++;
|
|
3527
|
+
const visible = getOrCreateVisibleState(container, createVisibleId(frame.component.id, visibleIndex), frame.component.id);
|
|
3528
|
+
visible.scopeId = lazyMeta ? registerScope(container, lazyMeta.captures()) : registerScope(container, []);
|
|
3529
|
+
visible.symbol = lazyMeta?.symbol ?? "";
|
|
3530
|
+
visible.run = lazyMeta ? null : fn;
|
|
3531
|
+
scheduleVisibleCallbacksCheck(container);
|
|
3532
|
+
};
|
|
3533
|
+
const createWatch = (fn, dependencies) => {
|
|
3534
|
+
const container = getCurrentContainer();
|
|
3535
|
+
const frame = getCurrentFrame();
|
|
3536
|
+
const watchMeta = getWatchMeta(fn);
|
|
3537
|
+
if (!container || !frame || frame.component.id === ROOT_COMPONENT_ID || !watchMeta) {
|
|
3538
|
+
const cleanupSlot = createCleanupSlot();
|
|
3539
|
+
const effect = {
|
|
3540
|
+
fn() {
|
|
3541
|
+
createLocalWatchRunner(effect, cleanupSlot, fn, dependencies)();
|
|
3542
|
+
},
|
|
3543
|
+
signals: /* @__PURE__ */ new Set()
|
|
3544
|
+
};
|
|
3545
|
+
effect.fn();
|
|
3546
|
+
return;
|
|
3547
|
+
}
|
|
3548
|
+
const watchIndex = frame.watchCursor++;
|
|
3549
|
+
const watch = getOrCreateWatchState(container, createWatchId(frame.component.id, watchIndex), frame.component.id);
|
|
3550
|
+
watch.mode = dependencies ? "explicit" : "dynamic";
|
|
3551
|
+
watch.scopeId = registerScope(container, watchMeta.captures());
|
|
3552
|
+
watch.symbol = watchMeta.symbol;
|
|
3553
|
+
watch.track = dependencies ? () => trackWatchDependencies(dependencies) : null;
|
|
3554
|
+
watch.run = createLocalWatchRunner(watch.effect, watch.cleanupSlot, fn, dependencies);
|
|
3555
|
+
watch.effect.fn();
|
|
3556
|
+
};
|
|
3557
|
+
const getResumePayloadScriptContent = (payload) => escapeJSONScriptText(JSON.stringify(payload));
|
|
3558
|
+
//#endregion
|
|
3559
|
+
//#region core/action.ts
|
|
3560
|
+
const ACTION_REGISTRY_KEY = Symbol.for("eclipsa.action-registry");
|
|
3561
|
+
const ACTION_CONTENT_TYPE = "application/eclipsa-action+json";
|
|
3562
|
+
const ACTION_STREAM_CONTENT_TYPE = "application/eclipsa-action-stream+json";
|
|
3563
|
+
const ACTION_FORM_ATTR = "data-e-action-form";
|
|
3564
|
+
const ACTION_FORM_FIELD = "__e_action";
|
|
3565
|
+
const ACTION_INPUT_CACHE_KEY = Symbol.for("eclipsa.action-input-cache");
|
|
3566
|
+
const getActionRegistry = () => {
|
|
3567
|
+
const globalRecord = globalThis;
|
|
3568
|
+
const existing = globalRecord[ACTION_REGISTRY_KEY];
|
|
3569
|
+
if (existing instanceof Map) return existing;
|
|
3570
|
+
const created = /* @__PURE__ */ new Map();
|
|
3571
|
+
globalRecord[ACTION_REGISTRY_KEY] = created;
|
|
3572
|
+
return created;
|
|
3573
|
+
};
|
|
3574
|
+
const isFormDataValue = (value) => typeof FormData !== "undefined" && value instanceof FormData;
|
|
3575
|
+
const formDataToInputObject = (value) => {
|
|
3576
|
+
const result = {};
|
|
3577
|
+
for (const [key, entry] of value.entries()) {
|
|
3578
|
+
const existing = result[key];
|
|
3579
|
+
if (existing === void 0) {
|
|
3580
|
+
result[key] = entry;
|
|
3581
|
+
continue;
|
|
3582
|
+
}
|
|
3583
|
+
result[key] = Array.isArray(existing) ? [...existing, entry] : [existing, entry];
|
|
3584
|
+
}
|
|
3585
|
+
return result;
|
|
3586
|
+
};
|
|
3587
|
+
const normalizeFormSubmissionInput = (value) => {
|
|
3588
|
+
if (!isFormDataValue(value)) return value;
|
|
3589
|
+
const normalized = formDataToInputObject(value);
|
|
3590
|
+
return Object.fromEntries(Object.entries(normalized).flatMap(([key, entry]) => {
|
|
3591
|
+
if (key === "__e_action") return [];
|
|
3592
|
+
if (Array.isArray(entry)) {
|
|
3593
|
+
const values = entry.filter((candidate) => typeof candidate === "string").map((candidate) => candidate);
|
|
3594
|
+
return values.length > 0 ? [[key, values.length === 1 ? values[0] : values]] : [];
|
|
3595
|
+
}
|
|
3596
|
+
return typeof entry === "string" ? [[key, entry]] : [];
|
|
3597
|
+
}));
|
|
3598
|
+
};
|
|
3599
|
+
const getActionInputCache = (c) => {
|
|
3600
|
+
const record = c;
|
|
3601
|
+
if (!record[ACTION_INPUT_CACHE_KEY]) record[ACTION_INPUT_CACHE_KEY] = (async () => {
|
|
3602
|
+
const contentType = c.req.header("content-type") ?? "";
|
|
3603
|
+
if (contentType.startsWith("application/eclipsa-action+json")) {
|
|
3604
|
+
let parsedBody;
|
|
3605
|
+
try {
|
|
3606
|
+
parsedBody = await c.req.json();
|
|
3607
|
+
} catch {
|
|
3608
|
+
throw new TypeError("Action input must be valid JSON.");
|
|
3609
|
+
}
|
|
3610
|
+
if (!parsedBody || typeof parsedBody !== "object" || !("input" in parsedBody)) throw new TypeError("Action request body must contain an input field.");
|
|
3611
|
+
return deserializeActionServerValue(parsedBody.input);
|
|
3612
|
+
}
|
|
3613
|
+
if (contentType.startsWith("application/x-www-form-urlencoded") || contentType.startsWith("multipart/form-data")) return c.req.formData();
|
|
3614
|
+
})();
|
|
3615
|
+
return record[ACTION_INPUT_CACHE_KEY];
|
|
3616
|
+
};
|
|
3617
|
+
const createActionRefScope = (container) => container ? serializeValue({ container: container.id }) : void 0;
|
|
3618
|
+
const readActionRefContainerId = (reference) => {
|
|
3619
|
+
if (!reference.data) return null;
|
|
3620
|
+
const decoded = deserializeValue(reference.data);
|
|
3621
|
+
if (!decoded || typeof decoded !== "object") return null;
|
|
3622
|
+
const containerId = decoded.container;
|
|
3623
|
+
return typeof containerId === "string" ? containerId : null;
|
|
3624
|
+
};
|
|
3625
|
+
const serializeActionClientValue = (container, value) => serializeValue(value, { serializeReference(candidate) {
|
|
3626
|
+
const signalId = getRuntimeSignalId(candidate);
|
|
3627
|
+
if (signalId) return {
|
|
3628
|
+
__eclipsa_type: "ref",
|
|
3629
|
+
data: createActionRefScope(container),
|
|
3630
|
+
kind: "signal",
|
|
3631
|
+
token: signalId
|
|
3632
|
+
};
|
|
3633
|
+
if (container && typeof Element !== "undefined" && candidate instanceof Element) return {
|
|
3634
|
+
__eclipsa_type: "ref",
|
|
3635
|
+
data: createActionRefScope(container),
|
|
3636
|
+
kind: "dom",
|
|
3637
|
+
token: ensureRuntimeElementId(container, candidate)
|
|
3638
|
+
};
|
|
3639
|
+
return null;
|
|
3640
|
+
} });
|
|
3641
|
+
const deserializeActionServerValue = (value) => deserializeValue(value, { deserializeReference(reference) {
|
|
3642
|
+
const containerId = readActionRefContainerId(reference);
|
|
3643
|
+
if (reference.kind === "signal") return {
|
|
3644
|
+
containerId,
|
|
3645
|
+
kind: "signal-ref",
|
|
3646
|
+
token: reference.token
|
|
3647
|
+
};
|
|
3648
|
+
if (reference.kind === "dom") return {
|
|
3649
|
+
containerId,
|
|
3650
|
+
kind: "dom-ref",
|
|
3651
|
+
token: reference.token
|
|
3652
|
+
};
|
|
3653
|
+
throw new TypeError(`Unsupported action input reference kind "${reference.kind}".`);
|
|
3654
|
+
} });
|
|
3655
|
+
const deserializeActionClientValue = (container, value) => deserializeValue(value, { deserializeReference(reference) {
|
|
3656
|
+
if (!container) throw new TypeError("Action references require an active runtime container.");
|
|
3657
|
+
if (readActionRefContainerId(reference) !== container.id) throw new TypeError("Action reference does not belong to the active runtime container.");
|
|
3658
|
+
if (reference.kind === "signal") {
|
|
3659
|
+
const signal = container.signals.get(reference.token);
|
|
3660
|
+
if (!signal) throw new Error(`Missing action signal ${reference.token}.`);
|
|
3661
|
+
return signal.handle;
|
|
3662
|
+
}
|
|
3663
|
+
if (reference.kind === "dom") {
|
|
3664
|
+
const element = findRuntimeElement(container, reference.token);
|
|
3665
|
+
if (!element) throw new Error(`Missing action DOM ref ${reference.token}.`);
|
|
3666
|
+
return element;
|
|
3667
|
+
}
|
|
3668
|
+
throw new TypeError(`Unsupported action output reference kind "${reference.kind}".`);
|
|
3669
|
+
} });
|
|
3670
|
+
const isOpaqueSignalRef = (value) => !!value && typeof value === "object" && value.kind === "signal-ref" && typeof value.token === "string";
|
|
3671
|
+
const isOpaqueDomRef = (value) => !!value && typeof value === "object" && value.kind === "dom-ref" && typeof value.token === "string";
|
|
3672
|
+
const serializeActionServerValue = (value) => serializeValue(value, { serializeReference(candidate) {
|
|
3673
|
+
if (isOpaqueSignalRef(candidate)) return {
|
|
3674
|
+
__eclipsa_type: "ref",
|
|
3675
|
+
data: candidate.containerId === null ? void 0 : serializeValue({ container: candidate.containerId }),
|
|
3676
|
+
kind: "signal",
|
|
3677
|
+
token: candidate.token
|
|
3678
|
+
};
|
|
3679
|
+
if (isOpaqueDomRef(candidate)) return {
|
|
3680
|
+
__eclipsa_type: "ref",
|
|
3681
|
+
data: candidate.containerId === null ? void 0 : serializeValue({ container: candidate.containerId }),
|
|
3682
|
+
kind: "dom",
|
|
3683
|
+
token: candidate.token
|
|
3684
|
+
};
|
|
3685
|
+
return null;
|
|
3686
|
+
} });
|
|
3687
|
+
const isReadableStreamValue = (value) => typeof ReadableStream !== "undefined" && value instanceof ReadableStream;
|
|
3688
|
+
const isAsyncGeneratorValue = (value) => !!value && typeof value === "object" && Symbol.asyncIterator in value;
|
|
3689
|
+
const toSerializedActionError = (error) => {
|
|
3690
|
+
if (error instanceof Error) return serializeActionServerValue({
|
|
3691
|
+
message: error.message,
|
|
3692
|
+
name: error.name
|
|
3693
|
+
});
|
|
3694
|
+
try {
|
|
3695
|
+
return serializeActionServerValue(error);
|
|
3696
|
+
} catch {
|
|
3697
|
+
return serializeActionServerValue({ message: "Action failed." });
|
|
3698
|
+
}
|
|
3699
|
+
};
|
|
3700
|
+
const toAsyncIterable = async function* (value) {
|
|
3701
|
+
if (isReadableStreamValue(value)) {
|
|
3702
|
+
const reader = value.getReader();
|
|
3703
|
+
try {
|
|
3704
|
+
while (true) {
|
|
3705
|
+
const next = await reader.read();
|
|
3706
|
+
if (next.done) return;
|
|
3707
|
+
yield next.value;
|
|
3708
|
+
}
|
|
3709
|
+
} finally {
|
|
3710
|
+
reader.releaseLock();
|
|
3711
|
+
}
|
|
3712
|
+
}
|
|
3713
|
+
yield* value;
|
|
3714
|
+
};
|
|
3715
|
+
const encodeStreamFrame = (frame) => `${JSON.stringify(frame)}
|
|
3716
|
+
`;
|
|
3717
|
+
const streamActionValue = (value, serializeChunk) => {
|
|
3718
|
+
const encoder = new TextEncoder();
|
|
3719
|
+
return new ReadableStream({ async start(controller) {
|
|
3720
|
+
try {
|
|
3721
|
+
for await (const chunk of toAsyncIterable(value)) controller.enqueue(encoder.encode(encodeStreamFrame({
|
|
3722
|
+
type: "chunk",
|
|
3723
|
+
value: serializeChunk(chunk)
|
|
3724
|
+
})));
|
|
3725
|
+
controller.enqueue(encoder.encode(encodeStreamFrame({ type: "done" })));
|
|
3726
|
+
} catch (error) {
|
|
3727
|
+
controller.enqueue(encoder.encode(encodeStreamFrame({
|
|
3728
|
+
error: toSerializedActionError(error),
|
|
3729
|
+
type: "error"
|
|
3730
|
+
})));
|
|
3731
|
+
} finally {
|
|
3732
|
+
controller.close();
|
|
3733
|
+
}
|
|
3734
|
+
} });
|
|
3735
|
+
};
|
|
3736
|
+
const parseJsonActionResponse = async (response, container) => {
|
|
3737
|
+
const body = await response.json();
|
|
3738
|
+
if (!body || typeof body !== "object" || typeof body.ok !== "boolean") throw new TypeError("Malformed action response.");
|
|
3739
|
+
if (!body.ok) throw deserializeActionClientValue(container, body.error);
|
|
3740
|
+
return deserializeActionClientValue(container, body.value);
|
|
3741
|
+
};
|
|
3742
|
+
const iterateStreamFrames = async function* (body) {
|
|
3743
|
+
const reader = body.getReader();
|
|
3744
|
+
const decoder = new TextDecoder();
|
|
3745
|
+
let buffer = "";
|
|
3746
|
+
try {
|
|
3747
|
+
while (true) {
|
|
3748
|
+
const next = await reader.read();
|
|
3749
|
+
if (next.done) break;
|
|
3750
|
+
buffer += decoder.decode(next.value, { stream: true });
|
|
3751
|
+
while (true) {
|
|
3752
|
+
const newlineIndex = buffer.indexOf("\n");
|
|
3753
|
+
if (newlineIndex < 0) break;
|
|
3754
|
+
const line = buffer.slice(0, newlineIndex).trim();
|
|
3755
|
+
buffer = buffer.slice(newlineIndex + 1);
|
|
3756
|
+
if (!line) continue;
|
|
3757
|
+
yield JSON.parse(line);
|
|
3758
|
+
}
|
|
3759
|
+
}
|
|
3760
|
+
const trailing = buffer.trim();
|
|
3761
|
+
if (trailing) yield JSON.parse(trailing);
|
|
3762
|
+
} finally {
|
|
3763
|
+
reader.releaseLock();
|
|
3764
|
+
}
|
|
3765
|
+
};
|
|
3766
|
+
const toClientStream = (response, container) => {
|
|
3767
|
+
if (!response.body) throw new TypeError("Missing action stream body.");
|
|
3768
|
+
return new ReadableStream({ async start(controller) {
|
|
3769
|
+
try {
|
|
3770
|
+
for await (const frame of iterateStreamFrames(response.body)) {
|
|
3771
|
+
if (frame.type === "chunk") {
|
|
3772
|
+
controller.enqueue(deserializeActionClientValue(container, frame.value));
|
|
3773
|
+
continue;
|
|
3774
|
+
}
|
|
3775
|
+
if (frame.type === "done") {
|
|
3776
|
+
controller.close();
|
|
3777
|
+
return;
|
|
3778
|
+
}
|
|
3779
|
+
controller.error(deserializeActionClientValue(container, frame.error));
|
|
3780
|
+
return;
|
|
3781
|
+
}
|
|
3782
|
+
controller.close();
|
|
3783
|
+
} catch (error) {
|
|
3784
|
+
controller.error(error);
|
|
3785
|
+
}
|
|
3786
|
+
} });
|
|
3787
|
+
};
|
|
3788
|
+
const toClientAsyncGenerator = async function* (response, container) {
|
|
3789
|
+
if (!response.body) throw new TypeError("Missing action stream body.");
|
|
3790
|
+
for await (const frame of iterateStreamFrames(response.body)) {
|
|
3791
|
+
if (frame.type === "chunk") {
|
|
3792
|
+
yield deserializeActionClientValue(container, frame.value);
|
|
3793
|
+
continue;
|
|
3794
|
+
}
|
|
3795
|
+
if (frame.type === "done") return;
|
|
3796
|
+
throw deserializeActionClientValue(container, frame.error);
|
|
3797
|
+
}
|
|
3798
|
+
};
|
|
3799
|
+
const invokeAction = async (id, input, container) => {
|
|
3800
|
+
const isFormSubmission = isFormDataValue(input);
|
|
3801
|
+
const response = await fetch(`/__eclipsa/action/${encodeURIComponent(id)}`, {
|
|
3802
|
+
body: isFormSubmission ? input : JSON.stringify({ input: serializeActionClientValue(container, input) }),
|
|
3803
|
+
headers: {
|
|
3804
|
+
accept: `${ACTION_STREAM_CONTENT_TYPE}, ${ACTION_CONTENT_TYPE}`,
|
|
3805
|
+
...isFormSubmission ? {} : { "content-type": ACTION_CONTENT_TYPE }
|
|
3806
|
+
},
|
|
3807
|
+
method: "POST"
|
|
3808
|
+
});
|
|
3809
|
+
const contentType = response.headers.get("content-type") ?? "";
|
|
3810
|
+
const streamKind = response.headers.get("x-eclipsa-stream-kind");
|
|
3811
|
+
if (contentType.startsWith(ACTION_STREAM_CONTENT_TYPE)) {
|
|
3812
|
+
if (streamKind === "async-generator") return toClientAsyncGenerator(response, container);
|
|
3813
|
+
return toClientStream(response, container);
|
|
3814
|
+
}
|
|
3815
|
+
return parseJsonActionResponse(response, container);
|
|
3816
|
+
};
|
|
3817
|
+
const composeMiddlewares = async (c, middlewares, handler) => {
|
|
3818
|
+
let index = -1;
|
|
3819
|
+
const dispatch = async (nextIndex) => {
|
|
3820
|
+
if (nextIndex <= index) throw new Error("Action middleware called next() multiple times.");
|
|
3821
|
+
index = nextIndex;
|
|
3822
|
+
const middleware = middlewares[nextIndex];
|
|
3823
|
+
if (!middleware) return handler(c);
|
|
3824
|
+
let nextResult = void 0;
|
|
3825
|
+
const result = await middleware(c, (async () => {
|
|
3826
|
+
nextResult = await dispatch(nextIndex + 1);
|
|
3827
|
+
}));
|
|
3828
|
+
if (result !== void 0) return result;
|
|
3829
|
+
return nextResult;
|
|
3830
|
+
};
|
|
3831
|
+
return dispatch(0);
|
|
3832
|
+
};
|
|
3833
|
+
const toActionResponse = (value) => {
|
|
3834
|
+
if (value instanceof Response) return value;
|
|
3835
|
+
if (isReadableStreamValue(value)) return new Response(streamActionValue(value, (entry) => serializeActionServerValue(entry)), { headers: {
|
|
3836
|
+
"content-type": ACTION_STREAM_CONTENT_TYPE,
|
|
3837
|
+
"x-eclipsa-stream-kind": "readable-stream"
|
|
3838
|
+
} });
|
|
3839
|
+
if (isAsyncGeneratorValue(value)) return new Response(streamActionValue(value, (entry) => serializeActionServerValue(entry)), { headers: {
|
|
3840
|
+
"content-type": ACTION_STREAM_CONTENT_TYPE,
|
|
3841
|
+
"x-eclipsa-stream-kind": "async-generator"
|
|
3842
|
+
} });
|
|
3843
|
+
return new Response(JSON.stringify({
|
|
3844
|
+
ok: true,
|
|
3845
|
+
value: serializeActionServerValue(value)
|
|
3846
|
+
}), { headers: { "content-type": ACTION_CONTENT_TYPE } });
|
|
3847
|
+
};
|
|
3848
|
+
const createHandleSignal = (container, id, key, initialValue) => {
|
|
3849
|
+
if (!container) throw new Error("Action handles require an active runtime container.");
|
|
3850
|
+
return createDetachedRuntimeSignal(container, `$action:${id}:${key}`, initialValue);
|
|
3851
|
+
};
|
|
3852
|
+
const readActionSubmissionInput = async (c) => {
|
|
3853
|
+
const cached = await getActionInputCache(c);
|
|
3854
|
+
const actionVars = c.var;
|
|
3855
|
+
return normalizeFormSubmissionInput(actionVars.__e_action_raw_input ?? cached);
|
|
3856
|
+
};
|
|
3857
|
+
const getNormalizedActionInput = (c) => readActionSubmissionInput(c);
|
|
3858
|
+
const getActionFormSubmissionId = async (c) => {
|
|
3859
|
+
const input = await getActionInputCache(c);
|
|
3860
|
+
if (!isFormDataValue(input)) return null;
|
|
3861
|
+
const actionId = input.get(ACTION_FORM_FIELD);
|
|
3862
|
+
return typeof actionId === "string" && actionId ? actionId : null;
|
|
3863
|
+
};
|
|
3864
|
+
const executeActionSubmission = async (id, c) => {
|
|
3865
|
+
const action = getActionRegistry().get(id);
|
|
3866
|
+
if (!action) throw new Error(`Unknown action ${id}.`);
|
|
3867
|
+
const input = await readActionSubmissionInput(c);
|
|
3868
|
+
const result = await composeMiddlewares(c, action.middlewares, action.handler);
|
|
3869
|
+
if (result instanceof Response) return {
|
|
3870
|
+
input,
|
|
3871
|
+
kind: "response",
|
|
3872
|
+
response: result
|
|
3873
|
+
};
|
|
3874
|
+
return {
|
|
3875
|
+
input,
|
|
3876
|
+
kind: "value",
|
|
3877
|
+
value: result
|
|
3878
|
+
};
|
|
3879
|
+
};
|
|
3880
|
+
const primeActionState = (container, id, snapshot) => {
|
|
3881
|
+
container.actionStates.set(id, {
|
|
3882
|
+
error: snapshot.error,
|
|
3883
|
+
input: snapshot.input,
|
|
3884
|
+
result: snapshot.result
|
|
3885
|
+
});
|
|
3886
|
+
};
|
|
3887
|
+
const appendClientChildren = (parent, value) => {
|
|
3888
|
+
let resolved = value;
|
|
3889
|
+
while (typeof resolved === "function") resolved = resolved();
|
|
3890
|
+
if (Array.isArray(resolved)) {
|
|
3891
|
+
for (const entry of resolved) appendClientChildren(parent, entry);
|
|
3892
|
+
return;
|
|
3893
|
+
}
|
|
3894
|
+
if (resolved === null || resolved === void 0 || resolved === false) return;
|
|
3895
|
+
if (resolved instanceof Node) {
|
|
3896
|
+
parent.appendChild(resolved);
|
|
3897
|
+
return;
|
|
3898
|
+
}
|
|
3899
|
+
parent.appendChild(document.createTextNode(String(resolved)));
|
|
3900
|
+
};
|
|
3901
|
+
const createActionFormNode = (id, props) => {
|
|
3902
|
+
const form = document.createElement("form");
|
|
3903
|
+
form.setAttribute(ACTION_FORM_ATTR, id);
|
|
3904
|
+
form.setAttribute("method", "post");
|
|
3905
|
+
const hiddenInput = document.createElement("input");
|
|
3906
|
+
hiddenInput.name = ACTION_FORM_FIELD;
|
|
3907
|
+
hiddenInput.type = "hidden";
|
|
3908
|
+
hiddenInput.value = id;
|
|
3909
|
+
form.appendChild(hiddenInput);
|
|
3910
|
+
for (const [name, value] of Object.entries(props)) {
|
|
3911
|
+
if (name === "children" || name === "action" || name === "method" || value === false || value === void 0 || value === null) continue;
|
|
3912
|
+
if (name === "class") {
|
|
3913
|
+
form.className = String(value);
|
|
3914
|
+
continue;
|
|
3915
|
+
}
|
|
3916
|
+
if (name === "style" && value && typeof value === "object") {
|
|
3917
|
+
form.setAttribute("style", Object.entries(value).map(([styleName, styleValue]) => `${styleName}: ${styleValue}`).join("; "));
|
|
3918
|
+
continue;
|
|
3919
|
+
}
|
|
3920
|
+
if (value === true) {
|
|
3921
|
+
form.setAttribute(name, "");
|
|
3922
|
+
continue;
|
|
3923
|
+
}
|
|
3924
|
+
form.setAttribute(name, String(value));
|
|
3925
|
+
}
|
|
3926
|
+
appendClientChildren(form, props.children);
|
|
3927
|
+
return form;
|
|
3928
|
+
};
|
|
3929
|
+
const action$ = (() => {
|
|
3930
|
+
throw new Error("action$() must be compiled by the Eclipsa analyzer before it can run.");
|
|
3931
|
+
});
|
|
3932
|
+
const validator = (schema) => {
|
|
3933
|
+
const middleware = async (c, next) => {
|
|
3934
|
+
let parsedBody;
|
|
3935
|
+
try {
|
|
3936
|
+
parsedBody = await getActionInputCache(c);
|
|
3937
|
+
} catch (error) {
|
|
3938
|
+
return c.json({
|
|
3939
|
+
error: serializeValue({ issues: [{ message: error instanceof Error && error.message ? error.message : "Action input could not be parsed." }] }),
|
|
3940
|
+
ok: false
|
|
3941
|
+
}, 400);
|
|
3942
|
+
}
|
|
3943
|
+
const rawInput = normalizeFormSubmissionInput(parsedBody);
|
|
3944
|
+
const validated = await schema["~standard"].validate(rawInput);
|
|
3945
|
+
if ("issues" in validated) return c.json({
|
|
3946
|
+
error: serializeValue({ issues: validated.issues }),
|
|
3947
|
+
ok: false
|
|
3948
|
+
}, 400);
|
|
3949
|
+
c.set("__e_action_raw_input", rawInput);
|
|
3950
|
+
c.set("input", validated.value);
|
|
3951
|
+
await next();
|
|
3952
|
+
};
|
|
3953
|
+
return middleware;
|
|
3954
|
+
};
|
|
3955
|
+
function registerAction(id, middlewares, handler) {
|
|
3956
|
+
getActionRegistry().set(id, {
|
|
3957
|
+
handler,
|
|
3958
|
+
id,
|
|
3959
|
+
middlewares: [...middlewares]
|
|
3960
|
+
});
|
|
3961
|
+
}
|
|
3962
|
+
const hasAction = (id) => getActionRegistry().has(id);
|
|
3963
|
+
const executeAction = async (id, c) => {
|
|
3964
|
+
try {
|
|
3965
|
+
const result = await executeActionSubmission(id, c);
|
|
3966
|
+
return result.kind === "response" ? result.response : toActionResponse(result.value);
|
|
3967
|
+
} catch (error) {
|
|
3968
|
+
return new Response(JSON.stringify({
|
|
3969
|
+
error: toSerializedActionError(error),
|
|
3970
|
+
ok: false
|
|
3971
|
+
}), {
|
|
3972
|
+
headers: { "content-type": ACTION_CONTENT_TYPE },
|
|
3973
|
+
status: 500
|
|
3974
|
+
});
|
|
3975
|
+
}
|
|
3976
|
+
};
|
|
3977
|
+
const __eclipsaAction = (id, middlewares, handler) => {
|
|
3978
|
+
if (typeof window === "undefined") getActionRegistry().set(id, {
|
|
3979
|
+
handler,
|
|
3980
|
+
id,
|
|
3981
|
+
middlewares: [...middlewares]
|
|
3982
|
+
});
|
|
3983
|
+
return registerActionHook(id, setActionHookMeta(() => {
|
|
3984
|
+
const container = getRuntimeContainer();
|
|
3985
|
+
const existing = container?.actions.get(id);
|
|
3986
|
+
if (existing) return existing;
|
|
3987
|
+
const initialState = container?.actionStates.get(id);
|
|
3988
|
+
const pending = createHandleSignal(container, id, "pending", false);
|
|
3989
|
+
const result = createHandleSignal(container, id, "result", initialState?.result);
|
|
3990
|
+
const error = createHandleSignal(container, id, "error", initialState?.error);
|
|
3991
|
+
const lastSubmission = createHandleSignal(container, id, "last-submission", initialState ? {
|
|
3992
|
+
error: initialState.error,
|
|
3993
|
+
input: initialState.input,
|
|
3994
|
+
result: initialState.result
|
|
3995
|
+
} : void 0);
|
|
3996
|
+
const syncSnapshot = () => {
|
|
3997
|
+
if (!container) return;
|
|
3998
|
+
const submission = lastSubmission.value;
|
|
3999
|
+
if (!submission) {
|
|
4000
|
+
container.actionStates.delete(id);
|
|
4001
|
+
return;
|
|
4002
|
+
}
|
|
4003
|
+
container.actionStates.set(id, {
|
|
4004
|
+
error: submission.error,
|
|
4005
|
+
input: submission.input,
|
|
4006
|
+
result: submission.result
|
|
4007
|
+
});
|
|
4008
|
+
};
|
|
4009
|
+
const invoke = async (input) => {
|
|
4010
|
+
pending.value = true;
|
|
4011
|
+
error.value = void 0;
|
|
4012
|
+
const normalizedInput = normalizeFormSubmissionInput(input);
|
|
4013
|
+
try {
|
|
4014
|
+
const value = await invokeAction(id, input, container);
|
|
4015
|
+
result.value = value;
|
|
4016
|
+
lastSubmission.value = {
|
|
4017
|
+
error: void 0,
|
|
4018
|
+
input: normalizedInput,
|
|
4019
|
+
result: value
|
|
4020
|
+
};
|
|
4021
|
+
syncSnapshot();
|
|
4022
|
+
return value;
|
|
4023
|
+
} catch (caught) {
|
|
4024
|
+
error.value = caught;
|
|
4025
|
+
lastSubmission.value = {
|
|
4026
|
+
error: caught,
|
|
4027
|
+
input: normalizedInput,
|
|
4028
|
+
result: void 0
|
|
4029
|
+
};
|
|
4030
|
+
syncSnapshot();
|
|
4031
|
+
throw caught;
|
|
4032
|
+
} finally {
|
|
4033
|
+
pending.value = false;
|
|
4034
|
+
}
|
|
4035
|
+
};
|
|
4036
|
+
const actionHandle = setActionHandleMeta({
|
|
4037
|
+
Form: component$((props) => {
|
|
4038
|
+
if (typeof document !== "undefined") return createActionFormNode(id, props);
|
|
4039
|
+
const nextProps = {
|
|
4040
|
+
...props,
|
|
4041
|
+
[ACTION_FORM_ATTR]: id,
|
|
4042
|
+
method: "post",
|
|
4043
|
+
children: [{
|
|
4044
|
+
isStatic: true,
|
|
4045
|
+
props: {
|
|
4046
|
+
name: ACTION_FORM_FIELD,
|
|
4047
|
+
type: "hidden",
|
|
4048
|
+
value: id
|
|
4049
|
+
},
|
|
4050
|
+
type: "input"
|
|
4051
|
+
}, props.children]
|
|
4052
|
+
};
|
|
4053
|
+
delete nextProps.action;
|
|
4054
|
+
delete nextProps.method;
|
|
4055
|
+
return {
|
|
4056
|
+
isStatic: true,
|
|
4057
|
+
props: nextProps,
|
|
4058
|
+
type: "form"
|
|
4059
|
+
};
|
|
4060
|
+
}),
|
|
4061
|
+
action: invoke,
|
|
4062
|
+
get error() {
|
|
4063
|
+
return error.value;
|
|
4064
|
+
},
|
|
4065
|
+
get formActionId() {
|
|
4066
|
+
return id;
|
|
4067
|
+
},
|
|
4068
|
+
get isPending() {
|
|
4069
|
+
return pending.value;
|
|
4070
|
+
},
|
|
4071
|
+
get lastSubmission() {
|
|
4072
|
+
return lastSubmission.value;
|
|
4073
|
+
},
|
|
4074
|
+
get result() {
|
|
4075
|
+
return result.value;
|
|
4076
|
+
}
|
|
4077
|
+
}, id);
|
|
4078
|
+
container?.actions.set(id, actionHandle);
|
|
4079
|
+
syncSnapshot();
|
|
4080
|
+
return actionHandle;
|
|
4081
|
+
}, id));
|
|
4082
|
+
};
|
|
4083
|
+
//#endregion
|
|
4084
|
+
export { getActionHandleMeta as $, getRuntimeContainer as A, registerLoader as At, renderString as B, serializeValue as Bt, createOnCleanup as C, executeLoader as Ct, createStandaloneRuntimeSignal as D, loader_exports as Dt, createResumeContainer as E, loader$ as Et, primeRouteModules as F, isSuspenseType as Ft, useRuntimeRouteParams as G, toResumePayload as H, component$ as Ht, readAsyncSignalSnapshot as I, deserializeValue as It, writeAsyncSignalSnapshot as J, useRuntimeSignal as K, registerResumeContainer as L, escapeJSONScriptText as Lt, getStreamingResumeBootstrapScriptContent as M, Suspense as Mt, installResumeListeners as N, createPendingSignalError as Nt, createWatch as O, markPendingSsrLoader as Ot, notFound as P, isPendingSignalError as Pt, __eclipsaWatch as Q, renderClientComponent as R, parseSerializedJSON as Rt, createEffect as S, consumePendingSsrLoaderIds as St, createOnVisible as T, isPendingSsrLoaderError as Tt, toResumePayloadSubset as U, restoreRegisteredRpcHandles as V, $ as Vt, useRuntimeNavigate as W, __eclipsaEvent as X, __eclipsaComponent as Y, __eclipsaLazy as Z, assignRuntimeRef as _, setLoaderHandleMeta as _t, action$ as a, getLoaderHookMeta as at, bindRuntimeEvent as b, setSignalMeta as bt, getActionFormSubmissionId as c, getRegisteredActionHookIds as ct, primeActionState as d, getSignalMeta as dt, getActionHookMeta as et, registerAction as f, getWatchMeta as ft, applyResumeHmrUpdateToRegisteredContainers as g, setActionHookMeta as gt, RESUME_STATE_ELEMENT_ID as h, setActionHandleMeta as ht, __eclipsaAction as i, getLoaderHandleMeta as it, getRuntimeSignalId as j, resolvePendingLoaders as jt, getResumePayloadScriptContent as k, primeLoaderState as kt, getNormalizedActionInput as l, getRegisteredLoaderHook as lt, RESUME_FINAL_STATE_ELEMENT_ID as m, registerLoaderHook as mt, ACTION_FORM_ATTR as n, getEventMeta as nt, executeAction as o, getNavigateMeta as ot, validator as p, registerActionHook as pt, withRuntimeContainer as q, ACTION_FORM_FIELD as r, getLazyMeta as rt, executeActionSubmission as s, getRegisteredActionHook as st, ACTION_CONTENT_TYPE as t, getComponentMeta as tt, hasAction as u, getRegisteredLoaderHookIds as ut, beginAsyncSSRContainer as v, setLoaderHookMeta as vt, createOnMount as w, hasLoader as wt, collectPendingSuspenseBoundaryIds as x, __eclipsaLoader as xt, beginSSRContainer as y, setNavigateMeta as yt, renderClientInsertable as z, serializeJSONScriptContent as zt };
|
|
4085
|
+
|
|
4086
|
+
//# sourceMappingURL=action-LXy_7T1f.mjs.map
|