@fictjs/runtime 0.5.0 → 0.5.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (51) hide show
  1. package/dist/advanced.cjs +9 -9
  2. package/dist/advanced.js +4 -4
  3. package/dist/{chunk-5AA7HP4S.js → chunk-4NUHM77Z.js} +3 -3
  4. package/dist/{chunk-BQG7VEBY.js → chunk-D2IWOO4X.js} +2 -2
  5. package/dist/{chunk-KYLNC4CD.cjs → chunk-KNGHYGK4.cjs} +17 -17
  6. package/dist/{chunk-KYLNC4CD.cjs.map → chunk-KNGHYGK4.cjs.map} +1 -1
  7. package/dist/{chunk-FKDMDAUR.js → chunk-LRFMCJY3.js} +119 -19
  8. package/dist/chunk-LRFMCJY3.js.map +1 -0
  9. package/dist/{chunk-GHUV2FLD.cjs → chunk-QB2UD62G.cjs} +8 -8
  10. package/dist/{chunk-GHUV2FLD.cjs.map → chunk-QB2UD62G.cjs.map} +1 -1
  11. package/dist/{chunk-KKKYW54Z.js → chunk-SLFAEVKJ.js} +3 -3
  12. package/dist/{chunk-TKWN42TA.cjs → chunk-Z6M3HKLG.cjs} +156 -156
  13. package/dist/{chunk-TKWN42TA.cjs.map → chunk-Z6M3HKLG.cjs.map} +1 -1
  14. package/dist/{chunk-6SOPF5LZ.cjs → chunk-ZR435MDC.cjs} +120 -20
  15. package/dist/chunk-ZR435MDC.cjs.map +1 -0
  16. package/dist/index.cjs +95 -45
  17. package/dist/index.cjs.map +1 -1
  18. package/dist/index.dev.js +120 -25
  19. package/dist/index.dev.js.map +1 -1
  20. package/dist/index.js +60 -10
  21. package/dist/index.js.map +1 -1
  22. package/dist/internal.cjs +64 -42
  23. package/dist/internal.cjs.map +1 -1
  24. package/dist/internal.d.cts +12 -3
  25. package/dist/internal.d.ts +12 -3
  26. package/dist/internal.js +25 -3
  27. package/dist/internal.js.map +1 -1
  28. package/dist/jsx-dev-runtime.d.cts +671 -0
  29. package/dist/jsx-dev-runtime.d.ts +671 -0
  30. package/dist/loader.cjs +60 -8
  31. package/dist/loader.cjs.map +1 -1
  32. package/dist/loader.d.cts +1 -1
  33. package/dist/loader.d.ts +1 -1
  34. package/dist/loader.js +53 -1
  35. package/dist/loader.js.map +1 -1
  36. package/dist/{resume-Dx8_l72o.d.ts → resume-CqeQ3v_q.d.ts} +5 -1
  37. package/dist/{resume-BrAkmSTY.d.cts → resume-i-A3EFox.d.cts} +5 -1
  38. package/package.json +1 -1
  39. package/src/cycle-guard.ts +1 -1
  40. package/src/internal.ts +4 -0
  41. package/src/list-helpers.ts +19 -4
  42. package/src/loader.ts +58 -0
  43. package/src/resume.ts +55 -0
  44. package/src/signal.ts +47 -22
  45. package/src/ssr-stream.ts +38 -0
  46. package/src/suspense.ts +62 -7
  47. package/dist/chunk-6SOPF5LZ.cjs.map +0 -1
  48. package/dist/chunk-FKDMDAUR.js.map +0 -1
  49. /package/dist/{chunk-5AA7HP4S.js.map → chunk-4NUHM77Z.js.map} +0 -0
  50. /package/dist/{chunk-BQG7VEBY.js.map → chunk-D2IWOO4X.js.map} +0 -0
  51. /package/dist/{chunk-KKKYW54Z.js.map → chunk-SLFAEVKJ.js.map} +0 -0
package/dist/loader.cjs CHANGED
@@ -6,7 +6,8 @@
6
6
 
7
7
 
8
8
 
9
- var _chunk6SOPF5LZcjs = require('./chunk-6SOPF5LZ.cjs');
9
+
10
+ var _chunkZR435MDCcjs = require('./chunk-ZR435MDC.cjs');
10
11
 
11
12
  // src/loader.ts
12
13
  function resolveModuleUrl(url) {
@@ -23,6 +24,8 @@ var hydratedScopes = /* @__PURE__ */ new Set();
23
24
  var prefetchedUrls = /* @__PURE__ */ new Set();
24
25
  var prefetchCleanup = null;
25
26
  var eventListenerCleanup = null;
27
+ var snapshotObserver = null;
28
+ var processedSnapshots = /* @__PURE__ */ new Set();
26
29
  function resetHydratedScopes() {
27
30
  hydratedScopes.clear();
28
31
  }
@@ -45,6 +48,7 @@ function installResumableLoader(options = {}) {
45
48
  const scriptId = _nullishCoalesce(options.snapshotScriptId, () => ( "__FICT_SNAPSHOT__"));
46
49
  hydratedScopes.clear();
47
50
  prefetchedUrls.clear();
51
+ processedSnapshots.clear();
48
52
  if (eventListenerCleanup) {
49
53
  eventListenerCleanup();
50
54
  eventListenerCleanup = null;
@@ -53,16 +57,50 @@ function installResumableLoader(options = {}) {
53
57
  prefetchCleanup();
54
58
  prefetchCleanup = null;
55
59
  }
60
+ if (snapshotObserver) {
61
+ snapshotObserver.disconnect();
62
+ snapshotObserver = null;
63
+ }
56
64
  const snapshotEl = doc.getElementById(scriptId);
57
65
  if (_optionalChain([snapshotEl, 'optionalAccess', _ => _.textContent])) {
58
66
  try {
59
67
  const state = JSON.parse(snapshotEl.textContent);
60
- _chunk6SOPF5LZcjs.__fictSetSSRState.call(void 0, state);
68
+ _chunkZR435MDCcjs.__fictSetSSRState.call(void 0, state);
61
69
  } catch (e) {
62
70
  }
63
71
  }
64
- _chunk6SOPF5LZcjs.__fictEnableResumable.call(void 0, );
65
- const events = _nullishCoalesce(options.events, () => ( Array.from(_chunk6SOPF5LZcjs.DelegatedEvents)));
72
+ const snapshotScripts = doc.querySelectorAll(
73
+ 'script[type="application/json"][data-fict-snapshot]'
74
+ );
75
+ for (const script of Array.from(snapshotScripts)) {
76
+ parseSnapshotScript(script);
77
+ }
78
+ if (typeof MutationObserver !== "undefined") {
79
+ snapshotObserver = new MutationObserver((mutations) => {
80
+ for (const mutation of mutations) {
81
+ for (const node of Array.from(mutation.addedNodes)) {
82
+ if (!(node instanceof Element)) continue;
83
+ if (node.tagName === "SCRIPT") {
84
+ const script = node;
85
+ if (isSnapshotScript(script)) {
86
+ parseSnapshotScript(script);
87
+ }
88
+ }
89
+ const nested = _optionalChain([node, 'access', _2 => _2.querySelectorAll, 'optionalCall', _3 => _3(
90
+ 'script[type="application/json"][data-fict-snapshot]'
91
+ )]);
92
+ if (nested && nested.length) {
93
+ for (const script of Array.from(nested)) {
94
+ parseSnapshotScript(script);
95
+ }
96
+ }
97
+ }
98
+ }
99
+ });
100
+ snapshotObserver.observe(_nullishCoalesce(doc.documentElement, () => ( doc)), { childList: true, subtree: true });
101
+ }
102
+ _chunkZR435MDCcjs.__fictEnableResumable.call(void 0, );
103
+ const events = _nullishCoalesce(options.events, () => ( Array.from(_chunkZR435MDCcjs.DelegatedEvents)));
66
104
  for (const eventName of events) {
67
105
  doc.addEventListener(eventName, handleResumableEvent, true);
68
106
  }
@@ -75,6 +113,20 @@ function installResumableLoader(options = {}) {
75
113
  prefetchCleanup = setupPrefetch(doc, _nullishCoalesce(options.prefetch, () => ( {})));
76
114
  }
77
115
  }
116
+ function isSnapshotScript(script) {
117
+ return script.type === "application/json" && script.hasAttribute("data-fict-snapshot");
118
+ }
119
+ function parseSnapshotScript(script) {
120
+ if (processedSnapshots.has(script)) return;
121
+ processedSnapshots.add(script);
122
+ const text = script.textContent;
123
+ if (!text) return;
124
+ try {
125
+ const state = JSON.parse(text);
126
+ _chunkZR435MDCcjs.__fictMergeSSRState.call(void 0, state);
127
+ } catch (e2) {
128
+ }
129
+ }
78
130
  function setupPrefetch(doc, strategy) {
79
131
  const cleanupFns = [];
80
132
  if (strategy.visibility !== false) {
@@ -209,9 +261,9 @@ async function handleResumableEventAsync(event) {
209
261
  if (!host) continue;
210
262
  const scopeId = host.getAttribute("data-fict-s");
211
263
  if (!scopeId) continue;
212
- const snapshot = _chunk6SOPF5LZcjs.__fictGetSSRScope.call(void 0, scopeId);
264
+ const snapshot = _chunkZR435MDCcjs.__fictGetSSRScope.call(void 0, scopeId);
213
265
  if (snapshot) {
214
- _chunk6SOPF5LZcjs.__fictEnsureScope.call(void 0, scopeId, host, snapshot);
266
+ _chunkZR435MDCcjs.__fictEnsureScope.call(void 0, scopeId, host, snapshot);
215
267
  }
216
268
  const { url, exportName } = parseQrl(qrl);
217
269
  if (event.cancelable && (event.type === "click" || event.type === "submit")) {
@@ -229,7 +281,7 @@ async function handleResumableEventAsync(event) {
229
281
  /* @vite-ignore */
230
282
  resolvedResumeUrl
231
283
  )));
232
- const resumeFn = _chunk6SOPF5LZcjs.__fictGetResume.call(void 0, resumeExport);
284
+ const resumeFn = _chunkZR435MDCcjs.__fictGetResume.call(void 0, resumeExport);
233
285
  if (typeof resumeFn === "function") {
234
286
  await resumeFn(scopeId, host);
235
287
  hydratedScopes.add(scopeId);
@@ -276,5 +328,5 @@ function buildEventPath(event) {
276
328
 
277
329
 
278
330
 
279
- exports.__fictUseLexicalScope = _chunk6SOPF5LZcjs.__fictUseLexicalScope; exports.cleanupEventListeners = cleanupEventListeners; exports.installResumableLoader = installResumableLoader; exports.resetHydratedScopes = resetHydratedScopes; exports.resetPrefetchedUrls = resetPrefetchedUrls; exports.waitForPendingHandlers = waitForPendingHandlers;
331
+ exports.__fictUseLexicalScope = _chunkZR435MDCcjs.__fictUseLexicalScope; exports.cleanupEventListeners = cleanupEventListeners; exports.installResumableLoader = installResumableLoader; exports.resetHydratedScopes = resetHydratedScopes; exports.resetPrefetchedUrls = resetPrefetchedUrls; exports.waitForPendingHandlers = waitForPendingHandlers;
280
332
  //# sourceMappingURL=loader.cjs.map
@@ -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 __fictEnableResumable,\n __fictEnsureScope,\n __fictGetResume,\n __fictGetSSRScope,\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 * Prefetch strategy configuration.\n * Set to false to disable all prefetching.\n * @default { visibility: true, hover: true }\n */\n prefetch?: PrefetchStrategy | false\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\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\n // Reset hydrated scopes for fresh loader installation\n hydratedScopes.clear()\n prefetchedUrls.clear()\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 const snapshotEl = doc.getElementById(scriptId)\n if (snapshotEl?.textContent) {\n try {\n const state = JSON.parse(snapshotEl.textContent)\n __fictSetSSRState(state)\n } catch {\n // Ignore parse errors\n }\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\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 __fictEnsureScope(scopeId, host, snapshot)\n }\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 __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 * Prefetch strategy configuration.\n * Set to false to disable all prefetching.\n * @default { visibility: true, hover: true }\n */\n prefetch?: PrefetchStrategy | false\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>()\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\n // Reset hydrated scopes for fresh loader installation\n hydratedScopes.clear()\n prefetchedUrls.clear()\n processedSnapshots.clear()\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 try {\n const state = JSON.parse(snapshotEl.textContent)\n __fictSetSSRState(state)\n } catch {\n // Ignore parse errors\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 try {\n const state = JSON.parse(text)\n __fictMergeSSRState(state)\n } catch {\n // Ignore parse errors\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 __fictEnsureScope(scopeId, host, snapshot)\n }\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
@@ -1,4 +1,4 @@
1
- export { x as __fictUseLexicalScope } from './resume-BrAkmSTY.cjs';
1
+ export { A as __fictUseLexicalScope } from './resume-i-A3EFox.cjs';
2
2
  import './signal-C4ISF17w.cjs';
3
3
 
4
4
  interface PrefetchStrategy {
package/dist/loader.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- export { x as __fictUseLexicalScope } from './resume-Dx8_l72o.js';
1
+ export { A as __fictUseLexicalScope } from './resume-CqeQ3v_q.js';
2
2
  import './signal-C4ISF17w.js';
3
3
 
4
4
  interface PrefetchStrategy {
package/dist/loader.js CHANGED
@@ -4,9 +4,10 @@ import {
4
4
  __fictEnsureScope,
5
5
  __fictGetResume,
6
6
  __fictGetSSRScope,
7
+ __fictMergeSSRState,
7
8
  __fictSetSSRState,
8
9
  __fictUseLexicalScope
9
- } from "./chunk-FKDMDAUR.js";
10
+ } from "./chunk-LRFMCJY3.js";
10
11
 
11
12
  // src/loader.ts
12
13
  function resolveModuleUrl(url) {
@@ -23,6 +24,8 @@ var hydratedScopes = /* @__PURE__ */ new Set();
23
24
  var prefetchedUrls = /* @__PURE__ */ new Set();
24
25
  var prefetchCleanup = null;
25
26
  var eventListenerCleanup = null;
27
+ var snapshotObserver = null;
28
+ var processedSnapshots = /* @__PURE__ */ new Set();
26
29
  function resetHydratedScopes() {
27
30
  hydratedScopes.clear();
28
31
  }
@@ -45,6 +48,7 @@ function installResumableLoader(options = {}) {
45
48
  const scriptId = options.snapshotScriptId ?? "__FICT_SNAPSHOT__";
46
49
  hydratedScopes.clear();
47
50
  prefetchedUrls.clear();
51
+ processedSnapshots.clear();
48
52
  if (eventListenerCleanup) {
49
53
  eventListenerCleanup();
50
54
  eventListenerCleanup = null;
@@ -53,6 +57,10 @@ function installResumableLoader(options = {}) {
53
57
  prefetchCleanup();
54
58
  prefetchCleanup = null;
55
59
  }
60
+ if (snapshotObserver) {
61
+ snapshotObserver.disconnect();
62
+ snapshotObserver = null;
63
+ }
56
64
  const snapshotEl = doc.getElementById(scriptId);
57
65
  if (snapshotEl?.textContent) {
58
66
  try {
@@ -61,6 +69,36 @@ function installResumableLoader(options = {}) {
61
69
  } catch {
62
70
  }
63
71
  }
72
+ const snapshotScripts = doc.querySelectorAll(
73
+ 'script[type="application/json"][data-fict-snapshot]'
74
+ );
75
+ for (const script of Array.from(snapshotScripts)) {
76
+ parseSnapshotScript(script);
77
+ }
78
+ if (typeof MutationObserver !== "undefined") {
79
+ snapshotObserver = new MutationObserver((mutations) => {
80
+ for (const mutation of mutations) {
81
+ for (const node of Array.from(mutation.addedNodes)) {
82
+ if (!(node instanceof Element)) continue;
83
+ if (node.tagName === "SCRIPT") {
84
+ const script = node;
85
+ if (isSnapshotScript(script)) {
86
+ parseSnapshotScript(script);
87
+ }
88
+ }
89
+ const nested = node.querySelectorAll?.(
90
+ 'script[type="application/json"][data-fict-snapshot]'
91
+ );
92
+ if (nested && nested.length) {
93
+ for (const script of Array.from(nested)) {
94
+ parseSnapshotScript(script);
95
+ }
96
+ }
97
+ }
98
+ }
99
+ });
100
+ snapshotObserver.observe(doc.documentElement ?? doc, { childList: true, subtree: true });
101
+ }
64
102
  __fictEnableResumable();
65
103
  const events = options.events ?? Array.from(DelegatedEvents);
66
104
  for (const eventName of events) {
@@ -75,6 +113,20 @@ function installResumableLoader(options = {}) {
75
113
  prefetchCleanup = setupPrefetch(doc, options.prefetch ?? {});
76
114
  }
77
115
  }
116
+ function isSnapshotScript(script) {
117
+ return script.type === "application/json" && script.hasAttribute("data-fict-snapshot");
118
+ }
119
+ function parseSnapshotScript(script) {
120
+ if (processedSnapshots.has(script)) return;
121
+ processedSnapshots.add(script);
122
+ const text = script.textContent;
123
+ if (!text) return;
124
+ try {
125
+ const state = JSON.parse(text);
126
+ __fictMergeSSRState(state);
127
+ } catch {
128
+ }
129
+ }
78
130
  function setupPrefetch(doc, strategy) {
79
131
  const cleanupFns = [];
80
132
  if (strategy.visibility !== false) {
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/loader.ts"],"sourcesContent":["import { DelegatedEvents } from './constants'\nimport {\n __fictEnableResumable,\n __fictEnsureScope,\n __fictGetResume,\n __fictGetSSRScope,\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 * Prefetch strategy configuration.\n * Set to false to disable all prefetching.\n * @default { visibility: true, hover: true }\n */\n prefetch?: PrefetchStrategy | false\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\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\n // Reset hydrated scopes for fresh loader installation\n hydratedScopes.clear()\n prefetchedUrls.clear()\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 const snapshotEl = doc.getElementById(scriptId)\n if (snapshotEl?.textContent) {\n try {\n const state = JSON.parse(snapshotEl.textContent)\n __fictSetSSRState(state)\n } catch {\n // Ignore parse errors\n }\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\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 __fictEnsureScope(scopeId, host, snapshot)\n }\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 __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 * Prefetch strategy configuration.\n * Set to false to disable all prefetching.\n * @default { visibility: true, hover: true }\n */\n prefetch?: PrefetchStrategy | false\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>()\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\n // Reset hydrated scopes for fresh loader installation\n hydratedScopes.clear()\n prefetchedUrls.clear()\n processedSnapshots.clear()\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 try {\n const state = JSON.parse(snapshotEl.textContent)\n __fictSetSSRState(state)\n } catch {\n // Ignore parse errors\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 try {\n const state = JSON.parse(text)\n __fictMergeSSRState(state)\n } catch {\n // Ignore parse errors\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 __fictEnsureScope(scopeId, host, snapshot)\n }\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":[]}
@@ -35,6 +35,7 @@ interface ScopeRecord {
35
35
  id: string;
36
36
  ctx: HookContext;
37
37
  host: Element;
38
+ boundaryId?: string;
38
39
  type?: string;
39
40
  props?: Record<string, unknown>;
40
41
  }
@@ -49,8 +50,11 @@ declare function __fictExitHydration(): void;
49
50
  declare function __fictIsHydrating(): boolean;
50
51
  declare function __fictRegisterScope(ctx: HookContext, host: Element, type?: string, props?: Record<string, unknown>): string;
51
52
  declare function __fictGetScopeRegistry(): Map<string, ScopeRecord>;
53
+ declare function __fictGetScopesForBoundary(boundaryId: string): string[];
52
54
  declare function __fictSerializeSSRState(): SSRState;
55
+ declare function __fictSerializeSSRStateForScopes(scopeIds: Iterable<string>): SSRState;
53
56
  declare function __fictSetSSRState(state: SSRState | null): void;
57
+ declare function __fictMergeSSRState(state: SSRState | null): void;
54
58
  declare function __fictGetSSRScope(id: string): ScopeSnapshot | undefined;
55
59
  declare function __fictEnsureScope(scopeId: string, host: Element, snapshot?: ScopeSnapshot): HookContext;
56
60
  declare function __fictUseLexicalScope(scopeId: string, names: string[]): unknown[];
@@ -76,4 +80,4 @@ declare function serializeValue(value: unknown, seen?: Map<object, string>, path
76
80
  */
77
81
  declare function deserializeValue(value: unknown, refs?: Map<string, unknown>, path?: string): unknown;
78
82
 
79
- export { __fictRegisterResume as A, __fictGetResume as B, serializeValue as C, deserializeValue as D, __fictUseContext as _, __fictPushContext as a, __fictPopContext as b, __fictUseSignal as c, __fictUseMemo as d, __fictUseEffect as e, __fictRender as f, __fictResetContext as g, __fictPrepareContext as h, __fictEnableSSR as i, __fictDisableSSR as j, __fictIsSSR as k, __fictEnableResumable as l, __fictDisableResumable as m, __fictIsResumable as n, __fictEnterHydration as o, __fictExitHydration as p, __fictIsHydrating as q, __fictRegisterScope as r, __fictGetScopeRegistry as s, __fictSerializeSSRState as t, __fictSetSSRState as u, __fictGetSSRScope as v, __fictEnsureScope as w, __fictUseLexicalScope as x, __fictGetScopeProps as y, __fictQrl as z };
83
+ export { __fictUseLexicalScope as A, __fictGetScopeProps as B, __fictQrl as C, __fictRegisterResume as D, __fictGetResume as E, serializeValue as F, deserializeValue as G, __fictUseContext as _, __fictPushContext as a, __fictPopContext as b, __fictUseSignal as c, __fictUseMemo as d, __fictUseEffect as e, __fictRender as f, __fictResetContext as g, __fictPrepareContext as h, __fictEnableSSR as i, __fictDisableSSR as j, __fictIsSSR as k, __fictEnableResumable as l, __fictDisableResumable as m, __fictIsResumable as n, __fictEnterHydration as o, __fictExitHydration as p, __fictIsHydrating as q, __fictRegisterScope as r, __fictGetScopeRegistry as s, __fictGetScopesForBoundary as t, __fictSerializeSSRState as u, __fictSerializeSSRStateForScopes as v, __fictSetSSRState as w, __fictMergeSSRState as x, __fictGetSSRScope as y, __fictEnsureScope as z };
@@ -35,6 +35,7 @@ interface ScopeRecord {
35
35
  id: string;
36
36
  ctx: HookContext;
37
37
  host: Element;
38
+ boundaryId?: string;
38
39
  type?: string;
39
40
  props?: Record<string, unknown>;
40
41
  }
@@ -49,8 +50,11 @@ declare function __fictExitHydration(): void;
49
50
  declare function __fictIsHydrating(): boolean;
50
51
  declare function __fictRegisterScope(ctx: HookContext, host: Element, type?: string, props?: Record<string, unknown>): string;
51
52
  declare function __fictGetScopeRegistry(): Map<string, ScopeRecord>;
53
+ declare function __fictGetScopesForBoundary(boundaryId: string): string[];
52
54
  declare function __fictSerializeSSRState(): SSRState;
55
+ declare function __fictSerializeSSRStateForScopes(scopeIds: Iterable<string>): SSRState;
53
56
  declare function __fictSetSSRState(state: SSRState | null): void;
57
+ declare function __fictMergeSSRState(state: SSRState | null): void;
54
58
  declare function __fictGetSSRScope(id: string): ScopeSnapshot | undefined;
55
59
  declare function __fictEnsureScope(scopeId: string, host: Element, snapshot?: ScopeSnapshot): HookContext;
56
60
  declare function __fictUseLexicalScope(scopeId: string, names: string[]): unknown[];
@@ -76,4 +80,4 @@ declare function serializeValue(value: unknown, seen?: Map<object, string>, path
76
80
  */
77
81
  declare function deserializeValue(value: unknown, refs?: Map<string, unknown>, path?: string): unknown;
78
82
 
79
- export { __fictRegisterResume as A, __fictGetResume as B, serializeValue as C, deserializeValue as D, __fictUseContext as _, __fictPushContext as a, __fictPopContext as b, __fictUseSignal as c, __fictUseMemo as d, __fictUseEffect as e, __fictRender as f, __fictResetContext as g, __fictPrepareContext as h, __fictEnableSSR as i, __fictDisableSSR as j, __fictIsSSR as k, __fictEnableResumable as l, __fictDisableResumable as m, __fictIsResumable as n, __fictEnterHydration as o, __fictExitHydration as p, __fictIsHydrating as q, __fictRegisterScope as r, __fictGetScopeRegistry as s, __fictSerializeSSRState as t, __fictSetSSRState as u, __fictGetSSRScope as v, __fictEnsureScope as w, __fictUseLexicalScope as x, __fictGetScopeProps as y, __fictQrl as z };
83
+ export { __fictUseLexicalScope as A, __fictGetScopeProps as B, __fictQrl as C, __fictRegisterResume as D, __fictGetResume as E, serializeValue as F, deserializeValue as G, __fictUseContext as _, __fictPushContext as a, __fictPopContext as b, __fictUseSignal as c, __fictUseMemo as d, __fictUseEffect as e, __fictRender as f, __fictResetContext as g, __fictPrepareContext as h, __fictEnableSSR as i, __fictDisableSSR as j, __fictIsSSR as k, __fictEnableResumable as l, __fictDisableResumable as m, __fictIsResumable as n, __fictEnterHydration as o, __fictExitHydration as p, __fictIsHydrating as q, __fictRegisterScope as r, __fictGetScopeRegistry as s, __fictGetScopesForBoundary as t, __fictSerializeSSRState as u, __fictSerializeSSRStateForScopes as v, __fictSetSSRState as w, __fictMergeSSRState as x, __fictGetSSRScope as y, __fictEnsureScope as z };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fictjs/runtime",
3
- "version": "0.5.0",
3
+ "version": "0.5.2",
4
4
  "description": "Fict reactive runtime",
5
5
  "publishConfig": {
6
6
  "access": "public",
@@ -35,7 +35,7 @@ let enterRootGuard: (root: object) => boolean = () => true
35
35
  let exitRootGuard: (root: object) => void = () => {}
36
36
 
37
37
  const defaultOptions = {
38
- enabled: true,
38
+ enabled: isDev,
39
39
  maxFlushCyclesPerMicrotask: 10_000,
40
40
  maxEffectRunsPerFlush: 20_000,
41
41
  windowSize: 5,
package/src/internal.ts CHANGED
@@ -50,8 +50,11 @@ export {
50
50
  __fictIsHydrating,
51
51
  __fictRegisterScope,
52
52
  __fictGetScopeRegistry,
53
+ __fictGetScopesForBoundary,
53
54
  __fictSerializeSSRState,
55
+ __fictSerializeSSRStateForScopes,
54
56
  __fictSetSSRState,
57
+ __fictMergeSSRState,
55
58
  __fictGetSSRScope,
56
59
  __fictEnsureScope,
57
60
  __fictUseLexicalScope,
@@ -62,6 +65,7 @@ export {
62
65
  serializeValue,
63
66
  deserializeValue,
64
67
  } from './resume'
68
+ export { __fictGetSSRStreamHooks, __fictSetSSRStreamHooks } from './ssr-stream'
65
69
 
66
70
  // ============================================================================
67
71
  // Props Helpers (Compiler-generated code)
@@ -32,6 +32,9 @@ const isDev =
32
32
  ? __DEV__
33
33
  : typeof process === 'undefined' || process.env?.NODE_ENV !== 'production'
34
34
 
35
+ const isShadowRoot = (node: Node): node is ShadowRoot =>
36
+ typeof ShadowRoot !== 'undefined' && node instanceof ShadowRoot
37
+
35
38
  // ============================================================================
36
39
  // Types
37
40
  // ============================================================================
@@ -469,7 +472,7 @@ function reorderByLIS<T>(
469
472
  * @param renderItem - Function that creates DOM nodes for each item
470
473
  * @returns Binding handle with markers and dispose function
471
474
  */
472
- export function createKeyedList<T extends object>(
475
+ export function createKeyedList<T>(
473
476
  getItems: () => T[],
474
477
  keyFn: (item: T, index: number) => string | number,
475
478
  renderItem: FineGrainedRenderItem<T>,
@@ -489,7 +492,7 @@ export function createKeyedList<T extends object>(
489
492
  )
490
493
  }
491
494
 
492
- function createFineGrainedKeyedList<T extends object>(
495
+ function createFineGrainedKeyedList<T>(
493
496
  getItems: () => T[],
494
497
  keyFn: (item: T, index: number) => string | number,
495
498
  renderItem: FineGrainedRenderItem<T>,
@@ -534,6 +537,12 @@ function createFineGrainedKeyedList<T extends object>(
534
537
  if ('isConnected' in parentNode && !parentNode.isConnected) return null
535
538
  return parentNode
536
539
  }
540
+ if (endParent && startParent && endParent === startParent && isShadowRoot(endParent as Node)) {
541
+ const shadowRoot = endParent as ShadowRoot
542
+ const host = shadowRoot.host
543
+ if ('isConnected' in host && !host.isConnected) return null
544
+ return shadowRoot as unknown as ParentNode & Node
545
+ }
537
546
  return null
538
547
  }
539
548
 
@@ -589,7 +598,7 @@ function createFineGrainedKeyedList<T extends object>(
589
598
  removeNodes(existing.nodes)
590
599
  }
591
600
  }
592
- const block = createKeyedBlock(key, item, index, renderItem, needsIndex, hostRoot)
601
+ const block = createKeyedBlock<T>(key, item, index, renderItem, needsIndex, hostRoot)
593
602
  createdBlocks.push(block)
594
603
  newBlocks.set(key, block)
595
604
  orderedIndexByKey.set(key, nextOrderedBlocks.length)
@@ -716,7 +725,7 @@ function createFineGrainedKeyedList<T extends object>(
716
725
  removeNodes(existingBlock.nodes)
717
726
  }
718
727
  // Create new block
719
- block = createKeyedBlock(key, item, index, renderItem, needsIndex, hostRoot)
728
+ block = createKeyedBlock<T>(key, item, index, renderItem, needsIndex, hostRoot)
720
729
  createdBlocks.push(block)
721
730
  }
722
731
 
@@ -925,6 +934,9 @@ function createFineGrainedKeyedList<T extends object>(
925
934
 
926
935
  const waitForConnection = () => {
927
936
  if (connectObserver || typeof MutationObserver === 'undefined') return
937
+ const root = container.startMarker.getRootNode?.() ?? document
938
+ const shadowRoot =
939
+ root && root.nodeType === 11 && isShadowRoot(root as Node) ? (root as ShadowRoot) : null
928
940
  connectObserver = new MutationObserver(() => {
929
941
  if (disposed) return
930
942
  if (getConnectedParent()) {
@@ -935,6 +947,9 @@ function createFineGrainedKeyedList<T extends object>(
935
947
  }
936
948
  })
937
949
  connectObserver.observe(document, { childList: true, subtree: true })
950
+ if (shadowRoot) {
951
+ connectObserver.observe(shadowRoot, { childList: true, subtree: true })
952
+ }
938
953
  }
939
954
 
940
955
  const scheduleStart = () => {