@source-crafter/xzor-runtime 0.1.9 → 0.1.11

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,1054 @@
1
+ // runtime/index.ts
2
+ var fineHooks;
3
+ var FINE = Symbol("xzor.fine");
4
+ var isFine = (c) => c[FINE] === true;
5
+ function setFineHooks(h) {
6
+ fineHooks = h;
7
+ }
8
+ function patchIsland(owner, app, scope, end, old, build) {
9
+ const builder = new RenderTreeBuilder(owner);
10
+ build(builder);
11
+ const next = builder.nodes;
12
+ batch(() => patchList(end.parentNode, old, next, end, owner, app, scope));
13
+ return next;
14
+ }
15
+ function disposeIsland(tree) {
16
+ for (const v of tree)
17
+ dispose(v);
18
+ }
19
+ var $isFragment = (value) => isFragment(value);
20
+ var SVG = "http://www.w3.org/2000/svg";
21
+ var $inSvg = (parent) => parent.namespaceURI === SVG && parent.nodeName !== "foreignObject";
22
+ var inSvg = $inSvg;
23
+ function fineNodes(root) {
24
+ const nodes = [];
25
+ for (let n = root.start;n !== null; n = n.nextSibling) {
26
+ nodes.push(n);
27
+ if (n === root.end)
28
+ break;
29
+ }
30
+ return nodes;
31
+ }
32
+
33
+ class Route {
34
+ path;
35
+ tag;
36
+ params;
37
+ query;
38
+ match;
39
+ #nav;
40
+ constructor(path, tag, params, query, match, nav) {
41
+ this.path = path;
42
+ this.tag = tag;
43
+ this.params = params;
44
+ this.query = query;
45
+ this.match = match;
46
+ this.#nav = nav;
47
+ }
48
+ is(tag) {
49
+ return this.tag === tag;
50
+ }
51
+ navigate(url, opts) {
52
+ if (this.#nav)
53
+ this.#nav.navigate(url, opts?.replace === true);
54
+ else
55
+ location.href = url;
56
+ }
57
+ }
58
+ var navigated = new Set;
59
+ var observers = new Set;
60
+ var EXTERNAL = /(^|\s)external(\s|$)/;
61
+ var transitions = false;
62
+ function viewTransitions(on) {
63
+ transitions = on;
64
+ }
65
+ function notifyNavigated() {
66
+ const doc = document;
67
+ if (transitions && doc.startViewTransition)
68
+ doc.startViewTransition(notifyAll).ready.catch(noop);
69
+ else
70
+ notifyAll();
71
+ }
72
+ function notifyAll() {
73
+ for (const fn of [...navigated])
74
+ fn();
75
+ for (const fn of [...observers])
76
+ fn();
77
+ }
78
+ var onPopState = () => notifyNavigated();
79
+ function onLinkClick(e) {
80
+ if (e.defaultPrevented || e.button !== 0 || e.ctrlKey || e.metaKey || e.shiftKey || e.altKey)
81
+ return;
82
+ const anchor = e.target?.closest?.("a");
83
+ const href = anchor?.getAttribute("href");
84
+ if (!anchor || href == null || href.startsWith("#"))
85
+ return;
86
+ const frame = anchor.getAttribute("target");
87
+ if (frame !== null && frame !== "_self" || anchor.hasAttribute("download"))
88
+ return;
89
+ const rel = anchor.getAttribute("rel");
90
+ if (rel !== null && EXTERNAL.test(rel))
91
+ return;
92
+ const url = new URL(href, document.baseURI);
93
+ if (url.origin !== location.origin)
94
+ return;
95
+ if (url.hash !== "" && url.pathname === location.pathname && url.search === location.search)
96
+ return;
97
+ e.preventDefault();
98
+ browserNavigation.navigate(url.href, false);
99
+ }
100
+ var currentUrl;
101
+ var browserNavigation = {
102
+ current: () => currentUrl?.href === location.href ? currentUrl : currentUrl = new URL(location.href),
103
+ navigate(url, replace) {
104
+ const href = new URL(url, document.baseURI).href;
105
+ if (replace)
106
+ history.replaceState(null, "", href);
107
+ else
108
+ history.pushState(null, "", href);
109
+ notifyNavigated();
110
+ },
111
+ subscribe(fn) {
112
+ navigated.add(fn);
113
+ if (navigated.size === 1) {
114
+ if (observers.size === 0)
115
+ window.addEventListener("popstate", onPopState);
116
+ document.addEventListener("click", onLinkClick);
117
+ }
118
+ return () => {
119
+ navigated.delete(fn);
120
+ if (navigated.size === 0) {
121
+ if (observers.size === 0)
122
+ window.removeEventListener("popstate", onPopState);
123
+ document.removeEventListener("click", onLinkClick);
124
+ }
125
+ };
126
+ },
127
+ observe(fn) {
128
+ observers.add(fn);
129
+ if (observers.size === 1 && navigated.size === 0)
130
+ window.addEventListener("popstate", onPopState);
131
+ return () => {
132
+ observers.delete(fn);
133
+ if (observers.size === 0 && navigated.size === 0)
134
+ window.removeEventListener("popstate", onPopState);
135
+ };
136
+ }
137
+ };
138
+ var testNavigation;
139
+ function setNavigationForTests(nav) {
140
+ testNavigation = nav;
141
+ }
142
+ var navigationOf = () => testNavigation ?? browserNavigation;
143
+ var ROUTER = Symbol("xzor.router");
144
+ function segmentsOf(pathname) {
145
+ const rest = pathname.startsWith("/") ? pathname.slice(1) : pathname;
146
+ return rest === "" ? [] : rest.split("/");
147
+ }
148
+ var baseFor;
149
+ var base = "";
150
+ function basePath() {
151
+ const uri = document.baseURI;
152
+ if (uri !== baseFor) {
153
+ baseFor = uri;
154
+ const path = document.querySelector("base[href]") ? new URL(uri).pathname : "/";
155
+ base = path.slice(0, path.lastIndexOf("/"));
156
+ }
157
+ return base;
158
+ }
159
+ function routePath(pathname) {
160
+ const b = basePath();
161
+ return b !== "" && (pathname === b || pathname.startsWith(`${b}/`)) ? pathname.slice(b.length) || "/" : pathname;
162
+ }
163
+ function groupsOf(re, pathname) {
164
+ re.lastIndex = 0;
165
+ const m = re.exec(pathname);
166
+ return m ? m.groups ?? {} : null;
167
+ }
168
+ function resolveRoute(router, table, sections, url) {
169
+ const path = routePath(url.pathname);
170
+ const segments = segmentsOf(path);
171
+ let page;
172
+ let params = null;
173
+ for (const entry of table) {
174
+ params = entry.match(segments);
175
+ if (params) {
176
+ page = entry;
177
+ break;
178
+ }
179
+ }
180
+ let section = -1;
181
+ let match = {};
182
+ for (let i = 0;i < sections.length && section < 0; i++) {
183
+ const s = sections[i];
184
+ if (page) {
185
+ if (s.kind === "tag") {
186
+ if (s.tag === page.tag)
187
+ section = i;
188
+ } else if (s.kind === "regex" && s.page) {
189
+ const groups = groupsOf(s.re, path);
190
+ if (groups) {
191
+ section = i;
192
+ match = groups;
193
+ }
194
+ } else if (s.kind === "pattern" && s.page) {
195
+ const own = s.match(segments);
196
+ if (own) {
197
+ section = i;
198
+ match = own;
199
+ }
200
+ }
201
+ } else if (s.kind === "regex" && !s.page) {
202
+ const groups = groupsOf(s.re, path);
203
+ if (groups) {
204
+ section = i;
205
+ match = groups;
206
+ }
207
+ } else if (s.kind === "pattern" && !s.page) {
208
+ const own = s.match(segments);
209
+ if (own) {
210
+ section = i;
211
+ params = own;
212
+ }
213
+ }
214
+ }
215
+ if (section < 0)
216
+ section = sections.findIndex((s) => s.kind === (page ? "default" : "notFound"));
217
+ router.href = url.href;
218
+ router.id++;
219
+ router.page = page;
220
+ router.params = params ?? {};
221
+ router.section = section;
222
+ router.route = new Route(path, page?.tag, router.params, url.searchParams, match, router.nav);
223
+ }
224
+ function renavigate(owner) {
225
+ if (depth === 0)
226
+ update(owner);
227
+ else
228
+ queueMicrotask(() => update(owner));
229
+ }
230
+ var noop = () => {};
231
+ var SELECTOR = /[#.[]/;
232
+ var rethrow = (err) => queueMicrotask(() => {
233
+ throw err;
234
+ });
235
+ function surface(result) {
236
+ if (result instanceof Promise)
237
+ result.catch(rethrow);
238
+ }
239
+ var wellKnown = Symbol;
240
+ function polyfillDisposeSymbols(target) {
241
+ target.dispose ??= Symbol.for("Symbol.dispose");
242
+ target.asyncDispose ??= Symbol.for("Symbol.asyncDispose");
243
+ }
244
+ polyfillDisposeSymbols(wellKnown);
245
+ var ServiceProvider = () => noop;
246
+ var Mount = (_component, _target) => noop;
247
+ var Import = (..._providers) => noop;
248
+ var Singleton = () => noop;
249
+ var Scoped = () => noop;
250
+ var Transient = () => noop;
251
+ var SharedInstance = Symbol("xzor.sharedInstance");
252
+ var mountedComponents = new WeakMap;
253
+ var elementHandlers = new WeakMap;
254
+ var FRAGMENT = Symbol("xzor.fragment");
255
+ function isFragment(value) {
256
+ return typeof value === "function" && FRAGMENT in value;
257
+ }
258
+
259
+ class ComponentBase {
260
+ static fragment(build) {
261
+ return Object.assign(build, { [FRAGMENT]: true });
262
+ }
263
+ static bindValue(e, current) {
264
+ const input = e.target;
265
+ if (typeof current === "boolean")
266
+ return input.checked;
267
+ if (typeof current === "number")
268
+ return Number(input.value);
269
+ return input.value;
270
+ }
271
+ slots = {};
272
+ onInitialized() {}
273
+ onInitializedAsync() {}
274
+ onParametersSet() {}
275
+ onParametersSetAsync() {}
276
+ onAfterRender(_firstRender) {}
277
+ onAfterRenderAsync(_firstRender) {}
278
+ shouldRender() {
279
+ return true;
280
+ }
281
+ dispose() {}
282
+ stateHasChanged() {
283
+ update(this);
284
+ }
285
+ }
286
+ var hooks = (c) => c;
287
+
288
+ class RenderTreeBuilder {
289
+ owner;
290
+ #root = [];
291
+ #open = [];
292
+ constructor(owner) {
293
+ this.owner = owner;
294
+ }
295
+ get nodes() {
296
+ return this.#root;
297
+ }
298
+ #append(node) {
299
+ (this.#open.at(-1)?.children ?? this.#root).push(node);
300
+ }
301
+ #current() {
302
+ const el = this.#open.at(-1);
303
+ if (el?.kind !== "element")
304
+ throw new Error("xzor: attribute outside of an element");
305
+ return el;
306
+ }
307
+ openElement(tag) {
308
+ const el = { kind: "element", tag, attrs: {}, events: {}, modifiers: {}, children: [] };
309
+ this.#append(el);
310
+ this.#open.push(el);
311
+ }
312
+ closeElement() {
313
+ this.#open.pop();
314
+ }
315
+ openRegion() {
316
+ const region = { kind: "region", children: [] };
317
+ this.#append(region);
318
+ this.#open.push(region);
319
+ }
320
+ openCascade(name, value, fixed) {
321
+ const region = { kind: "region", children: [], name, value, fixed: fixed === true };
322
+ this.#append(region);
323
+ this.#open.push(region);
324
+ }
325
+ closeRegion() {
326
+ this.#open.pop();
327
+ }
328
+ addRoutes(engine, table, sections) {
329
+ const owner = this.owner;
330
+ const nav = navigationOf();
331
+ let router = owner[ROUTER];
332
+ if (!router) {
333
+ router = owner[ROUTER] = {
334
+ nav: engine === "client" ? nav : undefined,
335
+ off: undefined,
336
+ onError: undefined,
337
+ caught: sections.find((s) => s.kind === "catch"),
338
+ href: "",
339
+ id: 0,
340
+ route: undefined,
341
+ section: -1,
342
+ page: undefined,
343
+ params: {},
344
+ error: undefined,
345
+ failed: false
346
+ };
347
+ const failing = router;
348
+ if (router.caught) {
349
+ router.onError = (err, id) => {
350
+ if (id !== failing.id)
351
+ return;
352
+ failing.error = err;
353
+ failing.failed = true;
354
+ queueMicrotask(() => update(owner));
355
+ };
356
+ }
357
+ if (router.nav)
358
+ router.off = router.nav.subscribe(() => renavigate(owner));
359
+ }
360
+ const state = router;
361
+ const url = nav.current();
362
+ if (url.href !== state.href || !state.route) {
363
+ state.failed = false;
364
+ state.error = undefined;
365
+ resolveRoute(state, table, sections, url);
366
+ }
367
+ const route = state.route;
368
+ const section = sections[state.section];
369
+ const page = state.page;
370
+ this.openCascade("route", route);
371
+ if (state.failed) {
372
+ state.caught.render(route, state.error)(this);
373
+ } else if (page) {
374
+ const entry = page;
375
+ const fragment = ComponentBase.fragment((b) => b.#addPage(entry, state));
376
+ if (section)
377
+ section.render(route, fragment)(this);
378
+ else
379
+ fragment(this);
380
+ } else if (section) {
381
+ section.render(route)(this);
382
+ }
383
+ this.closeRegion();
384
+ }
385
+ #addPage(entry, router) {
386
+ this.#append({
387
+ kind: "component",
388
+ type: entry.component,
389
+ props: router.params,
390
+ key: entry.family ?? entry.pattern,
391
+ onError: router.onError,
392
+ nav: router.id
393
+ });
394
+ }
395
+ addAttribute(name, value) {
396
+ const el = this.#current();
397
+ if (value === false || value == null)
398
+ delete el.attrs[name];
399
+ else
400
+ el.attrs[name] = value === true ? "" : String(value);
401
+ }
402
+ addAttributes(attrs) {
403
+ for (const [name, value] of Object.entries(attrs ?? {}))
404
+ this.addAttribute(name, value);
405
+ }
406
+ addEventModifier(event, modifier, enabled) {
407
+ const el = this.#current();
408
+ (el.modifiers[event] ??= {})[modifier] = enabled;
409
+ }
410
+ setKey(key) {
411
+ this.#current().key = key;
412
+ }
413
+ setRef(_tag, ref) {
414
+ this.#current().ref = ref;
415
+ }
416
+ addEventHandler(event, handler) {
417
+ this.#current().events[event] = handler;
418
+ }
419
+ addText(text) {
420
+ this.#append({ kind: "text", text });
421
+ }
422
+ addFine(build) {
423
+ this.#append({ kind: "fine", build });
424
+ }
425
+ addContent(value) {
426
+ if (isFragment(value)) {
427
+ this.openRegion();
428
+ value(this);
429
+ this.closeRegion();
430
+ return;
431
+ }
432
+ if (value === false || value == null)
433
+ value = "";
434
+ this.#append({ kind: "text", text: String(value) });
435
+ }
436
+ addComponent(type, props, slots, extra) {
437
+ this.#append({
438
+ kind: "component",
439
+ type,
440
+ props,
441
+ slots,
442
+ key: extra?.key,
443
+ ref: extra?.ref
444
+ });
445
+ }
446
+ callback(handler) {
447
+ const owner = this.owner;
448
+ if (isFine(owner))
449
+ return (arg) => {
450
+ const result = handler(arg);
451
+ fineHooks.handled(owner, result);
452
+ return result;
453
+ };
454
+ return (arg) => {
455
+ const result = handler(arg);
456
+ update(owner);
457
+ if (result instanceof Promise)
458
+ result.then(() => update(owner));
459
+ return result;
460
+ };
461
+ }
462
+ }
463
+ function setAttr(el, name, value) {
464
+ if (name.charCodeAt(0) === 45 && name.charCodeAt(1) === 45)
465
+ return el.style.setProperty(name, value);
466
+ el.setAttribute(name, value);
467
+ if (name === "value" && "value" in el)
468
+ el.value = value;
469
+ if (name === "checked" && "checked" in el)
470
+ el.checked = true;
471
+ }
472
+ function removeAttr(el, name) {
473
+ if (name.charCodeAt(0) === 45 && name.charCodeAt(1) === 45)
474
+ return void el.style.removeProperty(name);
475
+ el.removeAttribute(name);
476
+ if (name === "value" && "value" in el)
477
+ el.value = "";
478
+ if (name === "checked" && "checked" in el)
479
+ el.checked = false;
480
+ }
481
+ var observer;
482
+ function setRenderObserver(o) {
483
+ observer = o;
484
+ }
485
+ function render(component) {
486
+ const builder = new RenderTreeBuilder(component);
487
+ if (observer === undefined)
488
+ hooks(component).buildRenderTree(builder);
489
+ else {
490
+ observer.begin(component);
491
+ try {
492
+ hooks(component).buildRenderTree(builder);
493
+ } finally {
494
+ observer.end(component);
495
+ }
496
+ }
497
+ return builder.nodes;
498
+ }
499
+ var depth = 0;
500
+ var pass = 0;
501
+ var afterRender = [];
502
+ function batch(fn) {
503
+ if (depth++ === 0)
504
+ pass++;
505
+ try {
506
+ fn();
507
+ } finally {
508
+ if (--depth === 0)
509
+ for (const done of afterRender.splice(0))
510
+ done();
511
+ }
512
+ }
513
+ function settle(component, result, node) {
514
+ if (!(result instanceof Promise))
515
+ return;
516
+ result.then(() => update(component), node?.onError && ((err) => fail(node, err)));
517
+ }
518
+ function fail(v, err) {
519
+ if (v.onError)
520
+ v.onError(err, v.mounted?.nav ?? v.nav ?? 0);
521
+ else
522
+ rethrow(err);
523
+ }
524
+ function parametersSet(component) {
525
+ hooks(component).onParametersSet();
526
+ settle(component, hooks(component).onParametersSetAsync());
527
+ }
528
+ var cascadesOf = (type) => type.cascades;
529
+ var fields = (c) => c;
530
+ function findCascade(scope, name) {
531
+ for (let s = scope;s; s = s.parent)
532
+ if (s.name === name)
533
+ return s;
534
+ return;
535
+ }
536
+ function resolveCascades(instance, type, scope) {
537
+ const list = cascadesOf(type);
538
+ if (!list)
539
+ return;
540
+ for (const [prop, name] of list) {
541
+ const record = findCascade(scope, name);
542
+ if (!record)
543
+ continue;
544
+ fields(instance)[prop] = record.value;
545
+ if (!record.fixed)
546
+ (record.consumers ??= new Set).add(instance);
547
+ }
548
+ }
549
+ function unsubscribeCascades(instance, type, scope) {
550
+ const list = cascadesOf(type);
551
+ if (!list)
552
+ return;
553
+ for (const [, name] of list)
554
+ findCascade(scope, name)?.consumers?.delete(instance);
555
+ }
556
+ function assignCascade(record, consumers) {
557
+ for (const consumer of consumers) {
558
+ for (const [prop, name] of cascadesOf(consumer.constructor)) {
559
+ if (name === record.name)
560
+ fields(consumer)[prop] = record.value;
561
+ }
562
+ }
563
+ }
564
+ function notifyCascade(consumers) {
565
+ for (const consumer of consumers) {
566
+ const m = mountedComponents.get(consumer);
567
+ if (!m || m.pass === pass)
568
+ continue;
569
+ m.pass = pass;
570
+ try {
571
+ parametersSet(consumer);
572
+ update(consumer);
573
+ } catch (err) {
574
+ rethrow(err);
575
+ }
576
+ }
577
+ }
578
+ function queueAfterRender(component, first) {
579
+ afterRender.push(() => {
580
+ hooks(component).onAfterRender(first);
581
+ hooks(component).onAfterRenderAsync(first);
582
+ });
583
+ }
584
+ function update(component) {
585
+ batch(() => {
586
+ const m = mountedComponents.get(component);
587
+ const parent = m?.anchor.parentNode;
588
+ if (!m || !parent || !hooks(component).shouldRender())
589
+ return;
590
+ const next = render(component);
591
+ patchList(parent, m.tree, next, m.anchor, component, m.app, m.scope);
592
+ m.tree = next;
593
+ queueAfterRender(component, false);
594
+ });
595
+ }
596
+ function mountComponent(component, app, scope, into, node) {
597
+ hooks(component).onInitialized();
598
+ settle(component, hooks(component).onInitializedAsync(), node);
599
+ parametersSet(component);
600
+ const tree = render(component);
601
+ const mounted = into ?? { tree, anchor: document.createComment(""), app, scope, latest: undefined, pass };
602
+ if (into)
603
+ into.tree = tree;
604
+ mountedComponents.set(component, mounted);
605
+ const nodes = tree.flatMap((v) => create(v, component, app, scope));
606
+ queueAfterRender(component, true);
607
+ if (!into)
608
+ nodes.push(mounted.anchor);
609
+ return { nodes, mounted };
610
+ }
611
+ function construct(type, app) {
612
+ const inject = type.inject;
613
+ if (!inject?.length)
614
+ return new type;
615
+ const made = app?.createComponent?.(type);
616
+ if (made)
617
+ return made;
618
+ if (app)
619
+ throw new Error(`xzor: ${type.name} is not registered with the application`);
620
+ throw new Error(`xzor: ${type.name} needs injected services (${inject.join(", ")}) but has no application`);
621
+ }
622
+ function arrive(mounted, instance) {
623
+ const v = mounted.latest;
624
+ const parent = mounted.anchor.parentNode;
625
+ mounted.latest = undefined;
626
+ if (!v || !parent) {
627
+ disposeInstance(instance);
628
+ return;
629
+ }
630
+ try {
631
+ batch(() => {
632
+ applyProps(instance, v);
633
+ resolveCascades(instance, v.type, mounted.scope);
634
+ v.ref?.(instance);
635
+ for (const node of mountComponent(instance, mounted.app, mounted.scope, mounted, v).nodes)
636
+ parent.insertBefore(node, mounted.anchor);
637
+ v.instance = instance;
638
+ });
639
+ } catch (err) {
640
+ fail(v, err);
641
+ if (!v.instance) {
642
+ mountedComponents.delete(instance);
643
+ observer?.dispose(instance);
644
+ unsubscribeCascades(instance, v.type, mounted.scope);
645
+ const partial = mounted.tree;
646
+ mounted.tree = [];
647
+ for (const node of partial) {
648
+ try {
649
+ dispose(node);
650
+ } catch (disposeErr) {
651
+ rethrow(disposeErr);
652
+ }
653
+ }
654
+ }
655
+ }
656
+ }
657
+ function applyProps(instance, v) {
658
+ Object.assign(instance, v.props);
659
+ if (v.slots)
660
+ instance.slots = v.slots;
661
+ }
662
+ function propsChanged(o, n) {
663
+ if (n.slots)
664
+ return true;
665
+ for (const [key, value] of Object.entries(n.props)) {
666
+ if (!Object.is(o.props[key], value) || typeof value === "object" && value !== null)
667
+ return true;
668
+ }
669
+ return false;
670
+ }
671
+ var eventNames = (v) => new Set([...Object.keys(v.events), ...Object.keys(v.modifiers)]);
672
+ function listen(el, event) {
673
+ elementHandlers.get(el).listening.add(event);
674
+ el.addEventListener(event, (e) => {
675
+ const record = elementHandlers.get(el);
676
+ if (!record)
677
+ return;
678
+ const mods = record.modifiers[event];
679
+ if (mods?.preventDefault)
680
+ e.preventDefault();
681
+ if (mods?.stopPropagation)
682
+ e.stopPropagation();
683
+ const handler = record.events[event];
684
+ if (!handler)
685
+ return;
686
+ const result = handler(e);
687
+ if (isFine(record.owner))
688
+ return fineHooks.handled(record.owner, result);
689
+ update(record.owner);
690
+ if (result instanceof Promise)
691
+ result.then(() => update(record.owner));
692
+ });
693
+ }
694
+ function create(v, owner, app, scope, svg = false) {
695
+ switch (v.kind) {
696
+ case "text":
697
+ v.dom = document.createTextNode(v.text);
698
+ return [v.dom];
699
+ case "element": {
700
+ const el = svg || v.tag === "svg" ? document.createElementNS(SVG, v.tag) : document.createElement(v.tag);
701
+ for (const [name, value] of Object.entries(v.attrs))
702
+ setAttr(el, name, value);
703
+ elementHandlers.set(el, { owner, events: v.events, modifiers: v.modifiers, listening: new Set });
704
+ for (const event of eventNames(v))
705
+ listen(el, event);
706
+ const inner = inSvg(el);
707
+ for (const child of v.children)
708
+ el.append(...create(child, owner, app, scope, inner));
709
+ v.dom = el;
710
+ v.ref?.(el);
711
+ return [el];
712
+ }
713
+ case "region": {
714
+ v.anchor = document.createComment("");
715
+ const inner = v.name === undefined ? scope : v.cascade = { name: v.name, value: v.value, fixed: v.fixed === true, consumers: undefined, parent: scope };
716
+ return [...v.children.flatMap((c) => create(c, owner, app, inner, svg)), v.anchor];
717
+ }
718
+ case "component": {
719
+ if (!v.onError)
720
+ return createComponent(v, app, scope);
721
+ try {
722
+ return createComponent(v, app, scope);
723
+ } catch (err) {
724
+ return failedComponent(v, err, app, scope);
725
+ }
726
+ }
727
+ case "fine": {
728
+ if (!fineHooks)
729
+ throw new Error("xzor: a @fine component needs @source-crafter/xzor-runtime/model");
730
+ return fineNodes(v.root = fineHooks.create(v.build, owner, app, scope));
731
+ }
732
+ }
733
+ }
734
+ function createComponent(v, app, scope) {
735
+ const made = construct(v.type, app);
736
+ if (made instanceof Promise) {
737
+ const mounted2 = v.mounted = {
738
+ tree: [],
739
+ anchor: document.createComment(""),
740
+ app,
741
+ scope,
742
+ latest: v,
743
+ pass
744
+ };
745
+ made.then((instance) => arrive(mounted2, instance), (err) => {
746
+ const latest = mounted2.latest;
747
+ mounted2.latest = undefined;
748
+ if (latest)
749
+ fail(latest, err);
750
+ else if (!v.onError)
751
+ rethrow(err);
752
+ });
753
+ return [mounted2.anchor];
754
+ }
755
+ v.instance = made;
756
+ applyProps(made, v);
757
+ resolveCascades(made, v.type, scope);
758
+ v.ref?.(made);
759
+ const { nodes, mounted } = mountComponent(made, app, scope, undefined, v);
760
+ v.mounted = mounted;
761
+ return nodes;
762
+ }
763
+ function failedComponent(v, err, app, scope) {
764
+ const instance = v.instance;
765
+ v.instance = undefined;
766
+ if (instance) {
767
+ const partial = mountedComponents.get(instance);
768
+ mountedComponents.delete(instance);
769
+ observer?.dispose(instance);
770
+ unsubscribeCascades(instance, v.type, scope);
771
+ for (const node of partial?.tree ?? []) {
772
+ try {
773
+ dispose(node);
774
+ } catch (disposeErr) {
775
+ rethrow(disposeErr);
776
+ }
777
+ }
778
+ }
779
+ v.mounted = { tree: [], anchor: document.createComment(""), app, scope, latest: undefined, pass };
780
+ fail(v, err);
781
+ return [v.mounted.anchor];
782
+ }
783
+ function nodesOf(v) {
784
+ switch (v.kind) {
785
+ case "text":
786
+ case "element":
787
+ return [v.dom];
788
+ case "region":
789
+ return [...v.children.flatMap(nodesOf), v.anchor];
790
+ case "component":
791
+ return [...v.mounted.tree.flatMap(nodesOf), v.mounted.anchor];
792
+ case "fine":
793
+ return fineNodes(v.root);
794
+ }
795
+ }
796
+ function same(o, n) {
797
+ if (o.kind !== n.kind)
798
+ return false;
799
+ if (o.kind === "element")
800
+ return o.tag === n.tag && o.key === n.key;
801
+ if (o.kind === "component")
802
+ return o.type === n.type && o.key === n.key;
803
+ if (o.kind === "region")
804
+ return o.name === n.name && o.fixed === n.fixed;
805
+ return true;
806
+ }
807
+ function dispose(v) {
808
+ switch (v.kind) {
809
+ case "element":
810
+ v.children.forEach(dispose);
811
+ return;
812
+ case "region":
813
+ if (v.cascade)
814
+ v.cascade.consumers = undefined;
815
+ v.children.forEach(dispose);
816
+ return;
817
+ case "component":
818
+ if (!v.instance) {
819
+ if (v.mounted)
820
+ v.mounted.latest = undefined;
821
+ return;
822
+ }
823
+ v.mounted?.tree.forEach(dispose);
824
+ mountedComponents.delete(v.instance);
825
+ unsubscribeCascades(v.instance, v.type, v.mounted?.scope);
826
+ disposeInstance(v.instance);
827
+ return;
828
+ case "fine":
829
+ fineHooks.dispose(v.root);
830
+ return;
831
+ }
832
+ }
833
+ function disposeInstance(c) {
834
+ observer?.dispose(c);
835
+ c[ROUTER]?.off?.();
836
+ hooks(c).dispose();
837
+ const d = c;
838
+ d[wellKnown.dispose]?.();
839
+ surface(d[wellKnown.asyncDispose]?.());
840
+ }
841
+ function remove(parent, v) {
842
+ for (const node of nodesOf(v))
843
+ parent.removeChild(node);
844
+ dispose(v);
845
+ }
846
+ function patch(o, n, owner, app, scope) {
847
+ switch (n.kind) {
848
+ case "text": {
849
+ const dom = n.dom = o.dom;
850
+ if (dom.data !== n.text)
851
+ dom.data = n.text;
852
+ return;
853
+ }
854
+ case "element": {
855
+ const old = o;
856
+ const el = n.dom = old.dom;
857
+ for (const name of Object.keys(old.attrs))
858
+ if (!(name in n.attrs))
859
+ removeAttr(el, name);
860
+ for (const [name, value] of Object.entries(n.attrs)) {
861
+ const live = name === "value" ? el.value : undefined;
862
+ if (old.attrs[name] !== value || live !== undefined && live !== value)
863
+ setAttr(el, name, value);
864
+ }
865
+ const record = elementHandlers.get(el);
866
+ record.events = n.events;
867
+ record.modifiers = n.modifiers;
868
+ for (const event of eventNames(n))
869
+ if (!record.listening.has(event))
870
+ listen(el, event);
871
+ patchList(el, old.children, n.children, null, owner, app, scope);
872
+ n.ref?.(el);
873
+ return;
874
+ }
875
+ case "region": {
876
+ const old = o;
877
+ const anchor = n.anchor = old.anchor;
878
+ const record = old.cascade;
879
+ let subscribers;
880
+ let inner = scope;
881
+ if (record) {
882
+ record.parent = scope;
883
+ n.cascade = record;
884
+ inner = record;
885
+ if (!record.fixed && !Object.is(record.value, n.value)) {
886
+ record.value = n.value;
887
+ if (record.consumers?.size) {
888
+ subscribers = [...record.consumers];
889
+ assignCascade(record, subscribers);
890
+ }
891
+ }
892
+ }
893
+ patchList(anchor.parentNode, old.children, n.children, anchor, owner, app, inner);
894
+ if (subscribers)
895
+ notifyCascade(subscribers);
896
+ return;
897
+ }
898
+ case "component": {
899
+ const old = o;
900
+ n.mounted = old.mounted;
901
+ if (!old.instance) {
902
+ if (n.mounted.latest)
903
+ n.mounted.latest = n;
904
+ return;
905
+ }
906
+ const instance = n.instance = old.instance;
907
+ const changed = n.nav !== old.nav || propsChanged(old, n);
908
+ if (n.nav !== undefined)
909
+ n.mounted.nav = n.nav;
910
+ applyProps(instance, n);
911
+ n.ref?.(instance);
912
+ if (changed) {
913
+ n.mounted.pass = pass;
914
+ parametersSet(instance);
915
+ update(instance);
916
+ }
917
+ return;
918
+ }
919
+ case "fine": {
920
+ fineHooks.refresh(n.root = o.root);
921
+ return;
922
+ }
923
+ }
924
+ }
925
+ var keyOf = (v) => v && (v.kind === "element" || v.kind === "component") ? v.key : undefined;
926
+ function patchList(parent, old, next, end, owner, app, scope) {
927
+ if (old.some((v) => keyOf(v) != null) || next.some((v) => keyOf(v) != null)) {
928
+ patchKeyed(parent, old, next, end, owner, app, scope);
929
+ return;
930
+ }
931
+ for (let i = 0;i < next.length; i++) {
932
+ const n = next[i];
933
+ const o = old[i];
934
+ if (o && same(o, n)) {
935
+ patch(o, n, owner, app, scope);
936
+ continue;
937
+ }
938
+ const ref = o ? nodesOf(o)[0] : end;
939
+ for (const node of create(n, owner, app, scope, inSvg(parent)))
940
+ parent.insertBefore(node, ref);
941
+ if (o)
942
+ remove(parent, o);
943
+ }
944
+ for (const o of old.slice(next.length))
945
+ remove(parent, o);
946
+ }
947
+ function patchKeyed(parent, old, next, end, owner, app, scope) {
948
+ const byKey = new Map;
949
+ for (const o of old)
950
+ if (keyOf(o) != null)
951
+ byKey.set(keyOf(o), o);
952
+ const used = new Set;
953
+ const matches = next.map((n, i) => {
954
+ const key = keyOf(n);
955
+ const o = key != null ? byKey.get(key) : keyOf(old[i]) == null ? old[i] : undefined;
956
+ if (o && !used.has(o) && same(o, n)) {
957
+ used.add(o);
958
+ return o;
959
+ }
960
+ return;
961
+ });
962
+ for (const o of old)
963
+ if (!used.has(o))
964
+ remove(parent, o);
965
+ let ref = end;
966
+ for (let i = next.length - 1;i >= 0; i--) {
967
+ const n = next[i];
968
+ const o = matches[i];
969
+ let nodes;
970
+ if (o) {
971
+ patch(o, n, owner, app, scope);
972
+ nodes = nodesOf(n);
973
+ } else {
974
+ nodes = create(n, owner, app, scope, inSvg(parent));
975
+ }
976
+ for (let j = nodes.length - 1;j >= 0; j--) {
977
+ const node = nodes[j];
978
+ if (node.parentNode !== parent || node.nextSibling !== ref)
979
+ parent.insertBefore(node, ref);
980
+ ref = node;
981
+ }
982
+ }
983
+ }
984
+ function resolveTarget(target) {
985
+ if (typeof target !== "string")
986
+ return target;
987
+ const found = SELECTOR.test(target) ? document.querySelector(target) : document.getElementById(target);
988
+ if (!found)
989
+ throw new Error(`xzor: mount target "${target}" not found`);
990
+ return found;
991
+ }
992
+ function mount(type, target, app) {
993
+ const host = resolveTarget(target);
994
+ const root = construct(type, app);
995
+ if (root instanceof Promise)
996
+ throw new Error(`xzor: root component ${type.name} cannot be asynchronous`);
997
+ batch(() => host.replaceChildren(...mountComponent(root, app, undefined).nodes));
998
+ surface(app?.onStarted?.());
999
+ return root;
1000
+ }
1001
+
1002
+ class NavLink extends ComponentBase {
1003
+ href = "";
1004
+ activeClass = "active";
1005
+ match = "prefix";
1006
+ #path = "";
1007
+ #for;
1008
+ #target = null;
1009
+ #off;
1010
+ onInitialized() {
1011
+ const nav = navigationOf();
1012
+ this.#path = nav.current().pathname;
1013
+ const fn = () => {
1014
+ const path = nav.current().pathname;
1015
+ if (path === this.#path)
1016
+ return;
1017
+ this.#path = path;
1018
+ this.stateHasChanged();
1019
+ };
1020
+ this.#off = nav.observe ? nav.observe(fn) : nav.subscribe(fn);
1021
+ }
1022
+ dispose() {
1023
+ this.#off?.();
1024
+ }
1025
+ #active() {
1026
+ if (this.#for !== this.href) {
1027
+ this.#for = this.href;
1028
+ const url = new URL(this.href, document.baseURI);
1029
+ this.#target = url.origin === location.origin ? url.pathname : null;
1030
+ }
1031
+ const target = this.#target;
1032
+ if (target === null)
1033
+ return false;
1034
+ const path = this.#path;
1035
+ if (this.match === "exact" || target === `${basePath()}/`)
1036
+ return path === target;
1037
+ return path === target || path.startsWith(target.endsWith("/") ? target : `${target}/`);
1038
+ }
1039
+ buildRenderTree(b) {
1040
+ b.openElement("a");
1041
+ b.addAttribute("href", this.href);
1042
+ if (this.#active()) {
1043
+ b.addAttribute("class", this.activeClass);
1044
+ b.addAttribute("aria-current", "page");
1045
+ }
1046
+ b.addContent(this.slots.default?.() ?? null);
1047
+ b.closeElement();
1048
+ }
1049
+ }
1050
+
1051
+ export { FINE, setFineHooks, patchIsland, disposeIsland, $isFragment, SVG, $inSvg, Route, viewTransitions, setNavigationForTests, basePath, polyfillDisposeSymbols, ServiceProvider, Mount, Import, Singleton, Scoped, Transient, SharedInstance, ComponentBase, RenderTreeBuilder, setRenderObserver, mount, NavLink };
1052
+
1053
+ //# debugId=085B9300273EBAC364756E2164756E21
1054
+ //# sourceMappingURL=index-k290gfdd.js.map