@fictjs/runtime 0.9.0 → 0.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/advanced.cjs +9 -9
- package/dist/advanced.d.cts +4 -4
- package/dist/advanced.d.ts +4 -4
- package/dist/advanced.js +4 -4
- package/dist/{binding-BWchH3Kp.d.cts → binding-DcnhUSQK.d.ts} +5 -3
- package/dist/{binding-BWchH3Kp.d.ts → binding-FRyTeLDn.d.cts} +5 -3
- package/dist/{chunk-FVX77557.js → chunk-2UR2UWE2.js} +3 -3
- package/dist/{chunk-LBE6DC3V.cjs → chunk-44EQF3AR.cjs} +63 -52
- package/dist/chunk-44EQF3AR.cjs.map +1 -0
- package/dist/{chunk-OAM7HABA.cjs → chunk-4QGEN5SJ.cjs} +340 -263
- package/dist/chunk-4QGEN5SJ.cjs.map +1 -0
- package/dist/{chunk-PD6IQY2Y.cjs → chunk-C5IE4WUG.cjs} +8 -8
- package/dist/{chunk-PD6IQY2Y.cjs.map → chunk-C5IE4WUG.cjs.map} +1 -1
- package/dist/{chunk-DXG3TARY.js → chunk-DIK33H5U.js} +202 -30
- package/dist/chunk-DIK33H5U.js.map +1 -0
- package/dist/{chunk-JVYH76ZX.js → chunk-FESAXMHT.js} +7 -6
- package/dist/{chunk-JVYH76ZX.js.map → chunk-FESAXMHT.js.map} +1 -1
- package/dist/chunk-FHQZCAAK.cjs +112 -0
- package/dist/chunk-FHQZCAAK.cjs.map +1 -0
- package/dist/{chunk-UBFDB6OL.cjs → chunk-QNMYVXRL.cjs} +222 -50
- package/dist/chunk-QNMYVXRL.cjs.map +1 -0
- package/dist/{chunk-N6ODUM2Y.js → chunk-S63VBIWN.js} +27 -16
- package/dist/chunk-S63VBIWN.js.map +1 -0
- package/dist/{chunk-T2LNV5Q5.js → chunk-WIHNVN6L.js} +153 -76
- package/dist/chunk-WIHNVN6L.js.map +1 -0
- package/dist/{devtools-BDp76luf.d.ts → devtools-BtIkN77t.d.cts} +14 -2
- package/dist/{devtools-5AipK9CX.d.cts → devtools-D2z4llpA.d.ts} +14 -2
- package/dist/index.cjs +60 -58
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +5 -5
- package/dist/index.d.ts +5 -5
- package/dist/index.dev.js +300 -74
- package/dist/index.dev.js.map +1 -1
- package/dist/index.js +13 -11
- package/dist/index.js.map +1 -1
- package/dist/internal-list.cjs +4 -4
- package/dist/internal-list.d.cts +2 -2
- package/dist/internal-list.d.ts +2 -2
- package/dist/internal-list.js +3 -3
- package/dist/internal.cjs +5 -5
- package/dist/internal.d.cts +6 -6
- package/dist/internal.d.ts +6 -6
- package/dist/internal.js +4 -4
- package/dist/jsx-dev-runtime.d.cts +671 -0
- package/dist/jsx-dev-runtime.d.ts +671 -0
- package/dist/jsx-runtime.d.cts +671 -0
- package/dist/jsx-runtime.d.ts +671 -0
- package/dist/{list-DL5DOFcO.d.ts → list-BKM6YOPq.d.ts} +2 -2
- package/dist/{list-hP7hQ9Vk.d.cts → list-Bi8dDF8Q.d.cts} +2 -2
- package/dist/loader.cjs +34 -28
- package/dist/loader.cjs.map +1 -1
- package/dist/loader.d.cts +2 -2
- package/dist/loader.d.ts +2 -2
- package/dist/loader.js +17 -11
- package/dist/loader.js.map +1 -1
- package/dist/{props-BpZz0AOq.d.cts → props-9chMyBGb.d.cts} +2 -2
- package/dist/{props-CjLH0JE-.d.ts → props-D1nj2p_3.d.ts} +2 -2
- package/dist/{resume-BJ4oHLi_.d.cts → resume-C5IKAIdh.d.ts} +2 -2
- package/dist/{resume-CuyJWXP_.d.ts → resume-DPZxmA95.d.cts} +2 -2
- package/dist/{scope-jPt5DHRT.d.ts → scope-BSkhJr0a.d.ts} +1 -1
- package/dist/{scope-BJCtq8hJ.d.cts → scope-Bn3sxem5.d.cts} +1 -1
- package/dist/{signal-C4ISF17w.d.cts → signal-Z4KkDk9h.d.cts} +12 -1
- package/dist/{signal-C4ISF17w.d.ts → signal-Z4KkDk9h.d.ts} +12 -1
- package/package.json +2 -2
- package/src/binding.ts +59 -29
- package/src/context.ts +4 -3
- package/src/devtools.ts +19 -2
- package/src/dom.ts +122 -42
- package/src/effect.ts +5 -5
- package/src/error-boundary.ts +5 -5
- package/src/hooks.ts +13 -5
- package/src/lifecycle.ts +48 -3
- package/src/list-helpers.ts +30 -13
- package/src/loader.ts +20 -12
- package/src/node-ops.ts +8 -5
- package/src/signal.ts +191 -18
- package/src/suspense.ts +5 -4
- package/src/transition.ts +9 -3
- package/dist/chunk-DXG3TARY.js.map +0 -1
- package/dist/chunk-LBE6DC3V.cjs.map +0 -1
- package/dist/chunk-N6ODUM2Y.js.map +0 -1
- package/dist/chunk-OAM7HABA.cjs.map +0 -1
- package/dist/chunk-PG4QX2I2.cjs +0 -111
- package/dist/chunk-PG4QX2I2.cjs.map +0 -1
- package/dist/chunk-T2LNV5Q5.js.map +0 -1
- package/dist/chunk-UBFDB6OL.cjs.map +0 -1
- /package/dist/{chunk-FVX77557.js.map → chunk-2UR2UWE2.js.map} +0 -0
package/dist/loader.cjs
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
|
|
9
9
|
|
|
10
10
|
|
|
11
|
-
var
|
|
11
|
+
var _chunkQNMYVXRLcjs = require('./chunk-QNMYVXRL.cjs');
|
|
12
12
|
|
|
13
13
|
// src/loader.ts
|
|
14
14
|
function resolveModuleUrl(url) {
|
|
@@ -54,7 +54,7 @@ function installResumableLoader(options = {}) {
|
|
|
54
54
|
prefetchedUrls.clear();
|
|
55
55
|
processedSnapshots.clear();
|
|
56
56
|
emittedIssueKeys.clear();
|
|
57
|
-
|
|
57
|
+
_chunkQNMYVXRLcjs.__fictSetSSRState.call(void 0, null);
|
|
58
58
|
if (eventListenerCleanup) {
|
|
59
59
|
eventListenerCleanup();
|
|
60
60
|
eventListenerCleanup = null;
|
|
@@ -71,7 +71,7 @@ function installResumableLoader(options = {}) {
|
|
|
71
71
|
if (_optionalChain([snapshotEl, 'optionalAccess', _ => _.textContent])) {
|
|
72
72
|
const state = parseSnapshotText(snapshotEl.textContent, `#${scriptId}`);
|
|
73
73
|
if (state) {
|
|
74
|
-
|
|
74
|
+
_chunkQNMYVXRLcjs.__fictSetSSRState.call(void 0, state);
|
|
75
75
|
}
|
|
76
76
|
}
|
|
77
77
|
const snapshotScripts = doc.querySelectorAll(
|
|
@@ -104,8 +104,8 @@ function installResumableLoader(options = {}) {
|
|
|
104
104
|
});
|
|
105
105
|
snapshotObserver.observe(_nullishCoalesce(doc.documentElement, () => ( doc)), { childList: true, subtree: true });
|
|
106
106
|
}
|
|
107
|
-
|
|
108
|
-
const events = _nullishCoalesce(options.events, () => ( Array.from(
|
|
107
|
+
_chunkQNMYVXRLcjs.__fictEnableResumable.call(void 0, );
|
|
108
|
+
const events = _nullishCoalesce(options.events, () => ( Array.from(_chunkQNMYVXRLcjs.DelegatedEvents)));
|
|
109
109
|
for (const eventName of events) {
|
|
110
110
|
doc.addEventListener(eventName, handleResumableEvent, true);
|
|
111
111
|
}
|
|
@@ -129,7 +129,7 @@ function parseSnapshotScript(script) {
|
|
|
129
129
|
const source = script.id ? `#${script.id}` : "<script[data-fict-snapshot]>";
|
|
130
130
|
const state = parseSnapshotText(text, source);
|
|
131
131
|
if (state) {
|
|
132
|
-
|
|
132
|
+
_chunkQNMYVXRLcjs.__fictMergeSSRState.call(void 0, state);
|
|
133
133
|
}
|
|
134
134
|
}
|
|
135
135
|
function parseSnapshotText(text, source) {
|
|
@@ -141,7 +141,7 @@ function parseSnapshotText(text, source) {
|
|
|
141
141
|
code: "snapshot_parse_error",
|
|
142
142
|
message: "[fict/loader] Failed to parse SSR snapshot JSON.",
|
|
143
143
|
source,
|
|
144
|
-
expectedVersion:
|
|
144
|
+
expectedVersion: _chunkQNMYVXRLcjs.FICT_SSR_SNAPSHOT_SCHEMA_VERSION
|
|
145
145
|
});
|
|
146
146
|
return null;
|
|
147
147
|
}
|
|
@@ -153,18 +153,18 @@ function normalizeSnapshotState(value, source) {
|
|
|
153
153
|
code: "snapshot_invalid_shape",
|
|
154
154
|
message: "[fict/loader] Snapshot payload must be an object.",
|
|
155
155
|
source,
|
|
156
|
-
expectedVersion:
|
|
156
|
+
expectedVersion: _chunkQNMYVXRLcjs.FICT_SSR_SNAPSHOT_SCHEMA_VERSION
|
|
157
157
|
});
|
|
158
158
|
return null;
|
|
159
159
|
}
|
|
160
160
|
const rawVersion = value.v;
|
|
161
|
-
const version = rawVersion === void 0 ?
|
|
162
|
-
if (!Number.isInteger(version) || version !==
|
|
161
|
+
const version = rawVersion === void 0 ? _chunkQNMYVXRLcjs.FICT_SSR_SNAPSHOT_SCHEMA_VERSION : rawVersion;
|
|
162
|
+
if (!Number.isInteger(version) || version !== _chunkQNMYVXRLcjs.FICT_SSR_SNAPSHOT_SCHEMA_VERSION) {
|
|
163
163
|
const versionIssue = {
|
|
164
164
|
code: "snapshot_unsupported_version",
|
|
165
165
|
message: `[fict/loader] Snapshot schema version ${String(version)} is not supported by this runtime.`,
|
|
166
166
|
source,
|
|
167
|
-
expectedVersion:
|
|
167
|
+
expectedVersion: _chunkQNMYVXRLcjs.FICT_SSR_SNAPSHOT_SCHEMA_VERSION
|
|
168
168
|
};
|
|
169
169
|
if (typeof version === "number") {
|
|
170
170
|
versionIssue.actualVersion = version;
|
|
@@ -180,11 +180,11 @@ function normalizeSnapshotState(value, source) {
|
|
|
180
180
|
code: "snapshot_invalid_shape",
|
|
181
181
|
message: "[fict/loader] Snapshot payload is missing a valid `scopes` object.",
|
|
182
182
|
source,
|
|
183
|
-
expectedVersion:
|
|
183
|
+
expectedVersion: _chunkQNMYVXRLcjs.FICT_SSR_SNAPSHOT_SCHEMA_VERSION
|
|
184
184
|
});
|
|
185
185
|
return null;
|
|
186
186
|
}
|
|
187
|
-
return { v:
|
|
187
|
+
return { v: _chunkQNMYVXRLcjs.FICT_SSR_SNAPSHOT_SCHEMA_VERSION, scopes };
|
|
188
188
|
}
|
|
189
189
|
function isRecord(value) {
|
|
190
190
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
@@ -275,16 +275,17 @@ function setupHoverPrefetch(doc, delay) {
|
|
|
275
275
|
};
|
|
276
276
|
}
|
|
277
277
|
function prefetchElementQrls(el) {
|
|
278
|
+
const ownerDocument = _nullishCoalesce(el.ownerDocument, () => ( (typeof document !== "undefined" ? document : void 0)));
|
|
278
279
|
const eventAttrs = ["on:click", "on:input", "on:change", "on:submit", "on:keydown", "on:keyup"];
|
|
279
280
|
for (const attr of eventAttrs) {
|
|
280
281
|
const qrl = el.getAttribute(attr);
|
|
281
282
|
if (qrl) {
|
|
282
|
-
prefetchQrl(qrl);
|
|
283
|
+
prefetchQrl(qrl, ownerDocument);
|
|
283
284
|
}
|
|
284
285
|
}
|
|
285
286
|
const resumeQrl = el.getAttribute("data-fict-h");
|
|
286
287
|
if (resumeQrl) {
|
|
287
|
-
prefetchQrl(resumeQrl);
|
|
288
|
+
prefetchQrl(resumeQrl, ownerDocument);
|
|
288
289
|
}
|
|
289
290
|
const children = el.querySelectorAll(
|
|
290
291
|
"[on\\:click], [on\\:input], [on\\:change], [on\\:submit], [data-fict-h]"
|
|
@@ -293,32 +294,37 @@ function prefetchElementQrls(el) {
|
|
|
293
294
|
for (const attr of eventAttrs) {
|
|
294
295
|
const qrl = child.getAttribute(attr);
|
|
295
296
|
if (qrl) {
|
|
296
|
-
prefetchQrl(qrl);
|
|
297
|
+
prefetchQrl(qrl, ownerDocument);
|
|
297
298
|
}
|
|
298
299
|
}
|
|
299
300
|
const childResumeQrl = child.getAttribute("data-fict-h");
|
|
300
301
|
if (childResumeQrl) {
|
|
301
|
-
prefetchQrl(childResumeQrl);
|
|
302
|
+
prefetchQrl(childResumeQrl, ownerDocument);
|
|
302
303
|
}
|
|
303
304
|
});
|
|
304
305
|
}
|
|
305
|
-
function prefetchQrl(qrl) {
|
|
306
|
+
function prefetchQrl(qrl, ownerDocument) {
|
|
306
307
|
const { url } = parseQrl(qrl);
|
|
307
308
|
if (!url || prefetchedUrls.has(url)) return;
|
|
308
309
|
prefetchedUrls.add(url);
|
|
309
310
|
const resolvedUrl = resolveModuleUrl(url);
|
|
310
|
-
|
|
311
|
-
|
|
311
|
+
const doc = _nullishCoalesce(ownerDocument, () => ( (typeof document !== "undefined" ? document : void 0)));
|
|
312
|
+
if (doc) {
|
|
313
|
+
const link = doc.createElement("link");
|
|
312
314
|
link.rel = "modulepreload";
|
|
313
315
|
link.href = resolvedUrl;
|
|
314
316
|
link.crossOrigin = "anonymous";
|
|
315
|
-
|
|
317
|
+
_optionalChain([doc, 'access', _5 => _5.head, 'optionalAccess', _6 => _6.appendChild, 'call', _7 => _7(link)]);
|
|
316
318
|
}
|
|
317
319
|
}
|
|
318
320
|
function handleResumableEvent(event) {
|
|
319
321
|
const promise = handleResumableEventAsync(event);
|
|
320
322
|
pendingHandlers.add(promise);
|
|
321
|
-
promise.
|
|
323
|
+
void promise.catch((error) => {
|
|
324
|
+
if (typeof console !== "undefined" && typeof console.error === "function") {
|
|
325
|
+
console.error("[fict/loader] Failed to handle resumable event.", error);
|
|
326
|
+
}
|
|
327
|
+
}).finally(() => {
|
|
322
328
|
pendingHandlers.delete(promise);
|
|
323
329
|
});
|
|
324
330
|
}
|
|
@@ -332,18 +338,18 @@ async function handleResumableEventAsync(event) {
|
|
|
332
338
|
if (!host) continue;
|
|
333
339
|
const scopeId = host.getAttribute("data-fict-s");
|
|
334
340
|
if (!scopeId) continue;
|
|
335
|
-
const snapshot =
|
|
341
|
+
const snapshot = _chunkQNMYVXRLcjs.__fictGetSSRScope.call(void 0, scopeId);
|
|
336
342
|
if (!snapshot) {
|
|
337
343
|
emitSnapshotIssue({
|
|
338
344
|
code: "scope_snapshot_missing",
|
|
339
345
|
message: `[fict/loader] Missing scope snapshot for ${scopeId}; skipping resumable handler execution.`,
|
|
340
346
|
source: "event",
|
|
341
|
-
expectedVersion:
|
|
347
|
+
expectedVersion: _chunkQNMYVXRLcjs.FICT_SSR_SNAPSHOT_SCHEMA_VERSION,
|
|
342
348
|
scopeId
|
|
343
349
|
});
|
|
344
|
-
|
|
350
|
+
continue;
|
|
345
351
|
}
|
|
346
|
-
|
|
352
|
+
_chunkQNMYVXRLcjs.__fictEnsureScope.call(void 0, scopeId, host, snapshot);
|
|
347
353
|
const { url, exportName } = parseQrl(qrl);
|
|
348
354
|
if (event.cancelable && (event.type === "click" || event.type === "submit")) {
|
|
349
355
|
const tag = node.tagName.toLowerCase();
|
|
@@ -360,7 +366,7 @@ async function handleResumableEventAsync(event) {
|
|
|
360
366
|
/* @vite-ignore */
|
|
361
367
|
resolvedResumeUrl
|
|
362
368
|
)));
|
|
363
|
-
const resumeFn =
|
|
369
|
+
const resumeFn = _chunkQNMYVXRLcjs.__fictGetResume.call(void 0, resumeExport);
|
|
364
370
|
if (typeof resumeFn === "function") {
|
|
365
371
|
await resumeFn(scopeId, host);
|
|
366
372
|
hydratedScopes.add(scopeId);
|
|
@@ -407,5 +413,5 @@ function buildEventPath(event) {
|
|
|
407
413
|
|
|
408
414
|
|
|
409
415
|
|
|
410
|
-
exports.__fictUseLexicalScope =
|
|
416
|
+
exports.__fictUseLexicalScope = _chunkQNMYVXRLcjs.__fictUseLexicalScope; exports.cleanupEventListeners = cleanupEventListeners; exports.installResumableLoader = installResumableLoader; exports.resetHydratedScopes = resetHydratedScopes; exports.resetPrefetchedUrls = resetPrefetchedUrls; exports.waitForPendingHandlers = waitForPendingHandlers;
|
|
411
417
|
//# sourceMappingURL=loader.cjs.map
|
package/dist/loader.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["/home/runner/work/fict/fict/packages/runtime/dist/loader.cjs","../src/loader.ts"],"names":[],"mappings":"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","file":"/home/runner/work/fict/fict/packages/runtime/dist/loader.cjs","sourcesContent":[null,"import { DelegatedEvents } from './constants'\nimport {\n FICT_SSR_SNAPSHOT_SCHEMA_VERSION,\n type SSRState,\n __fictEnableResumable,\n __fictEnsureScope,\n __fictGetResume,\n __fictGetSSRScope,\n __fictMergeSSRState,\n __fictSetSSRState,\n __fictUseLexicalScope,\n} from './resume'\n\n// ============================================================================\n// Module Resolution\n// ============================================================================\n\n/**\n * Resolve a module URL through the manifest if available.\n * In production, virtual module URLs (virtual:fict-handler:...) are mapped\n * to their built chunk URLs through the manifest.\n */\nfunction resolveModuleUrl(url: string): string {\n const manifest = (globalThis as Record<string, unknown>).__FICT_MANIFEST__ as\n | Record<string, string>\n | undefined\n\n if (manifest) {\n // Check if the URL (without #fragment) is in the manifest\n const resolved = manifest[url]\n if (resolved) {\n return resolved\n }\n }\n\n return url\n}\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport interface PrefetchStrategy {\n /**\n * Enable visibility-based prefetch using IntersectionObserver.\n * Prefetches modules when interactive elements come into view.\n * @default true\n */\n visibility?: boolean\n /**\n * Root margin for IntersectionObserver (e.g., '200px' to prefetch earlier).\n * @default '200px'\n */\n visibilityMargin?: string\n /**\n * Enable hover-based prefetch using pointerover events.\n * Prefetches modules when user hovers over interactive elements.\n * @default true\n */\n hover?: boolean\n /**\n * Delay in ms before prefetching on hover (debounce rapid movements).\n * @default 50\n */\n hoverDelay?: number\n}\n\nexport interface ResumableLoaderOptions {\n document?: Document\n snapshotScriptId?: string\n events?: string[]\n /**\n * Receives structured snapshot/resume issues detected by the loader.\n * Useful for telemetry and fail-safe fallback orchestration.\n */\n onSnapshotIssue?: (issue: SnapshotIssue) => void\n /**\n * Prefetch strategy configuration.\n * Set to false to disable all prefetching.\n * @default { visibility: true, hover: true }\n */\n prefetch?: PrefetchStrategy | false\n}\n\nexport type SnapshotIssueCode =\n | 'snapshot_parse_error'\n | 'snapshot_invalid_shape'\n | 'snapshot_unsupported_version'\n | 'scope_snapshot_missing'\n\nexport interface SnapshotIssue {\n code: SnapshotIssueCode\n message: string\n source: string\n expectedVersion: number\n actualVersion?: number\n scopeId?: string\n}\n\n// ============================================================================\n// State\n// ============================================================================\n\nconst hydratedScopes = new Set<string>()\nconst prefetchedUrls = new Set<string>()\nlet prefetchCleanup: (() => void) | null = null\nlet eventListenerCleanup: (() => void) | null = null\nlet snapshotObserver: MutationObserver | null = null\nconst processedSnapshots = new Set<HTMLScriptElement>()\nlet snapshotIssueHandler: ((issue: SnapshotIssue) => void) | null = null\nconst emittedIssueKeys = new Set<string>()\n\n/**\n * Reset the hydrated scopes set. Useful for testing.\n */\nexport function resetHydratedScopes(): void {\n hydratedScopes.clear()\n}\n\n/**\n * Reset the prefetched URLs set. Useful for testing.\n */\nexport function resetPrefetchedUrls(): void {\n prefetchedUrls.clear()\n}\n\n/**\n * Set of pending handler promises. Used for testing to wait for all handlers to complete.\n */\nconst pendingHandlers = new Set<Promise<void>>()\n\n/**\n * Wait for all pending event handlers to complete. Useful for testing.\n */\nexport async function waitForPendingHandlers(): Promise<void> {\n if (pendingHandlers.size === 0) return\n await Promise.allSettled([...pendingHandlers])\n}\n\n/**\n * Clean up all registered event listeners. Useful for testing.\n */\nexport function cleanupEventListeners(): void {\n if (eventListenerCleanup) {\n eventListenerCleanup()\n eventListenerCleanup = null\n }\n}\n\n// ============================================================================\n// Main Entry Point\n// ============================================================================\n\nexport function installResumableLoader(options: ResumableLoaderOptions = {}): void {\n const doc = options.document ?? window.document\n const scriptId = options.snapshotScriptId ?? '__FICT_SNAPSHOT__'\n snapshotIssueHandler = options.onSnapshotIssue ?? null\n\n // Reset hydrated scopes for fresh loader installation\n hydratedScopes.clear()\n prefetchedUrls.clear()\n processedSnapshots.clear()\n emittedIssueKeys.clear()\n __fictSetSSRState(null)\n\n // Clean up previous event listeners\n if (eventListenerCleanup) {\n eventListenerCleanup()\n eventListenerCleanup = null\n }\n\n // Clean up previous prefetch handlers\n if (prefetchCleanup) {\n prefetchCleanup()\n prefetchCleanup = null\n }\n\n if (snapshotObserver) {\n snapshotObserver.disconnect()\n snapshotObserver = null\n }\n\n const snapshotEl = doc.getElementById(scriptId)\n if (snapshotEl?.textContent) {\n const state = parseSnapshotText(snapshotEl.textContent, `#${scriptId}`)\n if (state) {\n __fictSetSSRState(state)\n }\n }\n\n const snapshotScripts = doc.querySelectorAll(\n 'script[type=\"application/json\"][data-fict-snapshot]',\n )\n for (const script of Array.from(snapshotScripts)) {\n parseSnapshotScript(script as HTMLScriptElement)\n }\n\n if (typeof MutationObserver !== 'undefined') {\n snapshotObserver = new MutationObserver(mutations => {\n for (const mutation of mutations) {\n for (const node of Array.from(mutation.addedNodes)) {\n if (!(node instanceof Element)) continue\n if (node.tagName === 'SCRIPT') {\n const script = node as HTMLScriptElement\n if (isSnapshotScript(script)) {\n parseSnapshotScript(script)\n }\n }\n const nested = node.querySelectorAll?.(\n 'script[type=\"application/json\"][data-fict-snapshot]',\n )\n if (nested && nested.length) {\n for (const script of Array.from(nested)) {\n parseSnapshotScript(script as HTMLScriptElement)\n }\n }\n }\n }\n })\n snapshotObserver.observe(doc.documentElement ?? doc, { childList: true, subtree: true })\n }\n\n __fictEnableResumable()\n\n const events = options.events ?? Array.from(DelegatedEvents)\n for (const eventName of events) {\n doc.addEventListener(eventName, handleResumableEvent, true)\n }\n\n // Store cleanup function for event listeners\n eventListenerCleanup = () => {\n for (const eventName of events) {\n doc.removeEventListener(eventName, handleResumableEvent, true)\n }\n }\n\n // Setup prefetch if enabled\n if (options.prefetch !== false) {\n prefetchCleanup = setupPrefetch(doc, options.prefetch ?? {})\n }\n}\n\nfunction isSnapshotScript(script: HTMLScriptElement): boolean {\n return script.type === 'application/json' && script.hasAttribute('data-fict-snapshot')\n}\n\nfunction parseSnapshotScript(script: HTMLScriptElement): void {\n if (processedSnapshots.has(script)) return\n processedSnapshots.add(script)\n const text = script.textContent\n if (!text) return\n const source = script.id ? `#${script.id}` : '<script[data-fict-snapshot]>'\n const state = parseSnapshotText(text, source)\n if (state) {\n __fictMergeSSRState(state)\n }\n}\n\nfunction parseSnapshotText(text: string, source: string): SSRState | null {\n let parsed: unknown\n try {\n parsed = JSON.parse(text)\n } catch {\n emitSnapshotIssue({\n code: 'snapshot_parse_error',\n message: '[fict/loader] Failed to parse SSR snapshot JSON.',\n source,\n expectedVersion: FICT_SSR_SNAPSHOT_SCHEMA_VERSION,\n })\n return null\n }\n\n return normalizeSnapshotState(parsed, source)\n}\n\nfunction normalizeSnapshotState(value: unknown, source: string): SSRState | null {\n if (!isRecord(value)) {\n emitSnapshotIssue({\n code: 'snapshot_invalid_shape',\n message: '[fict/loader] Snapshot payload must be an object.',\n source,\n expectedVersion: FICT_SSR_SNAPSHOT_SCHEMA_VERSION,\n })\n return null\n }\n\n const rawVersion = value.v\n const version = rawVersion === undefined ? FICT_SSR_SNAPSHOT_SCHEMA_VERSION : rawVersion\n if (!Number.isInteger(version) || version !== FICT_SSR_SNAPSHOT_SCHEMA_VERSION) {\n const versionIssue: SnapshotIssue = {\n code: 'snapshot_unsupported_version',\n message: `[fict/loader] Snapshot schema version ${String(version)} is not supported by this runtime.`,\n source,\n expectedVersion: FICT_SSR_SNAPSHOT_SCHEMA_VERSION,\n }\n if (typeof version === 'number') {\n versionIssue.actualVersion = version\n }\n emitSnapshotIssue({\n ...versionIssue,\n })\n return null\n }\n\n const scopes = value.scopes\n if (!isRecord(scopes)) {\n emitSnapshotIssue({\n code: 'snapshot_invalid_shape',\n message: '[fict/loader] Snapshot payload is missing a valid `scopes` object.',\n source,\n expectedVersion: FICT_SSR_SNAPSHOT_SCHEMA_VERSION,\n })\n return null\n }\n\n return { v: FICT_SSR_SNAPSHOT_SCHEMA_VERSION, scopes: scopes as SSRState['scopes'] }\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value)\n}\n\nfunction emitSnapshotIssue(issue: SnapshotIssue): void {\n const key =\n `${issue.code}|${issue.source}|${issue.scopeId ?? ''}|` +\n `${issue.actualVersion ?? ''}|${issue.expectedVersion}`\n if (emittedIssueKeys.has(key)) return\n emittedIssueKeys.add(key)\n\n snapshotIssueHandler?.(issue)\n\n if (typeof console !== 'undefined' && typeof console.warn === 'function') {\n console.warn(issue.message)\n }\n}\n\n// ============================================================================\n// Prefetch Implementation\n// ============================================================================\n\nfunction setupPrefetch(doc: Document, strategy: PrefetchStrategy): () => void {\n const cleanupFns: (() => void)[] = []\n\n // Visibility-based prefetch\n if (strategy.visibility !== false) {\n const cleanup = setupVisibilityPrefetch(doc, strategy.visibilityMargin ?? '200px')\n cleanupFns.push(cleanup)\n }\n\n // Hover-based prefetch\n if (strategy.hover !== false) {\n const cleanup = setupHoverPrefetch(doc, strategy.hoverDelay ?? 50)\n cleanupFns.push(cleanup)\n }\n\n return () => {\n for (const cleanup of cleanupFns) {\n cleanup()\n }\n }\n}\n\nfunction setupVisibilityPrefetch(doc: Document, rootMargin: string): () => void {\n // Check if IntersectionObserver is available\n if (typeof IntersectionObserver === 'undefined') {\n return () => {}\n }\n\n const observer = new IntersectionObserver(\n entries => {\n for (const entry of entries) {\n if (entry.isIntersecting) {\n const el = entry.target as Element\n prefetchElementQrls(el)\n // Stop observing after prefetch\n observer.unobserve(el)\n }\n }\n },\n { rootMargin },\n )\n\n // Observe all elements with on:* attributes\n const interactiveElements = doc.querySelectorAll(\n '[on\\\\:click], [on\\\\:input], [on\\\\:change], [on\\\\:submit], [on\\\\:keydown], [on\\\\:keyup]',\n )\n interactiveElements.forEach(el => observer.observe(el))\n\n // Also observe elements with data-fict-h (resumable components)\n const resumableHosts = doc.querySelectorAll('[data-fict-h]')\n resumableHosts.forEach(el => observer.observe(el))\n\n return () => {\n observer.disconnect()\n }\n}\n\nfunction setupHoverPrefetch(doc: Document, delay: number): () => void {\n let hoverTimeout: ReturnType<typeof setTimeout> | null = null\n let lastHoveredElement: Element | null = null\n\n const handlePointerOver = (event: Event) => {\n const target = event.target\n if (!(target instanceof Element)) return\n\n // Find the closest element with interactive attributes\n const interactiveEl =\n target.closest('[on\\\\:click]') ||\n target.closest('[on\\\\:input]') ||\n target.closest('[on\\\\:change]') ||\n target.closest('[on\\\\:submit]') ||\n target.closest('[data-fict-h]')\n\n if (!interactiveEl || interactiveEl === lastHoveredElement) return\n\n lastHoveredElement = interactiveEl\n\n // Clear previous timeout\n if (hoverTimeout) {\n clearTimeout(hoverTimeout)\n }\n\n // Debounce prefetch\n hoverTimeout = setTimeout(() => {\n prefetchElementQrls(interactiveEl)\n }, delay)\n }\n\n const handlePointerOut = () => {\n if (hoverTimeout) {\n clearTimeout(hoverTimeout)\n hoverTimeout = null\n }\n lastHoveredElement = null\n }\n\n doc.addEventListener('pointerover', handlePointerOver, { passive: true })\n doc.addEventListener('pointerout', handlePointerOut, { passive: true })\n\n return () => {\n doc.removeEventListener('pointerover', handlePointerOver)\n doc.removeEventListener('pointerout', handlePointerOut)\n if (hoverTimeout) {\n clearTimeout(hoverTimeout)\n }\n }\n}\n\nfunction prefetchElementQrls(el: Element): void {\n // Prefetch event handler QRLs\n const eventAttrs = ['on:click', 'on:input', 'on:change', 'on:submit', 'on:keydown', 'on:keyup']\n for (const attr of eventAttrs) {\n const qrl = el.getAttribute(attr)\n if (qrl) {\n prefetchQrl(qrl)\n }\n }\n\n // Prefetch resume handler QRL\n const resumeQrl = el.getAttribute('data-fict-h')\n if (resumeQrl) {\n prefetchQrl(resumeQrl)\n }\n\n // Also check children for nested QRLs\n const children = el.querySelectorAll(\n '[on\\\\:click], [on\\\\:input], [on\\\\:change], [on\\\\:submit], [data-fict-h]',\n )\n children.forEach(child => {\n for (const attr of eventAttrs) {\n const qrl = child.getAttribute(attr)\n if (qrl) {\n prefetchQrl(qrl)\n }\n }\n const childResumeQrl = child.getAttribute('data-fict-h')\n if (childResumeQrl) {\n prefetchQrl(childResumeQrl)\n }\n })\n}\n\nfunction prefetchQrl(qrl: string): void {\n const { url } = parseQrl(qrl)\n if (!url || prefetchedUrls.has(url)) return\n\n prefetchedUrls.add(url)\n\n // Resolve through manifest for production builds\n const resolvedUrl = resolveModuleUrl(url)\n\n // Use modulepreload link for best browser support\n if (typeof document !== 'undefined') {\n const link = document.createElement('link')\n link.rel = 'modulepreload'\n link.href = resolvedUrl\n link.crossOrigin = 'anonymous'\n document.head.appendChild(link)\n }\n}\n\n// ============================================================================\n\n/**\n * Wrapper that tracks the async handler promise for testing.\n */\nfunction handleResumableEvent(event: Event): void {\n const promise = handleResumableEventAsync(event)\n pendingHandlers.add(promise)\n promise.finally(() => {\n pendingHandlers.delete(promise)\n })\n}\n\nasync function handleResumableEventAsync(event: Event): Promise<void> {\n const path =\n typeof event.composedPath === 'function' ? event.composedPath() : buildEventPath(event)\n\n for (const node of path) {\n if (!(node instanceof Element)) continue\n const qrl = node.getAttribute(`on:${event.type}`)\n if (!qrl) continue\n\n const host = node.closest('[data-fict-s]') as Element | null\n if (!host) continue\n const scopeId = host.getAttribute('data-fict-s')\n if (!scopeId) continue\n\n const snapshot = __fictGetSSRScope(scopeId)\n if (!snapshot) {\n emitSnapshotIssue({\n code: 'scope_snapshot_missing',\n message: `[fict/loader] Missing scope snapshot for ${scopeId}; skipping resumable handler execution.`,\n source: 'event',\n expectedVersion: FICT_SSR_SNAPSHOT_SCHEMA_VERSION,\n scopeId,\n })\n return\n }\n __fictEnsureScope(scopeId, host, snapshot)\n\n const { url, exportName } = parseQrl(qrl)\n\n // Pre-emptively prevent default on navigations/forms while we await modules\n if (event.cancelable && (event.type === 'click' || event.type === 'submit')) {\n const tag = node.tagName.toLowerCase()\n if (tag === 'a' || tag === 'form') {\n event.preventDefault()\n }\n }\n\n // Resume FIRST to set up reactive bindings BEFORE the handler runs\n if (!hydratedScopes.has(scopeId)) {\n const resumeQrl = host.getAttribute('data-fict-h')\n if (resumeQrl) {\n const { url: resumeUrl, exportName: resumeExport } = parseQrl(resumeQrl)\n const resolvedResumeUrl = resolveModuleUrl(resumeUrl)\n // Load the module to ensure resume functions are registered\n await import(/* @vite-ignore */ resolvedResumeUrl)\n // Get resume function from registry (not module exports)\n const resumeFn = __fictGetResume(resumeExport)\n if (typeof resumeFn === 'function') {\n await (resumeFn as (scopeId: string, host: Element) => unknown)(scopeId, host)\n hydratedScopes.add(scopeId)\n }\n }\n }\n\n // THEN run the handler - now signal updates will trigger DOM updates\n const resolvedUrl = resolveModuleUrl(url)\n const mod = await import(/* @vite-ignore */ resolvedUrl)\n const handler = (mod as Record<string, unknown>)[exportName]\n if (typeof handler === 'function') {\n await (handler as (scopeId: string, ev: Event, el: Element) => unknown)(scopeId, event, node)\n }\n\n return\n }\n}\n\nfunction parseQrl(qrl: string): { url: string; exportName: string } {\n const [ref] = qrl.split('[')\n if (!ref) {\n return { url: '', exportName: 'default' }\n }\n const hashIndex = ref.lastIndexOf('#')\n if (hashIndex === -1) {\n return { url: ref, exportName: 'default' }\n }\n return { url: ref.slice(0, hashIndex), exportName: ref.slice(hashIndex + 1) }\n}\n\nfunction buildEventPath(event: Event): EventTarget[] {\n const path: EventTarget[] = []\n let node: EventTarget | null = event.target\n while (node) {\n path.push(node)\n node = (node as Node).parentNode\n }\n path.push(window)\n return path\n}\n\n// Re-export for handler authors (optional)\nexport { __fictUseLexicalScope } from './resume'\n"]}
|
|
1
|
+
{"version":3,"sources":["/home/runner/work/fict/fict/packages/runtime/dist/loader.cjs","../src/loader.ts"],"names":[],"mappings":"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","file":"/home/runner/work/fict/fict/packages/runtime/dist/loader.cjs","sourcesContent":[null,"import { DelegatedEvents } from './constants'\nimport {\n FICT_SSR_SNAPSHOT_SCHEMA_VERSION,\n type SSRState,\n __fictEnableResumable,\n __fictEnsureScope,\n __fictGetResume,\n __fictGetSSRScope,\n __fictMergeSSRState,\n __fictSetSSRState,\n __fictUseLexicalScope,\n} from './resume'\n\n// ============================================================================\n// Module Resolution\n// ============================================================================\n\n/**\n * Resolve a module URL through the manifest if available.\n * In production, virtual module URLs (virtual:fict-handler:...) are mapped\n * to their built chunk URLs through the manifest.\n */\nfunction resolveModuleUrl(url: string): string {\n const manifest = (globalThis as Record<string, unknown>).__FICT_MANIFEST__ as\n | Record<string, string>\n | undefined\n\n if (manifest) {\n // Check if the URL (without #fragment) is in the manifest\n const resolved = manifest[url]\n if (resolved) {\n return resolved\n }\n }\n\n return url\n}\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport interface PrefetchStrategy {\n /**\n * Enable visibility-based prefetch using IntersectionObserver.\n * Prefetches modules when interactive elements come into view.\n * @default true\n */\n visibility?: boolean\n /**\n * Root margin for IntersectionObserver (e.g., '200px' to prefetch earlier).\n * @default '200px'\n */\n visibilityMargin?: string\n /**\n * Enable hover-based prefetch using pointerover events.\n * Prefetches modules when user hovers over interactive elements.\n * @default true\n */\n hover?: boolean\n /**\n * Delay in ms before prefetching on hover (debounce rapid movements).\n * @default 50\n */\n hoverDelay?: number\n}\n\nexport interface ResumableLoaderOptions {\n document?: Document\n snapshotScriptId?: string\n events?: string[]\n /**\n * Receives structured snapshot/resume issues detected by the loader.\n * Useful for telemetry and fail-safe fallback orchestration.\n */\n onSnapshotIssue?: (issue: SnapshotIssue) => void\n /**\n * Prefetch strategy configuration.\n * Set to false to disable all prefetching.\n * @default { visibility: true, hover: true }\n */\n prefetch?: PrefetchStrategy | false\n}\n\nexport type SnapshotIssueCode =\n | 'snapshot_parse_error'\n | 'snapshot_invalid_shape'\n | 'snapshot_unsupported_version'\n | 'scope_snapshot_missing'\n\nexport interface SnapshotIssue {\n code: SnapshotIssueCode\n message: string\n source: string\n expectedVersion: number\n actualVersion?: number\n scopeId?: string\n}\n\n// ============================================================================\n// State\n// ============================================================================\n\nconst hydratedScopes = new Set<string>()\nconst prefetchedUrls = new Set<string>()\nlet prefetchCleanup: (() => void) | null = null\nlet eventListenerCleanup: (() => void) | null = null\nlet snapshotObserver: MutationObserver | null = null\nconst processedSnapshots = new Set<HTMLScriptElement>()\nlet snapshotIssueHandler: ((issue: SnapshotIssue) => void) | null = null\nconst emittedIssueKeys = new Set<string>()\n\n/**\n * Reset the hydrated scopes set. Useful for testing.\n */\nexport function resetHydratedScopes(): void {\n hydratedScopes.clear()\n}\n\n/**\n * Reset the prefetched URLs set. Useful for testing.\n */\nexport function resetPrefetchedUrls(): void {\n prefetchedUrls.clear()\n}\n\n/**\n * Set of pending handler promises. Used for testing to wait for all handlers to complete.\n */\nconst pendingHandlers = new Set<Promise<void>>()\n\n/**\n * Wait for all pending event handlers to complete. Useful for testing.\n */\nexport async function waitForPendingHandlers(): Promise<void> {\n if (pendingHandlers.size === 0) return\n await Promise.allSettled([...pendingHandlers])\n}\n\n/**\n * Clean up all registered event listeners. Useful for testing.\n */\nexport function cleanupEventListeners(): void {\n if (eventListenerCleanup) {\n eventListenerCleanup()\n eventListenerCleanup = null\n }\n}\n\n// ============================================================================\n// Main Entry Point\n// ============================================================================\n\nexport function installResumableLoader(options: ResumableLoaderOptions = {}): void {\n const doc = options.document ?? window.document\n const scriptId = options.snapshotScriptId ?? '__FICT_SNAPSHOT__'\n snapshotIssueHandler = options.onSnapshotIssue ?? null\n\n // Reset hydrated scopes for fresh loader installation\n hydratedScopes.clear()\n prefetchedUrls.clear()\n processedSnapshots.clear()\n emittedIssueKeys.clear()\n __fictSetSSRState(null)\n\n // Clean up previous event listeners\n if (eventListenerCleanup) {\n eventListenerCleanup()\n eventListenerCleanup = null\n }\n\n // Clean up previous prefetch handlers\n if (prefetchCleanup) {\n prefetchCleanup()\n prefetchCleanup = null\n }\n\n if (snapshotObserver) {\n snapshotObserver.disconnect()\n snapshotObserver = null\n }\n\n const snapshotEl = doc.getElementById(scriptId)\n if (snapshotEl?.textContent) {\n const state = parseSnapshotText(snapshotEl.textContent, `#${scriptId}`)\n if (state) {\n __fictSetSSRState(state)\n }\n }\n\n const snapshotScripts = doc.querySelectorAll(\n 'script[type=\"application/json\"][data-fict-snapshot]',\n )\n for (const script of Array.from(snapshotScripts)) {\n parseSnapshotScript(script as HTMLScriptElement)\n }\n\n if (typeof MutationObserver !== 'undefined') {\n snapshotObserver = new MutationObserver(mutations => {\n for (const mutation of mutations) {\n for (const node of Array.from(mutation.addedNodes)) {\n if (!(node instanceof Element)) continue\n if (node.tagName === 'SCRIPT') {\n const script = node as HTMLScriptElement\n if (isSnapshotScript(script)) {\n parseSnapshotScript(script)\n }\n }\n const nested = node.querySelectorAll?.(\n 'script[type=\"application/json\"][data-fict-snapshot]',\n )\n if (nested && nested.length) {\n for (const script of Array.from(nested)) {\n parseSnapshotScript(script as HTMLScriptElement)\n }\n }\n }\n }\n })\n snapshotObserver.observe(doc.documentElement ?? doc, { childList: true, subtree: true })\n }\n\n __fictEnableResumable()\n\n const events = options.events ?? Array.from(DelegatedEvents)\n for (const eventName of events) {\n doc.addEventListener(eventName, handleResumableEvent, true)\n }\n\n // Store cleanup function for event listeners\n eventListenerCleanup = () => {\n for (const eventName of events) {\n doc.removeEventListener(eventName, handleResumableEvent, true)\n }\n }\n\n // Setup prefetch if enabled\n if (options.prefetch !== false) {\n prefetchCleanup = setupPrefetch(doc, options.prefetch ?? {})\n }\n}\n\nfunction isSnapshotScript(script: HTMLScriptElement): boolean {\n return script.type === 'application/json' && script.hasAttribute('data-fict-snapshot')\n}\n\nfunction parseSnapshotScript(script: HTMLScriptElement): void {\n if (processedSnapshots.has(script)) return\n processedSnapshots.add(script)\n const text = script.textContent\n if (!text) return\n const source = script.id ? `#${script.id}` : '<script[data-fict-snapshot]>'\n const state = parseSnapshotText(text, source)\n if (state) {\n __fictMergeSSRState(state)\n }\n}\n\nfunction parseSnapshotText(text: string, source: string): SSRState | null {\n let parsed: unknown\n try {\n parsed = JSON.parse(text)\n } catch {\n emitSnapshotIssue({\n code: 'snapshot_parse_error',\n message: '[fict/loader] Failed to parse SSR snapshot JSON.',\n source,\n expectedVersion: FICT_SSR_SNAPSHOT_SCHEMA_VERSION,\n })\n return null\n }\n\n return normalizeSnapshotState(parsed, source)\n}\n\nfunction normalizeSnapshotState(value: unknown, source: string): SSRState | null {\n if (!isRecord(value)) {\n emitSnapshotIssue({\n code: 'snapshot_invalid_shape',\n message: '[fict/loader] Snapshot payload must be an object.',\n source,\n expectedVersion: FICT_SSR_SNAPSHOT_SCHEMA_VERSION,\n })\n return null\n }\n\n const rawVersion = value.v\n const version = rawVersion === undefined ? FICT_SSR_SNAPSHOT_SCHEMA_VERSION : rawVersion\n if (!Number.isInteger(version) || version !== FICT_SSR_SNAPSHOT_SCHEMA_VERSION) {\n const versionIssue: SnapshotIssue = {\n code: 'snapshot_unsupported_version',\n message: `[fict/loader] Snapshot schema version ${String(version)} is not supported by this runtime.`,\n source,\n expectedVersion: FICT_SSR_SNAPSHOT_SCHEMA_VERSION,\n }\n if (typeof version === 'number') {\n versionIssue.actualVersion = version\n }\n emitSnapshotIssue({\n ...versionIssue,\n })\n return null\n }\n\n const scopes = value.scopes\n if (!isRecord(scopes)) {\n emitSnapshotIssue({\n code: 'snapshot_invalid_shape',\n message: '[fict/loader] Snapshot payload is missing a valid `scopes` object.',\n source,\n expectedVersion: FICT_SSR_SNAPSHOT_SCHEMA_VERSION,\n })\n return null\n }\n\n return { v: FICT_SSR_SNAPSHOT_SCHEMA_VERSION, scopes: scopes as SSRState['scopes'] }\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value)\n}\n\nfunction emitSnapshotIssue(issue: SnapshotIssue): void {\n const key =\n `${issue.code}|${issue.source}|${issue.scopeId ?? ''}|` +\n `${issue.actualVersion ?? ''}|${issue.expectedVersion}`\n if (emittedIssueKeys.has(key)) return\n emittedIssueKeys.add(key)\n\n snapshotIssueHandler?.(issue)\n\n if (typeof console !== 'undefined' && typeof console.warn === 'function') {\n console.warn(issue.message)\n }\n}\n\n// ============================================================================\n// Prefetch Implementation\n// ============================================================================\n\nfunction setupPrefetch(doc: Document, strategy: PrefetchStrategy): () => void {\n const cleanupFns: (() => void)[] = []\n\n // Visibility-based prefetch\n if (strategy.visibility !== false) {\n const cleanup = setupVisibilityPrefetch(doc, strategy.visibilityMargin ?? '200px')\n cleanupFns.push(cleanup)\n }\n\n // Hover-based prefetch\n if (strategy.hover !== false) {\n const cleanup = setupHoverPrefetch(doc, strategy.hoverDelay ?? 50)\n cleanupFns.push(cleanup)\n }\n\n return () => {\n for (const cleanup of cleanupFns) {\n cleanup()\n }\n }\n}\n\nfunction setupVisibilityPrefetch(doc: Document, rootMargin: string): () => void {\n // Check if IntersectionObserver is available\n if (typeof IntersectionObserver === 'undefined') {\n return () => {}\n }\n\n const observer = new IntersectionObserver(\n entries => {\n for (const entry of entries) {\n if (entry.isIntersecting) {\n const el = entry.target as Element\n prefetchElementQrls(el)\n // Stop observing after prefetch\n observer.unobserve(el)\n }\n }\n },\n { rootMargin },\n )\n\n // Observe all elements with on:* attributes\n const interactiveElements = doc.querySelectorAll(\n '[on\\\\:click], [on\\\\:input], [on\\\\:change], [on\\\\:submit], [on\\\\:keydown], [on\\\\:keyup]',\n )\n interactiveElements.forEach(el => observer.observe(el))\n\n // Also observe elements with data-fict-h (resumable components)\n const resumableHosts = doc.querySelectorAll('[data-fict-h]')\n resumableHosts.forEach(el => observer.observe(el))\n\n return () => {\n observer.disconnect()\n }\n}\n\nfunction setupHoverPrefetch(doc: Document, delay: number): () => void {\n let hoverTimeout: ReturnType<typeof setTimeout> | null = null\n let lastHoveredElement: Element | null = null\n\n const handlePointerOver = (event: Event) => {\n const target = event.target\n if (!(target instanceof Element)) return\n\n // Find the closest element with interactive attributes\n const interactiveEl =\n target.closest('[on\\\\:click]') ||\n target.closest('[on\\\\:input]') ||\n target.closest('[on\\\\:change]') ||\n target.closest('[on\\\\:submit]') ||\n target.closest('[data-fict-h]')\n\n if (!interactiveEl || interactiveEl === lastHoveredElement) return\n\n lastHoveredElement = interactiveEl\n\n // Clear previous timeout\n if (hoverTimeout) {\n clearTimeout(hoverTimeout)\n }\n\n // Debounce prefetch\n hoverTimeout = setTimeout(() => {\n prefetchElementQrls(interactiveEl)\n }, delay)\n }\n\n const handlePointerOut = () => {\n if (hoverTimeout) {\n clearTimeout(hoverTimeout)\n hoverTimeout = null\n }\n lastHoveredElement = null\n }\n\n doc.addEventListener('pointerover', handlePointerOver, { passive: true })\n doc.addEventListener('pointerout', handlePointerOut, { passive: true })\n\n return () => {\n doc.removeEventListener('pointerover', handlePointerOver)\n doc.removeEventListener('pointerout', handlePointerOut)\n if (hoverTimeout) {\n clearTimeout(hoverTimeout)\n }\n }\n}\n\nfunction prefetchElementQrls(el: Element): void {\n const ownerDocument = el.ownerDocument ?? (typeof document !== 'undefined' ? document : undefined)\n // Prefetch event handler QRLs\n const eventAttrs = ['on:click', 'on:input', 'on:change', 'on:submit', 'on:keydown', 'on:keyup']\n for (const attr of eventAttrs) {\n const qrl = el.getAttribute(attr)\n if (qrl) {\n prefetchQrl(qrl, ownerDocument)\n }\n }\n\n // Prefetch resume handler QRL\n const resumeQrl = el.getAttribute('data-fict-h')\n if (resumeQrl) {\n prefetchQrl(resumeQrl, ownerDocument)\n }\n\n // Also check children for nested QRLs\n const children = el.querySelectorAll(\n '[on\\\\:click], [on\\\\:input], [on\\\\:change], [on\\\\:submit], [data-fict-h]',\n )\n children.forEach(child => {\n for (const attr of eventAttrs) {\n const qrl = child.getAttribute(attr)\n if (qrl) {\n prefetchQrl(qrl, ownerDocument)\n }\n }\n const childResumeQrl = child.getAttribute('data-fict-h')\n if (childResumeQrl) {\n prefetchQrl(childResumeQrl, ownerDocument)\n }\n })\n}\n\nfunction prefetchQrl(qrl: string, ownerDocument?: Document): void {\n const { url } = parseQrl(qrl)\n if (!url || prefetchedUrls.has(url)) return\n\n prefetchedUrls.add(url)\n\n // Resolve through manifest for production builds\n const resolvedUrl = resolveModuleUrl(url)\n\n // Use modulepreload link for best browser support\n const doc = ownerDocument ?? (typeof document !== 'undefined' ? document : undefined)\n if (doc) {\n const link = doc.createElement('link')\n link.rel = 'modulepreload'\n link.href = resolvedUrl\n link.crossOrigin = 'anonymous'\n doc.head?.appendChild(link)\n }\n}\n\n// ============================================================================\n\n/**\n * Wrapper that tracks the async handler promise for testing.\n */\nfunction handleResumableEvent(event: Event): void {\n const promise = handleResumableEventAsync(event)\n pendingHandlers.add(promise)\n void promise\n .catch(error => {\n if (typeof console !== 'undefined' && typeof console.error === 'function') {\n console.error('[fict/loader] Failed to handle resumable event.', error)\n }\n })\n .finally(() => {\n pendingHandlers.delete(promise)\n })\n}\n\nasync function handleResumableEventAsync(event: Event): Promise<void> {\n const path =\n typeof event.composedPath === 'function' ? event.composedPath() : buildEventPath(event)\n\n for (const node of path) {\n if (!(node instanceof Element)) continue\n const qrl = node.getAttribute(`on:${event.type}`)\n if (!qrl) continue\n\n const host = node.closest('[data-fict-s]') as Element | null\n if (!host) continue\n const scopeId = host.getAttribute('data-fict-s')\n if (!scopeId) continue\n\n const snapshot = __fictGetSSRScope(scopeId)\n if (!snapshot) {\n emitSnapshotIssue({\n code: 'scope_snapshot_missing',\n message: `[fict/loader] Missing scope snapshot for ${scopeId}; skipping resumable handler execution.`,\n source: 'event',\n expectedVersion: FICT_SSR_SNAPSHOT_SCHEMA_VERSION,\n scopeId,\n })\n continue\n }\n __fictEnsureScope(scopeId, host, snapshot)\n\n const { url, exportName } = parseQrl(qrl)\n\n // Pre-emptively prevent default on navigations/forms while we await modules\n if (event.cancelable && (event.type === 'click' || event.type === 'submit')) {\n const tag = node.tagName.toLowerCase()\n if (tag === 'a' || tag === 'form') {\n event.preventDefault()\n }\n }\n\n // Resume FIRST to set up reactive bindings BEFORE the handler runs\n if (!hydratedScopes.has(scopeId)) {\n const resumeQrl = host.getAttribute('data-fict-h')\n if (resumeQrl) {\n const { url: resumeUrl, exportName: resumeExport } = parseQrl(resumeQrl)\n const resolvedResumeUrl = resolveModuleUrl(resumeUrl)\n // Load the module to ensure resume functions are registered\n await import(/* @vite-ignore */ resolvedResumeUrl)\n // Get resume function from registry (not module exports)\n const resumeFn = __fictGetResume(resumeExport)\n if (typeof resumeFn === 'function') {\n await (resumeFn as (scopeId: string, host: Element) => unknown)(scopeId, host)\n hydratedScopes.add(scopeId)\n }\n }\n }\n\n // THEN run the handler - now signal updates will trigger DOM updates\n const resolvedUrl = resolveModuleUrl(url)\n const mod = await import(/* @vite-ignore */ resolvedUrl)\n const handler = (mod as Record<string, unknown>)[exportName]\n if (typeof handler === 'function') {\n await (handler as (scopeId: string, ev: Event, el: Element) => unknown)(scopeId, event, node)\n }\n\n return\n }\n}\n\nfunction parseQrl(qrl: string): { url: string; exportName: string } {\n const [ref] = qrl.split('[')\n if (!ref) {\n return { url: '', exportName: 'default' }\n }\n const hashIndex = ref.lastIndexOf('#')\n if (hashIndex === -1) {\n return { url: ref, exportName: 'default' }\n }\n return { url: ref.slice(0, hashIndex), exportName: ref.slice(hashIndex + 1) }\n}\n\nfunction buildEventPath(event: Event): EventTarget[] {\n const path: EventTarget[] = []\n let node: EventTarget | null = event.target\n while (node) {\n path.push(node)\n node = (node as Node).parentNode\n }\n path.push(window)\n return path\n}\n\n// Re-export for handler authors (optional)\nexport { __fictUseLexicalScope } from './resume'\n"]}
|
package/dist/loader.d.cts
CHANGED
package/dist/loader.d.ts
CHANGED
package/dist/loader.js
CHANGED
|
@@ -8,7 +8,7 @@ import {
|
|
|
8
8
|
__fictMergeSSRState,
|
|
9
9
|
__fictSetSSRState,
|
|
10
10
|
__fictUseLexicalScope
|
|
11
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-DIK33H5U.js";
|
|
12
12
|
|
|
13
13
|
// src/loader.ts
|
|
14
14
|
function resolveModuleUrl(url) {
|
|
@@ -275,16 +275,17 @@ function setupHoverPrefetch(doc, delay) {
|
|
|
275
275
|
};
|
|
276
276
|
}
|
|
277
277
|
function prefetchElementQrls(el) {
|
|
278
|
+
const ownerDocument = el.ownerDocument ?? (typeof document !== "undefined" ? document : void 0);
|
|
278
279
|
const eventAttrs = ["on:click", "on:input", "on:change", "on:submit", "on:keydown", "on:keyup"];
|
|
279
280
|
for (const attr of eventAttrs) {
|
|
280
281
|
const qrl = el.getAttribute(attr);
|
|
281
282
|
if (qrl) {
|
|
282
|
-
prefetchQrl(qrl);
|
|
283
|
+
prefetchQrl(qrl, ownerDocument);
|
|
283
284
|
}
|
|
284
285
|
}
|
|
285
286
|
const resumeQrl = el.getAttribute("data-fict-h");
|
|
286
287
|
if (resumeQrl) {
|
|
287
|
-
prefetchQrl(resumeQrl);
|
|
288
|
+
prefetchQrl(resumeQrl, ownerDocument);
|
|
288
289
|
}
|
|
289
290
|
const children = el.querySelectorAll(
|
|
290
291
|
"[on\\:click], [on\\:input], [on\\:change], [on\\:submit], [data-fict-h]"
|
|
@@ -293,32 +294,37 @@ function prefetchElementQrls(el) {
|
|
|
293
294
|
for (const attr of eventAttrs) {
|
|
294
295
|
const qrl = child.getAttribute(attr);
|
|
295
296
|
if (qrl) {
|
|
296
|
-
prefetchQrl(qrl);
|
|
297
|
+
prefetchQrl(qrl, ownerDocument);
|
|
297
298
|
}
|
|
298
299
|
}
|
|
299
300
|
const childResumeQrl = child.getAttribute("data-fict-h");
|
|
300
301
|
if (childResumeQrl) {
|
|
301
|
-
prefetchQrl(childResumeQrl);
|
|
302
|
+
prefetchQrl(childResumeQrl, ownerDocument);
|
|
302
303
|
}
|
|
303
304
|
});
|
|
304
305
|
}
|
|
305
|
-
function prefetchQrl(qrl) {
|
|
306
|
+
function prefetchQrl(qrl, ownerDocument) {
|
|
306
307
|
const { url } = parseQrl(qrl);
|
|
307
308
|
if (!url || prefetchedUrls.has(url)) return;
|
|
308
309
|
prefetchedUrls.add(url);
|
|
309
310
|
const resolvedUrl = resolveModuleUrl(url);
|
|
310
|
-
|
|
311
|
-
|
|
311
|
+
const doc = ownerDocument ?? (typeof document !== "undefined" ? document : void 0);
|
|
312
|
+
if (doc) {
|
|
313
|
+
const link = doc.createElement("link");
|
|
312
314
|
link.rel = "modulepreload";
|
|
313
315
|
link.href = resolvedUrl;
|
|
314
316
|
link.crossOrigin = "anonymous";
|
|
315
|
-
|
|
317
|
+
doc.head?.appendChild(link);
|
|
316
318
|
}
|
|
317
319
|
}
|
|
318
320
|
function handleResumableEvent(event) {
|
|
319
321
|
const promise = handleResumableEventAsync(event);
|
|
320
322
|
pendingHandlers.add(promise);
|
|
321
|
-
promise.
|
|
323
|
+
void promise.catch((error) => {
|
|
324
|
+
if (typeof console !== "undefined" && typeof console.error === "function") {
|
|
325
|
+
console.error("[fict/loader] Failed to handle resumable event.", error);
|
|
326
|
+
}
|
|
327
|
+
}).finally(() => {
|
|
322
328
|
pendingHandlers.delete(promise);
|
|
323
329
|
});
|
|
324
330
|
}
|
|
@@ -341,7 +347,7 @@ async function handleResumableEventAsync(event) {
|
|
|
341
347
|
expectedVersion: FICT_SSR_SNAPSHOT_SCHEMA_VERSION,
|
|
342
348
|
scopeId
|
|
343
349
|
});
|
|
344
|
-
|
|
350
|
+
continue;
|
|
345
351
|
}
|
|
346
352
|
__fictEnsureScope(scopeId, host, snapshot);
|
|
347
353
|
const { url, exportName } = parseQrl(qrl);
|
package/dist/loader.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/loader.ts"],"sourcesContent":["import { DelegatedEvents } from './constants'\nimport {\n FICT_SSR_SNAPSHOT_SCHEMA_VERSION,\n type SSRState,\n __fictEnableResumable,\n __fictEnsureScope,\n __fictGetResume,\n __fictGetSSRScope,\n __fictMergeSSRState,\n __fictSetSSRState,\n __fictUseLexicalScope,\n} from './resume'\n\n// ============================================================================\n// Module Resolution\n// ============================================================================\n\n/**\n * Resolve a module URL through the manifest if available.\n * In production, virtual module URLs (virtual:fict-handler:...) are mapped\n * to their built chunk URLs through the manifest.\n */\nfunction resolveModuleUrl(url: string): string {\n const manifest = (globalThis as Record<string, unknown>).__FICT_MANIFEST__ as\n | Record<string, string>\n | undefined\n\n if (manifest) {\n // Check if the URL (without #fragment) is in the manifest\n const resolved = manifest[url]\n if (resolved) {\n return resolved\n }\n }\n\n return url\n}\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport interface PrefetchStrategy {\n /**\n * Enable visibility-based prefetch using IntersectionObserver.\n * Prefetches modules when interactive elements come into view.\n * @default true\n */\n visibility?: boolean\n /**\n * Root margin for IntersectionObserver (e.g., '200px' to prefetch earlier).\n * @default '200px'\n */\n visibilityMargin?: string\n /**\n * Enable hover-based prefetch using pointerover events.\n * Prefetches modules when user hovers over interactive elements.\n * @default true\n */\n hover?: boolean\n /**\n * Delay in ms before prefetching on hover (debounce rapid movements).\n * @default 50\n */\n hoverDelay?: number\n}\n\nexport interface ResumableLoaderOptions {\n document?: Document\n snapshotScriptId?: string\n events?: string[]\n /**\n * Receives structured snapshot/resume issues detected by the loader.\n * Useful for telemetry and fail-safe fallback orchestration.\n */\n onSnapshotIssue?: (issue: SnapshotIssue) => void\n /**\n * Prefetch strategy configuration.\n * Set to false to disable all prefetching.\n * @default { visibility: true, hover: true }\n */\n prefetch?: PrefetchStrategy | false\n}\n\nexport type SnapshotIssueCode =\n | 'snapshot_parse_error'\n | 'snapshot_invalid_shape'\n | 'snapshot_unsupported_version'\n | 'scope_snapshot_missing'\n\nexport interface SnapshotIssue {\n code: SnapshotIssueCode\n message: string\n source: string\n expectedVersion: number\n actualVersion?: number\n scopeId?: string\n}\n\n// ============================================================================\n// State\n// ============================================================================\n\nconst hydratedScopes = new Set<string>()\nconst prefetchedUrls = new Set<string>()\nlet prefetchCleanup: (() => void) | null = null\nlet eventListenerCleanup: (() => void) | null = null\nlet snapshotObserver: MutationObserver | null = null\nconst processedSnapshots = new Set<HTMLScriptElement>()\nlet snapshotIssueHandler: ((issue: SnapshotIssue) => void) | null = null\nconst emittedIssueKeys = new Set<string>()\n\n/**\n * Reset the hydrated scopes set. Useful for testing.\n */\nexport function resetHydratedScopes(): void {\n hydratedScopes.clear()\n}\n\n/**\n * Reset the prefetched URLs set. Useful for testing.\n */\nexport function resetPrefetchedUrls(): void {\n prefetchedUrls.clear()\n}\n\n/**\n * Set of pending handler promises. Used for testing to wait for all handlers to complete.\n */\nconst pendingHandlers = new Set<Promise<void>>()\n\n/**\n * Wait for all pending event handlers to complete. Useful for testing.\n */\nexport async function waitForPendingHandlers(): Promise<void> {\n if (pendingHandlers.size === 0) return\n await Promise.allSettled([...pendingHandlers])\n}\n\n/**\n * Clean up all registered event listeners. Useful for testing.\n */\nexport function cleanupEventListeners(): void {\n if (eventListenerCleanup) {\n eventListenerCleanup()\n eventListenerCleanup = null\n }\n}\n\n// ============================================================================\n// Main Entry Point\n// ============================================================================\n\nexport function installResumableLoader(options: ResumableLoaderOptions = {}): void {\n const doc = options.document ?? window.document\n const scriptId = options.snapshotScriptId ?? '__FICT_SNAPSHOT__'\n snapshotIssueHandler = options.onSnapshotIssue ?? null\n\n // Reset hydrated scopes for fresh loader installation\n hydratedScopes.clear()\n prefetchedUrls.clear()\n processedSnapshots.clear()\n emittedIssueKeys.clear()\n __fictSetSSRState(null)\n\n // Clean up previous event listeners\n if (eventListenerCleanup) {\n eventListenerCleanup()\n eventListenerCleanup = null\n }\n\n // Clean up previous prefetch handlers\n if (prefetchCleanup) {\n prefetchCleanup()\n prefetchCleanup = null\n }\n\n if (snapshotObserver) {\n snapshotObserver.disconnect()\n snapshotObserver = null\n }\n\n const snapshotEl = doc.getElementById(scriptId)\n if (snapshotEl?.textContent) {\n const state = parseSnapshotText(snapshotEl.textContent, `#${scriptId}`)\n if (state) {\n __fictSetSSRState(state)\n }\n }\n\n const snapshotScripts = doc.querySelectorAll(\n 'script[type=\"application/json\"][data-fict-snapshot]',\n )\n for (const script of Array.from(snapshotScripts)) {\n parseSnapshotScript(script as HTMLScriptElement)\n }\n\n if (typeof MutationObserver !== 'undefined') {\n snapshotObserver = new MutationObserver(mutations => {\n for (const mutation of mutations) {\n for (const node of Array.from(mutation.addedNodes)) {\n if (!(node instanceof Element)) continue\n if (node.tagName === 'SCRIPT') {\n const script = node as HTMLScriptElement\n if (isSnapshotScript(script)) {\n parseSnapshotScript(script)\n }\n }\n const nested = node.querySelectorAll?.(\n 'script[type=\"application/json\"][data-fict-snapshot]',\n )\n if (nested && nested.length) {\n for (const script of Array.from(nested)) {\n parseSnapshotScript(script as HTMLScriptElement)\n }\n }\n }\n }\n })\n snapshotObserver.observe(doc.documentElement ?? doc, { childList: true, subtree: true })\n }\n\n __fictEnableResumable()\n\n const events = options.events ?? Array.from(DelegatedEvents)\n for (const eventName of events) {\n doc.addEventListener(eventName, handleResumableEvent, true)\n }\n\n // Store cleanup function for event listeners\n eventListenerCleanup = () => {\n for (const eventName of events) {\n doc.removeEventListener(eventName, handleResumableEvent, true)\n }\n }\n\n // Setup prefetch if enabled\n if (options.prefetch !== false) {\n prefetchCleanup = setupPrefetch(doc, options.prefetch ?? {})\n }\n}\n\nfunction isSnapshotScript(script: HTMLScriptElement): boolean {\n return script.type === 'application/json' && script.hasAttribute('data-fict-snapshot')\n}\n\nfunction parseSnapshotScript(script: HTMLScriptElement): void {\n if (processedSnapshots.has(script)) return\n processedSnapshots.add(script)\n const text = script.textContent\n if (!text) return\n const source = script.id ? `#${script.id}` : '<script[data-fict-snapshot]>'\n const state = parseSnapshotText(text, source)\n if (state) {\n __fictMergeSSRState(state)\n }\n}\n\nfunction parseSnapshotText(text: string, source: string): SSRState | null {\n let parsed: unknown\n try {\n parsed = JSON.parse(text)\n } catch {\n emitSnapshotIssue({\n code: 'snapshot_parse_error',\n message: '[fict/loader] Failed to parse SSR snapshot JSON.',\n source,\n expectedVersion: FICT_SSR_SNAPSHOT_SCHEMA_VERSION,\n })\n return null\n }\n\n return normalizeSnapshotState(parsed, source)\n}\n\nfunction normalizeSnapshotState(value: unknown, source: string): SSRState | null {\n if (!isRecord(value)) {\n emitSnapshotIssue({\n code: 'snapshot_invalid_shape',\n message: '[fict/loader] Snapshot payload must be an object.',\n source,\n expectedVersion: FICT_SSR_SNAPSHOT_SCHEMA_VERSION,\n })\n return null\n }\n\n const rawVersion = value.v\n const version = rawVersion === undefined ? FICT_SSR_SNAPSHOT_SCHEMA_VERSION : rawVersion\n if (!Number.isInteger(version) || version !== FICT_SSR_SNAPSHOT_SCHEMA_VERSION) {\n const versionIssue: SnapshotIssue = {\n code: 'snapshot_unsupported_version',\n message: `[fict/loader] Snapshot schema version ${String(version)} is not supported by this runtime.`,\n source,\n expectedVersion: FICT_SSR_SNAPSHOT_SCHEMA_VERSION,\n }\n if (typeof version === 'number') {\n versionIssue.actualVersion = version\n }\n emitSnapshotIssue({\n ...versionIssue,\n })\n return null\n }\n\n const scopes = value.scopes\n if (!isRecord(scopes)) {\n emitSnapshotIssue({\n code: 'snapshot_invalid_shape',\n message: '[fict/loader] Snapshot payload is missing a valid `scopes` object.',\n source,\n expectedVersion: FICT_SSR_SNAPSHOT_SCHEMA_VERSION,\n })\n return null\n }\n\n return { v: FICT_SSR_SNAPSHOT_SCHEMA_VERSION, scopes: scopes as SSRState['scopes'] }\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value)\n}\n\nfunction emitSnapshotIssue(issue: SnapshotIssue): void {\n const key =\n `${issue.code}|${issue.source}|${issue.scopeId ?? ''}|` +\n `${issue.actualVersion ?? ''}|${issue.expectedVersion}`\n if (emittedIssueKeys.has(key)) return\n emittedIssueKeys.add(key)\n\n snapshotIssueHandler?.(issue)\n\n if (typeof console !== 'undefined' && typeof console.warn === 'function') {\n console.warn(issue.message)\n }\n}\n\n// ============================================================================\n// Prefetch Implementation\n// ============================================================================\n\nfunction setupPrefetch(doc: Document, strategy: PrefetchStrategy): () => void {\n const cleanupFns: (() => void)[] = []\n\n // Visibility-based prefetch\n if (strategy.visibility !== false) {\n const cleanup = setupVisibilityPrefetch(doc, strategy.visibilityMargin ?? '200px')\n cleanupFns.push(cleanup)\n }\n\n // Hover-based prefetch\n if (strategy.hover !== false) {\n const cleanup = setupHoverPrefetch(doc, strategy.hoverDelay ?? 50)\n cleanupFns.push(cleanup)\n }\n\n return () => {\n for (const cleanup of cleanupFns) {\n cleanup()\n }\n }\n}\n\nfunction setupVisibilityPrefetch(doc: Document, rootMargin: string): () => void {\n // Check if IntersectionObserver is available\n if (typeof IntersectionObserver === 'undefined') {\n return () => {}\n }\n\n const observer = new IntersectionObserver(\n entries => {\n for (const entry of entries) {\n if (entry.isIntersecting) {\n const el = entry.target as Element\n prefetchElementQrls(el)\n // Stop observing after prefetch\n observer.unobserve(el)\n }\n }\n },\n { rootMargin },\n )\n\n // Observe all elements with on:* attributes\n const interactiveElements = doc.querySelectorAll(\n '[on\\\\:click], [on\\\\:input], [on\\\\:change], [on\\\\:submit], [on\\\\:keydown], [on\\\\:keyup]',\n )\n interactiveElements.forEach(el => observer.observe(el))\n\n // Also observe elements with data-fict-h (resumable components)\n const resumableHosts = doc.querySelectorAll('[data-fict-h]')\n resumableHosts.forEach(el => observer.observe(el))\n\n return () => {\n observer.disconnect()\n }\n}\n\nfunction setupHoverPrefetch(doc: Document, delay: number): () => void {\n let hoverTimeout: ReturnType<typeof setTimeout> | null = null\n let lastHoveredElement: Element | null = null\n\n const handlePointerOver = (event: Event) => {\n const target = event.target\n if (!(target instanceof Element)) return\n\n // Find the closest element with interactive attributes\n const interactiveEl =\n target.closest('[on\\\\:click]') ||\n target.closest('[on\\\\:input]') ||\n target.closest('[on\\\\:change]') ||\n target.closest('[on\\\\:submit]') ||\n target.closest('[data-fict-h]')\n\n if (!interactiveEl || interactiveEl === lastHoveredElement) return\n\n lastHoveredElement = interactiveEl\n\n // Clear previous timeout\n if (hoverTimeout) {\n clearTimeout(hoverTimeout)\n }\n\n // Debounce prefetch\n hoverTimeout = setTimeout(() => {\n prefetchElementQrls(interactiveEl)\n }, delay)\n }\n\n const handlePointerOut = () => {\n if (hoverTimeout) {\n clearTimeout(hoverTimeout)\n hoverTimeout = null\n }\n lastHoveredElement = null\n }\n\n doc.addEventListener('pointerover', handlePointerOver, { passive: true })\n doc.addEventListener('pointerout', handlePointerOut, { passive: true })\n\n return () => {\n doc.removeEventListener('pointerover', handlePointerOver)\n doc.removeEventListener('pointerout', handlePointerOut)\n if (hoverTimeout) {\n clearTimeout(hoverTimeout)\n }\n }\n}\n\nfunction prefetchElementQrls(el: Element): void {\n // Prefetch event handler QRLs\n const eventAttrs = ['on:click', 'on:input', 'on:change', 'on:submit', 'on:keydown', 'on:keyup']\n for (const attr of eventAttrs) {\n const qrl = el.getAttribute(attr)\n if (qrl) {\n prefetchQrl(qrl)\n }\n }\n\n // Prefetch resume handler QRL\n const resumeQrl = el.getAttribute('data-fict-h')\n if (resumeQrl) {\n prefetchQrl(resumeQrl)\n }\n\n // Also check children for nested QRLs\n const children = el.querySelectorAll(\n '[on\\\\:click], [on\\\\:input], [on\\\\:change], [on\\\\:submit], [data-fict-h]',\n )\n children.forEach(child => {\n for (const attr of eventAttrs) {\n const qrl = child.getAttribute(attr)\n if (qrl) {\n prefetchQrl(qrl)\n }\n }\n const childResumeQrl = child.getAttribute('data-fict-h')\n if (childResumeQrl) {\n prefetchQrl(childResumeQrl)\n }\n })\n}\n\nfunction prefetchQrl(qrl: string): void {\n const { url } = parseQrl(qrl)\n if (!url || prefetchedUrls.has(url)) return\n\n prefetchedUrls.add(url)\n\n // Resolve through manifest for production builds\n const resolvedUrl = resolveModuleUrl(url)\n\n // Use modulepreload link for best browser support\n if (typeof document !== 'undefined') {\n const link = document.createElement('link')\n link.rel = 'modulepreload'\n link.href = resolvedUrl\n link.crossOrigin = 'anonymous'\n document.head.appendChild(link)\n }\n}\n\n// ============================================================================\n\n/**\n * Wrapper that tracks the async handler promise for testing.\n */\nfunction handleResumableEvent(event: Event): void {\n const promise = handleResumableEventAsync(event)\n pendingHandlers.add(promise)\n promise.finally(() => {\n pendingHandlers.delete(promise)\n })\n}\n\nasync function handleResumableEventAsync(event: Event): Promise<void> {\n const path =\n typeof event.composedPath === 'function' ? event.composedPath() : buildEventPath(event)\n\n for (const node of path) {\n if (!(node instanceof Element)) continue\n const qrl = node.getAttribute(`on:${event.type}`)\n if (!qrl) continue\n\n const host = node.closest('[data-fict-s]') as Element | null\n if (!host) continue\n const scopeId = host.getAttribute('data-fict-s')\n if (!scopeId) continue\n\n const snapshot = __fictGetSSRScope(scopeId)\n if (!snapshot) {\n emitSnapshotIssue({\n code: 'scope_snapshot_missing',\n message: `[fict/loader] Missing scope snapshot for ${scopeId}; skipping resumable handler execution.`,\n source: 'event',\n expectedVersion: FICT_SSR_SNAPSHOT_SCHEMA_VERSION,\n scopeId,\n })\n return\n }\n __fictEnsureScope(scopeId, host, snapshot)\n\n const { url, exportName } = parseQrl(qrl)\n\n // Pre-emptively prevent default on navigations/forms while we await modules\n if (event.cancelable && (event.type === 'click' || event.type === 'submit')) {\n const tag = node.tagName.toLowerCase()\n if (tag === 'a' || tag === 'form') {\n event.preventDefault()\n }\n }\n\n // Resume FIRST to set up reactive bindings BEFORE the handler runs\n if (!hydratedScopes.has(scopeId)) {\n const resumeQrl = host.getAttribute('data-fict-h')\n if (resumeQrl) {\n const { url: resumeUrl, exportName: resumeExport } = parseQrl(resumeQrl)\n const resolvedResumeUrl = resolveModuleUrl(resumeUrl)\n // Load the module to ensure resume functions are registered\n await import(/* @vite-ignore */ resolvedResumeUrl)\n // Get resume function from registry (not module exports)\n const resumeFn = __fictGetResume(resumeExport)\n if (typeof resumeFn === 'function') {\n await (resumeFn as (scopeId: string, host: Element) => unknown)(scopeId, host)\n hydratedScopes.add(scopeId)\n }\n }\n }\n\n // THEN run the handler - now signal updates will trigger DOM updates\n const resolvedUrl = resolveModuleUrl(url)\n const mod = await import(/* @vite-ignore */ resolvedUrl)\n const handler = (mod as Record<string, unknown>)[exportName]\n if (typeof handler === 'function') {\n await (handler as (scopeId: string, ev: Event, el: Element) => unknown)(scopeId, event, node)\n }\n\n return\n }\n}\n\nfunction parseQrl(qrl: string): { url: string; exportName: string } {\n const [ref] = qrl.split('[')\n if (!ref) {\n return { url: '', exportName: 'default' }\n }\n const hashIndex = ref.lastIndexOf('#')\n if (hashIndex === -1) {\n return { url: ref, exportName: 'default' }\n }\n return { url: ref.slice(0, hashIndex), exportName: ref.slice(hashIndex + 1) }\n}\n\nfunction buildEventPath(event: Event): EventTarget[] {\n const path: EventTarget[] = []\n let node: EventTarget | null = event.target\n while (node) {\n path.push(node)\n node = (node as Node).parentNode\n }\n path.push(window)\n return path\n}\n\n// Re-export for handler authors (optional)\nexport { __fictUseLexicalScope } from './resume'\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/loader.ts"],"sourcesContent":["import { DelegatedEvents } from './constants'\nimport {\n FICT_SSR_SNAPSHOT_SCHEMA_VERSION,\n type SSRState,\n __fictEnableResumable,\n __fictEnsureScope,\n __fictGetResume,\n __fictGetSSRScope,\n __fictMergeSSRState,\n __fictSetSSRState,\n __fictUseLexicalScope,\n} from './resume'\n\n// ============================================================================\n// Module Resolution\n// ============================================================================\n\n/**\n * Resolve a module URL through the manifest if available.\n * In production, virtual module URLs (virtual:fict-handler:...) are mapped\n * to their built chunk URLs through the manifest.\n */\nfunction resolveModuleUrl(url: string): string {\n const manifest = (globalThis as Record<string, unknown>).__FICT_MANIFEST__ as\n | Record<string, string>\n | undefined\n\n if (manifest) {\n // Check if the URL (without #fragment) is in the manifest\n const resolved = manifest[url]\n if (resolved) {\n return resolved\n }\n }\n\n return url\n}\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport interface PrefetchStrategy {\n /**\n * Enable visibility-based prefetch using IntersectionObserver.\n * Prefetches modules when interactive elements come into view.\n * @default true\n */\n visibility?: boolean\n /**\n * Root margin for IntersectionObserver (e.g., '200px' to prefetch earlier).\n * @default '200px'\n */\n visibilityMargin?: string\n /**\n * Enable hover-based prefetch using pointerover events.\n * Prefetches modules when user hovers over interactive elements.\n * @default true\n */\n hover?: boolean\n /**\n * Delay in ms before prefetching on hover (debounce rapid movements).\n * @default 50\n */\n hoverDelay?: number\n}\n\nexport interface ResumableLoaderOptions {\n document?: Document\n snapshotScriptId?: string\n events?: string[]\n /**\n * Receives structured snapshot/resume issues detected by the loader.\n * Useful for telemetry and fail-safe fallback orchestration.\n */\n onSnapshotIssue?: (issue: SnapshotIssue) => void\n /**\n * Prefetch strategy configuration.\n * Set to false to disable all prefetching.\n * @default { visibility: true, hover: true }\n */\n prefetch?: PrefetchStrategy | false\n}\n\nexport type SnapshotIssueCode =\n | 'snapshot_parse_error'\n | 'snapshot_invalid_shape'\n | 'snapshot_unsupported_version'\n | 'scope_snapshot_missing'\n\nexport interface SnapshotIssue {\n code: SnapshotIssueCode\n message: string\n source: string\n expectedVersion: number\n actualVersion?: number\n scopeId?: string\n}\n\n// ============================================================================\n// State\n// ============================================================================\n\nconst hydratedScopes = new Set<string>()\nconst prefetchedUrls = new Set<string>()\nlet prefetchCleanup: (() => void) | null = null\nlet eventListenerCleanup: (() => void) | null = null\nlet snapshotObserver: MutationObserver | null = null\nconst processedSnapshots = new Set<HTMLScriptElement>()\nlet snapshotIssueHandler: ((issue: SnapshotIssue) => void) | null = null\nconst emittedIssueKeys = new Set<string>()\n\n/**\n * Reset the hydrated scopes set. Useful for testing.\n */\nexport function resetHydratedScopes(): void {\n hydratedScopes.clear()\n}\n\n/**\n * Reset the prefetched URLs set. Useful for testing.\n */\nexport function resetPrefetchedUrls(): void {\n prefetchedUrls.clear()\n}\n\n/**\n * Set of pending handler promises. Used for testing to wait for all handlers to complete.\n */\nconst pendingHandlers = new Set<Promise<void>>()\n\n/**\n * Wait for all pending event handlers to complete. Useful for testing.\n */\nexport async function waitForPendingHandlers(): Promise<void> {\n if (pendingHandlers.size === 0) return\n await Promise.allSettled([...pendingHandlers])\n}\n\n/**\n * Clean up all registered event listeners. Useful for testing.\n */\nexport function cleanupEventListeners(): void {\n if (eventListenerCleanup) {\n eventListenerCleanup()\n eventListenerCleanup = null\n }\n}\n\n// ============================================================================\n// Main Entry Point\n// ============================================================================\n\nexport function installResumableLoader(options: ResumableLoaderOptions = {}): void {\n const doc = options.document ?? window.document\n const scriptId = options.snapshotScriptId ?? '__FICT_SNAPSHOT__'\n snapshotIssueHandler = options.onSnapshotIssue ?? null\n\n // Reset hydrated scopes for fresh loader installation\n hydratedScopes.clear()\n prefetchedUrls.clear()\n processedSnapshots.clear()\n emittedIssueKeys.clear()\n __fictSetSSRState(null)\n\n // Clean up previous event listeners\n if (eventListenerCleanup) {\n eventListenerCleanup()\n eventListenerCleanup = null\n }\n\n // Clean up previous prefetch handlers\n if (prefetchCleanup) {\n prefetchCleanup()\n prefetchCleanup = null\n }\n\n if (snapshotObserver) {\n snapshotObserver.disconnect()\n snapshotObserver = null\n }\n\n const snapshotEl = doc.getElementById(scriptId)\n if (snapshotEl?.textContent) {\n const state = parseSnapshotText(snapshotEl.textContent, `#${scriptId}`)\n if (state) {\n __fictSetSSRState(state)\n }\n }\n\n const snapshotScripts = doc.querySelectorAll(\n 'script[type=\"application/json\"][data-fict-snapshot]',\n )\n for (const script of Array.from(snapshotScripts)) {\n parseSnapshotScript(script as HTMLScriptElement)\n }\n\n if (typeof MutationObserver !== 'undefined') {\n snapshotObserver = new MutationObserver(mutations => {\n for (const mutation of mutations) {\n for (const node of Array.from(mutation.addedNodes)) {\n if (!(node instanceof Element)) continue\n if (node.tagName === 'SCRIPT') {\n const script = node as HTMLScriptElement\n if (isSnapshotScript(script)) {\n parseSnapshotScript(script)\n }\n }\n const nested = node.querySelectorAll?.(\n 'script[type=\"application/json\"][data-fict-snapshot]',\n )\n if (nested && nested.length) {\n for (const script of Array.from(nested)) {\n parseSnapshotScript(script as HTMLScriptElement)\n }\n }\n }\n }\n })\n snapshotObserver.observe(doc.documentElement ?? doc, { childList: true, subtree: true })\n }\n\n __fictEnableResumable()\n\n const events = options.events ?? Array.from(DelegatedEvents)\n for (const eventName of events) {\n doc.addEventListener(eventName, handleResumableEvent, true)\n }\n\n // Store cleanup function for event listeners\n eventListenerCleanup = () => {\n for (const eventName of events) {\n doc.removeEventListener(eventName, handleResumableEvent, true)\n }\n }\n\n // Setup prefetch if enabled\n if (options.prefetch !== false) {\n prefetchCleanup = setupPrefetch(doc, options.prefetch ?? {})\n }\n}\n\nfunction isSnapshotScript(script: HTMLScriptElement): boolean {\n return script.type === 'application/json' && script.hasAttribute('data-fict-snapshot')\n}\n\nfunction parseSnapshotScript(script: HTMLScriptElement): void {\n if (processedSnapshots.has(script)) return\n processedSnapshots.add(script)\n const text = script.textContent\n if (!text) return\n const source = script.id ? `#${script.id}` : '<script[data-fict-snapshot]>'\n const state = parseSnapshotText(text, source)\n if (state) {\n __fictMergeSSRState(state)\n }\n}\n\nfunction parseSnapshotText(text: string, source: string): SSRState | null {\n let parsed: unknown\n try {\n parsed = JSON.parse(text)\n } catch {\n emitSnapshotIssue({\n code: 'snapshot_parse_error',\n message: '[fict/loader] Failed to parse SSR snapshot JSON.',\n source,\n expectedVersion: FICT_SSR_SNAPSHOT_SCHEMA_VERSION,\n })\n return null\n }\n\n return normalizeSnapshotState(parsed, source)\n}\n\nfunction normalizeSnapshotState(value: unknown, source: string): SSRState | null {\n if (!isRecord(value)) {\n emitSnapshotIssue({\n code: 'snapshot_invalid_shape',\n message: '[fict/loader] Snapshot payload must be an object.',\n source,\n expectedVersion: FICT_SSR_SNAPSHOT_SCHEMA_VERSION,\n })\n return null\n }\n\n const rawVersion = value.v\n const version = rawVersion === undefined ? FICT_SSR_SNAPSHOT_SCHEMA_VERSION : rawVersion\n if (!Number.isInteger(version) || version !== FICT_SSR_SNAPSHOT_SCHEMA_VERSION) {\n const versionIssue: SnapshotIssue = {\n code: 'snapshot_unsupported_version',\n message: `[fict/loader] Snapshot schema version ${String(version)} is not supported by this runtime.`,\n source,\n expectedVersion: FICT_SSR_SNAPSHOT_SCHEMA_VERSION,\n }\n if (typeof version === 'number') {\n versionIssue.actualVersion = version\n }\n emitSnapshotIssue({\n ...versionIssue,\n })\n return null\n }\n\n const scopes = value.scopes\n if (!isRecord(scopes)) {\n emitSnapshotIssue({\n code: 'snapshot_invalid_shape',\n message: '[fict/loader] Snapshot payload is missing a valid `scopes` object.',\n source,\n expectedVersion: FICT_SSR_SNAPSHOT_SCHEMA_VERSION,\n })\n return null\n }\n\n return { v: FICT_SSR_SNAPSHOT_SCHEMA_VERSION, scopes: scopes as SSRState['scopes'] }\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value)\n}\n\nfunction emitSnapshotIssue(issue: SnapshotIssue): void {\n const key =\n `${issue.code}|${issue.source}|${issue.scopeId ?? ''}|` +\n `${issue.actualVersion ?? ''}|${issue.expectedVersion}`\n if (emittedIssueKeys.has(key)) return\n emittedIssueKeys.add(key)\n\n snapshotIssueHandler?.(issue)\n\n if (typeof console !== 'undefined' && typeof console.warn === 'function') {\n console.warn(issue.message)\n }\n}\n\n// ============================================================================\n// Prefetch Implementation\n// ============================================================================\n\nfunction setupPrefetch(doc: Document, strategy: PrefetchStrategy): () => void {\n const cleanupFns: (() => void)[] = []\n\n // Visibility-based prefetch\n if (strategy.visibility !== false) {\n const cleanup = setupVisibilityPrefetch(doc, strategy.visibilityMargin ?? '200px')\n cleanupFns.push(cleanup)\n }\n\n // Hover-based prefetch\n if (strategy.hover !== false) {\n const cleanup = setupHoverPrefetch(doc, strategy.hoverDelay ?? 50)\n cleanupFns.push(cleanup)\n }\n\n return () => {\n for (const cleanup of cleanupFns) {\n cleanup()\n }\n }\n}\n\nfunction setupVisibilityPrefetch(doc: Document, rootMargin: string): () => void {\n // Check if IntersectionObserver is available\n if (typeof IntersectionObserver === 'undefined') {\n return () => {}\n }\n\n const observer = new IntersectionObserver(\n entries => {\n for (const entry of entries) {\n if (entry.isIntersecting) {\n const el = entry.target as Element\n prefetchElementQrls(el)\n // Stop observing after prefetch\n observer.unobserve(el)\n }\n }\n },\n { rootMargin },\n )\n\n // Observe all elements with on:* attributes\n const interactiveElements = doc.querySelectorAll(\n '[on\\\\:click], [on\\\\:input], [on\\\\:change], [on\\\\:submit], [on\\\\:keydown], [on\\\\:keyup]',\n )\n interactiveElements.forEach(el => observer.observe(el))\n\n // Also observe elements with data-fict-h (resumable components)\n const resumableHosts = doc.querySelectorAll('[data-fict-h]')\n resumableHosts.forEach(el => observer.observe(el))\n\n return () => {\n observer.disconnect()\n }\n}\n\nfunction setupHoverPrefetch(doc: Document, delay: number): () => void {\n let hoverTimeout: ReturnType<typeof setTimeout> | null = null\n let lastHoveredElement: Element | null = null\n\n const handlePointerOver = (event: Event) => {\n const target = event.target\n if (!(target instanceof Element)) return\n\n // Find the closest element with interactive attributes\n const interactiveEl =\n target.closest('[on\\\\:click]') ||\n target.closest('[on\\\\:input]') ||\n target.closest('[on\\\\:change]') ||\n target.closest('[on\\\\:submit]') ||\n target.closest('[data-fict-h]')\n\n if (!interactiveEl || interactiveEl === lastHoveredElement) return\n\n lastHoveredElement = interactiveEl\n\n // Clear previous timeout\n if (hoverTimeout) {\n clearTimeout(hoverTimeout)\n }\n\n // Debounce prefetch\n hoverTimeout = setTimeout(() => {\n prefetchElementQrls(interactiveEl)\n }, delay)\n }\n\n const handlePointerOut = () => {\n if (hoverTimeout) {\n clearTimeout(hoverTimeout)\n hoverTimeout = null\n }\n lastHoveredElement = null\n }\n\n doc.addEventListener('pointerover', handlePointerOver, { passive: true })\n doc.addEventListener('pointerout', handlePointerOut, { passive: true })\n\n return () => {\n doc.removeEventListener('pointerover', handlePointerOver)\n doc.removeEventListener('pointerout', handlePointerOut)\n if (hoverTimeout) {\n clearTimeout(hoverTimeout)\n }\n }\n}\n\nfunction prefetchElementQrls(el: Element): void {\n const ownerDocument = el.ownerDocument ?? (typeof document !== 'undefined' ? document : undefined)\n // Prefetch event handler QRLs\n const eventAttrs = ['on:click', 'on:input', 'on:change', 'on:submit', 'on:keydown', 'on:keyup']\n for (const attr of eventAttrs) {\n const qrl = el.getAttribute(attr)\n if (qrl) {\n prefetchQrl(qrl, ownerDocument)\n }\n }\n\n // Prefetch resume handler QRL\n const resumeQrl = el.getAttribute('data-fict-h')\n if (resumeQrl) {\n prefetchQrl(resumeQrl, ownerDocument)\n }\n\n // Also check children for nested QRLs\n const children = el.querySelectorAll(\n '[on\\\\:click], [on\\\\:input], [on\\\\:change], [on\\\\:submit], [data-fict-h]',\n )\n children.forEach(child => {\n for (const attr of eventAttrs) {\n const qrl = child.getAttribute(attr)\n if (qrl) {\n prefetchQrl(qrl, ownerDocument)\n }\n }\n const childResumeQrl = child.getAttribute('data-fict-h')\n if (childResumeQrl) {\n prefetchQrl(childResumeQrl, ownerDocument)\n }\n })\n}\n\nfunction prefetchQrl(qrl: string, ownerDocument?: Document): void {\n const { url } = parseQrl(qrl)\n if (!url || prefetchedUrls.has(url)) return\n\n prefetchedUrls.add(url)\n\n // Resolve through manifest for production builds\n const resolvedUrl = resolveModuleUrl(url)\n\n // Use modulepreload link for best browser support\n const doc = ownerDocument ?? (typeof document !== 'undefined' ? document : undefined)\n if (doc) {\n const link = doc.createElement('link')\n link.rel = 'modulepreload'\n link.href = resolvedUrl\n link.crossOrigin = 'anonymous'\n doc.head?.appendChild(link)\n }\n}\n\n// ============================================================================\n\n/**\n * Wrapper that tracks the async handler promise for testing.\n */\nfunction handleResumableEvent(event: Event): void {\n const promise = handleResumableEventAsync(event)\n pendingHandlers.add(promise)\n void promise\n .catch(error => {\n if (typeof console !== 'undefined' && typeof console.error === 'function') {\n console.error('[fict/loader] Failed to handle resumable event.', error)\n }\n })\n .finally(() => {\n pendingHandlers.delete(promise)\n })\n}\n\nasync function handleResumableEventAsync(event: Event): Promise<void> {\n const path =\n typeof event.composedPath === 'function' ? event.composedPath() : buildEventPath(event)\n\n for (const node of path) {\n if (!(node instanceof Element)) continue\n const qrl = node.getAttribute(`on:${event.type}`)\n if (!qrl) continue\n\n const host = node.closest('[data-fict-s]') as Element | null\n if (!host) continue\n const scopeId = host.getAttribute('data-fict-s')\n if (!scopeId) continue\n\n const snapshot = __fictGetSSRScope(scopeId)\n if (!snapshot) {\n emitSnapshotIssue({\n code: 'scope_snapshot_missing',\n message: `[fict/loader] Missing scope snapshot for ${scopeId}; skipping resumable handler execution.`,\n source: 'event',\n expectedVersion: FICT_SSR_SNAPSHOT_SCHEMA_VERSION,\n scopeId,\n })\n continue\n }\n __fictEnsureScope(scopeId, host, snapshot)\n\n const { url, exportName } = parseQrl(qrl)\n\n // Pre-emptively prevent default on navigations/forms while we await modules\n if (event.cancelable && (event.type === 'click' || event.type === 'submit')) {\n const tag = node.tagName.toLowerCase()\n if (tag === 'a' || tag === 'form') {\n event.preventDefault()\n }\n }\n\n // Resume FIRST to set up reactive bindings BEFORE the handler runs\n if (!hydratedScopes.has(scopeId)) {\n const resumeQrl = host.getAttribute('data-fict-h')\n if (resumeQrl) {\n const { url: resumeUrl, exportName: resumeExport } = parseQrl(resumeQrl)\n const resolvedResumeUrl = resolveModuleUrl(resumeUrl)\n // Load the module to ensure resume functions are registered\n await import(/* @vite-ignore */ resolvedResumeUrl)\n // Get resume function from registry (not module exports)\n const resumeFn = __fictGetResume(resumeExport)\n if (typeof resumeFn === 'function') {\n await (resumeFn as (scopeId: string, host: Element) => unknown)(scopeId, host)\n hydratedScopes.add(scopeId)\n }\n }\n }\n\n // THEN run the handler - now signal updates will trigger DOM updates\n const resolvedUrl = resolveModuleUrl(url)\n const mod = await import(/* @vite-ignore */ resolvedUrl)\n const handler = (mod as Record<string, unknown>)[exportName]\n if (typeof handler === 'function') {\n await (handler as (scopeId: string, ev: Event, el: Element) => unknown)(scopeId, event, node)\n }\n\n return\n }\n}\n\nfunction parseQrl(qrl: string): { url: string; exportName: string } {\n const [ref] = qrl.split('[')\n if (!ref) {\n return { url: '', exportName: 'default' }\n }\n const hashIndex = ref.lastIndexOf('#')\n if (hashIndex === -1) {\n return { url: ref, exportName: 'default' }\n }\n return { url: ref.slice(0, hashIndex), exportName: ref.slice(hashIndex + 1) }\n}\n\nfunction buildEventPath(event: Event): EventTarget[] {\n const path: EventTarget[] = []\n let node: EventTarget | null = event.target\n while (node) {\n path.push(node)\n node = (node as Node).parentNode\n }\n path.push(window)\n return path\n}\n\n// Re-export for handler authors (optional)\nexport { __fictUseLexicalScope } from './resume'\n"],"mappings":"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","names":[]}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { M as MemoOptions } from './signal-
|
|
2
|
-
import { F as FictNode, R as DOMElement } from './binding-
|
|
1
|
+
import { M as MemoOptions } from './signal-Z4KkDk9h.cjs';
|
|
2
|
+
import { F as FictNode, R as DOMElement } from './binding-FRyTeLDn.cjs';
|
|
3
3
|
|
|
4
4
|
type Memo<T> = () => T;
|
|
5
5
|
declare function createMemo<T>(fn: () => T, options?: MemoOptions<T>): Memo<T>;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { M as MemoOptions } from './signal-
|
|
2
|
-
import { F as FictNode, R as DOMElement } from './binding-
|
|
1
|
+
import { M as MemoOptions } from './signal-Z4KkDk9h.js';
|
|
2
|
+
import { F as FictNode, R as DOMElement } from './binding-DcnhUSQK.js';
|
|
3
3
|
|
|
4
4
|
type Memo<T> = () => T;
|
|
5
5
|
declare function createMemo<T>(fn: () => T, options?: MemoOptions<T>): Memo<T>;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as SignalOptions, S as SignalAccessor, M as MemoOptions, C as ComputedAccessor } from './signal-
|
|
1
|
+
import { a as SignalOptions, S as SignalAccessor, M as MemoOptions, C as ComputedAccessor, E as EffectOptions } from './signal-Z4KkDk9h.js';
|
|
2
2
|
|
|
3
3
|
interface HookContext {
|
|
4
4
|
slots: unknown[];
|
|
@@ -17,7 +17,7 @@ declare function __fictPopContext(): void;
|
|
|
17
17
|
declare function __fictResetContext(): void;
|
|
18
18
|
declare function __fictUseSignal<T>(ctx: HookContext, initial: T, optionsOrSlot?: number | SignalOptions<T>, slot?: number): SignalAccessor<T>;
|
|
19
19
|
declare function __fictUseMemo<T>(ctx: HookContext, fn: () => T, optionsOrSlot?: number | MemoOptions<T>, slot?: number): ComputedAccessor<T>;
|
|
20
|
-
declare function __fictUseEffect(ctx: HookContext, fn: () => void, slot?: number): void;
|
|
20
|
+
declare function __fictUseEffect(ctx: HookContext, fn: () => void, optionsOrSlot?: number | EffectOptions, slot?: number): void;
|
|
21
21
|
declare function __fictRender<T>(ctx: HookContext, fn: () => T): T;
|
|
22
22
|
|
|
23
23
|
type SlotSnapshot = [index: number, type: 'sig', value: unknown] | [index: number, type: 'store', value: unknown] | [index: number, type: 'raw', value: unknown];
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as SignalOptions, S as SignalAccessor, M as MemoOptions, C as ComputedAccessor } from './signal-
|
|
1
|
+
import { a as SignalOptions, S as SignalAccessor, M as MemoOptions, C as ComputedAccessor, E as EffectOptions } from './signal-Z4KkDk9h.cjs';
|
|
2
2
|
|
|
3
3
|
interface HookContext {
|
|
4
4
|
slots: unknown[];
|
|
@@ -17,7 +17,7 @@ declare function __fictPopContext(): void;
|
|
|
17
17
|
declare function __fictResetContext(): void;
|
|
18
18
|
declare function __fictUseSignal<T>(ctx: HookContext, initial: T, optionsOrSlot?: number | SignalOptions<T>, slot?: number): SignalAccessor<T>;
|
|
19
19
|
declare function __fictUseMemo<T>(ctx: HookContext, fn: () => T, optionsOrSlot?: number | MemoOptions<T>, slot?: number): ComputedAccessor<T>;
|
|
20
|
-
declare function __fictUseEffect(ctx: HookContext, fn: () => void, slot?: number): void;
|
|
20
|
+
declare function __fictUseEffect(ctx: HookContext, fn: () => void, optionsOrSlot?: number | EffectOptions, slot?: number): void;
|
|
21
21
|
declare function __fictRender<T>(ctx: HookContext, fn: () => T): T;
|
|
22
22
|
|
|
23
23
|
type SlotSnapshot = [index: number, type: 'sig', value: unknown] | [index: number, type: 'store', value: unknown] | [index: number, type: 'raw', value: unknown];
|