@async/framework 0.5.0 → 0.6.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.
@@ -0,0 +1,4158 @@
1
+ // Generated by scripts/build-framework-bundle.js. Do not edit by hand.
2
+ // Bundled UMD entry for script-tag and CommonJS-style consumers.
3
+ (function (root, factory) {
4
+ const namespace = factory();
5
+ if (typeof define === "function" && define.amd) {
6
+ define([], function () { return namespace; });
7
+ } else if (typeof module === "object" && module.exports) {
8
+ module.exports = namespace;
9
+ } else {
10
+ root.AsyncFramework = namespace;
11
+ root.Async = namespace;
12
+ }
13
+ })(typeof globalThis !== "undefined" ? globalThis : typeof self !== "undefined" ? self : this, function () {
14
+ "use strict";
15
+ const __asyncSignalModule = (() => {
16
+ const asyncSignalKind = Symbol.for("@async/framework.asyncSignal");
17
+
18
+ function asyncSignal(id, fn) {
19
+ if (typeof id !== "string" || id.length === 0) {
20
+ throw new TypeError("asyncSignal(id, fn) requires a non-empty string id.");
21
+ }
22
+ if (typeof fn !== "function") {
23
+ throw new TypeError("asyncSignal(id, fn) requires a function.");
24
+ }
25
+
26
+ let value;
27
+ let loading = false;
28
+ let error = null;
29
+ let status = "idle";
30
+ let version = 0;
31
+ let registry;
32
+ let registeredId = id;
33
+ let activeController;
34
+ let activeAbort;
35
+ const subscribers = new Set();
36
+ const dependencyCleanups = new Set();
37
+
38
+ const state = {
39
+ [asyncSignalKind]: true,
40
+ kind: "async-signal",
41
+
42
+ get id() {
43
+ return registeredId;
44
+ },
45
+
46
+ get value() {
47
+ return value;
48
+ },
49
+
50
+ get loading() {
51
+ return loading;
52
+ },
53
+
54
+ get error() {
55
+ return error;
56
+ },
57
+
58
+ get status() {
59
+ return status;
60
+ },
61
+
62
+ get version() {
63
+ return version;
64
+ },
65
+
66
+ set(nextValue) {
67
+ value = nextValue;
68
+ loading = false;
69
+ error = null;
70
+ status = "ready";
71
+ notify();
72
+ return value;
73
+ },
74
+
75
+ refresh() {
76
+ if (!registry) {
77
+ throw new Error(`Async signal "${registeredId}" is not registered.`);
78
+ }
79
+
80
+ if (activeAbort && !activeAbort.aborted) {
81
+ activeAbort.cancel(new Error(`Async signal "${registeredId}" refreshed.`));
82
+ }
83
+
84
+ const runVersion = version + 1;
85
+ version = runVersion;
86
+ loading = true;
87
+ error = null;
88
+ status = "loading";
89
+
90
+ const controller = new AbortController();
91
+ activeController = controller;
92
+ activeAbort = controller.signal;
93
+ attachCancel(activeAbort, controller);
94
+ notify();
95
+
96
+ const context = {
97
+ signals: registry,
98
+ id: registeredId,
99
+ get server() {
100
+ const context = registry._context?.() ?? {};
101
+ const server = context.server;
102
+ if (typeof server?._withContext === "function") {
103
+ return server._withContext({
104
+ signals: registry,
105
+ router: context.router,
106
+ loader: context.loader,
107
+ cache: context.cache,
108
+ abort: activeAbort
109
+ });
110
+ }
111
+ return server;
112
+ },
113
+ get router() {
114
+ return registry._context?.().router;
115
+ },
116
+ get loader() {
117
+ return registry._context?.().loader;
118
+ },
119
+ get cache() {
120
+ return registry._context?.().cache;
121
+ },
122
+ get version() {
123
+ return runVersion;
124
+ },
125
+ get abort() {
126
+ return activeAbort;
127
+ },
128
+ refresh() {
129
+ return state.refresh();
130
+ }
131
+ };
132
+
133
+ let outcome;
134
+ try {
135
+ outcome = registry._collectDependencies(() => fn.call(context));
136
+ } catch (cause) {
137
+ finishError(runVersion, cause);
138
+ return Promise.reject(cause);
139
+ }
140
+
141
+ syncDependencies(outcome.dependencies);
142
+
143
+ return Promise.resolve(outcome.value).then(
144
+ (nextValue) => {
145
+ if (!isCurrent(runVersion)) {
146
+ return value;
147
+ }
148
+ value = nextValue;
149
+ loading = false;
150
+ error = null;
151
+ status = "ready";
152
+ notify();
153
+ return value;
154
+ },
155
+ (cause) => {
156
+ if (!isCurrent(runVersion)) {
157
+ return value;
158
+ }
159
+ if (activeAbort?.aborted) {
160
+ loading = false;
161
+ status = value === undefined ? "idle" : "ready";
162
+ notify();
163
+ return value;
164
+ }
165
+ finishError(runVersion, cause);
166
+ return value;
167
+ }
168
+ );
169
+ },
170
+
171
+ cancel(reason) {
172
+ if (activeAbort && !activeAbort.aborted) {
173
+ activeAbort.cancel(reason);
174
+ }
175
+ },
176
+
177
+ subscribe(fn) {
178
+ if (typeof fn !== "function") {
179
+ throw new TypeError("subscribe(fn) requires a function.");
180
+ }
181
+ subscribers.add(fn);
182
+ return () => subscribers.delete(fn);
183
+ },
184
+
185
+ snapshot() {
186
+ return {
187
+ value,
188
+ loading,
189
+ error,
190
+ status,
191
+ version
192
+ };
193
+ },
194
+
195
+ _bindRegistry(nextRegistry, nextId) {
196
+ registry = nextRegistry;
197
+ registeredId = nextId;
198
+ queueMicrotask(() => {
199
+ if (registry === nextRegistry && status === "idle") {
200
+ state.refresh();
201
+ }
202
+ });
203
+ },
204
+
205
+ _dispose() {
206
+ state.cancel(new Error(`Async signal "${registeredId}" disposed.`));
207
+ for (const cleanup of dependencyCleanups) {
208
+ cleanup();
209
+ }
210
+ dependencyCleanups.clear();
211
+ subscribers.clear();
212
+ }
213
+ };
214
+
215
+ function finishError(runVersion, cause) {
216
+ if (!isCurrent(runVersion)) {
217
+ return;
218
+ }
219
+ loading = false;
220
+ error = cause;
221
+ status = "error";
222
+ notify();
223
+ }
224
+
225
+ function isCurrent(runVersion) {
226
+ return runVersion === version && activeController?.signal === activeAbort;
227
+ }
228
+
229
+ function syncDependencies(dependencies) {
230
+ for (const cleanup of dependencyCleanups) {
231
+ cleanup();
232
+ }
233
+ dependencyCleanups.clear();
234
+
235
+ for (const dependency of dependencies) {
236
+ const dependencyId = String(dependency).split(".")[0];
237
+ if (dependencyId && dependencyId !== registeredId) {
238
+ dependencyCleanups.add(registry.subscribe(dependency, () => state.refresh()));
239
+ }
240
+ }
241
+ }
242
+
243
+ function notify() {
244
+ for (const subscriber of [...subscribers]) {
245
+ subscriber(state);
246
+ }
247
+ }
248
+
249
+ return state;
250
+ }
251
+
252
+ function isAsyncSignal(value) {
253
+ return Boolean(value?.[asyncSignalKind]);
254
+ }
255
+
256
+ function attachCancel(signal, controller) {
257
+ Object.defineProperty(signal, "cancel", {
258
+ configurable: true,
259
+ enumerable: false,
260
+ value(reason) {
261
+ controller.abort(reason);
262
+ }
263
+ });
264
+ }
265
+ return { asyncSignal, isAsyncSignal };
266
+ })();
267
+
268
+ const __registryStoreModule = (() => {
269
+ const declarationTypes = new Set(["signal", "handler", "server", "partial", "route", "component"]);
270
+ const cacheTypes = new Set(["cache.browser", "cache.server"]);
271
+ const cacheEntryTypes = new Set(["cache.browser.entries", "cache.server.entries"]);
272
+ const allTypes = new Set([...declarationTypes, ...cacheTypes, ...cacheEntryTypes]);
273
+
274
+ function createRegistryStore(initial = {}, options = {}) {
275
+ const backing = options.backing ?? createBacking();
276
+ const target = options.target ?? "server";
277
+
278
+ const registry = {
279
+ target,
280
+
281
+ register(type, id, value) {
282
+ const map = registry._map(type);
283
+ assertId(type, id);
284
+ if (map.has(id)) {
285
+ throw new Error(`${type} "${id}" is already registered.`);
286
+ }
287
+ map.set(id, value);
288
+ return id;
289
+ },
290
+
291
+ registerMany(type, map = {}) {
292
+ for (const [id, value] of Object.entries(map ?? {})) {
293
+ registry.register(type, id, value);
294
+ }
295
+ return registry;
296
+ },
297
+
298
+ set(type, id, value) {
299
+ const map = registry._map(type);
300
+ assertId(type, id);
301
+ map.set(id, value);
302
+ return value;
303
+ },
304
+
305
+ unregister(type, id) {
306
+ return registry.delete(type, id);
307
+ },
308
+
309
+ delete(type, id) {
310
+ return registry._map(type).delete(id);
311
+ },
312
+
313
+ keys(type) {
314
+ if (isHiddenInBrowser(type, target)) {
315
+ return [];
316
+ }
317
+ return [...registry._map(type).keys()];
318
+ },
319
+
320
+ entries(type, entryOptions = {}) {
321
+ const normalized = normalizeType(type);
322
+ if (isHiddenInBrowser(normalized, entryOptions.target ?? target)) {
323
+ return [];
324
+ }
325
+ return [...registry._map(normalized)].map(([id, value]) => [
326
+ id,
327
+ publicValue(normalized, id, value, { target, ...entryOptions })
328
+ ]);
329
+ },
330
+
331
+ has(type, id) {
332
+ assertId(type, id);
333
+ if (isHiddenInBrowser(type, target)) {
334
+ return false;
335
+ }
336
+ return registry._map(type).has(id);
337
+ },
338
+
339
+ get(type, id, getOptions = {}) {
340
+ assertId(type, id);
341
+ const normalized = normalizeType(type);
342
+ if (isHiddenInBrowser(normalized, getOptions.target ?? target)) {
343
+ return undefined;
344
+ }
345
+ const value = registry._map(normalized).get(id);
346
+ if (value === undefined) {
347
+ return undefined;
348
+ }
349
+ return publicValue(normalized, id, value, { target, ...getOptions });
350
+ },
351
+
352
+ snapshot(snapshotOptions = {}) {
353
+ const snapshotTarget = snapshotOptions.target ?? target;
354
+ return {
355
+ signal: snapshotSignals(backing.signal),
356
+ handler: snapshotDescriptors(backing.handler, "handler"),
357
+ server: snapshotDescriptors(backing.server, "server"),
358
+ partial: snapshotDescriptors(backing.partial, "partial"),
359
+ route: snapshotPlain(backing.route),
360
+ component: snapshotDescriptors(backing.component, "component"),
361
+ cache: {
362
+ browser: snapshotPlain(backing.cache.browser),
363
+ server: snapshotPlain(backing.cache.server)
364
+ },
365
+ entries: {
366
+ browser: snapshotCacheEntries(backing.cacheEntries.browser),
367
+ server: snapshotTarget === "browser" ? {} : snapshotCacheEntries(backing.cacheEntries.server)
368
+ }
369
+ };
370
+ },
371
+
372
+ rawSnapshot() {
373
+ return {
374
+ signal: Object.fromEntries(backing.signal),
375
+ handler: Object.fromEntries(backing.handler),
376
+ server: Object.fromEntries(backing.server),
377
+ partial: Object.fromEntries(backing.partial),
378
+ route: Object.fromEntries(backing.route),
379
+ component: Object.fromEntries(backing.component),
380
+ cache: {
381
+ browser: Object.fromEntries(backing.cache.browser),
382
+ server: Object.fromEntries(backing.cache.server)
383
+ }
384
+ };
385
+ },
386
+
387
+ view(viewOptions = {}) {
388
+ return createRegistryStore(undefined, {
389
+ backing,
390
+ target: viewOptions.target ?? target
391
+ });
392
+ },
393
+
394
+ _map(type) {
395
+ const normalized = normalizeType(type);
396
+ if (declarationTypes.has(normalized)) {
397
+ return backing[normalized];
398
+ }
399
+ if (normalized === "cache.browser") {
400
+ return backing.cache.browser;
401
+ }
402
+ if (normalized === "cache.server") {
403
+ return backing.cache.server;
404
+ }
405
+ if (normalized === "cache.browser.entries") {
406
+ return backing.cacheEntries.browser;
407
+ }
408
+ if (normalized === "cache.server.entries") {
409
+ return backing.cacheEntries.server;
410
+ }
411
+ throw new Error(`Unknown Async registry type "${type}".`);
412
+ }
413
+ };
414
+
415
+ applyInitial(registry, initial);
416
+ return registry;
417
+ }
418
+
419
+ function attachRegistryInspection(target, registry, type) {
420
+ Object.defineProperty(target, "registry", {
421
+ configurable: true,
422
+ enumerable: true,
423
+ value: registry
424
+ });
425
+ target.keys = () => registry.keys(type);
426
+ target.entries = () => registry.entries(type);
427
+ target.inspect = () => registry.entries(type);
428
+ return target;
429
+ }
430
+
431
+ function createBacking() {
432
+ return {
433
+ signal: new Map(),
434
+ handler: new Map(),
435
+ server: new Map(),
436
+ partial: new Map(),
437
+ route: new Map(),
438
+ component: new Map(),
439
+ cache: {
440
+ browser: new Map(),
441
+ server: new Map()
442
+ },
443
+ cacheEntries: {
444
+ browser: new Map(),
445
+ server: new Map()
446
+ }
447
+ };
448
+ }
449
+
450
+ function applyInitial(registry, initial = {}) {
451
+ registry.registerMany("signal", initial.signal);
452
+ registry.registerMany("handler", initial.handler);
453
+ registry.registerMany("server", initial.server);
454
+ registry.registerMany("partial", initial.partial);
455
+ registry.registerMany("route", initial.route);
456
+ registry.registerMany("component", initial.component);
457
+ registry.registerMany("cache.browser", initial.cache?.browser);
458
+ registry.registerMany("cache.server", initial.cache?.server);
459
+
460
+ const entries = initial.entries ?? {};
461
+ for (const [key, value] of Object.entries(entries.browser ?? {})) {
462
+ registry.set("cache.browser.entries", key, cacheEntry(value));
463
+ }
464
+ for (const [key, value] of Object.entries(entries.server ?? {})) {
465
+ registry.set("cache.server.entries", key, cacheEntry(value));
466
+ }
467
+ }
468
+
469
+ function normalizeType(type) {
470
+ if (!allTypes.has(type)) {
471
+ throw new Error(`Unknown Async registry type "${type}".`);
472
+ }
473
+ return type;
474
+ }
475
+
476
+ function assertId(type, id) {
477
+ if (typeof id !== "string" || id.length === 0) {
478
+ throw new TypeError(`${type} id must be a non-empty string.`);
479
+ }
480
+ }
481
+
482
+ function publicValue(type, id, value, options) {
483
+ if (type === "server" && options.target === "browser") {
484
+ return { id, kind: "server" };
485
+ }
486
+ if (cacheEntryTypes.has(type)) {
487
+ return value?.value;
488
+ }
489
+ return value;
490
+ }
491
+
492
+ function isHiddenInBrowser(type, target) {
493
+ return type === "cache.server.entries" && target === "browser";
494
+ }
495
+
496
+ function snapshotSignals(map) {
497
+ const snapshot = {};
498
+ for (const [id, entry] of map) {
499
+ snapshot[id] = typeof entry?.snapshot === "function" ? entry.snapshot() : entry?.value ?? entry;
500
+ }
501
+ return snapshot;
502
+ }
503
+
504
+ function snapshotDescriptors(map, kind) {
505
+ const snapshot = {};
506
+ for (const id of map.keys()) {
507
+ snapshot[id] = { id, kind };
508
+ }
509
+ return snapshot;
510
+ }
511
+
512
+ function snapshotPlain(map) {
513
+ return Object.fromEntries(map);
514
+ }
515
+
516
+ function snapshotCacheEntries(map) {
517
+ const snapshot = {};
518
+ for (const [id, entry] of map) {
519
+ snapshot[id] = entry?.value;
520
+ }
521
+ return snapshot;
522
+ }
523
+
524
+ function cacheEntry(value) {
525
+ if (value && typeof value === "object" && Object.hasOwn(value, "value")) {
526
+ return value;
527
+ }
528
+ return { value };
529
+ }
530
+ return { createRegistryStore, attachRegistryInspection };
531
+ })();
532
+
533
+ const __cacheModule = (() => {
534
+ const { attachRegistryInspection, createRegistryStore } = __registryStoreModule;
535
+ const cacheDefinitionKind = Symbol.for("@async/framework.cacheDefinition");
536
+
537
+ function defineCache(options = {}) {
538
+ return {
539
+ [cacheDefinitionKind]: true,
540
+ kind: "cache-definition",
541
+ store: options.store ?? "memory",
542
+ ttl: options.ttl
543
+ };
544
+ }
545
+
546
+ function createCacheRegistry(initialMap = {}, { now = () => Date.now(), registry, type = "cache.browser" } = {}) {
547
+ const registryStore = registry ?? createRegistryStore();
548
+ const definitions = registryStore._map(type);
549
+ const entries = registryStore._map(`${type}.entries`);
550
+
551
+ const registryApi = attachRegistryInspection({
552
+ register(id, definition = defineCache()) {
553
+ assertId(id);
554
+ const normalized = normalizeDefinition(definition);
555
+ if (definitions.has(id)) {
556
+ throw new Error(`Cache "${id}" is already registered.`);
557
+ }
558
+ definitions.set(id, normalized);
559
+ return id;
560
+ },
561
+
562
+ registerMany(map) {
563
+ for (const [id, definition] of Object.entries(map ?? {})) {
564
+ registryApi.register(id, definition);
565
+ }
566
+ return registryApi;
567
+ },
568
+
569
+ unregister(id) {
570
+ assertId(id);
571
+ return definitions.delete(id);
572
+ },
573
+
574
+ resolve(id) {
575
+ assertId(id);
576
+ return definitions.get(id);
577
+ },
578
+
579
+ get(key) {
580
+ assertKey(key);
581
+ const entry = entries.get(key);
582
+ if (!entry) {
583
+ return undefined;
584
+ }
585
+ if (entry.expiresAt !== undefined && entry.expiresAt <= now()) {
586
+ entries.delete(key);
587
+ return undefined;
588
+ }
589
+ return entry.value;
590
+ },
591
+
592
+ set(key, value, options = {}) {
593
+ assertKey(key);
594
+ const ttl = options.ttl ?? resolvePolicy(key, options.cache)?.ttl;
595
+ entries.set(key, {
596
+ value,
597
+ expiresAt: ttl === undefined ? undefined : now() + ttl
598
+ });
599
+ return value;
600
+ },
601
+
602
+ async getOrSet(key, fn, options = {}) {
603
+ assertKey(key);
604
+ if (typeof fn !== "function") {
605
+ throw new TypeError("cache.getOrSet(key, fn) requires a function.");
606
+ }
607
+ const cached = registryApi.get(key);
608
+ if (cached !== undefined) {
609
+ return cached;
610
+ }
611
+ const value = await fn();
612
+ registryApi.set(key, value, options);
613
+ return value;
614
+ },
615
+
616
+ delete(key) {
617
+ assertKey(key);
618
+ return entries.delete(key);
619
+ },
620
+
621
+ clear(prefix) {
622
+ if (prefix === undefined) {
623
+ entries.clear();
624
+ return registryApi;
625
+ }
626
+ for (const key of [...entries.keys()]) {
627
+ if (key.startsWith(prefix)) {
628
+ entries.delete(key);
629
+ }
630
+ }
631
+ return registryApi;
632
+ },
633
+
634
+ snapshot() {
635
+ const snapshot = {};
636
+ for (const [key] of entries) {
637
+ const value = registryApi.get(key);
638
+ if (value !== undefined) {
639
+ snapshot[key] = value;
640
+ }
641
+ }
642
+ return snapshot;
643
+ },
644
+
645
+ restore(snapshot = {}) {
646
+ for (const [key, value] of Object.entries(snapshot ?? {})) {
647
+ registryApi.set(key, value);
648
+ }
649
+ return registryApi;
650
+ },
651
+
652
+ entryKeys() {
653
+ return [...entries.keys()];
654
+ },
655
+
656
+ entryEntries() {
657
+ return registryStore.entries(`${type}.entries`);
658
+ },
659
+
660
+ _adoptMany() {
661
+ return registryApi;
662
+ }
663
+ }, registryStore, type);
664
+
665
+ registryApi.registerMany(initialMap);
666
+ return registryApi;
667
+
668
+ function resolvePolicy(key, explicitId) {
669
+ if (explicitId !== undefined) {
670
+ return definitions.get(explicitId);
671
+ }
672
+ if (definitions.has(key)) {
673
+ return definitions.get(key);
674
+ }
675
+ const prefix = key.split(":")[0];
676
+ return definitions.get(prefix);
677
+ }
678
+ }
679
+
680
+ function normalizeDefinition(definition) {
681
+ if (definition?.[cacheDefinitionKind]) {
682
+ return definition;
683
+ }
684
+ return defineCache(definition);
685
+ }
686
+
687
+ function assertId(id) {
688
+ if (typeof id !== "string" || id.length === 0) {
689
+ throw new TypeError("Cache id must be a non-empty string.");
690
+ }
691
+ }
692
+
693
+ function assertKey(key) {
694
+ if (typeof key !== "string" || key.length === 0) {
695
+ throw new TypeError("Cache key must be a non-empty string.");
696
+ }
697
+ }
698
+ return { defineCache, createCacheRegistry };
699
+ })();
700
+
701
+ const __attributesModule = (() => {
702
+ const defaultPrefixes = Object.freeze({
703
+ async: ["async:"],
704
+ class: ["class:"],
705
+ signal: ["signal:"],
706
+ on: ["on:"]
707
+ });
708
+
709
+ function defineAttributeConfig(config = {}) {
710
+ return normalizeAttributeConfig(config);
711
+ }
712
+
713
+ function normalizeAttributeConfig(config = {}) {
714
+ return {
715
+ async: normalizePrefixes(config.async, defaultPrefixes.async),
716
+ class: normalizePrefixes(config.class, defaultPrefixes.class),
717
+ signal: normalizePrefixes(config.signal, defaultPrefixes.signal),
718
+ on: normalizePrefixes(config.on, defaultPrefixes.on)
719
+ };
720
+ }
721
+
722
+ function attributeName(attributes, type, name) {
723
+ return normalizeAttributeConfig(attributes)[type][0] + name;
724
+ }
725
+
726
+ function readAttribute(element, attributes, type, name) {
727
+ for (const prefix of normalizeAttributeConfig(attributes)[type]) {
728
+ const attr = `${prefix}${name}`;
729
+ if (element.hasAttribute?.(attr)) {
730
+ return element.getAttribute(attr);
731
+ }
732
+ }
733
+ return null;
734
+ }
735
+
736
+ function matchAttribute(name, attributes, type) {
737
+ for (const prefix of normalizeAttributeConfig(attributes)[type]) {
738
+ if (name.startsWith(prefix)) {
739
+ return name.slice(prefix.length);
740
+ }
741
+ }
742
+ return null;
743
+ }
744
+
745
+ function normalizePrefixes(value, fallback) {
746
+ const prefixes = value == null ? fallback : Array.isArray(value) ? value : [value];
747
+ return prefixes.map((prefix) => {
748
+ if (typeof prefix !== "string" || prefix.length === 0) {
749
+ throw new TypeError("Attribute prefixes must be non-empty strings.");
750
+ }
751
+ return prefix;
752
+ });
753
+ }
754
+ return { defineAttributeConfig, normalizeAttributeConfig, attributeName, readAttribute, matchAttribute };
755
+ })();
756
+
757
+ const __signalsModule = (() => {
758
+ const { asyncSignal: createAsyncSignal, isAsyncSignal } = __asyncSignalModule;
759
+ const { attachRegistryInspection, createRegistryStore } = __registryStoreModule;
760
+ const signalKind = Symbol.for("@async/framework.signal");
761
+ const computedKind = Symbol.for("@async/framework.computed");
762
+ const effectKind = Symbol.for("@async/framework.effect");
763
+ const refKind = Symbol.for("@async/framework.signalRef");
764
+ const dependencyFrames = [];
765
+
766
+ function createSignal(initial) {
767
+ let value = initial;
768
+ const subscribers = new Set();
769
+
770
+ return {
771
+ [signalKind]: true,
772
+ kind: "signal",
773
+
774
+ get value() {
775
+ return value;
776
+ },
777
+
778
+ set value(nextValue) {
779
+ this.set(nextValue);
780
+ },
781
+
782
+ set(nextValue) {
783
+ if (Object.is(value, nextValue)) {
784
+ return value;
785
+ }
786
+ value = nextValue;
787
+ notify();
788
+ return value;
789
+ },
790
+
791
+ update(fn) {
792
+ return this.set(fn(value));
793
+ },
794
+
795
+ subscribe(fn) {
796
+ if (typeof fn !== "function") {
797
+ throw new TypeError("subscribe(fn) requires a function.");
798
+ }
799
+ subscribers.add(fn);
800
+ return () => subscribers.delete(fn);
801
+ },
802
+
803
+ snapshot() {
804
+ return value;
805
+ }
806
+ };
807
+
808
+ function notify() {
809
+ for (const subscriber of [...subscribers]) {
810
+ subscriber(value);
811
+ }
812
+ }
813
+ }
814
+
815
+ const signal = createSignal;
816
+
817
+ function computed(fn) {
818
+ if (typeof fn !== "function") {
819
+ throw new TypeError("computed(fn) requires a function.");
820
+ }
821
+ const backing = createSignal(undefined);
822
+
823
+ return {
824
+ [computedKind]: true,
825
+ kind: "computed",
826
+
827
+ get value() {
828
+ return backing.value;
829
+ },
830
+
831
+ set(nextValue) {
832
+ return backing.set(nextValue);
833
+ },
834
+
835
+ update(fn) {
836
+ return backing.update(fn);
837
+ },
838
+
839
+ subscribe(fn) {
840
+ return backing.subscribe(fn);
841
+ },
842
+
843
+ snapshot() {
844
+ return backing.snapshot();
845
+ },
846
+
847
+ _bindRegistry(registry, id) {
848
+ return registry.effect(() => {
849
+ backing.set(fn.call({
850
+ signals: registry,
851
+ id,
852
+ server: registry._context?.().server,
853
+ router: registry._context?.().router,
854
+ loader: registry._context?.().loader,
855
+ cache: registry._context?.().cache
856
+ }));
857
+ });
858
+ }
859
+ };
860
+ }
861
+
862
+ function effect(fn) {
863
+ if (typeof fn !== "function") {
864
+ throw new TypeError("effect(fn) requires a function.");
865
+ }
866
+ return {
867
+ [effectKind]: true,
868
+ kind: "effect",
869
+ fn,
870
+ _bindRegistry(registry) {
871
+ return registry.effect(fn);
872
+ }
873
+ };
874
+ }
875
+
876
+ function createSignalRegistry(initialMap = {}, options = {}) {
877
+ const registryStore = options.registry ?? createRegistryStore();
878
+ const type = options.type ?? "signal";
879
+ const entries = registryStore._map(type);
880
+ const registryCleanups = new Map();
881
+ const runtimeContext = {};
882
+ const boundEntries = new Set();
883
+
884
+ const registry = attachRegistryInspection({
885
+ register(id, signalLike) {
886
+ assertId(id);
887
+ if (entries.has(id)) {
888
+ throw new Error(`Signal "${id}" is already registered.`);
889
+ }
890
+ const entry = normalizeSignal(signalLike);
891
+ entries.set(id, entry);
892
+ bindEntry(id, entry);
893
+ return registry.ref(id);
894
+ },
895
+
896
+ registerMany(map) {
897
+ for (const [id, signalLike] of Object.entries(map ?? {})) {
898
+ registry.register(id, signalLike);
899
+ }
900
+ return registry;
901
+ },
902
+
903
+ unregister(id) {
904
+ assertId(id);
905
+ if (!entries.has(id)) {
906
+ return false;
907
+ }
908
+ registryCleanups.get(id)?.();
909
+ registryCleanups.delete(id);
910
+ entries.get(id)?._dispose?.();
911
+ entries.delete(id);
912
+ boundEntries.delete(id);
913
+ return true;
914
+ },
915
+
916
+ ensure(id, initial) {
917
+ assertId(id);
918
+ if (!entries.has(id)) {
919
+ registry.register(id, createSignal(initial));
920
+ }
921
+ return registry.ref(id);
922
+ },
923
+
924
+ has(id) {
925
+ return entries.has(id);
926
+ },
927
+
928
+ get(path) {
929
+ const parsed = parsePath(path, entries);
930
+ track(parsed.path);
931
+ const entry = requireEntry(entries, parsed.id);
932
+ return readEntry(entry, parsed.parts);
933
+ },
934
+
935
+ set(path, value) {
936
+ const parsed = parsePath(path, entries);
937
+ const entry = requireEntry(entries, parsed.id);
938
+ if (parsed.parts.length === 0) {
939
+ return entry.set(value);
940
+ }
941
+ const nextValue = setPath(entry.value, parsed.parts, value);
942
+ entry.set(nextValue);
943
+ return value;
944
+ },
945
+
946
+ update(path, fn) {
947
+ if (typeof fn !== "function") {
948
+ throw new TypeError("update(path, fn) requires a function.");
949
+ }
950
+ return registry.set(path, fn(registry.get(path)));
951
+ },
952
+
953
+ ref(id) {
954
+ assertId(id);
955
+ return createRef(registry, id);
956
+ },
957
+
958
+ subscribe(path, fn) {
959
+ if (typeof fn !== "function") {
960
+ throw new TypeError("subscribe(path, fn) requires a function.");
961
+ }
962
+ const parsed = parsePath(path, entries);
963
+ const entry = requireEntry(entries, parsed.id);
964
+ return entry.subscribe(() => {
965
+ fn(registry.get(parsed.path), {
966
+ id: parsed.id,
967
+ path: parsed.path,
968
+ signal: entry
969
+ });
970
+ });
971
+ },
972
+
973
+ snapshot() {
974
+ const snapshot = {};
975
+ for (const [id, entry] of entries) {
976
+ snapshot[id] = typeof entry.snapshot === "function" ? entry.snapshot() : entry.value;
977
+ }
978
+ return snapshot;
979
+ },
980
+
981
+ asyncSignal(id, fn) {
982
+ registry.register(id, createAsyncSignal(id, fn));
983
+ return registry.ref(id);
984
+ },
985
+
986
+ effect(fn) {
987
+ let cleanup;
988
+ let dependencyCleanups = [];
989
+ let stopped = false;
990
+
991
+ const run = () => {
992
+ if (stopped) {
993
+ return;
994
+ }
995
+ if (typeof cleanup === "function") {
996
+ cleanup();
997
+ }
998
+ for (const stop of dependencyCleanups) {
999
+ stop();
1000
+ }
1001
+ dependencyCleanups = [];
1002
+
1003
+ const outcome = registry._collectDependencies(() => fn.call({
1004
+ signals: registry,
1005
+ server: runtimeContext.server,
1006
+ router: runtimeContext.router,
1007
+ loader: runtimeContext.loader,
1008
+ cache: runtimeContext.cache
1009
+ }));
1010
+ cleanup = outcome.value;
1011
+ dependencyCleanups = outcome.dependencies.map((dependency) => registry.subscribe(dependency, run));
1012
+ };
1013
+
1014
+ run();
1015
+
1016
+ return () => {
1017
+ stopped = true;
1018
+ if (typeof cleanup === "function") {
1019
+ cleanup();
1020
+ }
1021
+ for (const stop of dependencyCleanups) {
1022
+ stop();
1023
+ }
1024
+ };
1025
+ },
1026
+
1027
+ destroy() {
1028
+ for (const cleanup of registryCleanups.values()) {
1029
+ cleanup();
1030
+ }
1031
+ registryCleanups.clear();
1032
+ for (const entry of entries.values()) {
1033
+ entry._dispose?.();
1034
+ }
1035
+ entries.clear();
1036
+ },
1037
+
1038
+ _collectDependencies(fn) {
1039
+ const frame = new Set();
1040
+ dependencyFrames.push(frame);
1041
+ try {
1042
+ const value = fn();
1043
+ return { value, dependencies: [...frame] };
1044
+ } finally {
1045
+ dependencyFrames.pop();
1046
+ }
1047
+ },
1048
+
1049
+ _entry(id) {
1050
+ return requireEntry(entries, id);
1051
+ },
1052
+
1053
+ _setContext(context = {}) {
1054
+ Object.assign(runtimeContext, context);
1055
+ return registry;
1056
+ },
1057
+
1058
+ _context() {
1059
+ return runtimeContext;
1060
+ },
1061
+
1062
+ _adoptMany(map = {}) {
1063
+ for (const id of Object.keys(map ?? {})) {
1064
+ if (entries.has(id)) {
1065
+ bindEntry(id, entries.get(id));
1066
+ }
1067
+ }
1068
+ return registry;
1069
+ }
1070
+ }, registryStore, type);
1071
+
1072
+ for (const [id, entry] of entries) {
1073
+ bindEntry(id, entry);
1074
+ }
1075
+ registry.registerMany(initialMap);
1076
+ return registry;
1077
+
1078
+ function bindEntry(id, entry) {
1079
+ if (boundEntries.has(id) || typeof entry?._bindRegistry !== "function") {
1080
+ return;
1081
+ }
1082
+ boundEntries.add(id);
1083
+ const cleanup = entry._bindRegistry(registry, id);
1084
+ if (typeof cleanup === "function") {
1085
+ registryCleanups.set(id, cleanup);
1086
+ }
1087
+ }
1088
+ }
1089
+
1090
+ function normalizeSignal(signalLike) {
1091
+ if (isSignalLike(signalLike)) {
1092
+ return signalLike;
1093
+ }
1094
+ return createSignal(signalLike);
1095
+ }
1096
+
1097
+ function isSignalLike(value) {
1098
+ return Boolean(value && typeof value === "object" && typeof value.subscribe === "function");
1099
+ }
1100
+
1101
+ function createRef(registry, id) {
1102
+ return {
1103
+ [refKind]: true,
1104
+ kind: "signal-ref",
1105
+ id,
1106
+
1107
+ get value() {
1108
+ return registry.get(id);
1109
+ },
1110
+
1111
+ set value(nextValue) {
1112
+ registry.set(id, nextValue);
1113
+ },
1114
+
1115
+ get loading() {
1116
+ return registry._entry(id).loading ?? false;
1117
+ },
1118
+
1119
+ get error() {
1120
+ return registry._entry(id).error ?? null;
1121
+ },
1122
+
1123
+ get status() {
1124
+ return registry._entry(id).status ?? "ready";
1125
+ },
1126
+
1127
+ get version() {
1128
+ return registry._entry(id).version ?? 0;
1129
+ },
1130
+
1131
+ get() {
1132
+ return registry.get(id);
1133
+ },
1134
+
1135
+ set(nextValue) {
1136
+ return registry.set(id, nextValue);
1137
+ },
1138
+
1139
+ update(fn) {
1140
+ return registry.update(id, fn);
1141
+ },
1142
+
1143
+ subscribe(fn) {
1144
+ return registry.subscribe(id, fn);
1145
+ },
1146
+
1147
+ refresh() {
1148
+ const entry = registry._entry(id);
1149
+ if (typeof entry.refresh !== "function") {
1150
+ throw new Error(`Signal "${id}" cannot refresh.`);
1151
+ }
1152
+ return entry.refresh();
1153
+ },
1154
+
1155
+ cancel(reason) {
1156
+ const entry = registry._entry(id);
1157
+ if (typeof entry.cancel !== "function") {
1158
+ throw new Error(`Signal "${id}" cannot cancel.`);
1159
+ }
1160
+ return entry.cancel(reason);
1161
+ },
1162
+
1163
+ toString() {
1164
+ return id;
1165
+ },
1166
+
1167
+ [Symbol.toPrimitive]() {
1168
+ return id;
1169
+ }
1170
+ };
1171
+ }
1172
+
1173
+ function isSignalRef(value) {
1174
+ return Boolean(value?.[refKind]);
1175
+ }
1176
+
1177
+ function parsePath(path, entries) {
1178
+ if (typeof path !== "string" || path.length === 0) {
1179
+ throw new TypeError("Signal path must be a non-empty string.");
1180
+ }
1181
+ const segments = path.split(".");
1182
+ for (let end = segments.length; end > 0; end -= 1) {
1183
+ const id = segments.slice(0, end).join(".");
1184
+ if (entries.has(id)) {
1185
+ return { id, parts: segments.slice(end), path };
1186
+ }
1187
+ }
1188
+ const [id, ...parts] = segments;
1189
+ return { id, parts, path };
1190
+ }
1191
+
1192
+ function readEntry(entry, parts) {
1193
+ if (isAsyncSignal(entry) && parts[0]?.startsWith("$")) {
1194
+ const metadata = readAsyncMetadata(entry, parts[0]);
1195
+ return readPath(metadata, parts.slice(1));
1196
+ }
1197
+ return readPath(entry.value, parts);
1198
+ }
1199
+
1200
+ function readAsyncMetadata(entry, part) {
1201
+ switch (part) {
1202
+ case "$value":
1203
+ return entry.value;
1204
+ case "$loading":
1205
+ return entry.loading;
1206
+ case "$error":
1207
+ return entry.error;
1208
+ case "$status":
1209
+ return entry.status;
1210
+ case "$version":
1211
+ return entry.version;
1212
+ default:
1213
+ return undefined;
1214
+ }
1215
+ }
1216
+
1217
+ function readPath(value, parts) {
1218
+ let cursor = value;
1219
+ for (const part of parts) {
1220
+ if (cursor == null) {
1221
+ return undefined;
1222
+ }
1223
+ cursor = cursor[part];
1224
+ }
1225
+ return cursor;
1226
+ }
1227
+
1228
+ function setPath(value, parts, nextValue) {
1229
+ const root = cloneContainer(value, parts[0]);
1230
+ let cursor = root;
1231
+ for (let index = 0; index < parts.length - 1; index += 1) {
1232
+ const part = parts[index];
1233
+ const nextPart = parts[index + 1];
1234
+ cursor[part] = cloneContainer(cursor[part], nextPart);
1235
+ cursor = cursor[part];
1236
+ }
1237
+ cursor[parts.at(-1)] = nextValue;
1238
+ return root;
1239
+ }
1240
+
1241
+ function cloneContainer(value, nextPart) {
1242
+ if (Array.isArray(value)) {
1243
+ return [...value];
1244
+ }
1245
+ if (value && typeof value === "object") {
1246
+ return { ...value };
1247
+ }
1248
+ return isArrayIndex(nextPart) ? [] : {};
1249
+ }
1250
+
1251
+ function isArrayIndex(part) {
1252
+ return String(Number(part)) === String(part);
1253
+ }
1254
+
1255
+ function requireEntry(entries, id) {
1256
+ const entry = entries.get(id);
1257
+ if (!entry) {
1258
+ throw new Error(`Signal "${id}" is not registered.`);
1259
+ }
1260
+ return entry;
1261
+ }
1262
+
1263
+ function assertId(id) {
1264
+ if (typeof id !== "string" || id.length === 0) {
1265
+ throw new TypeError("Signal id must be a non-empty string.");
1266
+ }
1267
+ }
1268
+
1269
+ function track(path) {
1270
+ const frame = dependencyFrames.at(-1);
1271
+ if (frame) {
1272
+ frame.add(path);
1273
+ }
1274
+ }
1275
+ return { createSignal, computed, effect, createSignalRegistry, isSignalRef, signal };
1276
+ })();
1277
+
1278
+ const __htmlModule = (() => {
1279
+ const { isSignalRef } = __signalsModule;
1280
+ const { attributeName, matchAttribute, normalizeAttributeConfig } = __attributesModule;
1281
+ const templateKind = Symbol.for("@async/framework.template");
1282
+ const rawKind = Symbol.for("@async/framework.rawHtml");
1283
+
1284
+ function html(strings, ...values) {
1285
+ return {
1286
+ [templateKind]: true,
1287
+ strings,
1288
+ values
1289
+ };
1290
+ }
1291
+
1292
+ function isTemplateResult(value) {
1293
+ return Boolean(value?.[templateKind]);
1294
+ }
1295
+
1296
+ function rawHtml(value) {
1297
+ return {
1298
+ [rawKind]: true,
1299
+ html: String(value ?? "")
1300
+ };
1301
+ }
1302
+
1303
+ function renderTemplate(value, options = {}) {
1304
+ if (isTemplateResult(value)) {
1305
+ const context = createRenderContext(options);
1306
+ let output = "";
1307
+ for (let index = 0; index < value.strings.length; index += 1) {
1308
+ output += value.strings[index];
1309
+ if (index < value.values.length) {
1310
+ output += renderValue(value.values[index], {
1311
+ ...context,
1312
+ attribute: readAttributeContext(value.strings[index])
1313
+ });
1314
+ }
1315
+ }
1316
+ return output;
1317
+ }
1318
+ return renderValue(value, createRenderContext(options));
1319
+ }
1320
+
1321
+ function renderValue(value, context = createRenderContext()) {
1322
+ if (value?.[rawKind]) {
1323
+ return value.html;
1324
+ }
1325
+ if (isTemplateResult(value)) {
1326
+ return renderTemplate(value, context);
1327
+ }
1328
+ if (context.attribute) {
1329
+ return renderAttributeValue(value, context);
1330
+ }
1331
+ if (Array.isArray(value)) {
1332
+ return value.map((item) => renderValue(item, context)).join("");
1333
+ }
1334
+ if (isSignalRef(value)) {
1335
+ return escapeHtml(value.value);
1336
+ }
1337
+ if (value == null || value === false) {
1338
+ return "";
1339
+ }
1340
+ return escapeHtml(value);
1341
+ }
1342
+
1343
+ function renderAttributeValue(value, context) {
1344
+ const signalName = matchAttribute(context.attribute.name, context.attributes, "signal");
1345
+ const className = matchAttribute(context.attribute.name, context.attributes, "class");
1346
+ const signalPath = signalPathFor(value, context);
1347
+
1348
+ if (context.attribute.name === "value" && signalPath) {
1349
+ const currentValue = readSignalValue(value, context);
1350
+ const signalValueAttribute = attributeName(context.attributes, "signal", "value");
1351
+ return `${escapeHtml(currentValue)}${context.attribute.quote} ${signalValueAttribute}=${context.attribute.quote}${escapeHtml(signalPath)}`;
1352
+ }
1353
+
1354
+ if (signalName != null || className != null) {
1355
+ if (signalPath) {
1356
+ return escapeHtml(signalPath);
1357
+ }
1358
+ if (isInlineBindingValue(value)) {
1359
+ return escapeHtml(registerInlineBinding(value, context));
1360
+ }
1361
+ }
1362
+
1363
+ return renderValueAsAttributeLiteral(value, context);
1364
+ }
1365
+
1366
+ function renderValueAsAttributeLiteral(value, context) {
1367
+ if (Array.isArray(value)) {
1368
+ return value.map((item) => renderValueAsAttributeLiteral(item, context)).join("");
1369
+ }
1370
+ if (isSignalRef(value)) {
1371
+ return escapeHtml(value.value);
1372
+ }
1373
+ if (value == null || value === false) {
1374
+ return "";
1375
+ }
1376
+ return escapeHtml(value);
1377
+ }
1378
+
1379
+ function createRenderContext(options = {}) {
1380
+ return {
1381
+ ...options,
1382
+ attributes: normalizeAttributeConfig(options.attributes)
1383
+ };
1384
+ }
1385
+
1386
+ function readAttributeContext(source) {
1387
+ const match = source.match(/(?:^|[\s<])([^\s"'=<>`]+)\s*=\s*(["'])$/);
1388
+ if (!match) {
1389
+ return null;
1390
+ }
1391
+ return {
1392
+ name: match[1],
1393
+ quote: match[2]
1394
+ };
1395
+ }
1396
+
1397
+ function signalPathFor(value, context) {
1398
+ if (isSignalRef(value)) {
1399
+ return value.id;
1400
+ }
1401
+ if (typeof value === "string" && context.signals?.has?.(value)) {
1402
+ return value;
1403
+ }
1404
+ return null;
1405
+ }
1406
+
1407
+ function readSignalValue(value, context) {
1408
+ if (isSignalRef(value)) {
1409
+ return value.value;
1410
+ }
1411
+ if (typeof value === "string" && context.signals?.has?.(value)) {
1412
+ return context.signals.get(value);
1413
+ }
1414
+ return value;
1415
+ }
1416
+
1417
+ function isInlineBindingValue(value) {
1418
+ return Boolean(value && typeof value === "object");
1419
+ }
1420
+
1421
+ function registerInlineBinding(value, context) {
1422
+ if (typeof context.bind !== "function") {
1423
+ return value;
1424
+ }
1425
+ return context.bind(value);
1426
+ }
1427
+
1428
+ function escapeHtml(value) {
1429
+ return String(value)
1430
+ .replaceAll("&", "&amp;")
1431
+ .replaceAll("<", "&lt;")
1432
+ .replaceAll(">", "&gt;")
1433
+ .replaceAll('"', "&quot;")
1434
+ .replaceAll("'", "&#39;");
1435
+ }
1436
+ return { html, isTemplateResult, rawHtml, renderTemplate, escapeHtml };
1437
+ })();
1438
+
1439
+ const __componentModule = (() => {
1440
+ const { attributeName } = __attributesModule;
1441
+ const { escapeHtml, rawHtml, renderTemplate } = __htmlModule;
1442
+ const { attachRegistryInspection, createRegistryStore } = __registryStoreModule;
1443
+ const componentKind = Symbol.for("@async/framework.component");
1444
+ let componentCounter = 0;
1445
+
1446
+ function defineComponent(fn) {
1447
+ if (typeof fn !== "function") {
1448
+ throw new TypeError("defineComponent(fn) requires a function.");
1449
+ }
1450
+ Object.defineProperty(fn, componentKind, {
1451
+ configurable: true,
1452
+ value: true
1453
+ });
1454
+ return fn;
1455
+ }
1456
+
1457
+ const component = defineComponent;
1458
+
1459
+ function createComponentRegistry(initialMap = {}, options = {}) {
1460
+ const registryStore = options.registry ?? createRegistryStore();
1461
+ const type = options.type ?? "component";
1462
+ const entries = registryStore._map(type);
1463
+
1464
+ const registry = attachRegistryInspection({
1465
+ register(id, Component) {
1466
+ if (typeof id !== "string" || id.length === 0) {
1467
+ throw new TypeError("Component id must be a non-empty string.");
1468
+ }
1469
+ if (!isComponent(Component) && typeof Component !== "function") {
1470
+ throw new TypeError(`Component "${id}" must be a component function.`);
1471
+ }
1472
+ if (entries.has(id)) {
1473
+ throw new Error(`Component "${id}" is already registered.`);
1474
+ }
1475
+ entries.set(id, Component);
1476
+ return id;
1477
+ },
1478
+
1479
+ registerMany(map) {
1480
+ for (const [id, Component] of Object.entries(map ?? {})) {
1481
+ registry.register(id, Component);
1482
+ }
1483
+ return registry;
1484
+ },
1485
+
1486
+ unregister(id) {
1487
+ if (typeof id !== "string" || id.length === 0) {
1488
+ throw new TypeError("Component id must be a non-empty string.");
1489
+ }
1490
+ return entries.delete(id);
1491
+ },
1492
+
1493
+ resolve(id) {
1494
+ if (typeof id !== "string" || id.length === 0) {
1495
+ throw new TypeError("Component id must be a non-empty string.");
1496
+ }
1497
+ return entries.get(id);
1498
+ },
1499
+
1500
+ _adoptMany() {
1501
+ return registry;
1502
+ }
1503
+ }, registryStore, type);
1504
+
1505
+ registry.registerMany(initialMap);
1506
+ return registry;
1507
+ }
1508
+
1509
+ function isComponent(value) {
1510
+ return Boolean(value?.[componentKind]);
1511
+ }
1512
+
1513
+ function renderComponent(Component, props = {}, runtime, parentScope = "component") {
1514
+ if (!isComponent(Component) && typeof Component !== "function") {
1515
+ throw new TypeError("renderComponent(Component) requires a component function.");
1516
+ }
1517
+
1518
+ const scope = `${parentScope}.${componentName(Component)}.${++componentCounter}`;
1519
+ const cleanups = [];
1520
+ const attachHooks = [];
1521
+ const visibleHooks = [];
1522
+ const destroyHooks = [];
1523
+ const bindingIds = [];
1524
+ const templateOptions = {
1525
+ attributes: runtime.attributes,
1526
+ signals: runtime.signals,
1527
+ bind(value) {
1528
+ const id = runtime.loader?._registerBinding?.(value);
1529
+ if (!id) {
1530
+ throw new Error("Inline template bindings require a Loader.");
1531
+ }
1532
+ bindingIds.push(id);
1533
+ return id;
1534
+ }
1535
+ };
1536
+ const renderScopedTemplate = (value) => renderTemplate(value, templateOptions);
1537
+ const context = createComponentContext({
1538
+ runtime,
1539
+ scope,
1540
+ cleanups,
1541
+ attachHooks,
1542
+ visibleHooks,
1543
+ destroyHooks,
1544
+ renderScopedTemplate
1545
+ });
1546
+
1547
+ const output = Component.call(context, props);
1548
+ const html = renderScopedTemplate(output);
1549
+
1550
+ return {
1551
+ html,
1552
+ attach(target) {
1553
+ for (const hook of attachHooks) {
1554
+ const cleanup = hook(target);
1555
+ if (typeof cleanup === "function") {
1556
+ cleanups.push(cleanup);
1557
+ }
1558
+ }
1559
+ },
1560
+ mount(target) {
1561
+ this.attach(target);
1562
+ },
1563
+ visible(target, observeVisible) {
1564
+ for (const hook of visibleHooks) {
1565
+ const cleanup = observeVisible(target, hook);
1566
+ if (typeof cleanup === "function") {
1567
+ cleanups.push(cleanup);
1568
+ }
1569
+ }
1570
+ },
1571
+ cleanup() {
1572
+ while (destroyHooks.length > 0) {
1573
+ destroyHooks.pop()?.();
1574
+ }
1575
+ while (cleanups.length > 0) {
1576
+ cleanups.pop()?.();
1577
+ }
1578
+ while (bindingIds.length > 0) {
1579
+ runtime.loader?._releaseBinding?.(bindingIds.pop());
1580
+ }
1581
+ }
1582
+ };
1583
+ }
1584
+
1585
+ function createComponentContext({ runtime, scope, cleanups, attachHooks, visibleHooks, destroyHooks, renderScopedTemplate }) {
1586
+ const { signals, handlers, loader, server, router, cache } = runtime;
1587
+ const generatedHandlers = new WeakMap();
1588
+ let generatedHandlerCounter = 0;
1589
+ let generatedSignalCounter = 0;
1590
+ const context = {
1591
+ scope,
1592
+ signals,
1593
+ handlers,
1594
+ loader,
1595
+ server,
1596
+ router,
1597
+ cache,
1598
+
1599
+ signal(name, initial) {
1600
+ if (arguments.length === 1) {
1601
+ const id = scoped(scope, `signal.${++generatedSignalCounter}`);
1602
+ const ref = signals.ensure(id, name);
1603
+ cleanups.push(() => signals.unregister?.(id));
1604
+ return ref;
1605
+ }
1606
+ const id = scoped(scope, name);
1607
+ const created = !signals.has(id);
1608
+ const ref = signals.ensure(id, initial);
1609
+ if (created) {
1610
+ cleanups.push(() => signals.unregister?.(id));
1611
+ }
1612
+ return ref;
1613
+ },
1614
+
1615
+ computed(name, fn) {
1616
+ const id = scoped(scope, name);
1617
+ const created = !signals.has(id);
1618
+ const ref = signals.ensure(id, undefined);
1619
+ if (created) {
1620
+ cleanups.push(() => signals.unregister?.(id));
1621
+ }
1622
+ const cleanup = signals.effect(() => {
1623
+ signals.set(id, fn.call(context));
1624
+ });
1625
+ cleanups.push(cleanup);
1626
+ return ref;
1627
+ },
1628
+
1629
+ asyncSignal(name, fn) {
1630
+ const id = scoped(scope, name);
1631
+ const created = !signals.has(id);
1632
+ if (!signals.has(id)) {
1633
+ signals.asyncSignal(id, fn);
1634
+ }
1635
+ if (created) {
1636
+ cleanups.push(() => signals.unregister?.(id));
1637
+ }
1638
+ return signals.ref(id);
1639
+ },
1640
+
1641
+ effect(fn) {
1642
+ const cleanup = signals.effect(() => fn.call(context));
1643
+ cleanups.push(cleanup);
1644
+ return cleanup;
1645
+ },
1646
+
1647
+ handler(name, fn) {
1648
+ if (typeof name === "function" && fn === undefined) {
1649
+ const inlineFn = name;
1650
+ if (generatedHandlers.has(inlineFn)) {
1651
+ return generatedHandlers.get(inlineFn);
1652
+ }
1653
+ const id = registerScopedHandler(`handler.${++generatedHandlerCounter}`, inlineFn);
1654
+ generatedHandlers.set(inlineFn, id);
1655
+ return id;
1656
+ }
1657
+ if (typeof fn !== "function") {
1658
+ throw new TypeError("this.handler(name, fn) or this.handler(fn) requires a function.");
1659
+ }
1660
+ return registerScopedHandler(name, fn);
1661
+ },
1662
+
1663
+ render(Child, childProps = {}) {
1664
+ const child = renderComponent(Child, childProps, runtime, scope);
1665
+ cleanups.push(child.cleanup);
1666
+ attachHooks.push((target) => child.attach(target));
1667
+ visibleHooks.push((target) => child.visible(target, loader._observeVisible));
1668
+ return rawHtml(child.html);
1669
+ },
1670
+
1671
+ suspense(signalRef, views) {
1672
+ const id = signalRef?.id;
1673
+ if (!id) {
1674
+ throw new TypeError("this.suspense(signalRef, views) requires a signal ref.");
1675
+ }
1676
+
1677
+ const normalized = normalizeSuspenseViews(views);
1678
+ const chunks = [];
1679
+ for (const state of ["loading", "ready", "error"]) {
1680
+ const view = normalized[state];
1681
+ if (!view) {
1682
+ continue;
1683
+ }
1684
+ const attr = attributeName(runtime.attributes, "async", state);
1685
+ const body = renderScopedTemplate(view.call(context, signalRef));
1686
+ chunks.push(`<template ${attr}="${escapeHtml(id)}">${body}</template>`);
1687
+ }
1688
+ return rawHtml(chunks.join(""));
1689
+ },
1690
+
1691
+ on(eventName, fn) {
1692
+ if (typeof eventName !== "string" || eventName.length === 0) {
1693
+ throw new TypeError("Component lifecycle event must be a non-empty string.");
1694
+ }
1695
+ if (typeof fn !== "function") {
1696
+ throw new TypeError(`Component lifecycle "${eventName}" requires a function.`);
1697
+ }
1698
+ const event = eventName === "mount" ? "attach" : eventName;
1699
+ if (event === "attach") {
1700
+ attachHooks.push((target) => fn.call(context, target));
1701
+ return;
1702
+ }
1703
+ if (event === "visible") {
1704
+ visibleHooks.push((target) => fn.call(context, target));
1705
+ return;
1706
+ }
1707
+ if (event === "destroy") {
1708
+ destroyHooks.push(() => fn.call(context));
1709
+ return;
1710
+ }
1711
+ throw new Error(`Unsupported component lifecycle event "${eventName}".`);
1712
+ },
1713
+
1714
+ onMount(fn) {
1715
+ context.on("attach", fn);
1716
+ },
1717
+
1718
+ onVisible(fn) {
1719
+ context.on("visible", fn);
1720
+ }
1721
+ };
1722
+
1723
+ return context;
1724
+
1725
+ function registerScopedHandler(name, fn) {
1726
+ const id = scoped(scope, name);
1727
+ handlers.register(id, function runComponentHandler(handlerContext) {
1728
+ return fn.call({ ...context, ...handlerContext }, handlerContext);
1729
+ });
1730
+ cleanups.push(() => handlers.unregister?.(id));
1731
+ return id;
1732
+ }
1733
+ }
1734
+
1735
+ function scoped(scope, name) {
1736
+ if (typeof name !== "string" || name.length === 0) {
1737
+ throw new TypeError("Scoped signal or handler name must be a non-empty string.");
1738
+ }
1739
+ return `${scope}.${name}`;
1740
+ }
1741
+
1742
+ function normalizeSuspenseViews(views) {
1743
+ const normalized = typeof views === "function" ? { ready: views } : views;
1744
+ if (!normalized || typeof normalized !== "object" || Array.isArray(normalized)) {
1745
+ throw new TypeError("this.suspense(signalRef, views) requires views to be a function or object.");
1746
+ }
1747
+
1748
+ for (const state of ["loading", "ready", "error"]) {
1749
+ if (Object.hasOwn(normalized, state) && normalized[state] !== undefined && typeof normalized[state] !== "function") {
1750
+ throw new TypeError(`this.suspense(signalRef, views) view "${state}" must be a function.`);
1751
+ }
1752
+ }
1753
+
1754
+ return normalized;
1755
+ }
1756
+
1757
+ function componentName(Component) {
1758
+ return Component.displayName || Component.name || "anonymous";
1759
+ }
1760
+ return { defineComponent, createComponentRegistry, isComponent, renderComponent, component };
1761
+ })();
1762
+
1763
+ const __serverModule = (() => {
1764
+ const { attachRegistryInspection, createRegistryStore } = __registryStoreModule;
1765
+ const serverEnvelopeKeys = new Set(["value", "signals", "boundary", "html", "redirect", "error"]);
1766
+
1767
+ function createServerRegistry(initialMap = {}, options = {}) {
1768
+ const registryStore = options.registry ?? createRegistryStore();
1769
+ const type = options.type ?? "server";
1770
+ const entries = registryStore._map(type);
1771
+ const defaults = {};
1772
+
1773
+ const registry = attachRegistryInspection({
1774
+ register(id, fn) {
1775
+ assertServerId(id);
1776
+ if (typeof fn !== "function") {
1777
+ throw new TypeError(`Server function "${id}" must be a function.`);
1778
+ }
1779
+ if (entries.has(id)) {
1780
+ throw new Error(`Server function "${id}" is already registered.`);
1781
+ }
1782
+ entries.set(id, fn);
1783
+ return id;
1784
+ },
1785
+
1786
+ registerMany(map) {
1787
+ for (const [id, fn] of Object.entries(map ?? {})) {
1788
+ registry.register(id, fn);
1789
+ }
1790
+ return registry;
1791
+ },
1792
+
1793
+ unregister(id) {
1794
+ assertServerId(id);
1795
+ return entries.delete(id);
1796
+ },
1797
+
1798
+ resolve(id) {
1799
+ assertServerId(id);
1800
+ return entries.get(id);
1801
+ },
1802
+
1803
+ async run(id, args = [], context = {}) {
1804
+ assertServerId(id);
1805
+ const fn = registry.resolve(id);
1806
+ if (!fn) {
1807
+ throw new Error(`Server function "${id}" is not registered.`);
1808
+ }
1809
+
1810
+ let runContext;
1811
+ const server = createServerNamespace((childId, childArgs, childContext = {}) => {
1812
+ return registry.run(childId, childArgs, { ...runContext, ...childContext });
1813
+ }, {}, () => runContext);
1814
+
1815
+ const mergedContext = {
1816
+ ...defaults,
1817
+ ...context,
1818
+ cache: defaults.cache ?? context.cache
1819
+ };
1820
+
1821
+ runContext = {
1822
+ ...mergedContext,
1823
+ id,
1824
+ args,
1825
+ input: mergedContext.input,
1826
+ signals: createSignalReader(mergedContext.signals),
1827
+ abort: mergedContext.abort,
1828
+ cache: mergedContext.cache,
1829
+ server
1830
+ };
1831
+
1832
+ return fn.call(runContext, ...args);
1833
+ },
1834
+
1835
+ _setContext(context = {}) {
1836
+ Object.assign(defaults, context);
1837
+ return registry;
1838
+ },
1839
+
1840
+ _adoptMany() {
1841
+ return registry;
1842
+ }
1843
+ }, registryStore, type);
1844
+
1845
+ registry.registerMany(initialMap);
1846
+ return createServerNamespace((id, args, context) => registry.run(id, args, context), registry, () => defaults);
1847
+ }
1848
+
1849
+ function createServerProxy({
1850
+ endpoint = "/__async/server",
1851
+ fetch: fetchImpl = globalThis.fetch?.bind(globalThis),
1852
+ signals,
1853
+ loader,
1854
+ router,
1855
+ cache,
1856
+ headers = {}
1857
+ } = {}) {
1858
+ if (typeof fetchImpl !== "function") {
1859
+ throw new TypeError("createServerProxy(...) requires fetch to be available.");
1860
+ }
1861
+
1862
+ const defaults = { signals, loader, router, cache };
1863
+
1864
+ async function run(id, args = [], context = {}) {
1865
+ assertServerId(id);
1866
+ const runContext = { ...defaults, ...context };
1867
+ const body = {
1868
+ args,
1869
+ input: context.input ?? defaultInput(runContext),
1870
+ signals: context.signalValues ?? snapshotSignalPaths(context.signalPaths, runContext.signals)
1871
+ };
1872
+
1873
+ const response = await fetchImpl(joinEndpoint(endpoint, id), {
1874
+ method: "POST",
1875
+ headers: {
1876
+ "content-type": "application/json",
1877
+ ...headers
1878
+ },
1879
+ body: JSON.stringify(body),
1880
+ signal: context.abort
1881
+ });
1882
+
1883
+ if (!response.ok) {
1884
+ throw new Error(`Server function "${id}" failed with ${response.status}.`);
1885
+ }
1886
+
1887
+ const result = await readServerResponse(response);
1888
+ await applyServerResult(result, runContext);
1889
+ return unwrapServerResult(result);
1890
+ }
1891
+
1892
+ return createServerNamespace(run, {
1893
+ run,
1894
+ _setContext(context = {}) {
1895
+ Object.assign(defaults, context);
1896
+ }
1897
+ }, () => defaults);
1898
+ }
1899
+
1900
+ function resolveServerCommandArguments(args, context = {}) {
1901
+ const resolved = [];
1902
+ const signalValues = {};
1903
+ const signalPaths = [];
1904
+
1905
+ for (const arg of args) {
1906
+ if (arg.type === "local") {
1907
+ resolved.push(resolveLocal(arg.name, context, { forServer: true }));
1908
+ continue;
1909
+ }
1910
+
1911
+ const value = readSignal(context.signals, arg.path);
1912
+ resolved.push(value);
1913
+ signalValues[arg.path] = value;
1914
+ signalPaths.push(arg.path);
1915
+ }
1916
+
1917
+ return { args: resolved, signalValues, signalPaths };
1918
+ }
1919
+
1920
+ async function applyServerResult(result, context = {}) {
1921
+ if (!isServerEnvelope(result)) {
1922
+ return result;
1923
+ }
1924
+
1925
+ if (result.signals && context.signals) {
1926
+ for (const [path, value] of Object.entries(result.signals)) {
1927
+ context.signals.set?.(path, value);
1928
+ }
1929
+ }
1930
+
1931
+ if (result.cache?.browser && context.cache?.restore) {
1932
+ context.cache.restore(result.cache.browser);
1933
+ }
1934
+
1935
+ if (result.boundary && Object.hasOwn(result, "html")) {
1936
+ context.loader?.swap?.(result.boundary, result.html);
1937
+ }
1938
+
1939
+ if (result.redirect) {
1940
+ await context.router?.navigate?.(result.redirect);
1941
+ }
1942
+
1943
+ if (result.error) {
1944
+ throw toError(result.error);
1945
+ }
1946
+
1947
+ return result;
1948
+ }
1949
+
1950
+ function unwrapServerResult(result) {
1951
+ if (isServerEnvelope(result) && Object.hasOwn(result, "value")) {
1952
+ return result.value;
1953
+ }
1954
+ return result;
1955
+ }
1956
+
1957
+ function defaultInput(context = {}) {
1958
+ const form = findForm(context);
1959
+ if (form) {
1960
+ return formDataToObject(new form.ownerDocument.defaultView.FormData(form));
1961
+ }
1962
+
1963
+ const element = context.element ?? context.el ?? context.event?.target;
1964
+ if (!element) {
1965
+ return {};
1966
+ }
1967
+
1968
+ return {
1969
+ value: "value" in element ? element.value : undefined,
1970
+ checked: "checked" in element ? element.checked : undefined,
1971
+ dataset: element.dataset ? { ...element.dataset } : {}
1972
+ };
1973
+ }
1974
+
1975
+ function createServerNamespace(run, root = {}, contextProvider = () => ({})) {
1976
+ const cache = new Map();
1977
+
1978
+ function namespace(parts) {
1979
+ const cacheKey = parts.join(".");
1980
+ if (cache.has(cacheKey)) {
1981
+ return cache.get(cacheKey);
1982
+ }
1983
+
1984
+ const callable = async (...args) => {
1985
+ if (parts.length === 0) {
1986
+ throw new Error("Server namespace is not directly callable.");
1987
+ }
1988
+ const context = contextProvider() ?? {};
1989
+ const result = await run(parts.join("."), args, context);
1990
+ await applyServerResult(result, context);
1991
+ return unwrapServerResult(result);
1992
+ };
1993
+
1994
+ const proxy = new Proxy(callable, {
1995
+ get(_target, prop) {
1996
+ if (prop === "then") {
1997
+ return undefined;
1998
+ }
1999
+ if (prop in _target) {
2000
+ return _target[prop];
2001
+ }
2002
+ if (parts.length === 0 && prop === "_withContext") {
2003
+ return (context = {}) => createServerNamespace(run, root, () => ({
2004
+ ...(contextProvider() ?? {}),
2005
+ ...context
2006
+ }));
2007
+ }
2008
+ if (parts.length === 0 && prop === "run" && typeof root.run === "function") {
2009
+ return (id, args = [], context = {}) => root.run(id, args, {
2010
+ ...(contextProvider() ?? {}),
2011
+ ...context
2012
+ });
2013
+ }
2014
+ if (parts.length === 0 && Object.hasOwn(root, prop)) {
2015
+ return root[prop];
2016
+ }
2017
+ if (prop === Symbol.toStringTag) {
2018
+ return "AsyncServerNamespace";
2019
+ }
2020
+ if (prop === "toString") {
2021
+ return () => parts.length === 0 ? "server" : `server.${parts.join(".")}`;
2022
+ }
2023
+ return namespace([...parts, String(prop)]);
2024
+ }
2025
+ });
2026
+
2027
+ cache.set(cacheKey, proxy);
2028
+ return proxy;
2029
+ }
2030
+
2031
+ return namespace([]);
2032
+ }
2033
+
2034
+ async function readServerResponse(response) {
2035
+ const type = response.headers.get("content-type") ?? "";
2036
+ if (type.includes("application/json")) {
2037
+ return response.json();
2038
+ }
2039
+ return { value: await response.text() };
2040
+ }
2041
+
2042
+ function snapshotSignalPaths(paths = [], signals) {
2043
+ const snapshot = {};
2044
+ for (const path of paths) {
2045
+ snapshot[path] = readSignal(signals, path);
2046
+ }
2047
+ return snapshot;
2048
+ }
2049
+
2050
+ function readSignal(signals, path) {
2051
+ if (!signals || typeof signals.get !== "function") {
2052
+ throw new Error(`Signal "${path}" cannot be read without a signal registry.`);
2053
+ }
2054
+ return signals.get(path);
2055
+ }
2056
+
2057
+ function createSignalReader(signals) {
2058
+ if (!signals || typeof signals.get === "function") {
2059
+ return signals;
2060
+ }
2061
+
2062
+ return {
2063
+ get(path) {
2064
+ return readPath(signals, path);
2065
+ },
2066
+ snapshot() {
2067
+ return { ...signals };
2068
+ }
2069
+ };
2070
+ }
2071
+
2072
+ function readPath(source, path) {
2073
+ return String(path)
2074
+ .split(".")
2075
+ .reduce((value, part) => value?.[part], source);
2076
+ }
2077
+
2078
+ function resolveLocal(name, context, { forServer } = {}) {
2079
+ if ((name === "$event" || name === "$el") && forServer) {
2080
+ throw new Error(`${name} cannot be passed to a server command.`);
2081
+ }
2082
+ if (name === "$event") {
2083
+ return context.event;
2084
+ }
2085
+ if (name === "$el") {
2086
+ return context.element ?? context.el;
2087
+ }
2088
+ if (name === "$value") {
2089
+ const element = context.element ?? context.el ?? context.event?.target;
2090
+ return element?.value;
2091
+ }
2092
+ if (name === "$checked") {
2093
+ const element = context.element ?? context.el ?? context.event?.target;
2094
+ return element?.checked;
2095
+ }
2096
+ if (name === "$form") {
2097
+ const form = findForm(context);
2098
+ return form ? formDataToObject(new form.ownerDocument.defaultView.FormData(form)) : {};
2099
+ }
2100
+ if (name === "$dataset") {
2101
+ const element = context.element ?? context.el ?? context.event?.target;
2102
+ return element?.dataset ? { ...element.dataset } : {};
2103
+ }
2104
+ throw new Error(`Event local "${name}" is not supported.`);
2105
+ }
2106
+
2107
+ function findForm(context) {
2108
+ const event = context.event;
2109
+ const element = context.element ?? context.el ?? event?.target;
2110
+ if (element?.tagName === "FORM") {
2111
+ return element;
2112
+ }
2113
+ if (event?.type === "submit" && event.target?.tagName === "FORM") {
2114
+ return event.target;
2115
+ }
2116
+ if (event?.type === "submit" && element?.form) {
2117
+ return element.form;
2118
+ }
2119
+ return null;
2120
+ }
2121
+
2122
+ function formDataToObject(formData) {
2123
+ const output = {};
2124
+ for (const [key, value] of formData.entries()) {
2125
+ if (Object.hasOwn(output, key)) {
2126
+ output[key] = Array.isArray(output[key]) ? [...output[key], value] : [output[key], value];
2127
+ continue;
2128
+ }
2129
+ output[key] = value;
2130
+ }
2131
+ return output;
2132
+ }
2133
+
2134
+ function joinEndpoint(endpoint, id) {
2135
+ return `${String(endpoint).replace(/\/$/, "")}/${encodeURIComponent(id)}`;
2136
+ }
2137
+
2138
+ function isServerEnvelope(value) {
2139
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
2140
+ return false;
2141
+ }
2142
+ return Object.keys(value).some((key) => serverEnvelopeKeys.has(key));
2143
+ }
2144
+
2145
+ function toError(value) {
2146
+ if (value instanceof Error) {
2147
+ return value;
2148
+ }
2149
+ if (value && typeof value === "object" && typeof value.message === "string") {
2150
+ return Object.assign(new Error(value.message), value);
2151
+ }
2152
+ return new Error(String(value));
2153
+ }
2154
+
2155
+ function assertServerId(id) {
2156
+ if (typeof id !== "string" || id.length === 0) {
2157
+ throw new TypeError("Server function id must be a non-empty string.");
2158
+ }
2159
+ }
2160
+ return { createServerRegistry, createServerProxy, resolveServerCommandArguments, applyServerResult, unwrapServerResult, defaultInput };
2161
+ })();
2162
+
2163
+ const __handlersModule = (() => {
2164
+ const { applyServerResult, defaultInput, resolveServerCommandArguments, unwrapServerResult } = __serverModule;
2165
+ const { attachRegistryInspection, createRegistryStore } = __registryStoreModule;
2166
+ const builtInTokens = new Set(["prevent", "preventDefault", "stopPropagation", "stopImmediatePropagation"]);
2167
+ const builtInHandlers = {
2168
+ prevent: preventDefault,
2169
+ preventDefault,
2170
+ stopPropagation() {
2171
+ this.event?.stopPropagation?.();
2172
+ },
2173
+ stopImmediatePropagation() {
2174
+ this.event?.stopImmediatePropagation?.();
2175
+ }
2176
+ };
2177
+
2178
+ function preventDefault() {
2179
+ this.event?.preventDefault?.();
2180
+ }
2181
+
2182
+ function createHandlerRegistry(initialMap = {}, options = {}) {
2183
+ const registryStore = options.registry ?? createRegistryStore();
2184
+ const type = options.type ?? "handler";
2185
+ const handlers = registryStore._map(type);
2186
+
2187
+ const registry = attachRegistryInspection({
2188
+ register(id, fn) {
2189
+ assertId(id);
2190
+ if (typeof fn !== "function") {
2191
+ throw new TypeError(`Handler "${id}" must be a function.`);
2192
+ }
2193
+ if (handlers.has(id)) {
2194
+ throw new Error(`Handler "${id}" is already registered.`);
2195
+ }
2196
+ handlers.set(id, fn);
2197
+ return id;
2198
+ },
2199
+
2200
+ registerMany(map) {
2201
+ for (const [id, fn] of Object.entries(map ?? {})) {
2202
+ registry.register(id, fn);
2203
+ }
2204
+ return registry;
2205
+ },
2206
+
2207
+ unregister(id) {
2208
+ assertId(id);
2209
+ return handlers.delete(id);
2210
+ },
2211
+
2212
+ resolve(id) {
2213
+ assertId(id);
2214
+ return handlers.get(id);
2215
+ },
2216
+
2217
+ async run(ref, context = {}) {
2218
+ const steps = parseHandlerRef(ref);
2219
+ const results = [];
2220
+ let stopped = false;
2221
+ const runContext = {
2222
+ ...context,
2223
+ handlers: registry,
2224
+ input: context.input ?? defaultInput(context),
2225
+ stop() {
2226
+ stopped = true;
2227
+ }
2228
+ };
2229
+
2230
+ for (const step of steps) {
2231
+ if (stopped) {
2232
+ break;
2233
+ }
2234
+
2235
+ if (step.type === "server") {
2236
+ if (!runContext.server || typeof runContext.server.run !== "function") {
2237
+ throw new Error(`Server command "${step.id}" cannot run without a server registry.`);
2238
+ }
2239
+ const resolved = resolveServerCommandArguments(step.args, runContext);
2240
+ const result = await runContext.server.run(step.id, resolved.args, {
2241
+ ...runContext,
2242
+ signalPaths: resolved.signalPaths,
2243
+ signalValues: resolved.signalValues
2244
+ });
2245
+ await applyServerResult(result, runContext);
2246
+ results.push(unwrapServerResult(result));
2247
+ continue;
2248
+ }
2249
+
2250
+ const handler = registry.resolve(step.id);
2251
+ if (!handler) {
2252
+ throw new Error(`Handler "${step.id}" is not registered.`);
2253
+ }
2254
+ const value = await handler.call(runContext, runContext);
2255
+ if (!(builtInTokens.has(step.id) && handler === builtInHandlers[step.id])) {
2256
+ results.push(value);
2257
+ }
2258
+ }
2259
+
2260
+ return results;
2261
+ },
2262
+
2263
+ _adoptMany() {
2264
+ return registry;
2265
+ }
2266
+ }, registryStore, type);
2267
+
2268
+ registerBuiltIns(registry, handlers);
2269
+ registry.registerMany(initialMap);
2270
+ return registry;
2271
+ }
2272
+
2273
+ function registerBuiltIns(registry, handlers) {
2274
+ for (const [id, fn] of Object.entries(builtInHandlers)) {
2275
+ if (!handlers.has(id)) {
2276
+ registry.register(id, fn);
2277
+ continue;
2278
+ }
2279
+ if (handlers.get(id) !== fn) {
2280
+ throw new Error(`Handler "${id}" is already registered.`);
2281
+ }
2282
+ }
2283
+ }
2284
+
2285
+ function parseHandlerRef(ref) {
2286
+ if (typeof ref !== "string" || ref.trim().length === 0) {
2287
+ throw new TypeError("Handler ref must be a non-empty string.");
2288
+ }
2289
+
2290
+ return ref
2291
+ .split(";")
2292
+ .map((part) => part.trim())
2293
+ .filter(Boolean)
2294
+ .map(parseCommand);
2295
+ }
2296
+
2297
+ function isHandlerToken(value) {
2298
+ return builtInTokens.has(value);
2299
+ }
2300
+
2301
+ function assertId(id) {
2302
+ if (typeof id !== "string" || id.length === 0) {
2303
+ throw new TypeError("Handler id must be a non-empty string.");
2304
+ }
2305
+ }
2306
+
2307
+ function parseCommand(command) {
2308
+ if (command.startsWith("server.")) {
2309
+ return parseServerCommand(command);
2310
+ }
2311
+ if (command.includes("(") || command.includes(")")) {
2312
+ throw new Error(`Command "${command}" is not supported.`);
2313
+ }
2314
+ return { type: "handler", id: command };
2315
+ }
2316
+
2317
+ function parseServerCommand(command) {
2318
+ const open = command.indexOf("(");
2319
+ if (open === -1 || !command.endsWith(")")) {
2320
+ throw new Error(`Server command "${command}" must be called with parentheses.`);
2321
+ }
2322
+
2323
+ const id = command.slice("server.".length, open).trim();
2324
+ if (!isServerCommandId(id)) {
2325
+ throw new Error(`Server command "${command}" has an invalid function id.`);
2326
+ }
2327
+
2328
+ return {
2329
+ type: "server",
2330
+ id,
2331
+ args: parseArguments(command.slice(open + 1, -1))
2332
+ };
2333
+ }
2334
+
2335
+ function parseArguments(source) {
2336
+ if (source.trim().length === 0) {
2337
+ return [];
2338
+ }
2339
+
2340
+ return source
2341
+ .split(",")
2342
+ .map((part) => part.trim())
2343
+ .filter(Boolean)
2344
+ .map(parseArgument);
2345
+ }
2346
+
2347
+ function parseArgument(token) {
2348
+ if (!/^[^\s,();]+$/.test(token)) {
2349
+ throw new Error(`Argument "${token}" is not supported.`);
2350
+ }
2351
+ if (token.startsWith("$")) {
2352
+ return { type: "local", name: token };
2353
+ }
2354
+ return { type: "signal", path: token };
2355
+ }
2356
+
2357
+ function isServerCommandId(id) {
2358
+ return /^[^.\s();]+(?:\.[^.\s();]+)*$/.test(id);
2359
+ }
2360
+ return { createHandlerRegistry, parseHandlerRef, isHandlerToken };
2361
+ })();
2362
+
2363
+ const __loaderModule = (() => {
2364
+ const { renderComponent } = __componentModule;
2365
+ const { createHandlerRegistry } = __handlersModule;
2366
+ const { createSignalRegistry, isSignalRef } = __signalsModule;
2367
+ const { matchAttribute, normalizeAttributeConfig, readAttribute } = __attributesModule;
2368
+ const inlineBindingPrefix = "__async:inline:";
2369
+
2370
+ function Loader({ root, signals, handlers, server, router, cache, attributes } = {}) {
2371
+ const documentRef = root?.ownerDocument ?? root ?? globalThis.document;
2372
+ const rootNode = root ?? documentRef;
2373
+ const signalRegistry = signals ?? createSignalRegistry();
2374
+ const handlerRegistry = handlers ?? createHandlerRegistry();
2375
+ const attributeConfig = normalizeAttributeConfig(attributes);
2376
+ const cleanups = new Set();
2377
+ const eventBindings = new WeakMap();
2378
+ const signalBindings = new WeakMap();
2379
+ const mountedElements = new WeakSet();
2380
+ const visibleElements = new WeakSet();
2381
+ const boundaryState = new WeakMap();
2382
+ const renderingBoundaries = new WeakSet();
2383
+ const inlineBindings = new Map();
2384
+ const scopedCleanups = new WeakMap();
2385
+ let inlineBindingCounter = 0;
2386
+ let destroyed = false;
2387
+
2388
+ const api = {
2389
+ root: rootNode,
2390
+ signals: signalRegistry,
2391
+ handlers: handlerRegistry,
2392
+ server,
2393
+ router,
2394
+ cache,
2395
+ attributes: attributeConfig,
2396
+
2397
+ start() {
2398
+ assertActive();
2399
+ api.scan(rootNode);
2400
+ return api;
2401
+ },
2402
+
2403
+ scan(rootOrFragment = rootNode) {
2404
+ assertActive();
2405
+ bindSignalAttributes(rootOrFragment);
2406
+ bindClassAttributes(rootOrFragment);
2407
+ bindEventAttributes(rootOrFragment);
2408
+ bindBoundaries(rootOrFragment);
2409
+ runPseudoEvents(rootOrFragment);
2410
+ return api;
2411
+ },
2412
+
2413
+ swap(boundaryId, fragmentOrTemplate) {
2414
+ assertActive();
2415
+ const boundary = findBoundary(rootNode, boundaryId, attributeConfig);
2416
+ if (!boundary) {
2417
+ throw new Error(`Boundary "${boundaryId}" was not found.`);
2418
+ }
2419
+ cleanupChildren(boundary);
2420
+ boundary.replaceChildren(toFragment(fragmentOrTemplate, documentRef));
2421
+ api.scan(boundary);
2422
+ return boundary;
2423
+ },
2424
+
2425
+ mount(target, Component, props = {}) {
2426
+ assertActive();
2427
+ const rendered = renderComponent(Component, props, {
2428
+ signals: signalRegistry,
2429
+ handlers: handlerRegistry,
2430
+ loader: api,
2431
+ server: api.server,
2432
+ router: api.router,
2433
+ cache: api.cache,
2434
+ attributes: attributeConfig
2435
+ });
2436
+ cleanupChildren(target);
2437
+ target.replaceChildren(toFragment(rendered.html, target.ownerDocument));
2438
+ api.scan(target);
2439
+ rendered.mount(target);
2440
+ rendered.visible(target, api._observeVisible);
2441
+ addCleanup(rendered.cleanup, target, "children");
2442
+ return rendered;
2443
+ },
2444
+
2445
+ destroy() {
2446
+ if (destroyed) {
2447
+ return;
2448
+ }
2449
+ destroyed = true;
2450
+ for (const cleanup of [...cleanups]) {
2451
+ runCleanup(cleanup);
2452
+ }
2453
+ cleanups.clear();
2454
+ },
2455
+
2456
+ _observeVisible(target, fn) {
2457
+ return observeVisible(target, fn);
2458
+ },
2459
+
2460
+ _registerBinding(value) {
2461
+ const id = `${inlineBindingPrefix}${++inlineBindingCounter}`;
2462
+ inlineBindings.set(id, value);
2463
+ return id;
2464
+ },
2465
+
2466
+ _releaseBinding(id) {
2467
+ inlineBindings.delete(id);
2468
+ }
2469
+ };
2470
+
2471
+ signalRegistry._setContext?.({ server: api.server, router: api.router, loader: api, cache: api.cache });
2472
+ api.server?._setContext?.({
2473
+ signals: signalRegistry,
2474
+ handlers: handlerRegistry,
2475
+ loader: api,
2476
+ router: api.router,
2477
+ cache: api.cache
2478
+ });
2479
+
2480
+ function bindEventAttributes(scope) {
2481
+ for (const element of elementsIn(scope)) {
2482
+ if (typeof element.getAttributeNames !== "function") {
2483
+ continue;
2484
+ }
2485
+ for (const name of element.getAttributeNames()) {
2486
+ const eventName = matchAttribute(name, attributeConfig, "on");
2487
+ if (!eventName) {
2488
+ continue;
2489
+ }
2490
+ if (eventName === "attach" || eventName === "mount" || eventName === "visible") {
2491
+ continue;
2492
+ }
2493
+ bindEvent(element, eventName, element.getAttribute(name));
2494
+ }
2495
+ }
2496
+ }
2497
+
2498
+ function bindEvent(element, eventName, ref) {
2499
+ const key = `${eventName}:${ref}`;
2500
+ const bound = eventBindings.get(element) ?? new Set();
2501
+ if (bound.has(key)) {
2502
+ return;
2503
+ }
2504
+ bound.add(key);
2505
+ eventBindings.set(element, bound);
2506
+
2507
+ const listener = async (event) => {
2508
+ try {
2509
+ await handlerRegistry.run(ref, {
2510
+ signals: signalRegistry,
2511
+ handlers: handlerRegistry,
2512
+ loader: api,
2513
+ server: api.server,
2514
+ router: api.router,
2515
+ cache: api.cache,
2516
+ event,
2517
+ element,
2518
+ el: element,
2519
+ root: rootNode
2520
+ });
2521
+ } catch (error) {
2522
+ dispatchAsyncError(element, error);
2523
+ }
2524
+ };
2525
+
2526
+ element.addEventListener(eventName, listener);
2527
+ addCleanup(() => element.removeEventListener(eventName, listener), element);
2528
+ }
2529
+
2530
+ function bindSignalAttributes(scope) {
2531
+ for (const element of elementsIn(scope)) {
2532
+ for (const name of element.getAttributeNames?.() ?? []) {
2533
+ const signalName = matchAttribute(name, attributeConfig, "signal");
2534
+ if (!signalName) {
2535
+ continue;
2536
+ }
2537
+ if (signalName === "text") {
2538
+ const path = element.getAttribute(name);
2539
+ bindSignal(element, `text:${path}`, path, (value) => {
2540
+ element.textContent = value ?? "";
2541
+ });
2542
+ continue;
2543
+ }
2544
+ if (signalName === "value") {
2545
+ const path = element.getAttribute(name);
2546
+ bindSignal(element, `value:${path}`, path, (value) => {
2547
+ if ("value" in element && element.value !== String(value ?? "")) {
2548
+ element.value = value ?? "";
2549
+ } else if (!("value" in element)) {
2550
+ element.setAttribute("value", value ?? "");
2551
+ }
2552
+ });
2553
+ bindValueWriter(element, path);
2554
+ continue;
2555
+ }
2556
+ if (signalName.startsWith("attr:")) {
2557
+ const attr = signalName.slice("attr:".length);
2558
+ const path = element.getAttribute(name);
2559
+ bindSignal(element, `attr:${attr}:${path}`, path, (value) => updateAttribute(element, attr, value));
2560
+ continue;
2561
+ }
2562
+ if (signalName.startsWith("prop:")) {
2563
+ const prop = signalName.slice("prop:".length);
2564
+ const path = element.getAttribute(name);
2565
+ bindSignal(element, `prop:${prop}:${path}`, path, (value) => updateProperty(element, prop, value));
2566
+ continue;
2567
+ }
2568
+ if (signalName.startsWith("class:")) {
2569
+ const className = signalName.slice("class:".length);
2570
+ const path = element.getAttribute(name);
2571
+ if (className === "" || className === "{}") {
2572
+ bindClass(element, className, path);
2573
+ } else {
2574
+ bindSignal(element, `class:${className}:${path}`, path, (value) => {
2575
+ element.classList.toggle(className, Boolean(value));
2576
+ });
2577
+ }
2578
+ continue;
2579
+ }
2580
+ if (signalName === "class") {
2581
+ const path = element.getAttribute(name);
2582
+ bindClass(element, "{}", path);
2583
+ }
2584
+ }
2585
+ }
2586
+ }
2587
+
2588
+ function bindClassAttributes(scope) {
2589
+ for (const element of elementsIn(scope)) {
2590
+ for (const name of element.getAttributeNames?.() ?? []) {
2591
+ const className = matchAttribute(name, attributeConfig, "class");
2592
+ if (className == null) {
2593
+ continue;
2594
+ }
2595
+ bindClass(element, className, element.getAttribute(name));
2596
+ }
2597
+ }
2598
+ }
2599
+
2600
+ function bindClass(element, className, path) {
2601
+ if (className === "" || className === "{}") {
2602
+ const staticClasses = readClassTokens(element);
2603
+ let previous = new Set();
2604
+ bindSignal(element, `class:{}:${path}`, path, (value) => {
2605
+ const next = normalizeClassTokens(value);
2606
+ const current = readClassTokens(element);
2607
+ for (const token of previous) {
2608
+ if (!next.has(token) && !staticClasses.has(token)) {
2609
+ current.delete(token);
2610
+ }
2611
+ }
2612
+ for (const token of next) {
2613
+ current.add(token);
2614
+ }
2615
+ writeClassTokens(element, current);
2616
+ previous = next;
2617
+ }, { rawInline: true });
2618
+ return;
2619
+ }
2620
+
2621
+ bindSignal(element, `class:${className}:${path}`, path, (value) => {
2622
+ updateClassToken(element, className, Boolean(value));
2623
+ });
2624
+ }
2625
+
2626
+ function bindSignal(element, key, path, apply, options = {}) {
2627
+ const bound = signalBindings.get(element) ?? new Set();
2628
+ if (bound.has(key)) {
2629
+ return;
2630
+ }
2631
+ bound.add(key);
2632
+ signalBindings.set(element, bound);
2633
+
2634
+ const read = () => readBinding(path, options);
2635
+ apply(read());
2636
+ addCleanup(subscribeBinding(path, () => apply(read())), element);
2637
+ }
2638
+
2639
+ function bindValueWriter(element, path) {
2640
+ bindEvent(element, "input", `__async:set:${path}`);
2641
+ bindEvent(element, "change", `__async:set:${path}`);
2642
+ if (!handlerRegistry.resolve(`__async:set:${path}`)) {
2643
+ handlerRegistry.register(`__async:set:${path}`, function writeValue({ element }) {
2644
+ writeBinding(path, element.value);
2645
+ });
2646
+ }
2647
+ }
2648
+
2649
+ function readBinding(path, options = {}) {
2650
+ if (isInlineBinding(path)) {
2651
+ const value = inlineBindings.get(path);
2652
+ return options.rawInline ? value : resolveInlineValue(value);
2653
+ }
2654
+ return signalRegistry.get(path);
2655
+ }
2656
+
2657
+ function writeBinding(path, value) {
2658
+ if (!isInlineBinding(path)) {
2659
+ return signalRegistry.set(path, value);
2660
+ }
2661
+ const binding = inlineBindings.get(path);
2662
+ if (isSignalRef(binding)) {
2663
+ return binding.set(value);
2664
+ }
2665
+ throw new Error(`Inline binding "${path}" is not writable.`);
2666
+ }
2667
+
2668
+ function subscribeBinding(path, fn) {
2669
+ if (!isInlineBinding(path)) {
2670
+ return signalRegistry.subscribe(path, fn);
2671
+ }
2672
+ const cleanups = collectSignalRefs(inlineBindings.get(path)).map((ref) => ref.subscribe(fn));
2673
+ return () => {
2674
+ for (const cleanup of cleanups) {
2675
+ cleanup();
2676
+ }
2677
+ };
2678
+ }
2679
+
2680
+ function bindBoundaries(scope) {
2681
+ for (const boundary of elementsIn(scope)) {
2682
+ if (renderingBoundaries.has(boundary)) {
2683
+ continue;
2684
+ }
2685
+ const id = readAttribute(boundary, attributeConfig, "async", "boundary");
2686
+ if (id == null) {
2687
+ continue;
2688
+ }
2689
+ if (!boundaryState.has(boundary)) {
2690
+ const templates = collectBoundaryTemplates(boundary, id, attributeConfig);
2691
+ if (Object.keys(templates).length === 0 || !signalRegistry.has(id)) {
2692
+ continue;
2693
+ }
2694
+ const state = {
2695
+ id,
2696
+ templates,
2697
+ cleanup: signalRegistry.subscribe(`${id}.$status`, () => renderBoundary(boundary))
2698
+ };
2699
+ boundaryState.set(boundary, state);
2700
+ addCleanup(state.cleanup, boundary);
2701
+ }
2702
+ renderBoundary(boundary);
2703
+ }
2704
+ }
2705
+
2706
+ function renderBoundary(boundary) {
2707
+ const state = boundaryState.get(boundary);
2708
+ if (!state) {
2709
+ return;
2710
+ }
2711
+ const status = signalRegistry.get(`${state.id}.$status`);
2712
+ const template = chooseBoundaryTemplate(state.templates, status);
2713
+ if (!template) {
2714
+ return;
2715
+ }
2716
+ cleanupChildren(boundary);
2717
+ boundary.replaceChildren(template.content.cloneNode(true));
2718
+ renderingBoundaries.add(boundary);
2719
+ try {
2720
+ api.scan(boundary);
2721
+ } finally {
2722
+ renderingBoundaries.delete(boundary);
2723
+ }
2724
+ }
2725
+
2726
+ function runPseudoEvents(scope) {
2727
+ for (const element of elementsIn(scope)) {
2728
+ const refs = readPseudoRefs(element, ["attach", "mount"]);
2729
+ if (refs.length === 0) {
2730
+ continue;
2731
+ }
2732
+ if (mountedElements.has(element)) {
2733
+ continue;
2734
+ }
2735
+ mountedElements.add(element);
2736
+ for (const ref of refs) {
2737
+ runPseudo(element, ref);
2738
+ }
2739
+ }
2740
+
2741
+ for (const element of elementsIn(scope)) {
2742
+ const ref = readAttribute(element, attributeConfig, "on", "visible");
2743
+ if (ref == null) {
2744
+ continue;
2745
+ }
2746
+ if (visibleElements.has(element)) {
2747
+ continue;
2748
+ }
2749
+ visibleElements.add(element);
2750
+ addCleanup(observeVisible(element, () => runPseudo(element, ref)), element);
2751
+ }
2752
+ }
2753
+
2754
+ function readPseudoRefs(element, names) {
2755
+ const refs = [];
2756
+ for (const name of names) {
2757
+ const ref = readAttribute(element, attributeConfig, "on", name);
2758
+ if (ref != null) {
2759
+ refs.push(ref);
2760
+ }
2761
+ }
2762
+ return refs;
2763
+ }
2764
+
2765
+ async function runPseudo(element, ref) {
2766
+ try {
2767
+ const results = await handlerRegistry.run(ref, {
2768
+ signals: signalRegistry,
2769
+ handlers: handlerRegistry,
2770
+ loader: api,
2771
+ server: api.server,
2772
+ router: api.router,
2773
+ cache: api.cache,
2774
+ element,
2775
+ el: element,
2776
+ root: rootNode
2777
+ });
2778
+ for (const result of results) {
2779
+ if (typeof result === "function") {
2780
+ addCleanup(result, element);
2781
+ }
2782
+ }
2783
+ } catch (error) {
2784
+ dispatchAsyncError(element, error);
2785
+ }
2786
+ }
2787
+
2788
+ function observeVisible(target, fn) {
2789
+ const ownerWindow = target.ownerDocument?.defaultView ?? globalThis;
2790
+ const Observer = ownerWindow.IntersectionObserver ?? globalThis.IntersectionObserver;
2791
+ if (!Observer) {
2792
+ queueMicrotask(() => {
2793
+ if (!destroyed) {
2794
+ fn(target);
2795
+ }
2796
+ });
2797
+ return () => {};
2798
+ }
2799
+
2800
+ const observer = new Observer((entries) => {
2801
+ if (entries.some((entry) => entry.isIntersecting)) {
2802
+ observer.disconnect();
2803
+ fn(target);
2804
+ }
2805
+ });
2806
+ observer.observe(target);
2807
+ return () => observer.disconnect();
2808
+ }
2809
+
2810
+ function assertActive() {
2811
+ if (destroyed) {
2812
+ throw new Error("Loader has been destroyed.");
2813
+ }
2814
+ }
2815
+
2816
+ function addCleanup(cleanup, owner, mode = "self") {
2817
+ if (typeof cleanup !== "function") {
2818
+ return cleanup;
2819
+ }
2820
+ cleanups.add(cleanup);
2821
+ if (owner) {
2822
+ const records = scopedCleanups.get(owner) ?? [];
2823
+ records.push({ cleanup, mode });
2824
+ scopedCleanups.set(owner, records);
2825
+ }
2826
+ return cleanup;
2827
+ }
2828
+
2829
+ function runCleanup(cleanup) {
2830
+ if (typeof cleanup !== "function" || !cleanups.has(cleanup)) {
2831
+ return;
2832
+ }
2833
+ cleanups.delete(cleanup);
2834
+ cleanup();
2835
+ }
2836
+
2837
+ function cleanupChildren(container) {
2838
+ runScopedCleanups(container, "children");
2839
+ for (const child of [...(container.childNodes ?? [])]) {
2840
+ cleanupNode(child);
2841
+ }
2842
+ }
2843
+
2844
+ function cleanupNode(node) {
2845
+ if (node.nodeType !== 1) {
2846
+ return;
2847
+ }
2848
+ for (const element of elementsIn(node)) {
2849
+ runScopedCleanups(element);
2850
+ }
2851
+ }
2852
+
2853
+ function runScopedCleanups(element, mode) {
2854
+ const records = scopedCleanups.get(element);
2855
+ if (!records) {
2856
+ return;
2857
+ }
2858
+ const remaining = [];
2859
+ for (const record of records) {
2860
+ if (mode && record.mode !== mode) {
2861
+ remaining.push(record);
2862
+ continue;
2863
+ }
2864
+ runCleanup(record.cleanup);
2865
+ }
2866
+ if (remaining.length > 0) {
2867
+ scopedCleanups.set(element, remaining);
2868
+ } else {
2869
+ scopedCleanups.delete(element);
2870
+ }
2871
+ }
2872
+
2873
+ return api;
2874
+ }
2875
+
2876
+ const AsyncLoader = Loader;
2877
+
2878
+ function normalizeClassTokens(value, tokens = new Set()) {
2879
+ if (value == null || value === false) {
2880
+ return tokens;
2881
+ }
2882
+ if (isSignalRef(value)) {
2883
+ const signalValue = value.value;
2884
+ if (signalValue === true) {
2885
+ tokens.add(signalClassName(value.id));
2886
+ return tokens;
2887
+ }
2888
+ return normalizeClassTokens(signalValue, tokens);
2889
+ }
2890
+ if (typeof value === "string") {
2891
+ for (const token of value.split(/\s+/).filter(Boolean)) {
2892
+ tokens.add(token);
2893
+ }
2894
+ return tokens;
2895
+ }
2896
+ if (Array.isArray(value)) {
2897
+ for (const item of value) {
2898
+ normalizeClassTokens(item, tokens);
2899
+ }
2900
+ return tokens;
2901
+ }
2902
+ if (typeof value === "object") {
2903
+ for (const [token, enabled] of Object.entries(value)) {
2904
+ const value = isSignalRef(enabled) ? enabled.value : enabled;
2905
+ if (value) {
2906
+ normalizeClassTokens(token, tokens);
2907
+ }
2908
+ }
2909
+ return tokens;
2910
+ }
2911
+ if (value !== true) {
2912
+ tokens.add(String(value));
2913
+ }
2914
+ return tokens;
2915
+ }
2916
+
2917
+ function resolveInlineValue(value) {
2918
+ if (isSignalRef(value)) {
2919
+ return value.value;
2920
+ }
2921
+ if (Array.isArray(value)) {
2922
+ return value.map(resolveInlineValue);
2923
+ }
2924
+ if (value && typeof value === "object") {
2925
+ return Object.fromEntries(Object.entries(value).map(([key, entry]) => [key, resolveInlineValue(entry)]));
2926
+ }
2927
+ return value;
2928
+ }
2929
+
2930
+ function collectSignalRefs(value, refs = new Map()) {
2931
+ if (isSignalRef(value)) {
2932
+ refs.set(value.id, value);
2933
+ return [...refs.values()];
2934
+ }
2935
+ if (Array.isArray(value)) {
2936
+ for (const item of value) {
2937
+ collectSignalRefs(item, refs);
2938
+ }
2939
+ return [...refs.values()];
2940
+ }
2941
+ if (value && typeof value === "object") {
2942
+ for (const item of Object.values(value)) {
2943
+ collectSignalRefs(item, refs);
2944
+ }
2945
+ }
2946
+ return [...refs.values()];
2947
+ }
2948
+
2949
+ function isInlineBinding(value) {
2950
+ return typeof value === "string" && value.startsWith(inlineBindingPrefix);
2951
+ }
2952
+
2953
+ function signalClassName(id) {
2954
+ return id.split(".").at(-1);
2955
+ }
2956
+
2957
+ function updateClassToken(element, className, enabled) {
2958
+ const tokens = readClassTokens(element);
2959
+ for (const token of normalizeClassTokens(className)) {
2960
+ if (enabled) {
2961
+ tokens.add(token);
2962
+ } else {
2963
+ tokens.delete(token);
2964
+ }
2965
+ }
2966
+ writeClassTokens(element, tokens);
2967
+ }
2968
+
2969
+ function readClassTokens(element) {
2970
+ return normalizeClassTokens(element.getAttribute("class") ?? "");
2971
+ }
2972
+
2973
+ function writeClassTokens(element, tokens) {
2974
+ const value = [...tokens].join(" ");
2975
+ if (value.length === 0) {
2976
+ element.removeAttribute("class");
2977
+ return;
2978
+ }
2979
+ element.setAttribute("class", value);
2980
+ }
2981
+
2982
+ function collectBoundaryTemplates(boundary, id, attributeConfig) {
2983
+ const templates = {};
2984
+ for (const template of [...boundary.children].filter((child) => child.tagName === "TEMPLATE")) {
2985
+ if (readAttribute(template, attributeConfig, "async", "loading") === id) {
2986
+ templates.loading = template;
2987
+ }
2988
+ if (readAttribute(template, attributeConfig, "async", "ready") === id) {
2989
+ templates.ready = template;
2990
+ }
2991
+ if (readAttribute(template, attributeConfig, "async", "error") === id) {
2992
+ templates.error = template;
2993
+ }
2994
+ }
2995
+ return templates;
2996
+ }
2997
+
2998
+ function chooseBoundaryTemplate(templates, status) {
2999
+ if (status === "ready") {
3000
+ return templates.ready ?? templates.loading ?? templates.error;
3001
+ }
3002
+ if (status === "error") {
3003
+ return templates.error ?? templates.ready ?? templates.loading;
3004
+ }
3005
+ return templates.loading ?? templates.ready ?? templates.error;
3006
+ }
3007
+
3008
+ function updateAttribute(element, attr, value) {
3009
+ if (value === false || value == null) {
3010
+ element.removeAttribute(attr);
3011
+ if (attr in element) {
3012
+ element[attr] = false;
3013
+ }
3014
+ return;
3015
+ }
3016
+ element.setAttribute(attr, value === true ? "" : String(value));
3017
+ if (attr in element) {
3018
+ element[attr] = value;
3019
+ }
3020
+ }
3021
+
3022
+ function updateProperty(element, prop, value) {
3023
+ if (value == null) {
3024
+ element[prop] = "";
3025
+ return;
3026
+ }
3027
+ element[prop] = value;
3028
+ }
3029
+
3030
+ function selectAll(scope, selector) {
3031
+ const elements = [];
3032
+ if (scope?.nodeType === 1 && scope.matches?.(selector)) {
3033
+ elements.push(scope);
3034
+ }
3035
+ elements.push(...(scope?.querySelectorAll?.(selector) ?? []));
3036
+ return elements;
3037
+ }
3038
+
3039
+ function elementsIn(scope) {
3040
+ return selectAll(scope, "*");
3041
+ }
3042
+
3043
+ function findBoundary(root, boundaryId, attributeConfig) {
3044
+ for (const element of elementsIn(root)) {
3045
+ if (readAttribute(element, attributeConfig, "async", "boundary") === String(boundaryId)) {
3046
+ return element;
3047
+ }
3048
+ }
3049
+ return null;
3050
+ }
3051
+
3052
+ function toFragment(value, documentRef) {
3053
+ if (value?.nodeType === 11) {
3054
+ return value;
3055
+ }
3056
+ if (value?.tagName === "TEMPLATE") {
3057
+ return value.content.cloneNode(true);
3058
+ }
3059
+ if (value?.nodeType) {
3060
+ const fragment = documentRef.createDocumentFragment();
3061
+ fragment.append(value);
3062
+ return fragment;
3063
+ }
3064
+ const template = documentRef.createElement("template");
3065
+ template.innerHTML = String(value ?? "");
3066
+ return template.content.cloneNode(true);
3067
+ }
3068
+
3069
+ function dispatchAsyncError(element, error) {
3070
+ const EventCtor = element.ownerDocument?.defaultView?.CustomEvent ?? globalThis.CustomEvent;
3071
+ element.dispatchEvent(
3072
+ new EventCtor("async:error", {
3073
+ bubbles: true,
3074
+ detail: { error }
3075
+ })
3076
+ );
3077
+ }
3078
+ return { Loader, AsyncLoader };
3079
+ })();
3080
+
3081
+ const __partialsModule = (() => {
3082
+ const { isTemplateResult, renderTemplate } = __htmlModule;
3083
+ const { attachRegistryInspection, createRegistryStore } = __registryStoreModule;
3084
+ function createPartialRegistry(initialMap = {}, options = {}) {
3085
+ const registryStore = options.registry ?? createRegistryStore();
3086
+ const type = options.type ?? "partial";
3087
+ const entries = registryStore._map(type);
3088
+
3089
+ const registry = attachRegistryInspection({
3090
+ register(id, fn) {
3091
+ assertId(id);
3092
+ if (typeof fn !== "function") {
3093
+ throw new TypeError(`Partial "${id}" must be a function.`);
3094
+ }
3095
+ if (entries.has(id)) {
3096
+ throw new Error(`Partial "${id}" is already registered.`);
3097
+ }
3098
+ entries.set(id, fn);
3099
+ return id;
3100
+ },
3101
+
3102
+ registerMany(map) {
3103
+ for (const [id, fn] of Object.entries(map ?? {})) {
3104
+ registry.register(id, fn);
3105
+ }
3106
+ return registry;
3107
+ },
3108
+
3109
+ unregister(id) {
3110
+ assertId(id);
3111
+ return entries.delete(id);
3112
+ },
3113
+
3114
+ resolve(id) {
3115
+ assertId(id);
3116
+ return entries.get(id);
3117
+ },
3118
+
3119
+ async render(id, props = {}, context = {}) {
3120
+ assertId(id);
3121
+ const fn = registry.resolve(id);
3122
+ if (!fn) {
3123
+ throw new Error(`Partial "${id}" is not registered.`);
3124
+ }
3125
+
3126
+ const partialContext = {
3127
+ ...context,
3128
+ id,
3129
+ props,
3130
+ cache: context.cache,
3131
+ partials: registry
3132
+ };
3133
+ const result = await fn.call(partialContext, props);
3134
+ return normalizePartialResult(result, partialContext);
3135
+ },
3136
+
3137
+ _adoptMany() {
3138
+ return registry;
3139
+ }
3140
+ }, registryStore, type);
3141
+
3142
+ registry.registerMany(initialMap);
3143
+ return registry;
3144
+ }
3145
+
3146
+ function normalizePartialResult(result, context = {}) {
3147
+ if (isPartialEnvelope(result)) {
3148
+ return {
3149
+ ...result,
3150
+ html: Object.hasOwn(result, "html") ? renderPartialValue(result.html, context) : result.html
3151
+ };
3152
+ }
3153
+
3154
+ return { html: renderPartialValue(result, context) };
3155
+ }
3156
+
3157
+ function renderPartialValue(value, context) {
3158
+ if (value?.nodeType) {
3159
+ return value;
3160
+ }
3161
+ if (typeof value === "string") {
3162
+ return value;
3163
+ }
3164
+ if (isTemplateResult(value)) {
3165
+ return renderTemplate(value, templateRenderOptions(context));
3166
+ }
3167
+ return renderTemplate(value, templateRenderOptions(context));
3168
+ }
3169
+
3170
+ function templateRenderOptions(context) {
3171
+ return {
3172
+ attributes: context.loader?.attributes,
3173
+ signals: context.signals,
3174
+ bind: context.loader?._registerBinding?.bind(context.loader)
3175
+ };
3176
+ }
3177
+
3178
+ function isPartialEnvelope(value) {
3179
+ return Boolean(
3180
+ value &&
3181
+ typeof value === "object" &&
3182
+ !Array.isArray(value) &&
3183
+ (Object.hasOwn(value, "html") ||
3184
+ Object.hasOwn(value, "signals") ||
3185
+ Object.hasOwn(value, "boundary") ||
3186
+ Object.hasOwn(value, "redirect") ||
3187
+ Object.hasOwn(value, "status") ||
3188
+ Object.hasOwn(value, "cache"))
3189
+ );
3190
+ }
3191
+
3192
+ function assertId(id) {
3193
+ if (typeof id !== "string" || id.length === 0) {
3194
+ throw new TypeError("Partial id must be a non-empty string.");
3195
+ }
3196
+ }
3197
+ return { createPartialRegistry, normalizePartialResult };
3198
+ })();
3199
+
3200
+ const __routerModule = (() => {
3201
+ const { Loader } = __loaderModule;
3202
+ const { createHandlerRegistry } = __handlersModule;
3203
+ const { createSignalRegistry } = __signalsModule;
3204
+ const { applyServerResult } = __serverModule;
3205
+ const { createRegistryStore } = __registryStoreModule;
3206
+ const { normalizeAttributeConfig } = __attributesModule;
3207
+ function defineRoute(partial, options = {}) {
3208
+ return {
3209
+ ...options,
3210
+ partial
3211
+ };
3212
+ }
3213
+
3214
+ const route = defineRoute;
3215
+
3216
+ function createRouteRegistry(initialMap = {}, options = {}) {
3217
+ const registryStore = options.registry ?? createRegistryStore();
3218
+ const type = options.type ?? "route";
3219
+ const entries = registryStore._map(type);
3220
+ const routes = [];
3221
+
3222
+ const registry = {
3223
+ registry: registryStore,
3224
+
3225
+ register(pattern, definition) {
3226
+ assertPattern(pattern);
3227
+ if (routes.some((candidate) => candidate.pattern === pattern)) {
3228
+ throw new Error(`Route "${pattern}" is already registered.`);
3229
+ }
3230
+ const nextRoute = normalizeRoute(pattern, definition);
3231
+ entries.set(pattern, nextRoute.definition);
3232
+ routes.push(nextRoute);
3233
+ return nextRoute;
3234
+ },
3235
+
3236
+ registerMany(map) {
3237
+ for (const [pattern, definition] of Object.entries(map ?? {})) {
3238
+ registry.register(pattern, definition);
3239
+ }
3240
+ return registry;
3241
+ },
3242
+
3243
+ unregister(pattern) {
3244
+ assertPattern(pattern);
3245
+ const index = routes.findIndex((candidate) => candidate.pattern === pattern);
3246
+ if (index !== -1) {
3247
+ routes.splice(index, 1);
3248
+ }
3249
+ return entries.delete(pattern);
3250
+ },
3251
+
3252
+ match(url) {
3253
+ const path = toUrl(url).pathname;
3254
+ for (const candidate of routes) {
3255
+ const match = candidate.regex.exec(path);
3256
+ if (!match) {
3257
+ continue;
3258
+ }
3259
+ const params = {};
3260
+ candidate.keys.forEach((key, index) => {
3261
+ params[key] = decodeURIComponent(match[index + 1] ?? "");
3262
+ });
3263
+ return {
3264
+ pattern: candidate.pattern,
3265
+ params,
3266
+ route: candidate.definition
3267
+ };
3268
+ }
3269
+ return null;
3270
+ },
3271
+
3272
+ entries() {
3273
+ return routes.map(({ pattern, definition }) => ({ pattern, route: definition }));
3274
+ },
3275
+
3276
+ keys() {
3277
+ return [...entries.keys()];
3278
+ },
3279
+
3280
+ inspect() {
3281
+ return registryStore.entries(type);
3282
+ },
3283
+
3284
+ _adoptMany(map = {}) {
3285
+ for (const pattern of Object.keys(map ?? {})) {
3286
+ adoptRoute(pattern, entries.get(pattern));
3287
+ }
3288
+ return registry;
3289
+ }
3290
+ };
3291
+
3292
+ for (const [pattern, definition] of entries) {
3293
+ adoptRoute(pattern, definition);
3294
+ }
3295
+ registry.registerMany(initialMap);
3296
+ return registry;
3297
+
3298
+ function adoptRoute(pattern, definition) {
3299
+ if (routes.some((candidate) => candidate.pattern === pattern)) {
3300
+ return;
3301
+ }
3302
+ const nextRoute = normalizeRoute(pattern, definition);
3303
+ entries.set(pattern, nextRoute.definition);
3304
+ routes.push(nextRoute);
3305
+ }
3306
+ }
3307
+
3308
+ function createRouter({
3309
+ mode = "ssr-spa",
3310
+ root,
3311
+ boundary = "route",
3312
+ routes = createRouteRegistry(),
3313
+ loader,
3314
+ signals,
3315
+ handlers,
3316
+ server,
3317
+ cache,
3318
+ partials,
3319
+ fetch: fetchImpl = globalThis.fetch?.bind(globalThis),
3320
+ routeEndpoint = "/__async/route",
3321
+ attributes
3322
+ } = {}) {
3323
+ const documentRef = root?.ownerDocument ?? root ?? globalThis.document;
3324
+ const rootNode = root ?? documentRef;
3325
+ const signalRegistry = signals ?? loader?.signals ?? createSignalRegistry();
3326
+ const handlerRegistry = handlers ?? loader?.handlers ?? createHandlerRegistry();
3327
+ const attributeConfig = normalizeAttributeConfig(attributes ?? loader?.attributes);
3328
+ const loaderInstance =
3329
+ loader ??
3330
+ Loader({
3331
+ root: rootNode,
3332
+ signals: signalRegistry,
3333
+ handlers: handlerRegistry,
3334
+ server,
3335
+ cache,
3336
+ attributes: attributeConfig
3337
+ });
3338
+ const ownsLoader = !loader;
3339
+ const cleanups = new Set();
3340
+ let destroyed = false;
3341
+
3342
+ const api = {
3343
+ mode,
3344
+ root: rootNode,
3345
+ boundary,
3346
+ routes,
3347
+ loader: loaderInstance,
3348
+ signals: signalRegistry,
3349
+ handlers: handlerRegistry,
3350
+ server,
3351
+ cache,
3352
+ partials,
3353
+ attributes: attributeConfig,
3354
+
3355
+ start() {
3356
+ assertActive();
3357
+ loaderInstance.router = api;
3358
+ signalRegistry._setContext?.({ router: api, loader: loaderInstance, server, cache });
3359
+ if (ownsLoader) {
3360
+ loaderInstance.start();
3361
+ }
3362
+ if (mode === "mpa" || mode === "ssr") {
3363
+ updateStateFromLocation();
3364
+ return api;
3365
+ }
3366
+ bindNavigation();
3367
+ if (mode === "csr") {
3368
+ void api.navigate(currentUrl(), {
3369
+ replace: true,
3370
+ initial: true,
3371
+ source: "client"
3372
+ }).catch(() => {});
3373
+ return api;
3374
+ }
3375
+ updateStateFromLocation();
3376
+ return api;
3377
+ },
3378
+
3379
+ match(url) {
3380
+ return routes.match(url);
3381
+ },
3382
+
3383
+ prefetch(url) {
3384
+ assertActive();
3385
+ if (mode === "ssr-spa" && typeof fetchImpl === "function") {
3386
+ return fetchRoute(url, { prefetch: true });
3387
+ }
3388
+ const matched = api.match(url);
3389
+ if (matched?.route?.partial && partials?.resolve?.(matched.route.partial)) {
3390
+ return partials.render(matched.route.partial, matched.params, contextFor(matched));
3391
+ }
3392
+ if (typeof fetchImpl === "function") {
3393
+ return fetchRoute(url, { prefetch: true });
3394
+ }
3395
+ return Promise.resolve(null);
3396
+ },
3397
+
3398
+ async navigate(url, options = {}) {
3399
+ assertActive();
3400
+ if (mode === "mpa" || mode === "ssr") {
3401
+ documentRef.defaultView?.location?.assign?.(url);
3402
+ return null;
3403
+ }
3404
+
3405
+ const target = toUrl(url);
3406
+ if (mode === "ssr-spa") {
3407
+ return fetchRoutePartial(target, options);
3408
+ }
3409
+ return renderLocalRoutePartial(target, options);
3410
+ },
3411
+
3412
+ destroy() {
3413
+ if (destroyed) {
3414
+ return;
3415
+ }
3416
+ destroyed = true;
3417
+ for (const cleanup of cleanups) {
3418
+ cleanup();
3419
+ }
3420
+ cleanups.clear();
3421
+ }
3422
+ };
3423
+
3424
+ return api;
3425
+
3426
+ function bindNavigation() {
3427
+ const click = (event) => {
3428
+ const anchor = closest(event.target, "a[href]");
3429
+ if (!anchor || shouldIgnoreLink(event, anchor)) {
3430
+ return;
3431
+ }
3432
+ event.preventDefault();
3433
+ api.navigate(anchor.href);
3434
+ };
3435
+ const submit = (event) => {
3436
+ const form = closest(event.target, "form");
3437
+ if (!form || shouldIgnoreForm(form)) {
3438
+ return;
3439
+ }
3440
+ event.preventDefault();
3441
+ api.navigate(formActionUrl(form));
3442
+ };
3443
+ const popstate = () => api.navigate(currentUrl(), { history: false });
3444
+
3445
+ rootNode.addEventListener?.("click", click);
3446
+ rootNode.addEventListener?.("submit", submit);
3447
+ documentRef.defaultView?.addEventListener?.("popstate", popstate);
3448
+ cleanups.add(() => rootNode.removeEventListener?.("click", click));
3449
+ cleanups.add(() => rootNode.removeEventListener?.("submit", submit));
3450
+ cleanups.add(() => documentRef.defaultView?.removeEventListener?.("popstate", popstate));
3451
+ }
3452
+
3453
+ async function renderLocalRoutePartial(target, options = {}) {
3454
+ const matched = api.match(target);
3455
+ if (!matched) {
3456
+ setNoRouteError(target);
3457
+ return null;
3458
+ }
3459
+
3460
+ setMatchedRouterState(target, matched, { pending: true, error: null });
3461
+
3462
+ try {
3463
+ if (!matched.route?.partial || !partials?.resolve?.(matched.route.partial)) {
3464
+ const error = new Error(`Route "${target.pathname}" does not have a registered partial.`);
3465
+ setRouterState({ pending: false, error });
3466
+ return null;
3467
+ }
3468
+
3469
+ const result = await partials.render(matched.route.partial, matched.params, contextFor(matched));
3470
+ await applyNavigationResult(result, target, options);
3471
+ setRouterState({ pending: false, error: null });
3472
+ return result;
3473
+ } catch (error) {
3474
+ setRouterState({ pending: false, error });
3475
+ throw error;
3476
+ }
3477
+ }
3478
+
3479
+ async function fetchRoutePartial(target, options = {}) {
3480
+ const matched = api.match(target);
3481
+ setMatchedRouterState(target, matched, { pending: true, error: null });
3482
+
3483
+ try {
3484
+ const result = await fetchRoute(target.href);
3485
+ await applyNavigationResult(result, target, options);
3486
+ setRouterState({ pending: false, error: null });
3487
+ return result;
3488
+ } catch (error) {
3489
+ setRouterState({ pending: false, error });
3490
+ throw error;
3491
+ }
3492
+ }
3493
+
3494
+ async function applyNavigationResult(result, target, options) {
3495
+ await applyServerResult(result, {
3496
+ signals: signalRegistry,
3497
+ loader: loaderInstance,
3498
+ router: api,
3499
+ cache
3500
+ });
3501
+ if (result?.html != null && !result.boundary && !result.redirect) {
3502
+ loaderInstance.swap(boundary, result.html);
3503
+ }
3504
+ if (result?.redirect || options.history === false) {
3505
+ return;
3506
+ }
3507
+ if (options.replace) {
3508
+ documentRef.defaultView?.history?.replaceState?.({}, "", target.href);
3509
+ return;
3510
+ }
3511
+ documentRef.defaultView?.history?.pushState?.({}, "", target.href);
3512
+ }
3513
+
3514
+ async function fetchRoute(url, { prefetch = false } = {}) {
3515
+ if (typeof fetchImpl !== "function") {
3516
+ throw new Error("Router navigation requires a partial registry or fetch.");
3517
+ }
3518
+ const response = await fetchImpl(`${routeEndpoint}?to=${encodeURIComponent(String(url))}`, {
3519
+ headers: {
3520
+ accept: "application/json, text/html"
3521
+ }
3522
+ });
3523
+ if (!response.ok) {
3524
+ throw new Error(`Route "${url}" failed with ${response.status}.`);
3525
+ }
3526
+ if (prefetch) {
3527
+ return response;
3528
+ }
3529
+ const type = response.headers.get("content-type") ?? "";
3530
+ if (type.includes("application/json")) {
3531
+ return response.json();
3532
+ }
3533
+ return { boundary, html: await response.text() };
3534
+ }
3535
+
3536
+ function contextFor(matched) {
3537
+ return {
3538
+ params: matched.params,
3539
+ route: matched.route,
3540
+ router: api,
3541
+ signals: signalRegistry,
3542
+ handlers: handlerRegistry,
3543
+ loader: loaderInstance,
3544
+ server,
3545
+ cache,
3546
+ abort: undefined
3547
+ };
3548
+ }
3549
+
3550
+ function updateStateFromLocation() {
3551
+ const url = currentUrl();
3552
+ const matched = api.match(url);
3553
+ setMatchedRouterState(url, matched, { pending: false, error: null });
3554
+ }
3555
+
3556
+ function setMatchedRouterState(url, matched, patch = {}) {
3557
+ signalRegistry.ensure("router", {});
3558
+ setRouterState({
3559
+ url: url.href,
3560
+ path: url.pathname,
3561
+ query: queryObject(url),
3562
+ params: matched?.params ?? {},
3563
+ route: matched?.route ?? null,
3564
+ ...patch
3565
+ });
3566
+ }
3567
+
3568
+ function setNoRouteError(url) {
3569
+ const error = new Error(`No route matched ${url.pathname}${url.search}`);
3570
+ setMatchedRouterState(url, null, {
3571
+ pending: false,
3572
+ error
3573
+ });
3574
+ }
3575
+
3576
+ function setRouterState(patch) {
3577
+ signalRegistry.ensure("router", {});
3578
+ for (const [key, value] of Object.entries(patch)) {
3579
+ signalRegistry.set(`router.${key}`, value);
3580
+ }
3581
+ }
3582
+
3583
+ function currentUrl() {
3584
+ return toUrl(documentRef.defaultView?.location?.href ?? "http://localhost/");
3585
+ }
3586
+
3587
+ function assertActive() {
3588
+ if (destroyed) {
3589
+ throw new Error("Router has been destroyed.");
3590
+ }
3591
+ }
3592
+ }
3593
+
3594
+ function normalizeRoute(pattern, definition) {
3595
+ const normalized = typeof definition === "string" ? defineRoute(definition) : definition;
3596
+ const { regex, keys } = compilePattern(pattern);
3597
+ return {
3598
+ pattern,
3599
+ regex,
3600
+ keys,
3601
+ definition: normalized
3602
+ };
3603
+ }
3604
+
3605
+ function compilePattern(pattern) {
3606
+ const keys = [];
3607
+ if (pattern === "*") {
3608
+ return { regex: /^.*$/, keys };
3609
+ }
3610
+ if (pattern === "/") {
3611
+ return { regex: /^\/$/, keys };
3612
+ }
3613
+
3614
+ const source = pattern
3615
+ .split("/")
3616
+ .map((segment) => {
3617
+ if (segment.startsWith(":")) {
3618
+ keys.push(segment.slice(1));
3619
+ return "([^/]+)";
3620
+ }
3621
+ return escapeRegExp(segment);
3622
+ })
3623
+ .join("/");
3624
+
3625
+ return { regex: new RegExp(`^${source}$`), keys };
3626
+ }
3627
+
3628
+ function shouldIgnoreLink(event, anchor) {
3629
+ if (event.defaultPrevented || event.metaKey || event.ctrlKey || event.shiftKey || event.altKey) {
3630
+ return true;
3631
+ }
3632
+ if (anchor.target || anchor.hasAttribute("download")) {
3633
+ return true;
3634
+ }
3635
+ return toUrl(anchor.href).origin !== toUrl(anchor.ownerDocument.defaultView.location.href).origin;
3636
+ }
3637
+
3638
+ function shouldIgnoreForm(form) {
3639
+ const method = String(form.method || "get").toLowerCase();
3640
+ return method !== "get" || toUrl(form.action).origin !== toUrl(form.ownerDocument.defaultView.location.href).origin;
3641
+ }
3642
+
3643
+ function formActionUrl(form) {
3644
+ const url = toUrl(form.action || form.ownerDocument.defaultView.location.href);
3645
+ const formData = new form.ownerDocument.defaultView.FormData(form);
3646
+ url.search = new URLSearchParams(formData).toString();
3647
+ return url.href;
3648
+ }
3649
+
3650
+ function closest(target, selector) {
3651
+ return target?.closest?.(selector);
3652
+ }
3653
+
3654
+ function toUrl(url) {
3655
+ if (url instanceof URL) {
3656
+ return url;
3657
+ }
3658
+ return new URL(String(url), globalThis.location?.href ?? "http://localhost/");
3659
+ }
3660
+
3661
+ function queryObject(url) {
3662
+ return Object.fromEntries(url.searchParams.entries());
3663
+ }
3664
+
3665
+ function escapeRegExp(value) {
3666
+ return String(value).replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
3667
+ }
3668
+
3669
+ function assertPattern(pattern) {
3670
+ if (typeof pattern !== "string" || (pattern !== "*" && !pattern.startsWith("/"))) {
3671
+ throw new TypeError("Route pattern must be a path string or \"*\".");
3672
+ }
3673
+ }
3674
+ return { defineRoute, createRouteRegistry, createRouter, route };
3675
+ })();
3676
+
3677
+ const __appModule = (() => {
3678
+ const { createCacheRegistry } = __cacheModule;
3679
+ const { createComponentRegistry } = __componentModule;
3680
+ const { createHandlerRegistry } = __handlersModule;
3681
+ const { Loader } = __loaderModule;
3682
+ const { createPartialRegistry } = __partialsModule;
3683
+ const { createRouteRegistry, createRouter } = __routerModule;
3684
+ const { createServerRegistry } = __serverModule;
3685
+ const { createSignal, createSignalRegistry } = __signalsModule;
3686
+ const { createRegistryStore } = __registryStoreModule;
3687
+ const { attributeName, normalizeAttributeConfig } = __attributesModule;
3688
+ const registryTypes = new Set(["signal", "handler", "server", "partial", "route", "component"]);
3689
+
3690
+ function defineApp(initial) {
3691
+ const registry = createRegistryStore(undefined, { target: "browser" });
3692
+ const runtimes = new Set();
3693
+
3694
+ const app = {
3695
+ registry,
3696
+
3697
+ use(typeOrModule, entries) {
3698
+ const normalized = normalizeUse(typeOrModule, entries);
3699
+ appendDeclarations(registry, normalized);
3700
+ for (const runtime of runtimes) {
3701
+ runtime._applyUse(normalized);
3702
+ }
3703
+ return app;
3704
+ },
3705
+
3706
+ snapshot() {
3707
+ return registry.rawSnapshot();
3708
+ },
3709
+
3710
+ start(options = {}) {
3711
+ const runtime = createApp(app, options).start();
3712
+ app.runtime = runtime;
3713
+ return runtime;
3714
+ },
3715
+
3716
+ _attach(runtime) {
3717
+ runtimes.add(runtime);
3718
+ return () => app._detach(runtime);
3719
+ },
3720
+
3721
+ _detach(runtime) {
3722
+ runtimes.delete(runtime);
3723
+ }
3724
+ };
3725
+
3726
+ if (initial) {
3727
+ app.use(initial);
3728
+ }
3729
+
3730
+ return app;
3731
+ }
3732
+
3733
+ function createApp(appOrDefinition = Async, options = {}) {
3734
+ const app = isAppHub(appOrDefinition) ? appOrDefinition : defineApp(appOrDefinition ?? {});
3735
+ const target = options.target ?? "browser";
3736
+ const attributes = normalizeAttributeConfig(options.attributes);
3737
+ const registry = options.registry ?? app.registry.view({ target });
3738
+ const signals = options.signals ?? createSignalRegistry(undefined, { registry, type: "signal" });
3739
+ const handlers = options.handlers ?? createHandlerRegistry(undefined, { registry, type: "handler" });
3740
+ const serverCache = createCacheRegistry(undefined, { registry, type: "cache.server" });
3741
+ const browserCache = createCacheRegistry(undefined, { registry, type: "cache.browser" });
3742
+ const server = options.server ?? createServerRegistry(undefined, { registry, type: "server" });
3743
+ const partials = options.partials ?? createPartialRegistry(undefined, { registry, type: "partial" });
3744
+ const routes = options.routes ?? createRouteRegistry(undefined, { registry, type: "route" });
3745
+ const components = options.components ?? createComponentRegistry(undefined, { registry, type: "component" });
3746
+ let loader = options.loader;
3747
+ let router = options.router;
3748
+ let detach = () => {};
3749
+ let started = false;
3750
+ let destroyed = false;
3751
+
3752
+ applySnapshot(signals, browserCache, options.snapshot);
3753
+ attachServerCache(server, serverCache);
3754
+
3755
+ const runtime = {
3756
+ app,
3757
+ registry,
3758
+ target,
3759
+ signals,
3760
+ handlers,
3761
+ server,
3762
+ partials,
3763
+ routes,
3764
+ components,
3765
+ browser: {
3766
+ cache: browserCache
3767
+ },
3768
+ loader,
3769
+ router,
3770
+ attributes,
3771
+
3772
+ start() {
3773
+ assertActive();
3774
+ if (started) {
3775
+ return runtime;
3776
+ }
3777
+ started = true;
3778
+
3779
+ if (target !== "server") {
3780
+ loader = loader ?? Loader({
3781
+ root: options.root,
3782
+ signals,
3783
+ handlers,
3784
+ server,
3785
+ cache: browserCache,
3786
+ attributes
3787
+ });
3788
+ runtime.loader = loader;
3789
+
3790
+ configureServerContext({ cache: browserCache });
3791
+ signals._setContext?.({ server, loader, cache: browserCache });
3792
+
3793
+ loader.start();
3794
+
3795
+ if (router !== false && (router || shouldStartRouter(routes, options))) {
3796
+ router = router ?? createRouter({
3797
+ mode: options.mode ?? "ssr-spa",
3798
+ root: options.root,
3799
+ boundary: options.boundary ?? "route",
3800
+ routes,
3801
+ loader,
3802
+ signals,
3803
+ handlers,
3804
+ server,
3805
+ cache: browserCache,
3806
+ partials,
3807
+ fetch: options.fetch,
3808
+ routeEndpoint: options.routeEndpoint,
3809
+ attributes
3810
+ });
3811
+ runtime.router = router;
3812
+ loader.router = router;
3813
+ configureServerContext({ cache: browserCache, router });
3814
+ router.start();
3815
+ }
3816
+ } else {
3817
+ configureServerContext({ cache: serverCache });
3818
+ signals._setContext?.({ server, cache: serverCache });
3819
+ }
3820
+
3821
+ return runtime;
3822
+ },
3823
+
3824
+ use(typeOrModule, entries) {
3825
+ app.use(typeOrModule, entries);
3826
+ return runtime;
3827
+ },
3828
+
3829
+ async render(url) {
3830
+ assertActive();
3831
+ configureServerContext({ cache: serverCache });
3832
+ signals._setContext?.({ server, cache: serverCache });
3833
+ const matched = routes.match(url);
3834
+ if (!matched) {
3835
+ return {
3836
+ html: renderDocument("", { status: 404, signals, browserCache, boundary: options.boundary ?? "route", attributes }),
3837
+ status: 404,
3838
+ signals: signals.snapshot(),
3839
+ cache: { browser: browserCache.snapshot() }
3840
+ };
3841
+ }
3842
+
3843
+ const partialId = matched.route.partial;
3844
+ const result = partialId && partials.resolve(partialId)
3845
+ ? await partials.render(partialId, matched.params, {
3846
+ params: matched.params,
3847
+ route: matched.route,
3848
+ signals,
3849
+ handlers,
3850
+ server,
3851
+ cache: serverCache,
3852
+ browserCache,
3853
+ partials,
3854
+ request: options.request,
3855
+ locals: options.locals
3856
+ })
3857
+ : { html: "" };
3858
+
3859
+ if (result.signals) {
3860
+ for (const [path, value] of Object.entries(result.signals)) {
3861
+ setOrRegisterSignal(signals, path, value);
3862
+ }
3863
+ }
3864
+ if (result.cache?.browser) {
3865
+ browserCache.restore(result.cache.browser);
3866
+ }
3867
+
3868
+ const status = result.status ?? 200;
3869
+ return {
3870
+ html: renderDocument(result.html, { status, signals, browserCache, boundary: result.boundary ?? options.boundary ?? "route", attributes }),
3871
+ status,
3872
+ signals: signals.snapshot(),
3873
+ cache: { browser: browserCache.snapshot() }
3874
+ };
3875
+ },
3876
+
3877
+ destroy() {
3878
+ if (destroyed) {
3879
+ return;
3880
+ }
3881
+ destroyed = true;
3882
+ detach();
3883
+ router?.destroy?.();
3884
+ loader?.destroy?.();
3885
+ signals.destroy?.();
3886
+ },
3887
+
3888
+ _applyUse(normalized) {
3889
+ applyUseToRuntime(runtime, normalized);
3890
+ }
3891
+ };
3892
+
3893
+ server.cache = serverCache;
3894
+ runtime.server.cache = serverCache;
3895
+ detach = app._attach(runtime);
3896
+
3897
+ return runtime;
3898
+
3899
+ function configureServerContext(extra = {}) {
3900
+ const cache = isLocalServerRegistry(server) ? serverCache : extra.cache;
3901
+ server._setContext?.({
3902
+ signals,
3903
+ loader,
3904
+ router,
3905
+ cache,
3906
+ request: options.request,
3907
+ locals: options.locals
3908
+ });
3909
+ }
3910
+
3911
+ function assertActive() {
3912
+ if (destroyed) {
3913
+ throw new Error("Async app runtime has been destroyed.");
3914
+ }
3915
+ }
3916
+ }
3917
+
3918
+ const Async = defineApp();
3919
+
3920
+ function applyUseToRuntime(runtime, normalized) {
3921
+ applyRegistryUse(runtime.signals, runtime.registry, normalized.signal);
3922
+ applyRegistryUse(runtime.handlers, runtime.registry, normalized.handler);
3923
+ applyRegistryUse(runtime.server, runtime.registry, normalized.server);
3924
+ applyRegistryUse(runtime.partials, runtime.registry, normalized.partial);
3925
+ applyRegistryUse(runtime.routes, runtime.registry, normalized.route);
3926
+ applyRegistryUse(runtime.components, runtime.registry, normalized.component);
3927
+ applyRegistryUse(runtime.browser.cache, runtime.registry, normalized.cache.browser);
3928
+ applyRegistryUse(runtime.server.cache, runtime.registry, normalized.cache.server);
3929
+ }
3930
+
3931
+ function applyRegistryUse(registry, runtimeRegistry, entries) {
3932
+ if (!entries || Object.keys(entries).length === 0) {
3933
+ return;
3934
+ }
3935
+ if (registry?.registry === runtimeRegistry) {
3936
+ registry._adoptMany?.(entries);
3937
+ return;
3938
+ }
3939
+ registry?.registerMany?.(entries);
3940
+ }
3941
+
3942
+ function emptyDeclarations() {
3943
+ return {
3944
+ signal: {},
3945
+ handler: {},
3946
+ server: {},
3947
+ partial: {},
3948
+ route: {},
3949
+ component: {},
3950
+ cache: {
3951
+ browser: {},
3952
+ server: {}
3953
+ }
3954
+ };
3955
+ }
3956
+
3957
+ function normalizeUse(typeOrModule, entries) {
3958
+ const normalized = emptyDeclarations();
3959
+
3960
+ if (typeof typeOrModule === "string") {
3961
+ if (!registryTypes.has(typeOrModule)) {
3962
+ throw new Error(`Unknown Async registry type "${typeOrModule}".`);
3963
+ }
3964
+ normalized[typeOrModule] = normalizeEntries(typeOrModule, entries);
3965
+ return normalized;
3966
+ }
3967
+
3968
+ if (!typeOrModule || typeof typeOrModule !== "object") {
3969
+ throw new TypeError("Async.use(...) requires a registry type or module object.");
3970
+ }
3971
+
3972
+ for (const [type, value] of Object.entries(typeOrModule)) {
3973
+ if (type === "cache") {
3974
+ normalized.cache.browser = { ...(value?.browser ?? {}) };
3975
+ normalized.cache.server = { ...(value?.server ?? {}) };
3976
+ continue;
3977
+ }
3978
+ if (!registryTypes.has(type)) {
3979
+ throw new Error(`Unknown Async registry type "${type}".`);
3980
+ }
3981
+ normalized[type] = normalizeEntries(type, value);
3982
+ }
3983
+
3984
+ return normalized;
3985
+ }
3986
+
3987
+ function appendDeclarations(target, source) {
3988
+ for (const type of registryTypes) {
3989
+ addEntries(target, type, source[type]);
3990
+ }
3991
+ addEntries(target, "cache.browser", source.cache.browser);
3992
+ addEntries(target, "cache.server", source.cache.server);
3993
+ }
3994
+
3995
+ function addEntries(registry, type, source) {
3996
+ for (const [id, value] of Object.entries(source ?? {})) {
3997
+ registry.register(type, id, value);
3998
+ }
3999
+ }
4000
+
4001
+ function isAppHub(value) {
4002
+ return Boolean(value && typeof value.use === "function" && typeof value.snapshot === "function" && value.registry);
4003
+ }
4004
+
4005
+ function applySnapshot(signals, browserCache, snapshot = {}) {
4006
+ for (const [path, value] of Object.entries(snapshot.signals ?? {})) {
4007
+ setOrRegisterSignal(signals, path, value);
4008
+ }
4009
+ browserCache.restore(snapshot.cache?.browser);
4010
+ }
4011
+
4012
+ function setOrRegisterSignal(signals, path, value) {
4013
+ const id = String(path).split(".")[0];
4014
+ if (signals.has?.(id)) {
4015
+ signals.set(path, value);
4016
+ return;
4017
+ }
4018
+ signals.register(id, createSignal(path === id ? value : {}));
4019
+ if (path !== id) {
4020
+ signals.set(path, value);
4021
+ }
4022
+ }
4023
+
4024
+ function attachServerCache(server, cache) {
4025
+ try {
4026
+ server.cache = cache;
4027
+ } catch {
4028
+ // Proxies that reject assignment can still receive cache through _setContext.
4029
+ }
4030
+ }
4031
+
4032
+ function normalizeEntries(type, entries = {}) {
4033
+ if (type !== "signal") {
4034
+ return { ...(entries ?? {}) };
4035
+ }
4036
+ const normalized = {};
4037
+ for (const [id, value] of Object.entries(entries ?? {})) {
4038
+ normalized[id] = normalizeSignalDeclaration(value);
4039
+ }
4040
+ return normalized;
4041
+ }
4042
+
4043
+ function normalizeSignalDeclaration(value) {
4044
+ if (value && typeof value === "object" && typeof value.subscribe === "function") {
4045
+ return value;
4046
+ }
4047
+ return createSignal(value);
4048
+ }
4049
+
4050
+ function isLocalServerRegistry(server) {
4051
+ return typeof server?.registerMany === "function";
4052
+ }
4053
+
4054
+ function shouldStartRouter(routes, options) {
4055
+ return Boolean(options.routerOptions || options.mode || routes.entries().length > 0);
4056
+ }
4057
+
4058
+ function renderDocument(routeHtml, { signals, browserCache, boundary, attributes }) {
4059
+ const snapshot = {
4060
+ signals: signals.snapshot(),
4061
+ cache: {
4062
+ browser: browserCache.snapshot()
4063
+ }
4064
+ };
4065
+ const boundaryAttr = attributeName(attributes, "async", "boundary");
4066
+ const snapshotAttr = attributeName(attributes, "async", "snapshot");
4067
+ return `<section ${boundaryAttr}="${escapeAttribute(boundary)}">${routeHtml ?? ""}</section><script type="application/json" ${snapshotAttr}>${escapeScriptJson(snapshot)}</script>`;
4068
+ }
4069
+
4070
+ function escapeAttribute(value) {
4071
+ return String(value)
4072
+ .replaceAll("&", "&amp;")
4073
+ .replaceAll('"', "&quot;")
4074
+ .replaceAll("<", "&lt;");
4075
+ }
4076
+
4077
+ function escapeScriptJson(value) {
4078
+ return JSON.stringify(value).replaceAll("<", "\\u003c");
4079
+ }
4080
+ return { defineApp, createApp, Async };
4081
+ })();
4082
+
4083
+ const __delayModule = (() => {
4084
+ function delay(ms, signal) {
4085
+ if (signal?.aborted) {
4086
+ return Promise.reject(abortReason(signal));
4087
+ }
4088
+
4089
+ return new Promise((resolve, reject) => {
4090
+ let timer = setTimeout(done, ms);
4091
+
4092
+ function done() {
4093
+ timer = undefined;
4094
+ signal?.removeEventListener?.("abort", aborted);
4095
+ resolve();
4096
+ }
4097
+
4098
+ function aborted() {
4099
+ if (timer !== undefined) {
4100
+ clearTimeout(timer);
4101
+ }
4102
+ timer = undefined;
4103
+ signal?.removeEventListener?.("abort", aborted);
4104
+ reject(abortReason(signal));
4105
+ }
4106
+
4107
+ signal?.addEventListener?.("abort", aborted, { once: true });
4108
+ });
4109
+ }
4110
+
4111
+ function abortReason(signal) {
4112
+ return signal?.reason ?? new Error("Operation aborted");
4113
+ }
4114
+ return { delay };
4115
+ })();
4116
+
4117
+ const { asyncSignal: asyncSignal } = __asyncSignalModule;
4118
+ const { Async: Async } = __appModule;
4119
+ const { createApp: createApp } = __appModule;
4120
+ const { defineApp: defineApp } = __appModule;
4121
+ const { attributeName: attributeName } = __attributesModule;
4122
+ const { defineAttributeConfig: defineAttributeConfig } = __attributesModule;
4123
+ const { createCacheRegistry: createCacheRegistry } = __cacheModule;
4124
+ const { defineCache: defineCache } = __cacheModule;
4125
+ const { component: component } = __componentModule;
4126
+ const { createComponentRegistry: createComponentRegistry } = __componentModule;
4127
+ const { defineComponent: defineComponent } = __componentModule;
4128
+ const { delay: delay } = __delayModule;
4129
+ const { createHandlerRegistry: createHandlerRegistry } = __handlersModule;
4130
+ const { html: html } = __htmlModule;
4131
+ const { Loader: Loader } = __loaderModule;
4132
+ const { AsyncLoader: AsyncLoader } = __loaderModule;
4133
+ const { createPartialRegistry: createPartialRegistry } = __partialsModule;
4134
+ const { createRegistryStore: createRegistryStore } = __registryStoreModule;
4135
+ const { createRouteRegistry: createRouteRegistry } = __routerModule;
4136
+ const { createRouter: createRouter } = __routerModule;
4137
+ const { defineRoute: defineRoute } = __routerModule;
4138
+ const { route: route } = __routerModule;
4139
+ const { createServerProxy: createServerProxy } = __serverModule;
4140
+ const { createServerRegistry: createServerRegistry } = __serverModule;
4141
+ const { computed: computed } = __signalsModule;
4142
+ const { createSignal: createSignal } = __signalsModule;
4143
+ const { createSignalRegistry: createSignalRegistry } = __signalsModule;
4144
+ const { effect: effect } = __signalsModule;
4145
+ const { signal: signal } = __signalsModule;
4146
+ const api = { asyncSignal, Async, createApp, defineApp, attributeName, defineAttributeConfig, createCacheRegistry, defineCache, component, createComponentRegistry, defineComponent, delay, createHandlerRegistry, html, Loader, AsyncLoader, createPartialRegistry, createRegistryStore, createRouteRegistry, createRouter, defineRoute, route, createServerProxy, createServerRegistry, computed, createSignal, createSignalRegistry, effect, signal };
4147
+ assertNoUmdNamespaceConflicts(api, Async);
4148
+ Object.assign(Async, api);
4149
+ Async.Async = Async;
4150
+ return Async;
4151
+
4152
+ function assertNoUmdNamespaceConflicts(api, target) {
4153
+ const conflicts = Object.keys(api).filter((key) => key !== "Async" && Object.prototype.hasOwnProperty.call(target, key));
4154
+ if (conflicts.length > 0) {
4155
+ throw new Error(`UMD Async namespace export conflict: ${conflicts.join(", ")}.`);
4156
+ }
4157
+ }
4158
+ });