@async/framework 0.10.2 → 0.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +20 -0
- package/README.md +23 -7
- package/browser.d.ts +4 -7
- package/browser.js +17 -66
- package/browser.min.js +1 -1
- package/browser.ts +17 -66
- package/browser.umd.js +17 -66
- package/browser.umd.min.js +1 -1
- package/{server.d.ts → framework.d.ts} +4 -7
- package/framework.ts +5946 -0
- package/package.json +25 -17
- package/server.js +5945 -0
- package/examples/cache/index.html +0 -16
- package/examples/cache/main.js +0 -47
- package/examples/components/index.html +0 -11
- package/examples/components/main.js +0 -26
- package/examples/counter/index.html +0 -15
- package/examples/counter/main.js +0 -17
- package/examples/partials/index.html +0 -15
- package/examples/partials/main.js +0 -43
- package/examples/product/index.html +0 -32
- package/examples/product/main.js +0 -24
- package/examples/router/index.html +0 -18
- package/examples/router/main.js +0 -52
- package/examples/server-call/index.html +0 -21
- package/examples/server-call/main.js +0 -22
- package/examples/ssr/index.html +0 -12
- package/examples/ssr/main.js +0 -89
- package/examples/streaming/index.html +0 -16
- package/examples/streaming/main.js +0 -30
- package/src/app.js +0 -802
- package/src/async-signal.js +0 -277
- package/src/attributes.js +0 -52
- package/src/boundary-receiver.js +0 -302
- package/src/browser.js +0 -18
- package/src/cache.js +0 -193
- package/src/component.js +0 -373
- package/src/delay.js +0 -30
- package/src/elements.js +0 -63
- package/src/handlers.js +0 -219
- package/src/html.js +0 -158
- package/src/index.js +0 -20
- package/src/lazy-registry.js +0 -218
- package/src/loader.js +0 -772
- package/src/partials.js +0 -133
- package/src/registry-store.js +0 -267
- package/src/request-context.js +0 -40
- package/src/router.js +0 -617
- package/src/scheduler.js +0 -300
- package/src/server-entry.js +0 -20
- package/src/server-registry.js +0 -97
- package/src/server.js +0 -362
- package/src/signals.js +0 -592
package/src/loader.js
DELETED
|
@@ -1,772 +0,0 @@
|
|
|
1
|
-
import { renderComponent } from "./component.js";
|
|
2
|
-
import { createHandlerRegistry } from "./handlers.js";
|
|
3
|
-
import { createScheduler } from "./scheduler.js";
|
|
4
|
-
import { createSignalRegistry, isSignalRef } from "./signals.js";
|
|
5
|
-
import { matchAttribute, normalizeAttributeConfig, readAttribute } from "./attributes.js";
|
|
6
|
-
|
|
7
|
-
const inlineBindingPrefix = "__async:inline:";
|
|
8
|
-
|
|
9
|
-
export function Loader({ root, signals, handlers, server, router, cache, attributes, scheduler } = {}) {
|
|
10
|
-
const documentRef = root?.ownerDocument ?? root ?? globalThis.document;
|
|
11
|
-
const rootNode = root ?? documentRef;
|
|
12
|
-
const signalRegistry = signals ?? createSignalRegistry();
|
|
13
|
-
const handlerRegistry = handlers ?? createHandlerRegistry();
|
|
14
|
-
const schedulerInstance = scheduler ?? createScheduler();
|
|
15
|
-
const ownsScheduler = !scheduler;
|
|
16
|
-
const attributeConfig = normalizeAttributeConfig(attributes);
|
|
17
|
-
const cleanups = new Set();
|
|
18
|
-
const eventBindings = new WeakMap();
|
|
19
|
-
const signalBindings = new WeakMap();
|
|
20
|
-
const mountedElements = new WeakSet();
|
|
21
|
-
const visibleElements = new WeakSet();
|
|
22
|
-
const boundaryState = new WeakMap();
|
|
23
|
-
const renderingBoundaries = new WeakSet();
|
|
24
|
-
const inlineBindings = new Map();
|
|
25
|
-
const scopedCleanups = new WeakMap();
|
|
26
|
-
let inlineBindingCounter = 0;
|
|
27
|
-
let destroyed = false;
|
|
28
|
-
|
|
29
|
-
const api = {
|
|
30
|
-
root: rootNode,
|
|
31
|
-
signals: signalRegistry,
|
|
32
|
-
handlers: handlerRegistry,
|
|
33
|
-
server,
|
|
34
|
-
router,
|
|
35
|
-
cache,
|
|
36
|
-
scheduler: schedulerInstance,
|
|
37
|
-
attributes: attributeConfig,
|
|
38
|
-
|
|
39
|
-
start() {
|
|
40
|
-
assertActive();
|
|
41
|
-
api.scan(rootNode);
|
|
42
|
-
return api;
|
|
43
|
-
},
|
|
44
|
-
|
|
45
|
-
scan(rootOrFragment = rootNode) {
|
|
46
|
-
assertActive();
|
|
47
|
-
bindSignalAttributes(rootOrFragment);
|
|
48
|
-
bindClassAttributes(rootOrFragment);
|
|
49
|
-
bindEventAttributes(rootOrFragment);
|
|
50
|
-
bindBoundaries(rootOrFragment);
|
|
51
|
-
runPseudoEvents(rootOrFragment);
|
|
52
|
-
return api;
|
|
53
|
-
},
|
|
54
|
-
|
|
55
|
-
swap(boundaryId, fragmentOrTemplate) {
|
|
56
|
-
assertActive();
|
|
57
|
-
const boundary = findBoundary(rootNode, boundaryId, attributeConfig);
|
|
58
|
-
if (!boundary) {
|
|
59
|
-
throw new Error(`Boundary "${boundaryId}" was not found.`);
|
|
60
|
-
}
|
|
61
|
-
cleanupChildren(boundary);
|
|
62
|
-
boundary.replaceChildren(toFragment(fragmentOrTemplate, documentRef));
|
|
63
|
-
api.scan(boundary);
|
|
64
|
-
return boundary;
|
|
65
|
-
},
|
|
66
|
-
|
|
67
|
-
mount(target, Component, props = {}) {
|
|
68
|
-
assertActive();
|
|
69
|
-
const rendered = renderComponent(Component, props, {
|
|
70
|
-
signals: signalRegistry,
|
|
71
|
-
handlers: handlerRegistry,
|
|
72
|
-
loader: api,
|
|
73
|
-
server: api.server,
|
|
74
|
-
router: api.router,
|
|
75
|
-
cache: api.cache,
|
|
76
|
-
scheduler: schedulerInstance,
|
|
77
|
-
attributes: attributeConfig
|
|
78
|
-
});
|
|
79
|
-
cleanupChildren(target);
|
|
80
|
-
target.replaceChildren(toFragment(rendered.html, target.ownerDocument));
|
|
81
|
-
api.scan(target);
|
|
82
|
-
rendered.mount(target);
|
|
83
|
-
rendered.visible(target, api._observeVisible);
|
|
84
|
-
addCleanup(rendered.cleanup, target, "children");
|
|
85
|
-
return rendered;
|
|
86
|
-
},
|
|
87
|
-
|
|
88
|
-
destroy() {
|
|
89
|
-
if (destroyed) {
|
|
90
|
-
return;
|
|
91
|
-
}
|
|
92
|
-
destroyed = true;
|
|
93
|
-
markDestroyedScopes(rootNode);
|
|
94
|
-
for (const cleanup of [...cleanups]) {
|
|
95
|
-
runCleanup(cleanup);
|
|
96
|
-
}
|
|
97
|
-
cleanups.clear();
|
|
98
|
-
if (ownsScheduler) {
|
|
99
|
-
schedulerInstance.destroy();
|
|
100
|
-
}
|
|
101
|
-
},
|
|
102
|
-
|
|
103
|
-
_observeVisible(target, fn) {
|
|
104
|
-
return observeVisible(target, fn);
|
|
105
|
-
},
|
|
106
|
-
|
|
107
|
-
_registerBinding(value) {
|
|
108
|
-
const id = `${inlineBindingPrefix}${++inlineBindingCounter}`;
|
|
109
|
-
inlineBindings.set(id, value);
|
|
110
|
-
return id;
|
|
111
|
-
},
|
|
112
|
-
|
|
113
|
-
_releaseBinding(id) {
|
|
114
|
-
inlineBindings.delete(id);
|
|
115
|
-
}
|
|
116
|
-
};
|
|
117
|
-
|
|
118
|
-
signalRegistry._setContext?.({ server: api.server, router: api.router, loader: api, cache: api.cache, scheduler: schedulerInstance });
|
|
119
|
-
api.server?._setContext?.({
|
|
120
|
-
signals: signalRegistry,
|
|
121
|
-
handlers: handlerRegistry,
|
|
122
|
-
loader: api,
|
|
123
|
-
router: api.router,
|
|
124
|
-
cache: api.cache,
|
|
125
|
-
scheduler: schedulerInstance
|
|
126
|
-
});
|
|
127
|
-
|
|
128
|
-
function bindEventAttributes(scope) {
|
|
129
|
-
for (const element of elementsIn(scope)) {
|
|
130
|
-
if (typeof element.getAttributeNames !== "function") {
|
|
131
|
-
continue;
|
|
132
|
-
}
|
|
133
|
-
for (const name of element.getAttributeNames()) {
|
|
134
|
-
const eventName = matchAttribute(name, attributeConfig, "on");
|
|
135
|
-
if (!eventName) {
|
|
136
|
-
continue;
|
|
137
|
-
}
|
|
138
|
-
if (eventName === "attach" || eventName === "mount" || eventName === "visible") {
|
|
139
|
-
continue;
|
|
140
|
-
}
|
|
141
|
-
bindEvent(element, eventName, element.getAttribute(name));
|
|
142
|
-
}
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
function bindEvent(element, eventName, ref) {
|
|
147
|
-
const key = `${eventName}:${ref}`;
|
|
148
|
-
const bound = eventBindings.get(element) ?? new Set();
|
|
149
|
-
if (bound.has(key)) {
|
|
150
|
-
return;
|
|
151
|
-
}
|
|
152
|
-
bound.add(key);
|
|
153
|
-
eventBindings.set(element, bound);
|
|
154
|
-
|
|
155
|
-
const listener = async (event) => {
|
|
156
|
-
try {
|
|
157
|
-
await schedulerInstance.batch(() => handlerRegistry.run(ref, {
|
|
158
|
-
signals: signalRegistry,
|
|
159
|
-
handlers: handlerRegistry,
|
|
160
|
-
loader: api,
|
|
161
|
-
server: api.server,
|
|
162
|
-
router: api.router,
|
|
163
|
-
cache: api.cache,
|
|
164
|
-
scheduler: schedulerInstance,
|
|
165
|
-
event,
|
|
166
|
-
element,
|
|
167
|
-
el: element,
|
|
168
|
-
root: rootNode
|
|
169
|
-
}));
|
|
170
|
-
} catch (error) {
|
|
171
|
-
dispatchAsyncError(element, error);
|
|
172
|
-
}
|
|
173
|
-
};
|
|
174
|
-
|
|
175
|
-
element.addEventListener(eventName, listener);
|
|
176
|
-
addCleanup(() => element.removeEventListener(eventName, listener), element);
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
function bindSignalAttributes(scope) {
|
|
180
|
-
for (const element of elementsIn(scope)) {
|
|
181
|
-
for (const name of element.getAttributeNames?.() ?? []) {
|
|
182
|
-
const signalName = matchAttribute(name, attributeConfig, "signal");
|
|
183
|
-
if (!signalName) {
|
|
184
|
-
continue;
|
|
185
|
-
}
|
|
186
|
-
if (signalName === "text") {
|
|
187
|
-
const path = element.getAttribute(name);
|
|
188
|
-
bindSignal(element, `text:${path}`, path, (value) => {
|
|
189
|
-
element.textContent = value ?? "";
|
|
190
|
-
});
|
|
191
|
-
continue;
|
|
192
|
-
}
|
|
193
|
-
if (signalName === "value") {
|
|
194
|
-
const path = element.getAttribute(name);
|
|
195
|
-
bindSignal(element, `value:${path}`, path, (value) => {
|
|
196
|
-
if ("value" in element && element.value !== String(value ?? "")) {
|
|
197
|
-
element.value = value ?? "";
|
|
198
|
-
} else if (!("value" in element)) {
|
|
199
|
-
element.setAttribute("value", value ?? "");
|
|
200
|
-
}
|
|
201
|
-
});
|
|
202
|
-
bindValueWriter(element, path);
|
|
203
|
-
continue;
|
|
204
|
-
}
|
|
205
|
-
if (signalName.startsWith("attr:")) {
|
|
206
|
-
const attr = signalName.slice("attr:".length);
|
|
207
|
-
const path = element.getAttribute(name);
|
|
208
|
-
bindSignal(element, `attr:${attr}:${path}`, path, (value) => updateAttribute(element, attr, value));
|
|
209
|
-
continue;
|
|
210
|
-
}
|
|
211
|
-
if (signalName.startsWith("prop:")) {
|
|
212
|
-
const prop = signalName.slice("prop:".length);
|
|
213
|
-
const path = element.getAttribute(name);
|
|
214
|
-
bindSignal(element, `prop:${prop}:${path}`, path, (value) => updateProperty(element, prop, value));
|
|
215
|
-
continue;
|
|
216
|
-
}
|
|
217
|
-
if (signalName.startsWith("class:")) {
|
|
218
|
-
const className = signalName.slice("class:".length);
|
|
219
|
-
const path = element.getAttribute(name);
|
|
220
|
-
if (className === "" || className === "{}") {
|
|
221
|
-
bindClass(element, className, path);
|
|
222
|
-
} else {
|
|
223
|
-
bindSignal(element, `class:${className}:${path}`, path, (value) => {
|
|
224
|
-
element.classList.toggle(className, Boolean(value));
|
|
225
|
-
});
|
|
226
|
-
}
|
|
227
|
-
continue;
|
|
228
|
-
}
|
|
229
|
-
if (signalName === "class") {
|
|
230
|
-
const path = element.getAttribute(name);
|
|
231
|
-
bindClass(element, "{}", path);
|
|
232
|
-
}
|
|
233
|
-
}
|
|
234
|
-
}
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
function bindClassAttributes(scope) {
|
|
238
|
-
for (const element of elementsIn(scope)) {
|
|
239
|
-
for (const name of element.getAttributeNames?.() ?? []) {
|
|
240
|
-
const className = matchAttribute(name, attributeConfig, "class");
|
|
241
|
-
if (className == null) {
|
|
242
|
-
continue;
|
|
243
|
-
}
|
|
244
|
-
bindClass(element, className, element.getAttribute(name));
|
|
245
|
-
}
|
|
246
|
-
}
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
function bindClass(element, className, path) {
|
|
250
|
-
if (className === "" || className === "{}") {
|
|
251
|
-
const staticClasses = readClassTokens(element);
|
|
252
|
-
let previous = new Set();
|
|
253
|
-
bindSignal(element, `class:{}:${path}`, path, (value) => {
|
|
254
|
-
const next = normalizeClassTokens(value);
|
|
255
|
-
const current = readClassTokens(element);
|
|
256
|
-
for (const token of previous) {
|
|
257
|
-
if (!next.has(token) && !staticClasses.has(token)) {
|
|
258
|
-
current.delete(token);
|
|
259
|
-
}
|
|
260
|
-
}
|
|
261
|
-
for (const token of next) {
|
|
262
|
-
current.add(token);
|
|
263
|
-
}
|
|
264
|
-
writeClassTokens(element, current);
|
|
265
|
-
previous = next;
|
|
266
|
-
}, { rawInline: true });
|
|
267
|
-
return;
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
bindSignal(element, `class:${className}:${path}`, path, (value) => {
|
|
271
|
-
updateClassToken(element, className, Boolean(value));
|
|
272
|
-
});
|
|
273
|
-
}
|
|
274
|
-
|
|
275
|
-
function bindSignal(element, key, path, apply, options = {}) {
|
|
276
|
-
const bound = signalBindings.get(element) ?? new Set();
|
|
277
|
-
if (bound.has(key)) {
|
|
278
|
-
return;
|
|
279
|
-
}
|
|
280
|
-
bound.add(key);
|
|
281
|
-
signalBindings.set(element, bound);
|
|
282
|
-
|
|
283
|
-
const read = () => readBinding(path, options);
|
|
284
|
-
apply(read());
|
|
285
|
-
addCleanup(subscribeBinding(path, () => {
|
|
286
|
-
schedulerInstance.enqueue("binding", () => apply(read()), {
|
|
287
|
-
scope: element,
|
|
288
|
-
key
|
|
289
|
-
});
|
|
290
|
-
}), element);
|
|
291
|
-
}
|
|
292
|
-
|
|
293
|
-
function bindValueWriter(element, path) {
|
|
294
|
-
bindEvent(element, "input", `__async:set:${path}`);
|
|
295
|
-
bindEvent(element, "change", `__async:set:${path}`);
|
|
296
|
-
if (!handlerRegistry.resolve(`__async:set:${path}`)) {
|
|
297
|
-
handlerRegistry.register(`__async:set:${path}`, function writeValue({ element }) {
|
|
298
|
-
writeBinding(path, element.value);
|
|
299
|
-
});
|
|
300
|
-
}
|
|
301
|
-
}
|
|
302
|
-
|
|
303
|
-
function readBinding(path, options = {}) {
|
|
304
|
-
if (isInlineBinding(path)) {
|
|
305
|
-
const value = inlineBindings.get(path);
|
|
306
|
-
return options.rawInline ? value : resolveInlineValue(value);
|
|
307
|
-
}
|
|
308
|
-
return signalRegistry.get(path);
|
|
309
|
-
}
|
|
310
|
-
|
|
311
|
-
function writeBinding(path, value) {
|
|
312
|
-
if (!isInlineBinding(path)) {
|
|
313
|
-
return signalRegistry.set(path, value);
|
|
314
|
-
}
|
|
315
|
-
const binding = inlineBindings.get(path);
|
|
316
|
-
if (isSignalRef(binding)) {
|
|
317
|
-
return binding.set(value);
|
|
318
|
-
}
|
|
319
|
-
throw new Error(`Inline binding "${path}" is not writable.`);
|
|
320
|
-
}
|
|
321
|
-
|
|
322
|
-
function subscribeBinding(path, fn) {
|
|
323
|
-
if (!isInlineBinding(path)) {
|
|
324
|
-
return signalRegistry.subscribe(path, fn);
|
|
325
|
-
}
|
|
326
|
-
const cleanups = collectSignalRefs(inlineBindings.get(path)).map((ref) => ref.subscribe(fn));
|
|
327
|
-
return () => {
|
|
328
|
-
for (const cleanup of cleanups) {
|
|
329
|
-
cleanup();
|
|
330
|
-
}
|
|
331
|
-
};
|
|
332
|
-
}
|
|
333
|
-
|
|
334
|
-
function bindBoundaries(scope) {
|
|
335
|
-
for (const boundary of elementsIn(scope)) {
|
|
336
|
-
if (renderingBoundaries.has(boundary)) {
|
|
337
|
-
continue;
|
|
338
|
-
}
|
|
339
|
-
const id = boundaryIdFor(boundary, attributeConfig);
|
|
340
|
-
if (id == null) {
|
|
341
|
-
continue;
|
|
342
|
-
}
|
|
343
|
-
if (!boundaryState.has(boundary)) {
|
|
344
|
-
const templates = collectBoundaryTemplates(boundary, id, attributeConfig);
|
|
345
|
-
if (Object.keys(templates).length === 0 || !signalRegistry.has(id)) {
|
|
346
|
-
continue;
|
|
347
|
-
}
|
|
348
|
-
const state = {
|
|
349
|
-
id,
|
|
350
|
-
templates,
|
|
351
|
-
cleanup: signalRegistry.subscribe(`${id}.$status`, () => {
|
|
352
|
-
schedulerInstance.enqueue("binding", () => renderBoundary(boundary), {
|
|
353
|
-
scope: boundary,
|
|
354
|
-
key: `boundary:${id}`
|
|
355
|
-
});
|
|
356
|
-
})
|
|
357
|
-
};
|
|
358
|
-
boundaryState.set(boundary, state);
|
|
359
|
-
addCleanup(state.cleanup, boundary);
|
|
360
|
-
}
|
|
361
|
-
renderBoundary(boundary);
|
|
362
|
-
}
|
|
363
|
-
}
|
|
364
|
-
|
|
365
|
-
function renderBoundary(boundary) {
|
|
366
|
-
const state = boundaryState.get(boundary);
|
|
367
|
-
if (!state) {
|
|
368
|
-
return;
|
|
369
|
-
}
|
|
370
|
-
const status = signalRegistry.get(`${state.id}.$status`);
|
|
371
|
-
const template = chooseBoundaryTemplate(state.templates, status);
|
|
372
|
-
if (!template) {
|
|
373
|
-
return;
|
|
374
|
-
}
|
|
375
|
-
cleanupChildren(boundary);
|
|
376
|
-
boundary.replaceChildren(template.content.cloneNode(true));
|
|
377
|
-
renderingBoundaries.add(boundary);
|
|
378
|
-
try {
|
|
379
|
-
api.scan(boundary);
|
|
380
|
-
} finally {
|
|
381
|
-
renderingBoundaries.delete(boundary);
|
|
382
|
-
}
|
|
383
|
-
}
|
|
384
|
-
|
|
385
|
-
function runPseudoEvents(scope) {
|
|
386
|
-
for (const element of elementsIn(scope)) {
|
|
387
|
-
const refs = readPseudoRefs(element, ["attach", "mount"]);
|
|
388
|
-
if (refs.length === 0) {
|
|
389
|
-
continue;
|
|
390
|
-
}
|
|
391
|
-
if (mountedElements.has(element)) {
|
|
392
|
-
continue;
|
|
393
|
-
}
|
|
394
|
-
mountedElements.add(element);
|
|
395
|
-
for (const ref of refs) {
|
|
396
|
-
scheduleLifecycle(element, () => runPseudo(element, ref), `attach:${ref}`);
|
|
397
|
-
}
|
|
398
|
-
}
|
|
399
|
-
|
|
400
|
-
for (const element of elementsIn(scope)) {
|
|
401
|
-
const ref = readAttribute(element, attributeConfig, "on", "visible");
|
|
402
|
-
if (ref == null) {
|
|
403
|
-
continue;
|
|
404
|
-
}
|
|
405
|
-
if (visibleElements.has(element)) {
|
|
406
|
-
continue;
|
|
407
|
-
}
|
|
408
|
-
visibleElements.add(element);
|
|
409
|
-
addCleanup(observeVisible(element, () => scheduleLifecycle(element, () => runPseudo(element, ref), `visible:${ref}`)), element);
|
|
410
|
-
}
|
|
411
|
-
}
|
|
412
|
-
|
|
413
|
-
function readPseudoRefs(element, names) {
|
|
414
|
-
const refs = [];
|
|
415
|
-
for (const name of names) {
|
|
416
|
-
const ref = readAttribute(element, attributeConfig, "on", name);
|
|
417
|
-
if (ref != null) {
|
|
418
|
-
refs.push(ref);
|
|
419
|
-
}
|
|
420
|
-
}
|
|
421
|
-
return refs;
|
|
422
|
-
}
|
|
423
|
-
|
|
424
|
-
async function runPseudo(element, ref) {
|
|
425
|
-
try {
|
|
426
|
-
const results = await handlerRegistry.run(ref, {
|
|
427
|
-
signals: signalRegistry,
|
|
428
|
-
handlers: handlerRegistry,
|
|
429
|
-
loader: api,
|
|
430
|
-
server: api.server,
|
|
431
|
-
router: api.router,
|
|
432
|
-
cache: api.cache,
|
|
433
|
-
scheduler: schedulerInstance,
|
|
434
|
-
element,
|
|
435
|
-
el: element,
|
|
436
|
-
root: rootNode
|
|
437
|
-
});
|
|
438
|
-
for (const result of results) {
|
|
439
|
-
if (typeof result === "function") {
|
|
440
|
-
addCleanup(result, element);
|
|
441
|
-
}
|
|
442
|
-
}
|
|
443
|
-
} catch (error) {
|
|
444
|
-
dispatchAsyncError(element, error);
|
|
445
|
-
}
|
|
446
|
-
}
|
|
447
|
-
|
|
448
|
-
function observeVisible(target, fn) {
|
|
449
|
-
const ownerWindow = target.ownerDocument?.defaultView ?? globalThis;
|
|
450
|
-
const Observer = ownerWindow.IntersectionObserver ?? globalThis.IntersectionObserver;
|
|
451
|
-
if (!Observer) {
|
|
452
|
-
schedulerInstance.enqueue("lifecycle", () => {
|
|
453
|
-
if (!destroyed) {
|
|
454
|
-
fn(target);
|
|
455
|
-
}
|
|
456
|
-
}, {
|
|
457
|
-
scope: target,
|
|
458
|
-
key: "visible:fallback"
|
|
459
|
-
});
|
|
460
|
-
return () => {};
|
|
461
|
-
}
|
|
462
|
-
|
|
463
|
-
const observer = new Observer((entries) => {
|
|
464
|
-
if (entries.some((entry) => entry.isIntersecting)) {
|
|
465
|
-
observer.disconnect();
|
|
466
|
-
fn(target);
|
|
467
|
-
}
|
|
468
|
-
});
|
|
469
|
-
observer.observe(target);
|
|
470
|
-
return () => observer.disconnect();
|
|
471
|
-
}
|
|
472
|
-
|
|
473
|
-
function assertActive() {
|
|
474
|
-
if (destroyed) {
|
|
475
|
-
throw new Error("Loader has been destroyed.");
|
|
476
|
-
}
|
|
477
|
-
}
|
|
478
|
-
|
|
479
|
-
function addCleanup(cleanup, owner, mode = "self") {
|
|
480
|
-
if (typeof cleanup !== "function") {
|
|
481
|
-
return cleanup;
|
|
482
|
-
}
|
|
483
|
-
cleanups.add(cleanup);
|
|
484
|
-
if (owner) {
|
|
485
|
-
const records = scopedCleanups.get(owner) ?? [];
|
|
486
|
-
records.push({ cleanup, mode });
|
|
487
|
-
scopedCleanups.set(owner, records);
|
|
488
|
-
}
|
|
489
|
-
return cleanup;
|
|
490
|
-
}
|
|
491
|
-
|
|
492
|
-
function runCleanup(cleanup) {
|
|
493
|
-
if (typeof cleanup !== "function" || !cleanups.has(cleanup)) {
|
|
494
|
-
return;
|
|
495
|
-
}
|
|
496
|
-
cleanups.delete(cleanup);
|
|
497
|
-
cleanup();
|
|
498
|
-
}
|
|
499
|
-
|
|
500
|
-
function cleanupChildren(container) {
|
|
501
|
-
runScopedCleanups(container, "children");
|
|
502
|
-
for (const child of [...(container.childNodes ?? [])]) {
|
|
503
|
-
cleanupNode(child);
|
|
504
|
-
}
|
|
505
|
-
}
|
|
506
|
-
|
|
507
|
-
function cleanupNode(node) {
|
|
508
|
-
if (node.nodeType !== 1) {
|
|
509
|
-
return;
|
|
510
|
-
}
|
|
511
|
-
for (const element of elementsIn(node)) {
|
|
512
|
-
runScopedCleanups(element);
|
|
513
|
-
schedulerInstance.markScopeDestroyed(element);
|
|
514
|
-
}
|
|
515
|
-
}
|
|
516
|
-
|
|
517
|
-
function runScopedCleanups(element, mode) {
|
|
518
|
-
const records = scopedCleanups.get(element);
|
|
519
|
-
if (!records) {
|
|
520
|
-
return;
|
|
521
|
-
}
|
|
522
|
-
const remaining = [];
|
|
523
|
-
for (const record of records) {
|
|
524
|
-
if (mode && record.mode !== mode) {
|
|
525
|
-
remaining.push(record);
|
|
526
|
-
continue;
|
|
527
|
-
}
|
|
528
|
-
runCleanup(record.cleanup);
|
|
529
|
-
}
|
|
530
|
-
if (remaining.length > 0) {
|
|
531
|
-
scopedCleanups.set(element, remaining);
|
|
532
|
-
} else {
|
|
533
|
-
scopedCleanups.delete(element);
|
|
534
|
-
}
|
|
535
|
-
}
|
|
536
|
-
|
|
537
|
-
function scheduleLifecycle(element, fn, key) {
|
|
538
|
-
schedulerInstance.enqueue("lifecycle", fn, {
|
|
539
|
-
scope: element,
|
|
540
|
-
key
|
|
541
|
-
});
|
|
542
|
-
}
|
|
543
|
-
|
|
544
|
-
function markDestroyedScopes(scope) {
|
|
545
|
-
for (const element of elementsIn(scope)) {
|
|
546
|
-
schedulerInstance.markScopeDestroyed(element);
|
|
547
|
-
}
|
|
548
|
-
}
|
|
549
|
-
|
|
550
|
-
return api;
|
|
551
|
-
}
|
|
552
|
-
|
|
553
|
-
export const AsyncLoader = Loader;
|
|
554
|
-
|
|
555
|
-
function normalizeClassTokens(value, tokens = new Set()) {
|
|
556
|
-
if (value == null || value === false) {
|
|
557
|
-
return tokens;
|
|
558
|
-
}
|
|
559
|
-
if (isSignalRef(value)) {
|
|
560
|
-
const signalValue = value.value;
|
|
561
|
-
if (signalValue === true) {
|
|
562
|
-
tokens.add(signalClassName(value.id));
|
|
563
|
-
return tokens;
|
|
564
|
-
}
|
|
565
|
-
return normalizeClassTokens(signalValue, tokens);
|
|
566
|
-
}
|
|
567
|
-
if (typeof value === "string") {
|
|
568
|
-
for (const token of value.split(/\s+/).filter(Boolean)) {
|
|
569
|
-
tokens.add(token);
|
|
570
|
-
}
|
|
571
|
-
return tokens;
|
|
572
|
-
}
|
|
573
|
-
if (Array.isArray(value)) {
|
|
574
|
-
for (const item of value) {
|
|
575
|
-
normalizeClassTokens(item, tokens);
|
|
576
|
-
}
|
|
577
|
-
return tokens;
|
|
578
|
-
}
|
|
579
|
-
if (typeof value === "object") {
|
|
580
|
-
for (const [token, enabled] of Object.entries(value)) {
|
|
581
|
-
const value = isSignalRef(enabled) ? enabled.value : enabled;
|
|
582
|
-
if (value) {
|
|
583
|
-
normalizeClassTokens(token, tokens);
|
|
584
|
-
}
|
|
585
|
-
}
|
|
586
|
-
return tokens;
|
|
587
|
-
}
|
|
588
|
-
if (value !== true) {
|
|
589
|
-
tokens.add(String(value));
|
|
590
|
-
}
|
|
591
|
-
return tokens;
|
|
592
|
-
}
|
|
593
|
-
|
|
594
|
-
function resolveInlineValue(value) {
|
|
595
|
-
if (isSignalRef(value)) {
|
|
596
|
-
return value.value;
|
|
597
|
-
}
|
|
598
|
-
if (Array.isArray(value)) {
|
|
599
|
-
return value.map(resolveInlineValue);
|
|
600
|
-
}
|
|
601
|
-
if (value && typeof value === "object") {
|
|
602
|
-
return Object.fromEntries(Object.entries(value).map(([key, entry]) => [key, resolveInlineValue(entry)]));
|
|
603
|
-
}
|
|
604
|
-
return value;
|
|
605
|
-
}
|
|
606
|
-
|
|
607
|
-
function collectSignalRefs(value, refs = new Map()) {
|
|
608
|
-
if (isSignalRef(value)) {
|
|
609
|
-
refs.set(value.id, value);
|
|
610
|
-
return [...refs.values()];
|
|
611
|
-
}
|
|
612
|
-
if (Array.isArray(value)) {
|
|
613
|
-
for (const item of value) {
|
|
614
|
-
collectSignalRefs(item, refs);
|
|
615
|
-
}
|
|
616
|
-
return [...refs.values()];
|
|
617
|
-
}
|
|
618
|
-
if (value && typeof value === "object") {
|
|
619
|
-
for (const item of Object.values(value)) {
|
|
620
|
-
collectSignalRefs(item, refs);
|
|
621
|
-
}
|
|
622
|
-
}
|
|
623
|
-
return [...refs.values()];
|
|
624
|
-
}
|
|
625
|
-
|
|
626
|
-
function isInlineBinding(value) {
|
|
627
|
-
return typeof value === "string" && value.startsWith(inlineBindingPrefix);
|
|
628
|
-
}
|
|
629
|
-
|
|
630
|
-
function signalClassName(id) {
|
|
631
|
-
return id.split(".").at(-1);
|
|
632
|
-
}
|
|
633
|
-
|
|
634
|
-
function updateClassToken(element, className, enabled) {
|
|
635
|
-
const tokens = readClassTokens(element);
|
|
636
|
-
for (const token of normalizeClassTokens(className)) {
|
|
637
|
-
if (enabled) {
|
|
638
|
-
tokens.add(token);
|
|
639
|
-
} else {
|
|
640
|
-
tokens.delete(token);
|
|
641
|
-
}
|
|
642
|
-
}
|
|
643
|
-
writeClassTokens(element, tokens);
|
|
644
|
-
}
|
|
645
|
-
|
|
646
|
-
function readClassTokens(element) {
|
|
647
|
-
return normalizeClassTokens(element.getAttribute("class") ?? "");
|
|
648
|
-
}
|
|
649
|
-
|
|
650
|
-
function writeClassTokens(element, tokens) {
|
|
651
|
-
const value = [...tokens].join(" ");
|
|
652
|
-
if (value.length === 0) {
|
|
653
|
-
element.removeAttribute("class");
|
|
654
|
-
return;
|
|
655
|
-
}
|
|
656
|
-
element.setAttribute("class", value);
|
|
657
|
-
}
|
|
658
|
-
|
|
659
|
-
function collectBoundaryTemplates(boundary, id, attributeConfig) {
|
|
660
|
-
const templates = {};
|
|
661
|
-
for (const template of [...boundary.children].filter((child) => child.tagName === "TEMPLATE")) {
|
|
662
|
-
if (templateMatchesState(template, "loading", id, boundary, attributeConfig)) {
|
|
663
|
-
templates.loading = template;
|
|
664
|
-
}
|
|
665
|
-
if (templateMatchesState(template, "ready", id, boundary, attributeConfig)) {
|
|
666
|
-
templates.ready = template;
|
|
667
|
-
}
|
|
668
|
-
if (templateMatchesState(template, "error", id, boundary, attributeConfig)) {
|
|
669
|
-
templates.error = template;
|
|
670
|
-
}
|
|
671
|
-
}
|
|
672
|
-
return templates;
|
|
673
|
-
}
|
|
674
|
-
|
|
675
|
-
function templateMatchesState(template, state, id, boundary, attributeConfig) {
|
|
676
|
-
if (readAttribute(template, attributeConfig, "async", state) === id) {
|
|
677
|
-
return true;
|
|
678
|
-
}
|
|
679
|
-
return isAsyncSuspense(boundary) && template.hasAttribute?.(state);
|
|
680
|
-
}
|
|
681
|
-
|
|
682
|
-
function chooseBoundaryTemplate(templates, status) {
|
|
683
|
-
if (status === "ready") {
|
|
684
|
-
return templates.ready ?? templates.loading ?? templates.error;
|
|
685
|
-
}
|
|
686
|
-
if (status === "error") {
|
|
687
|
-
return templates.error ?? templates.ready ?? templates.loading;
|
|
688
|
-
}
|
|
689
|
-
return templates.loading ?? templates.ready ?? templates.error;
|
|
690
|
-
}
|
|
691
|
-
|
|
692
|
-
function updateAttribute(element, attr, value) {
|
|
693
|
-
if (value === false || value == null) {
|
|
694
|
-
element.removeAttribute(attr);
|
|
695
|
-
if (attr in element) {
|
|
696
|
-
element[attr] = false;
|
|
697
|
-
}
|
|
698
|
-
return;
|
|
699
|
-
}
|
|
700
|
-
element.setAttribute(attr, value === true ? "" : String(value));
|
|
701
|
-
if (attr in element) {
|
|
702
|
-
element[attr] = value;
|
|
703
|
-
}
|
|
704
|
-
}
|
|
705
|
-
|
|
706
|
-
function updateProperty(element, prop, value) {
|
|
707
|
-
if (value == null) {
|
|
708
|
-
element[prop] = "";
|
|
709
|
-
return;
|
|
710
|
-
}
|
|
711
|
-
element[prop] = value;
|
|
712
|
-
}
|
|
713
|
-
|
|
714
|
-
function selectAll(scope, selector) {
|
|
715
|
-
const elements = [];
|
|
716
|
-
if (scope?.nodeType === 1 && scope.matches?.(selector)) {
|
|
717
|
-
elements.push(scope);
|
|
718
|
-
}
|
|
719
|
-
elements.push(...(scope?.querySelectorAll?.(selector) ?? []));
|
|
720
|
-
return elements;
|
|
721
|
-
}
|
|
722
|
-
|
|
723
|
-
function elementsIn(scope) {
|
|
724
|
-
return selectAll(scope, "*");
|
|
725
|
-
}
|
|
726
|
-
|
|
727
|
-
function findBoundary(root, boundaryId, attributeConfig) {
|
|
728
|
-
for (const element of elementsIn(root)) {
|
|
729
|
-
if (boundaryIdFor(element, attributeConfig) === String(boundaryId)) {
|
|
730
|
-
return element;
|
|
731
|
-
}
|
|
732
|
-
}
|
|
733
|
-
return null;
|
|
734
|
-
}
|
|
735
|
-
|
|
736
|
-
function boundaryIdFor(element, attributeConfig) {
|
|
737
|
-
if (isAsyncSuspense(element) && element.hasAttribute?.("for")) {
|
|
738
|
-
return element.getAttribute("for");
|
|
739
|
-
}
|
|
740
|
-
return readAttribute(element, attributeConfig, "async", "boundary");
|
|
741
|
-
}
|
|
742
|
-
|
|
743
|
-
function isAsyncSuspense(element) {
|
|
744
|
-
return element?.tagName === "ASYNC-SUSPENSE";
|
|
745
|
-
}
|
|
746
|
-
|
|
747
|
-
function toFragment(value, documentRef) {
|
|
748
|
-
if (value?.nodeType === 11) {
|
|
749
|
-
return value;
|
|
750
|
-
}
|
|
751
|
-
if (value?.tagName === "TEMPLATE") {
|
|
752
|
-
return value.content.cloneNode(true);
|
|
753
|
-
}
|
|
754
|
-
if (value?.nodeType) {
|
|
755
|
-
const fragment = documentRef.createDocumentFragment();
|
|
756
|
-
fragment.append(value);
|
|
757
|
-
return fragment;
|
|
758
|
-
}
|
|
759
|
-
const template = documentRef.createElement("template");
|
|
760
|
-
template.innerHTML = String(value ?? "");
|
|
761
|
-
return template.content.cloneNode(true);
|
|
762
|
-
}
|
|
763
|
-
|
|
764
|
-
function dispatchAsyncError(element, error) {
|
|
765
|
-
const EventCtor = element.ownerDocument?.defaultView?.CustomEvent ?? globalThis.CustomEvent;
|
|
766
|
-
element.dispatchEvent(
|
|
767
|
-
new EventCtor("async:error", {
|
|
768
|
-
bubbles: true,
|
|
769
|
-
detail: { error }
|
|
770
|
-
})
|
|
771
|
-
);
|
|
772
|
-
}
|