@fictjs/runtime 0.3.0 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/advanced.cjs +10 -8
- package/dist/advanced.cjs.map +1 -1
- package/dist/advanced.d.cts +4 -3
- package/dist/advanced.d.ts +4 -3
- package/dist/advanced.js +10 -8
- package/dist/advanced.js.map +1 -1
- package/dist/{chunk-TWELIZRY.js → chunk-5AA7HP4S.js} +5 -3
- package/dist/{chunk-TWELIZRY.js.map → chunk-5AA7HP4S.js.map} +1 -1
- package/dist/chunk-6SOPF5LZ.cjs +2363 -0
- package/dist/chunk-6SOPF5LZ.cjs.map +1 -0
- package/dist/{chunk-SO6X7G5S.js → chunk-BQG7VEBY.js} +501 -1880
- package/dist/chunk-BQG7VEBY.js.map +1 -0
- package/dist/chunk-FKDMDAUR.js +2363 -0
- package/dist/chunk-FKDMDAUR.js.map +1 -0
- package/dist/{chunk-L4DIV3RC.cjs → chunk-GHUV2FLD.cjs} +9 -7
- package/dist/chunk-GHUV2FLD.cjs.map +1 -0
- package/dist/{chunk-XLIZJMMJ.js → chunk-KKKYW54Z.js} +8 -6
- package/dist/{chunk-XLIZJMMJ.js.map → chunk-KKKYW54Z.js.map} +1 -1
- package/dist/{chunk-M2TSXZ4C.cjs → chunk-KYLNC4CD.cjs} +18 -16
- package/dist/chunk-KYLNC4CD.cjs.map +1 -0
- package/dist/chunk-TKWN42TA.cjs +2259 -0
- package/dist/chunk-TKWN42TA.cjs.map +1 -0
- package/dist/{context-B25xyQrJ.d.cts → context-CTBE00S_.d.cts} +1 -1
- package/dist/{context-CGdP7_Jb.d.ts → context-lkLhbkFJ.d.ts} +1 -1
- package/dist/{effect-D6kaLM2-.d.cts → effect-BpSNEJJz.d.cts} +7 -67
- package/dist/{effect-D6kaLM2-.d.ts → effect-BpSNEJJz.d.ts} +7 -67
- package/dist/index.cjs +40 -38
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +5 -4
- package/dist/index.d.ts +5 -4
- package/dist/index.dev.js +92 -4
- package/dist/index.dev.js.map +1 -1
- package/dist/index.js +19 -17
- package/dist/index.js.map +1 -1
- package/dist/internal.cjs +189 -202
- package/dist/internal.cjs.map +1 -1
- package/dist/internal.d.cts +13 -23
- package/dist/internal.d.ts +13 -23
- package/dist/internal.js +195 -208
- package/dist/internal.js.map +1 -1
- package/dist/loader.cjs +280 -0
- package/dist/loader.cjs.map +1 -0
- package/dist/loader.d.cts +57 -0
- package/dist/loader.d.ts +57 -0
- package/dist/loader.js +280 -0
- package/dist/loader.js.map +1 -0
- package/dist/{props-BIfromL0.d.cts → props-XTHYD19o.d.cts} +13 -2
- package/dist/{props-BEgIVMRx.d.ts → props-x-HbI-jX.d.ts} +13 -2
- package/dist/resume-BrAkmSTY.d.cts +79 -0
- package/dist/resume-Dx8_l72o.d.ts +79 -0
- package/dist/{scope-CzNkn587.d.ts → scope-CdbGmsFf.d.ts} +1 -1
- package/dist/{scope-Cx_3CjIZ.d.cts → scope-DfcP9I-A.d.cts} +1 -1
- package/dist/signal-C4ISF17w.d.cts +66 -0
- package/dist/signal-C4ISF17w.d.ts +66 -0
- package/package.json +8 -3
- package/src/binding.ts +254 -5
- package/src/dom.ts +103 -5
- package/src/hooks.ts +15 -2
- package/src/hydration.ts +75 -0
- package/src/internal.ts +34 -2
- package/src/list-helpers.ts +113 -12
- package/src/loader.ts +437 -0
- package/src/node-ops.ts +65 -0
- package/src/resume.ts +517 -0
- package/src/store.ts +8 -0
- package/dist/chunk-ID3WBWNO.cjs +0 -3638
- package/dist/chunk-ID3WBWNO.cjs.map +0 -1
- package/dist/chunk-L4DIV3RC.cjs.map +0 -1
- package/dist/chunk-M2TSXZ4C.cjs.map +0 -1
- package/dist/chunk-SO6X7G5S.js.map +0 -1
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
export { x as __fictUseLexicalScope } from './resume-BrAkmSTY.cjs';
|
|
2
|
+
import './signal-C4ISF17w.cjs';
|
|
3
|
+
|
|
4
|
+
interface PrefetchStrategy {
|
|
5
|
+
/**
|
|
6
|
+
* Enable visibility-based prefetch using IntersectionObserver.
|
|
7
|
+
* Prefetches modules when interactive elements come into view.
|
|
8
|
+
* @default true
|
|
9
|
+
*/
|
|
10
|
+
visibility?: boolean;
|
|
11
|
+
/**
|
|
12
|
+
* Root margin for IntersectionObserver (e.g., '200px' to prefetch earlier).
|
|
13
|
+
* @default '200px'
|
|
14
|
+
*/
|
|
15
|
+
visibilityMargin?: string;
|
|
16
|
+
/**
|
|
17
|
+
* Enable hover-based prefetch using pointerover events.
|
|
18
|
+
* Prefetches modules when user hovers over interactive elements.
|
|
19
|
+
* @default true
|
|
20
|
+
*/
|
|
21
|
+
hover?: boolean;
|
|
22
|
+
/**
|
|
23
|
+
* Delay in ms before prefetching on hover (debounce rapid movements).
|
|
24
|
+
* @default 50
|
|
25
|
+
*/
|
|
26
|
+
hoverDelay?: number;
|
|
27
|
+
}
|
|
28
|
+
interface ResumableLoaderOptions {
|
|
29
|
+
document?: Document;
|
|
30
|
+
snapshotScriptId?: string;
|
|
31
|
+
events?: string[];
|
|
32
|
+
/**
|
|
33
|
+
* Prefetch strategy configuration.
|
|
34
|
+
* Set to false to disable all prefetching.
|
|
35
|
+
* @default { visibility: true, hover: true }
|
|
36
|
+
*/
|
|
37
|
+
prefetch?: PrefetchStrategy | false;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Reset the hydrated scopes set. Useful for testing.
|
|
41
|
+
*/
|
|
42
|
+
declare function resetHydratedScopes(): void;
|
|
43
|
+
/**
|
|
44
|
+
* Reset the prefetched URLs set. Useful for testing.
|
|
45
|
+
*/
|
|
46
|
+
declare function resetPrefetchedUrls(): void;
|
|
47
|
+
/**
|
|
48
|
+
* Wait for all pending event handlers to complete. Useful for testing.
|
|
49
|
+
*/
|
|
50
|
+
declare function waitForPendingHandlers(): Promise<void>;
|
|
51
|
+
/**
|
|
52
|
+
* Clean up all registered event listeners. Useful for testing.
|
|
53
|
+
*/
|
|
54
|
+
declare function cleanupEventListeners(): void;
|
|
55
|
+
declare function installResumableLoader(options?: ResumableLoaderOptions): void;
|
|
56
|
+
|
|
57
|
+
export { type PrefetchStrategy, type ResumableLoaderOptions, cleanupEventListeners, installResumableLoader, resetHydratedScopes, resetPrefetchedUrls, waitForPendingHandlers };
|
package/dist/loader.d.ts
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
export { x as __fictUseLexicalScope } from './resume-Dx8_l72o.js';
|
|
2
|
+
import './signal-C4ISF17w.js';
|
|
3
|
+
|
|
4
|
+
interface PrefetchStrategy {
|
|
5
|
+
/**
|
|
6
|
+
* Enable visibility-based prefetch using IntersectionObserver.
|
|
7
|
+
* Prefetches modules when interactive elements come into view.
|
|
8
|
+
* @default true
|
|
9
|
+
*/
|
|
10
|
+
visibility?: boolean;
|
|
11
|
+
/**
|
|
12
|
+
* Root margin for IntersectionObserver (e.g., '200px' to prefetch earlier).
|
|
13
|
+
* @default '200px'
|
|
14
|
+
*/
|
|
15
|
+
visibilityMargin?: string;
|
|
16
|
+
/**
|
|
17
|
+
* Enable hover-based prefetch using pointerover events.
|
|
18
|
+
* Prefetches modules when user hovers over interactive elements.
|
|
19
|
+
* @default true
|
|
20
|
+
*/
|
|
21
|
+
hover?: boolean;
|
|
22
|
+
/**
|
|
23
|
+
* Delay in ms before prefetching on hover (debounce rapid movements).
|
|
24
|
+
* @default 50
|
|
25
|
+
*/
|
|
26
|
+
hoverDelay?: number;
|
|
27
|
+
}
|
|
28
|
+
interface ResumableLoaderOptions {
|
|
29
|
+
document?: Document;
|
|
30
|
+
snapshotScriptId?: string;
|
|
31
|
+
events?: string[];
|
|
32
|
+
/**
|
|
33
|
+
* Prefetch strategy configuration.
|
|
34
|
+
* Set to false to disable all prefetching.
|
|
35
|
+
* @default { visibility: true, hover: true }
|
|
36
|
+
*/
|
|
37
|
+
prefetch?: PrefetchStrategy | false;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Reset the hydrated scopes set. Useful for testing.
|
|
41
|
+
*/
|
|
42
|
+
declare function resetHydratedScopes(): void;
|
|
43
|
+
/**
|
|
44
|
+
* Reset the prefetched URLs set. Useful for testing.
|
|
45
|
+
*/
|
|
46
|
+
declare function resetPrefetchedUrls(): void;
|
|
47
|
+
/**
|
|
48
|
+
* Wait for all pending event handlers to complete. Useful for testing.
|
|
49
|
+
*/
|
|
50
|
+
declare function waitForPendingHandlers(): Promise<void>;
|
|
51
|
+
/**
|
|
52
|
+
* Clean up all registered event listeners. Useful for testing.
|
|
53
|
+
*/
|
|
54
|
+
declare function cleanupEventListeners(): void;
|
|
55
|
+
declare function installResumableLoader(options?: ResumableLoaderOptions): void;
|
|
56
|
+
|
|
57
|
+
export { type PrefetchStrategy, type ResumableLoaderOptions, cleanupEventListeners, installResumableLoader, resetHydratedScopes, resetPrefetchedUrls, waitForPendingHandlers };
|
package/dist/loader.js
ADDED
|
@@ -0,0 +1,280 @@
|
|
|
1
|
+
import {
|
|
2
|
+
DelegatedEvents,
|
|
3
|
+
__fictEnableResumable,
|
|
4
|
+
__fictEnsureScope,
|
|
5
|
+
__fictGetResume,
|
|
6
|
+
__fictGetSSRScope,
|
|
7
|
+
__fictSetSSRState,
|
|
8
|
+
__fictUseLexicalScope
|
|
9
|
+
} from "./chunk-FKDMDAUR.js";
|
|
10
|
+
|
|
11
|
+
// src/loader.ts
|
|
12
|
+
function resolveModuleUrl(url) {
|
|
13
|
+
const manifest = globalThis.__FICT_MANIFEST__;
|
|
14
|
+
if (manifest) {
|
|
15
|
+
const resolved = manifest[url];
|
|
16
|
+
if (resolved) {
|
|
17
|
+
return resolved;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
return url;
|
|
21
|
+
}
|
|
22
|
+
var hydratedScopes = /* @__PURE__ */ new Set();
|
|
23
|
+
var prefetchedUrls = /* @__PURE__ */ new Set();
|
|
24
|
+
var prefetchCleanup = null;
|
|
25
|
+
var eventListenerCleanup = null;
|
|
26
|
+
function resetHydratedScopes() {
|
|
27
|
+
hydratedScopes.clear();
|
|
28
|
+
}
|
|
29
|
+
function resetPrefetchedUrls() {
|
|
30
|
+
prefetchedUrls.clear();
|
|
31
|
+
}
|
|
32
|
+
var pendingHandlers = /* @__PURE__ */ new Set();
|
|
33
|
+
async function waitForPendingHandlers() {
|
|
34
|
+
if (pendingHandlers.size === 0) return;
|
|
35
|
+
await Promise.allSettled([...pendingHandlers]);
|
|
36
|
+
}
|
|
37
|
+
function cleanupEventListeners() {
|
|
38
|
+
if (eventListenerCleanup) {
|
|
39
|
+
eventListenerCleanup();
|
|
40
|
+
eventListenerCleanup = null;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
function installResumableLoader(options = {}) {
|
|
44
|
+
const doc = options.document ?? window.document;
|
|
45
|
+
const scriptId = options.snapshotScriptId ?? "__FICT_SNAPSHOT__";
|
|
46
|
+
hydratedScopes.clear();
|
|
47
|
+
prefetchedUrls.clear();
|
|
48
|
+
if (eventListenerCleanup) {
|
|
49
|
+
eventListenerCleanup();
|
|
50
|
+
eventListenerCleanup = null;
|
|
51
|
+
}
|
|
52
|
+
if (prefetchCleanup) {
|
|
53
|
+
prefetchCleanup();
|
|
54
|
+
prefetchCleanup = null;
|
|
55
|
+
}
|
|
56
|
+
const snapshotEl = doc.getElementById(scriptId);
|
|
57
|
+
if (snapshotEl?.textContent) {
|
|
58
|
+
try {
|
|
59
|
+
const state = JSON.parse(snapshotEl.textContent);
|
|
60
|
+
__fictSetSSRState(state);
|
|
61
|
+
} catch {
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
__fictEnableResumable();
|
|
65
|
+
const events = options.events ?? Array.from(DelegatedEvents);
|
|
66
|
+
for (const eventName of events) {
|
|
67
|
+
doc.addEventListener(eventName, handleResumableEvent, true);
|
|
68
|
+
}
|
|
69
|
+
eventListenerCleanup = () => {
|
|
70
|
+
for (const eventName of events) {
|
|
71
|
+
doc.removeEventListener(eventName, handleResumableEvent, true);
|
|
72
|
+
}
|
|
73
|
+
};
|
|
74
|
+
if (options.prefetch !== false) {
|
|
75
|
+
prefetchCleanup = setupPrefetch(doc, options.prefetch ?? {});
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
function setupPrefetch(doc, strategy) {
|
|
79
|
+
const cleanupFns = [];
|
|
80
|
+
if (strategy.visibility !== false) {
|
|
81
|
+
const cleanup = setupVisibilityPrefetch(doc, strategy.visibilityMargin ?? "200px");
|
|
82
|
+
cleanupFns.push(cleanup);
|
|
83
|
+
}
|
|
84
|
+
if (strategy.hover !== false) {
|
|
85
|
+
const cleanup = setupHoverPrefetch(doc, strategy.hoverDelay ?? 50);
|
|
86
|
+
cleanupFns.push(cleanup);
|
|
87
|
+
}
|
|
88
|
+
return () => {
|
|
89
|
+
for (const cleanup of cleanupFns) {
|
|
90
|
+
cleanup();
|
|
91
|
+
}
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
function setupVisibilityPrefetch(doc, rootMargin) {
|
|
95
|
+
if (typeof IntersectionObserver === "undefined") {
|
|
96
|
+
return () => {
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
const observer = new IntersectionObserver(
|
|
100
|
+
(entries) => {
|
|
101
|
+
for (const entry of entries) {
|
|
102
|
+
if (entry.isIntersecting) {
|
|
103
|
+
const el = entry.target;
|
|
104
|
+
prefetchElementQrls(el);
|
|
105
|
+
observer.unobserve(el);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
},
|
|
109
|
+
{ rootMargin }
|
|
110
|
+
);
|
|
111
|
+
const interactiveElements = doc.querySelectorAll(
|
|
112
|
+
"[on\\:click], [on\\:input], [on\\:change], [on\\:submit], [on\\:keydown], [on\\:keyup]"
|
|
113
|
+
);
|
|
114
|
+
interactiveElements.forEach((el) => observer.observe(el));
|
|
115
|
+
const resumableHosts = doc.querySelectorAll("[data-fict-h]");
|
|
116
|
+
resumableHosts.forEach((el) => observer.observe(el));
|
|
117
|
+
return () => {
|
|
118
|
+
observer.disconnect();
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
function setupHoverPrefetch(doc, delay) {
|
|
122
|
+
let hoverTimeout = null;
|
|
123
|
+
let lastHoveredElement = null;
|
|
124
|
+
const handlePointerOver = (event) => {
|
|
125
|
+
const target = event.target;
|
|
126
|
+
if (!(target instanceof Element)) return;
|
|
127
|
+
const interactiveEl = target.closest("[on\\:click]") || target.closest("[on\\:input]") || target.closest("[on\\:change]") || target.closest("[on\\:submit]") || target.closest("[data-fict-h]");
|
|
128
|
+
if (!interactiveEl || interactiveEl === lastHoveredElement) return;
|
|
129
|
+
lastHoveredElement = interactiveEl;
|
|
130
|
+
if (hoverTimeout) {
|
|
131
|
+
clearTimeout(hoverTimeout);
|
|
132
|
+
}
|
|
133
|
+
hoverTimeout = setTimeout(() => {
|
|
134
|
+
prefetchElementQrls(interactiveEl);
|
|
135
|
+
}, delay);
|
|
136
|
+
};
|
|
137
|
+
const handlePointerOut = () => {
|
|
138
|
+
if (hoverTimeout) {
|
|
139
|
+
clearTimeout(hoverTimeout);
|
|
140
|
+
hoverTimeout = null;
|
|
141
|
+
}
|
|
142
|
+
lastHoveredElement = null;
|
|
143
|
+
};
|
|
144
|
+
doc.addEventListener("pointerover", handlePointerOver, { passive: true });
|
|
145
|
+
doc.addEventListener("pointerout", handlePointerOut, { passive: true });
|
|
146
|
+
return () => {
|
|
147
|
+
doc.removeEventListener("pointerover", handlePointerOver);
|
|
148
|
+
doc.removeEventListener("pointerout", handlePointerOut);
|
|
149
|
+
if (hoverTimeout) {
|
|
150
|
+
clearTimeout(hoverTimeout);
|
|
151
|
+
}
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
function prefetchElementQrls(el) {
|
|
155
|
+
const eventAttrs = ["on:click", "on:input", "on:change", "on:submit", "on:keydown", "on:keyup"];
|
|
156
|
+
for (const attr of eventAttrs) {
|
|
157
|
+
const qrl = el.getAttribute(attr);
|
|
158
|
+
if (qrl) {
|
|
159
|
+
prefetchQrl(qrl);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
const resumeQrl = el.getAttribute("data-fict-h");
|
|
163
|
+
if (resumeQrl) {
|
|
164
|
+
prefetchQrl(resumeQrl);
|
|
165
|
+
}
|
|
166
|
+
const children = el.querySelectorAll(
|
|
167
|
+
"[on\\:click], [on\\:input], [on\\:change], [on\\:submit], [data-fict-h]"
|
|
168
|
+
);
|
|
169
|
+
children.forEach((child) => {
|
|
170
|
+
for (const attr of eventAttrs) {
|
|
171
|
+
const qrl = child.getAttribute(attr);
|
|
172
|
+
if (qrl) {
|
|
173
|
+
prefetchQrl(qrl);
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
const childResumeQrl = child.getAttribute("data-fict-h");
|
|
177
|
+
if (childResumeQrl) {
|
|
178
|
+
prefetchQrl(childResumeQrl);
|
|
179
|
+
}
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
function prefetchQrl(qrl) {
|
|
183
|
+
const { url } = parseQrl(qrl);
|
|
184
|
+
if (!url || prefetchedUrls.has(url)) return;
|
|
185
|
+
prefetchedUrls.add(url);
|
|
186
|
+
const resolvedUrl = resolveModuleUrl(url);
|
|
187
|
+
if (typeof document !== "undefined") {
|
|
188
|
+
const link = document.createElement("link");
|
|
189
|
+
link.rel = "modulepreload";
|
|
190
|
+
link.href = resolvedUrl;
|
|
191
|
+
link.crossOrigin = "anonymous";
|
|
192
|
+
document.head.appendChild(link);
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
function handleResumableEvent(event) {
|
|
196
|
+
const promise = handleResumableEventAsync(event);
|
|
197
|
+
pendingHandlers.add(promise);
|
|
198
|
+
promise.finally(() => {
|
|
199
|
+
pendingHandlers.delete(promise);
|
|
200
|
+
});
|
|
201
|
+
}
|
|
202
|
+
async function handleResumableEventAsync(event) {
|
|
203
|
+
const path = typeof event.composedPath === "function" ? event.composedPath() : buildEventPath(event);
|
|
204
|
+
for (const node of path) {
|
|
205
|
+
if (!(node instanceof Element)) continue;
|
|
206
|
+
const qrl = node.getAttribute(`on:${event.type}`);
|
|
207
|
+
if (!qrl) continue;
|
|
208
|
+
const host = node.closest("[data-fict-s]");
|
|
209
|
+
if (!host) continue;
|
|
210
|
+
const scopeId = host.getAttribute("data-fict-s");
|
|
211
|
+
if (!scopeId) continue;
|
|
212
|
+
const snapshot = __fictGetSSRScope(scopeId);
|
|
213
|
+
if (snapshot) {
|
|
214
|
+
__fictEnsureScope(scopeId, host, snapshot);
|
|
215
|
+
}
|
|
216
|
+
const { url, exportName } = parseQrl(qrl);
|
|
217
|
+
if (event.cancelable && (event.type === "click" || event.type === "submit")) {
|
|
218
|
+
const tag = node.tagName.toLowerCase();
|
|
219
|
+
if (tag === "a" || tag === "form") {
|
|
220
|
+
event.preventDefault();
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
if (!hydratedScopes.has(scopeId)) {
|
|
224
|
+
const resumeQrl = host.getAttribute("data-fict-h");
|
|
225
|
+
if (resumeQrl) {
|
|
226
|
+
const { url: resumeUrl, exportName: resumeExport } = parseQrl(resumeQrl);
|
|
227
|
+
const resolvedResumeUrl = resolveModuleUrl(resumeUrl);
|
|
228
|
+
await import(
|
|
229
|
+
/* @vite-ignore */
|
|
230
|
+
resolvedResumeUrl
|
|
231
|
+
);
|
|
232
|
+
const resumeFn = __fictGetResume(resumeExport);
|
|
233
|
+
if (typeof resumeFn === "function") {
|
|
234
|
+
await resumeFn(scopeId, host);
|
|
235
|
+
hydratedScopes.add(scopeId);
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
const resolvedUrl = resolveModuleUrl(url);
|
|
240
|
+
const mod = await import(
|
|
241
|
+
/* @vite-ignore */
|
|
242
|
+
resolvedUrl
|
|
243
|
+
);
|
|
244
|
+
const handler = mod[exportName];
|
|
245
|
+
if (typeof handler === "function") {
|
|
246
|
+
await handler(scopeId, event, node);
|
|
247
|
+
}
|
|
248
|
+
return;
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
function parseQrl(qrl) {
|
|
252
|
+
const [ref] = qrl.split("[");
|
|
253
|
+
if (!ref) {
|
|
254
|
+
return { url: "", exportName: "default" };
|
|
255
|
+
}
|
|
256
|
+
const hashIndex = ref.lastIndexOf("#");
|
|
257
|
+
if (hashIndex === -1) {
|
|
258
|
+
return { url: ref, exportName: "default" };
|
|
259
|
+
}
|
|
260
|
+
return { url: ref.slice(0, hashIndex), exportName: ref.slice(hashIndex + 1) };
|
|
261
|
+
}
|
|
262
|
+
function buildEventPath(event) {
|
|
263
|
+
const path = [];
|
|
264
|
+
let node = event.target;
|
|
265
|
+
while (node) {
|
|
266
|
+
path.push(node);
|
|
267
|
+
node = node.parentNode;
|
|
268
|
+
}
|
|
269
|
+
path.push(window);
|
|
270
|
+
return path;
|
|
271
|
+
}
|
|
272
|
+
export {
|
|
273
|
+
__fictUseLexicalScope,
|
|
274
|
+
cleanupEventListeners,
|
|
275
|
+
installResumableLoader,
|
|
276
|
+
resetHydratedScopes,
|
|
277
|
+
resetPrefetchedUrls,
|
|
278
|
+
waitForPendingHandlers
|
|
279
|
+
};
|
|
280
|
+
//# sourceMappingURL=loader.js.map
|
|
@@ -0,0 +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,4 +1,5 @@
|
|
|
1
|
-
import { M as MemoOptions
|
|
1
|
+
import { M as MemoOptions } from './signal-C4ISF17w.cjs';
|
|
2
|
+
import { F as FictNode, D as DOMElement } from './effect-BpSNEJJz.cjs';
|
|
2
3
|
|
|
3
4
|
type Memo<T> = () => T;
|
|
4
5
|
declare function createMemo<T>(fn: () => T, options?: MemoOptions<T>): Memo<T>;
|
|
@@ -683,6 +684,16 @@ interface SVGAttributes<T> extends HTMLAttributes<T> {
|
|
|
683
684
|
* ```
|
|
684
685
|
*/
|
|
685
686
|
declare function render(view: () => FictNode, container: HTMLElement): () => void;
|
|
687
|
+
/**
|
|
688
|
+
* Hydrate a component into an existing DOM container.
|
|
689
|
+
* Unlike render(), this runs the view function INSIDE the hydration context
|
|
690
|
+
* so that template() can claim existing DOM nodes.
|
|
691
|
+
*
|
|
692
|
+
* @param view - A function that returns the view to hydrate
|
|
693
|
+
* @param container - The DOM container with existing SSR content
|
|
694
|
+
* @returns A teardown function to unmount the view
|
|
695
|
+
*/
|
|
696
|
+
declare function hydrateComponent(view: () => FictNode, container: HTMLElement): () => void;
|
|
686
697
|
/**
|
|
687
698
|
* Create a DOM element from a Fict node.
|
|
688
699
|
* This is the main entry point for converting virtual nodes to real DOM.
|
|
@@ -757,4 +768,4 @@ declare function keyed<T, K extends string | number | symbol>(target: T | PropGe
|
|
|
757
768
|
*/
|
|
758
769
|
declare function prop<T>(getter: () => T, options?: PropOptions): PropGetter<T>;
|
|
759
770
|
|
|
760
|
-
export { Fragment as F, JSX as J, type Memo as M, __fictProp as _, createElement as a, __fictPropsRest as b, createMemo as c, createPropsProxy as d, keyed as k, mergeProps as m, prop as p, render as r, template as t };
|
|
771
|
+
export { Fragment as F, JSX as J, type Memo as M, __fictProp as _, createElement as a, __fictPropsRest as b, createMemo as c, createPropsProxy as d, hydrateComponent as h, keyed as k, mergeProps as m, prop as p, render as r, template as t };
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { M as MemoOptions
|
|
1
|
+
import { M as MemoOptions } from './signal-C4ISF17w.js';
|
|
2
|
+
import { F as FictNode, D as DOMElement } from './effect-BpSNEJJz.js';
|
|
2
3
|
|
|
3
4
|
type Memo<T> = () => T;
|
|
4
5
|
declare function createMemo<T>(fn: () => T, options?: MemoOptions<T>): Memo<T>;
|
|
@@ -683,6 +684,16 @@ interface SVGAttributes<T> extends HTMLAttributes<T> {
|
|
|
683
684
|
* ```
|
|
684
685
|
*/
|
|
685
686
|
declare function render(view: () => FictNode, container: HTMLElement): () => void;
|
|
687
|
+
/**
|
|
688
|
+
* Hydrate a component into an existing DOM container.
|
|
689
|
+
* Unlike render(), this runs the view function INSIDE the hydration context
|
|
690
|
+
* so that template() can claim existing DOM nodes.
|
|
691
|
+
*
|
|
692
|
+
* @param view - A function that returns the view to hydrate
|
|
693
|
+
* @param container - The DOM container with existing SSR content
|
|
694
|
+
* @returns A teardown function to unmount the view
|
|
695
|
+
*/
|
|
696
|
+
declare function hydrateComponent(view: () => FictNode, container: HTMLElement): () => void;
|
|
686
697
|
/**
|
|
687
698
|
* Create a DOM element from a Fict node.
|
|
688
699
|
* This is the main entry point for converting virtual nodes to real DOM.
|
|
@@ -757,4 +768,4 @@ declare function keyed<T, K extends string | number | symbol>(target: T | PropGe
|
|
|
757
768
|
*/
|
|
758
769
|
declare function prop<T>(getter: () => T, options?: PropOptions): PropGetter<T>;
|
|
759
770
|
|
|
760
|
-
export { Fragment as F, JSX as J, type Memo as M, __fictProp as _, createElement as a, __fictPropsRest as b, createMemo as c, createPropsProxy as d, keyed as k, mergeProps as m, prop as p, render as r, template as t };
|
|
771
|
+
export { Fragment as F, JSX as J, type Memo as M, __fictProp as _, createElement as a, __fictPropsRest as b, createMemo as c, createPropsProxy as d, hydrateComponent as h, keyed as k, mergeProps as m, prop as p, render as r, template as t };
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { a as SignalOptions, S as SignalAccessor, M as MemoOptions, C as ComputedAccessor } from './signal-C4ISF17w.cjs';
|
|
2
|
+
|
|
3
|
+
interface HookContext {
|
|
4
|
+
slots: unknown[];
|
|
5
|
+
cursor: number;
|
|
6
|
+
rendering?: boolean;
|
|
7
|
+
componentId?: number;
|
|
8
|
+
parentId?: number;
|
|
9
|
+
scopeId?: string;
|
|
10
|
+
scopeType?: string;
|
|
11
|
+
slotMap?: Record<string, number>;
|
|
12
|
+
}
|
|
13
|
+
declare function __fictUseContext(): HookContext;
|
|
14
|
+
declare function __fictPushContext(): HookContext;
|
|
15
|
+
declare function __fictPrepareContext(ctx: HookContext): void;
|
|
16
|
+
declare function __fictPopContext(): void;
|
|
17
|
+
declare function __fictResetContext(): void;
|
|
18
|
+
declare function __fictUseSignal<T>(ctx: HookContext, initial: T, optionsOrSlot?: number | SignalOptions<T>, slot?: number): SignalAccessor<T>;
|
|
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;
|
|
21
|
+
declare function __fictRender<T>(ctx: HookContext, fn: () => T): T;
|
|
22
|
+
|
|
23
|
+
type SlotSnapshot = [index: number, type: 'sig', value: unknown] | [index: number, type: 'store', value: unknown] | [index: number, type: 'raw', value: unknown];
|
|
24
|
+
interface ScopeSnapshot {
|
|
25
|
+
id: string;
|
|
26
|
+
t?: string;
|
|
27
|
+
slots: SlotSnapshot[];
|
|
28
|
+
props?: Record<string, unknown>;
|
|
29
|
+
vars?: Record<string, number>;
|
|
30
|
+
}
|
|
31
|
+
interface SSRState {
|
|
32
|
+
scopes: Record<string, ScopeSnapshot>;
|
|
33
|
+
}
|
|
34
|
+
interface ScopeRecord {
|
|
35
|
+
id: string;
|
|
36
|
+
ctx: HookContext;
|
|
37
|
+
host: Element;
|
|
38
|
+
type?: string;
|
|
39
|
+
props?: Record<string, unknown>;
|
|
40
|
+
}
|
|
41
|
+
declare function __fictEnableSSR(): void;
|
|
42
|
+
declare function __fictDisableSSR(): void;
|
|
43
|
+
declare function __fictEnableResumable(): void;
|
|
44
|
+
declare function __fictDisableResumable(): void;
|
|
45
|
+
declare function __fictIsResumable(): boolean;
|
|
46
|
+
declare function __fictIsSSR(): boolean;
|
|
47
|
+
declare function __fictEnterHydration(): void;
|
|
48
|
+
declare function __fictExitHydration(): void;
|
|
49
|
+
declare function __fictIsHydrating(): boolean;
|
|
50
|
+
declare function __fictRegisterScope(ctx: HookContext, host: Element, type?: string, props?: Record<string, unknown>): string;
|
|
51
|
+
declare function __fictGetScopeRegistry(): Map<string, ScopeRecord>;
|
|
52
|
+
declare function __fictSerializeSSRState(): SSRState;
|
|
53
|
+
declare function __fictSetSSRState(state: SSRState | null): void;
|
|
54
|
+
declare function __fictGetSSRScope(id: string): ScopeSnapshot | undefined;
|
|
55
|
+
declare function __fictEnsureScope(scopeId: string, host: Element, snapshot?: ScopeSnapshot): HookContext;
|
|
56
|
+
declare function __fictUseLexicalScope(scopeId: string, names: string[]): unknown[];
|
|
57
|
+
declare function __fictGetScopeProps(scopeId: string): Record<string, unknown> | undefined;
|
|
58
|
+
declare function __fictQrl(moduleId: string, exportName: string): string;
|
|
59
|
+
/**
|
|
60
|
+
* Register a resume function to prevent it from being tree-shaken.
|
|
61
|
+
* This is called at module load time by compiled component code.
|
|
62
|
+
*/
|
|
63
|
+
declare function __fictRegisterResume(name: string, fn: (...args: unknown[]) => unknown): void;
|
|
64
|
+
/**
|
|
65
|
+
* Get a registered resume function by name.
|
|
66
|
+
* Used by the loader to find resume functions.
|
|
67
|
+
*/
|
|
68
|
+
declare function __fictGetResume(name: string): ((...args: unknown[]) => unknown) | undefined;
|
|
69
|
+
/**
|
|
70
|
+
* Serialize a value with support for complex types.
|
|
71
|
+
* Handles: Date, Map, Set, RegExp, undefined, NaN, Infinity, -Infinity, BigInt, circular references
|
|
72
|
+
*/
|
|
73
|
+
declare function serializeValue(value: unknown, seen?: Map<object, string>, path?: string): unknown;
|
|
74
|
+
/**
|
|
75
|
+
* Deserialize a value, restoring complex types from their serialized form.
|
|
76
|
+
*/
|
|
77
|
+
declare function deserializeValue(value: unknown, refs?: Map<string, unknown>, path?: string): unknown;
|
|
78
|
+
|
|
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 };
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { a as SignalOptions, S as SignalAccessor, M as MemoOptions, C as ComputedAccessor } from './signal-C4ISF17w.js';
|
|
2
|
+
|
|
3
|
+
interface HookContext {
|
|
4
|
+
slots: unknown[];
|
|
5
|
+
cursor: number;
|
|
6
|
+
rendering?: boolean;
|
|
7
|
+
componentId?: number;
|
|
8
|
+
parentId?: number;
|
|
9
|
+
scopeId?: string;
|
|
10
|
+
scopeType?: string;
|
|
11
|
+
slotMap?: Record<string, number>;
|
|
12
|
+
}
|
|
13
|
+
declare function __fictUseContext(): HookContext;
|
|
14
|
+
declare function __fictPushContext(): HookContext;
|
|
15
|
+
declare function __fictPrepareContext(ctx: HookContext): void;
|
|
16
|
+
declare function __fictPopContext(): void;
|
|
17
|
+
declare function __fictResetContext(): void;
|
|
18
|
+
declare function __fictUseSignal<T>(ctx: HookContext, initial: T, optionsOrSlot?: number | SignalOptions<T>, slot?: number): SignalAccessor<T>;
|
|
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;
|
|
21
|
+
declare function __fictRender<T>(ctx: HookContext, fn: () => T): T;
|
|
22
|
+
|
|
23
|
+
type SlotSnapshot = [index: number, type: 'sig', value: unknown] | [index: number, type: 'store', value: unknown] | [index: number, type: 'raw', value: unknown];
|
|
24
|
+
interface ScopeSnapshot {
|
|
25
|
+
id: string;
|
|
26
|
+
t?: string;
|
|
27
|
+
slots: SlotSnapshot[];
|
|
28
|
+
props?: Record<string, unknown>;
|
|
29
|
+
vars?: Record<string, number>;
|
|
30
|
+
}
|
|
31
|
+
interface SSRState {
|
|
32
|
+
scopes: Record<string, ScopeSnapshot>;
|
|
33
|
+
}
|
|
34
|
+
interface ScopeRecord {
|
|
35
|
+
id: string;
|
|
36
|
+
ctx: HookContext;
|
|
37
|
+
host: Element;
|
|
38
|
+
type?: string;
|
|
39
|
+
props?: Record<string, unknown>;
|
|
40
|
+
}
|
|
41
|
+
declare function __fictEnableSSR(): void;
|
|
42
|
+
declare function __fictDisableSSR(): void;
|
|
43
|
+
declare function __fictEnableResumable(): void;
|
|
44
|
+
declare function __fictDisableResumable(): void;
|
|
45
|
+
declare function __fictIsResumable(): boolean;
|
|
46
|
+
declare function __fictIsSSR(): boolean;
|
|
47
|
+
declare function __fictEnterHydration(): void;
|
|
48
|
+
declare function __fictExitHydration(): void;
|
|
49
|
+
declare function __fictIsHydrating(): boolean;
|
|
50
|
+
declare function __fictRegisterScope(ctx: HookContext, host: Element, type?: string, props?: Record<string, unknown>): string;
|
|
51
|
+
declare function __fictGetScopeRegistry(): Map<string, ScopeRecord>;
|
|
52
|
+
declare function __fictSerializeSSRState(): SSRState;
|
|
53
|
+
declare function __fictSetSSRState(state: SSRState | null): void;
|
|
54
|
+
declare function __fictGetSSRScope(id: string): ScopeSnapshot | undefined;
|
|
55
|
+
declare function __fictEnsureScope(scopeId: string, host: Element, snapshot?: ScopeSnapshot): HookContext;
|
|
56
|
+
declare function __fictUseLexicalScope(scopeId: string, names: string[]): unknown[];
|
|
57
|
+
declare function __fictGetScopeProps(scopeId: string): Record<string, unknown> | undefined;
|
|
58
|
+
declare function __fictQrl(moduleId: string, exportName: string): string;
|
|
59
|
+
/**
|
|
60
|
+
* Register a resume function to prevent it from being tree-shaken.
|
|
61
|
+
* This is called at module load time by compiled component code.
|
|
62
|
+
*/
|
|
63
|
+
declare function __fictRegisterResume(name: string, fn: (...args: unknown[]) => unknown): void;
|
|
64
|
+
/**
|
|
65
|
+
* Get a registered resume function by name.
|
|
66
|
+
* Used by the loader to find resume functions.
|
|
67
|
+
*/
|
|
68
|
+
declare function __fictGetResume(name: string): ((...args: unknown[]) => unknown) | undefined;
|
|
69
|
+
/**
|
|
70
|
+
* Serialize a value with support for complex types.
|
|
71
|
+
* Handles: Date, Map, Set, RegExp, undefined, NaN, Infinity, -Infinity, BigInt, circular references
|
|
72
|
+
*/
|
|
73
|
+
declare function serializeValue(value: unknown, seen?: Map<object, string>, path?: string): unknown;
|
|
74
|
+
/**
|
|
75
|
+
* Deserialize a value, restoring complex types from their serialized form.
|
|
76
|
+
*/
|
|
77
|
+
declare function deserializeValue(value: unknown, refs?: Map<string, unknown>, path?: string): unknown;
|
|
78
|
+
|
|
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 };
|