@askrjs/askr 0.0.3 → 0.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (71) hide show
  1. package/README.md +8 -5
  2. package/dist/chunk-64C7W2AE.js +43 -0
  3. package/dist/chunk-64C7W2AE.js.map +1 -0
  4. package/dist/chunk-6FLMH4EL.js +124 -0
  5. package/dist/chunk-6FLMH4EL.js.map +1 -0
  6. package/dist/{chunk-JHOGWTAW.js → chunk-FJUXFA72.js} +2 -2
  7. package/dist/chunk-FJUXFA72.js.map +1 -0
  8. package/dist/{chunk-H3NSVHA7.js → chunk-VRAEBIJ3.js} +7 -5
  9. package/dist/chunk-VRAEBIJ3.js.map +1 -0
  10. package/dist/chunk-WTFWRSHB.js +98 -0
  11. package/dist/chunk-WTFWRSHB.js.map +1 -0
  12. package/dist/chunk-XHKZGJE3.js +2907 -0
  13. package/dist/chunk-XHKZGJE3.js.map +1 -0
  14. package/dist/chunk-Z5ZSTLYF.js +420 -0
  15. package/dist/chunk-Z5ZSTLYF.js.map +1 -0
  16. package/dist/fx/index.cjs +1193 -0
  17. package/dist/fx/index.cjs.map +1 -0
  18. package/dist/fx/index.d.cts +186 -0
  19. package/dist/fx/index.d.ts +186 -0
  20. package/dist/fx/index.js +418 -0
  21. package/dist/fx/index.js.map +1 -0
  22. package/dist/index.cjs +1369 -2525
  23. package/dist/index.cjs.map +1 -1
  24. package/dist/index.d.cts +74 -407
  25. package/dist/index.d.ts +74 -407
  26. package/dist/index.js +63 -803
  27. package/dist/index.js.map +1 -1
  28. package/dist/jsx/jsx-dev-runtime.cjs +1 -1
  29. package/dist/jsx/jsx-dev-runtime.cjs.map +1 -1
  30. package/dist/jsx/jsx-dev-runtime.d.cts +3 -2
  31. package/dist/jsx/jsx-dev-runtime.d.ts +3 -2
  32. package/dist/jsx/jsx-dev-runtime.js +1 -1
  33. package/dist/jsx/jsx-runtime.d.cts +2 -1
  34. package/dist/jsx/jsx-runtime.d.ts +2 -1
  35. package/dist/jsx/jsx-runtime.js +1 -1
  36. package/dist/{types-DLTViI21.d.cts → jsx-AzPM8gMS.d.cts} +6 -21
  37. package/dist/{types-DLTViI21.d.ts → jsx-AzPM8gMS.d.ts} +6 -21
  38. package/dist/{navigate-CZEUXFPM.js → navigate-LUVYHYZZ.js} +5 -4
  39. package/dist/resources/index.cjs +1200 -0
  40. package/dist/resources/index.cjs.map +1 -0
  41. package/dist/resources/index.d.cts +21 -0
  42. package/dist/resources/index.d.ts +21 -0
  43. package/dist/resources/index.js +278 -0
  44. package/dist/resources/index.js.map +1 -0
  45. package/dist/{route-USEXGOBT.js → route-BCND6MPK.js} +4 -3
  46. package/dist/{chunk-2ONGHQ7Z.js → router/index.cjs} +775 -643
  47. package/dist/router/index.cjs.map +1 -0
  48. package/dist/router/index.d.cts +64 -0
  49. package/dist/router/index.d.ts +64 -0
  50. package/dist/router/index.js +49 -0
  51. package/dist/router/index.js.map +1 -0
  52. package/dist/router-DaGtH1Sq.d.cts +36 -0
  53. package/dist/router-DaGtH1Sq.d.ts +36 -0
  54. package/dist/ssr/index.cjs +4059 -0
  55. package/dist/ssr/index.cjs.map +1 -0
  56. package/dist/ssr/index.d.cts +123 -0
  57. package/dist/ssr/index.d.ts +123 -0
  58. package/dist/{chunk-OFW6DFBM.js → ssr/index.js} +202 -252
  59. package/dist/ssr/index.js.map +1 -0
  60. package/dist/types-CZ5sWur5.d.cts +23 -0
  61. package/dist/types-CZHOAiC1.d.ts +23 -0
  62. package/package.json +19 -6
  63. package/src/jsx/types.ts +4 -17
  64. package/dist/chunk-2ONGHQ7Z.js.map +0 -1
  65. package/dist/chunk-H3NSVHA7.js.map +0 -1
  66. package/dist/chunk-JHOGWTAW.js.map +0 -1
  67. package/dist/chunk-OFW6DFBM.js.map +0 -1
  68. package/dist/ssr-QJ5NTQR6.js +0 -28
  69. package/dist/ssr-QJ5NTQR6.js.map +0 -1
  70. /package/dist/{navigate-CZEUXFPM.js.map → navigate-LUVYHYZZ.js.map} +0 -0
  71. /package/dist/{route-USEXGOBT.js.map → route-BCND6MPK.js.map} +0 -0
package/dist/index.cjs CHANGED
@@ -85,36 +85,11 @@ function isBulkCommitActive() {
85
85
  function isSchedulerExecuting() {
86
86
  return globalScheduler.isExecuting();
87
87
  }
88
- function scheduleEventHandler(handler) {
89
- return (event) => {
90
- globalScheduler.setInHandler(true);
91
- try {
92
- handler.call(null, event);
93
- } catch (error) {
94
- logger.error("[Askr] Event handler error:", error);
95
- } finally {
96
- globalScheduler.setInHandler(false);
97
- const state2 = globalScheduler.getState();
98
- if ((state2.queueLength ?? 0) > 0 && !state2.running) {
99
- queueMicrotask(() => {
100
- try {
101
- if (!globalScheduler.isExecuting()) globalScheduler.flush();
102
- } catch (err) {
103
- setTimeout(() => {
104
- throw err;
105
- });
106
- }
107
- });
108
- }
109
- }
110
- };
111
- }
112
88
  var MAX_FLUSH_DEPTH, Scheduler, globalScheduler;
113
89
  var init_scheduler = __esm({
114
90
  "src/runtime/scheduler.ts"() {
115
91
  "use strict";
116
92
  init_invariant();
117
- init_logger();
118
93
  MAX_FLUSH_DEPTH = 50;
119
94
  Scheduler = class {
120
95
  constructor() {
@@ -136,9 +111,9 @@ var init_scheduler = __esm({
136
111
  // Keep a lightweight taskCount for compatibility/diagnostics
137
112
  this.taskCount = 0;
138
113
  }
139
- enqueue(task2) {
114
+ enqueue(task) {
140
115
  assertSchedulingPrecondition(
141
- typeof task2 === "function",
116
+ typeof task === "function",
142
117
  "enqueue() requires a function"
143
118
  );
144
119
  if (isBulkCommitActive() && !this.allowSyncProgress) {
@@ -149,7 +124,7 @@ var init_scheduler = __esm({
149
124
  }
150
125
  return;
151
126
  }
152
- this.q.push(task2);
127
+ this.q.push(task);
153
128
  this.taskCount++;
154
129
  if (!this.running && !this.kickScheduled && !this.inHandler && !isBulkCommitActive()) {
155
130
  this.kickScheduled = true;
@@ -190,10 +165,10 @@ var init_scheduler = __esm({
190
165
  `[Scheduler] exceeded MAX_FLUSH_DEPTH (${MAX_FLUSH_DEPTH}). Likely infinite update loop.`
191
166
  );
192
167
  }
193
- const task2 = this.q[this.head++];
168
+ const task = this.q[this.head++];
194
169
  try {
195
170
  this.executionDepth++;
196
- task2();
171
+ task();
197
172
  this.executionDepth--;
198
173
  } catch (err) {
199
174
  if (this.executionDepth > 0) this.executionDepth = 0;
@@ -211,14 +186,10 @@ var init_scheduler = __esm({
211
186
  this.head = 0;
212
187
  } else if (this.head > 0) {
213
188
  const remaining = this.q.length - this.head;
214
- if (this.head > 1024 || this.head > remaining) {
215
- this.q = this.q.slice(this.head);
216
- } else {
217
- for (let i = 0; i < remaining; i++) {
218
- this.q[i] = this.q[this.head + i];
219
- }
220
- this.q.length = remaining;
189
+ for (let i = 0; i < remaining; i++) {
190
+ this.q[i] = this.q[this.head + i];
221
191
  }
192
+ this.q.length = remaining;
222
193
  this.head = 0;
223
194
  }
224
195
  this.flushVersion++;
@@ -373,123 +344,15 @@ function withContext(frame, fn) {
373
344
  currentContextFrame = oldFrame;
374
345
  }
375
346
  }
376
- function withAsyncResourceContext(frame, fn) {
377
- const oldFrame = currentAsyncResourceFrame;
378
- currentAsyncResourceFrame = frame;
379
- try {
380
- return fn();
381
- } finally {
382
- currentAsyncResourceFrame = oldFrame;
383
- }
384
- }
385
- function defineContext(defaultValue) {
386
- const key = /* @__PURE__ */ Symbol("AskrContext");
387
- return {
388
- key,
389
- defaultValue,
390
- Scope: (props) => {
391
- const value = props.value;
392
- return {
393
- type: ContextScopeComponent,
394
- props: { key, value, children: props.children }
395
- };
396
- }
397
- };
398
- }
399
- function readContext(context) {
400
- const frame = currentContextFrame || currentAsyncResourceFrame;
401
- if (!frame) {
402
- throw new Error(
403
- "readContext() can only be called during component render or async resource execution. Ensure you are calling this from inside your component or resource function."
404
- );
405
- }
406
- let current2 = frame;
407
- while (current2) {
408
- const values = current2.values;
409
- if (values && values.has(context.key)) {
410
- return values.get(context.key);
411
- }
412
- current2 = current2.parent;
413
- }
414
- return context.defaultValue;
415
- }
416
- function ContextScopeComponent(props) {
417
- const key = props["key"];
418
- const value = props["value"];
419
- const children = props["children"];
420
- const instance = getCurrentComponentInstance();
421
- const parentFrame = (() => {
422
- if (currentContextFrame) return currentContextFrame;
423
- if (instance && instance.ownerFrame) return instance.ownerFrame;
424
- return null;
425
- })();
426
- const newFrame = {
427
- parent: parentFrame,
428
- values: /* @__PURE__ */ new Map([[key, value]])
429
- };
430
- function createFunctionChildInvoker(fn, frame, owner) {
431
- return {
432
- type: ContextFunctionChildInvoker,
433
- props: { fn, __frame: frame, __owner: owner }
434
- };
435
- }
436
- if (Array.isArray(children)) {
437
- return children.map((child) => {
438
- if (typeof child === "function") {
439
- return createFunctionChildInvoker(
440
- child,
441
- newFrame,
442
- getCurrentComponentInstance()
443
- );
444
- }
445
- return markWithFrame(child, newFrame);
446
- });
447
- } else if (typeof children === "function") {
448
- return createFunctionChildInvoker(
449
- children,
450
- newFrame,
451
- getCurrentComponentInstance()
452
- );
453
- } else if (children) {
454
- return markWithFrame(children, newFrame);
455
- }
456
- return null;
457
- }
458
- function markWithFrame(node, frame) {
459
- if (typeof node === "object" && node !== null) {
460
- const obj = node;
461
- obj[CONTEXT_FRAME_SYMBOL] = frame;
462
- const children = obj.children;
463
- if (Array.isArray(children)) {
464
- for (let i = 0; i < children.length; i++) {
465
- const child = children[i];
466
- if (child) {
467
- children[i] = markWithFrame(child, frame);
468
- }
469
- }
470
- } else if (children) {
471
- obj.children = markWithFrame(children, frame);
472
- }
473
- }
474
- return node;
475
- }
476
- function ContextFunctionChildInvoker(props) {
477
- const { fn, __frame } = props;
478
- const res = withContext(__frame, () => fn());
479
- if (res) return markWithFrame(res, __frame);
480
- return null;
481
- }
482
347
  function getCurrentContextFrame() {
483
348
  return currentContextFrame;
484
349
  }
485
- var CONTEXT_FRAME_SYMBOL, currentContextFrame, currentAsyncResourceFrame;
350
+ var CONTEXT_FRAME_SYMBOL, currentContextFrame;
486
351
  var init_context = __esm({
487
352
  "src/runtime/context.ts"() {
488
353
  "use strict";
489
- init_component();
490
354
  CONTEXT_FRAME_SYMBOL = /* @__PURE__ */ Symbol("__tempoContextFrame__");
491
355
  currentContextFrame = null;
492
- currentAsyncResourceFrame = null;
493
356
  }
494
357
  });
495
358
 
@@ -555,172 +418,6 @@ var init_diag = __esm({
555
418
  }
556
419
  });
557
420
 
558
- // src/runtime/dev-namespace.ts
559
- function getDevNamespace() {
560
- if (process.env.NODE_ENV === "production") return {};
561
- try {
562
- const g = globalThis;
563
- if (!g.__ASKR__) g.__ASKR__ = {};
564
- return g.__ASKR__;
565
- } catch {
566
- return {};
567
- }
568
- }
569
- function setDevValue(key, value) {
570
- if (process.env.NODE_ENV === "production") return;
571
- try {
572
- getDevNamespace()[key] = value;
573
- } catch {
574
- }
575
- }
576
- function getDevValue(key) {
577
- if (process.env.NODE_ENV === "production") return void 0;
578
- try {
579
- return getDevNamespace()[key];
580
- } catch {
581
- return void 0;
582
- }
583
- }
584
- var init_dev_namespace = __esm({
585
- "src/runtime/dev-namespace.ts"() {
586
- "use strict";
587
- }
588
- });
589
-
590
- // src/runtime/fastlane-shared.ts
591
- function enterBulkCommit() {
592
- _bulkCommitActive = true;
593
- _appliedParents = /* @__PURE__ */ new WeakSet();
594
- try {
595
- const cleared = globalScheduler.clearPendingSyncTasks?.() ?? 0;
596
- setDevValue("__ASKR_FASTLANE_CLEARED_TASKS", cleared);
597
- } catch (err) {
598
- if (process.env.NODE_ENV !== "production") throw err;
599
- }
600
- }
601
- function exitBulkCommit() {
602
- _bulkCommitActive = false;
603
- _appliedParents = null;
604
- }
605
- function isBulkCommitActive2() {
606
- return _bulkCommitActive;
607
- }
608
- function markFastPathApplied(parent) {
609
- if (!_appliedParents) return;
610
- try {
611
- _appliedParents.add(parent);
612
- } catch (e) {
613
- void e;
614
- }
615
- }
616
- function isFastPathApplied(parent) {
617
- return !!(_appliedParents && _appliedParents.has(parent));
618
- }
619
- var _bulkCommitActive, _appliedParents;
620
- var init_fastlane_shared = __esm({
621
- "src/runtime/fastlane-shared.ts"() {
622
- "use strict";
623
- init_scheduler();
624
- init_dev_namespace();
625
- _bulkCommitActive = false;
626
- _appliedParents = null;
627
- }
628
- });
629
-
630
- // src/renderer/types.ts
631
- function _isDOMElement(node) {
632
- return typeof node === "object" && node !== null && "type" in node;
633
- }
634
- var init_types = __esm({
635
- "src/renderer/types.ts"() {
636
- "use strict";
637
- }
638
- });
639
-
640
- // src/renderer/cleanup.ts
641
- function cleanupSingleInstance(node, errors, strict) {
642
- const inst = node.__ASKR_INSTANCE;
643
- if (!inst) return;
644
- try {
645
- cleanupComponent(inst);
646
- } catch (err) {
647
- if (strict) errors.push(err);
648
- else logger.warn("[Askr] cleanupComponent failed:", err);
649
- }
650
- try {
651
- delete node.__ASKR_INSTANCE;
652
- } catch (e) {
653
- if (strict) errors.push(e);
654
- }
655
- }
656
- function cleanupInstanceIfPresent(node, opts) {
657
- if (!node || !(node instanceof Element)) return;
658
- const errors = [];
659
- const strict = opts?.strict ?? false;
660
- try {
661
- cleanupSingleInstance(node, errors, strict);
662
- } catch (err) {
663
- if (strict) errors.push(err);
664
- else logger.warn("[Askr] cleanupInstanceIfPresent failed:", err);
665
- }
666
- try {
667
- const descendants = node.querySelectorAll("*");
668
- for (const d of Array.from(descendants)) {
669
- try {
670
- cleanupSingleInstance(d, errors, strict);
671
- } catch (err) {
672
- if (strict) errors.push(err);
673
- else
674
- logger.warn(
675
- "[Askr] cleanupInstanceIfPresent descendant cleanup failed:",
676
- err
677
- );
678
- }
679
- }
680
- } catch (err) {
681
- if (strict) errors.push(err);
682
- else
683
- logger.warn(
684
- "[Askr] cleanupInstanceIfPresent descendant query failed:",
685
- err
686
- );
687
- }
688
- if (errors.length > 0) {
689
- throw new AggregateError(errors, "cleanupInstanceIfPresent failed");
690
- }
691
- }
692
- function cleanupInstancesUnder(node, opts) {
693
- cleanupInstanceIfPresent(node, opts);
694
- }
695
- function removeElementListeners(element) {
696
- const map = elementListeners.get(element);
697
- if (map) {
698
- for (const [eventName, entry] of map) {
699
- if (entry.options !== void 0)
700
- element.removeEventListener(eventName, entry.handler, entry.options);
701
- else element.removeEventListener(eventName, entry.handler);
702
- }
703
- elementListeners.delete(element);
704
- }
705
- }
706
- function removeAllListeners(root) {
707
- if (!root) return;
708
- removeElementListeners(root);
709
- const children = root.querySelectorAll("*");
710
- for (let i = 0; i < children.length; i++) {
711
- removeElementListeners(children[i]);
712
- }
713
- }
714
- var elementListeners;
715
- var init_cleanup = __esm({
716
- "src/renderer/cleanup.ts"() {
717
- "use strict";
718
- init_component();
719
- init_logger();
720
- elementListeners = /* @__PURE__ */ new WeakMap();
721
- }
722
- });
723
-
724
421
  // src/renderer/utils.ts
725
422
  function parseEventName(propName) {
726
423
  if (!propName.startsWith("on") || propName.length <= 2) return null;
@@ -809,8 +506,7 @@ function extractKey(vnode) {
809
506
  }
810
507
  function buildKeyMapFromChildren(parent) {
811
508
  const map = /* @__PURE__ */ new Map();
812
- const children = Array.from(parent.children);
813
- for (const ch of children) {
509
+ for (let ch = parent.firstElementChild; ch; ch = ch.nextElementSibling) {
814
510
  const k = ch.getAttribute("data-key");
815
511
  if (k !== null) {
816
512
  map.set(k, ch);
@@ -985,269 +681,668 @@ var init_keyed = __esm({
985
681
  }
986
682
  });
987
683
 
988
- // src/jsx/types.ts
684
+ // src/common/jsx.ts
989
685
  var ELEMENT_TYPE, Fragment;
990
- var init_types2 = __esm({
991
- "src/jsx/types.ts"() {
686
+ var init_jsx = __esm({
687
+ "src/common/jsx.ts"() {
992
688
  "use strict";
993
689
  ELEMENT_TYPE = /* @__PURE__ */ Symbol.for("askr.element");
994
690
  Fragment = /* @__PURE__ */ Symbol.for("askr.fragment");
995
691
  }
996
692
  });
997
693
 
998
- // src/jsx/jsx-runtime.ts
999
- function jsxDEV(type, props, key) {
1000
- return {
1001
- $$typeof: ELEMENT_TYPE2,
1002
- type,
1003
- props: props ?? {},
1004
- key: key ?? null
1005
- };
694
+ // src/runtime/dev-namespace.ts
695
+ function getDevNamespace() {
696
+ if (process.env.NODE_ENV === "production") return {};
697
+ try {
698
+ const g = globalThis;
699
+ if (!g.__ASKR__) g.__ASKR__ = {};
700
+ return g.__ASKR__;
701
+ } catch {
702
+ return {};
703
+ }
1006
704
  }
1007
- function jsx(type, props, key) {
1008
- return jsxDEV(type, props, key);
705
+ function setDevValue(key, value) {
706
+ if (process.env.NODE_ENV === "production") return;
707
+ try {
708
+ getDevNamespace()[key] = value;
709
+ } catch {
710
+ }
1009
711
  }
1010
- function jsxs(type, props, key) {
1011
- return jsxDEV(type, props, key);
712
+ function getDevValue(key) {
713
+ if (process.env.NODE_ENV === "production") return void 0;
714
+ try {
715
+ return getDevNamespace()[key];
716
+ } catch {
717
+ return void 0;
718
+ }
1012
719
  }
1013
- var ELEMENT_TYPE2, Fragment2;
1014
- var init_jsx_runtime = __esm({
1015
- "src/jsx/jsx-runtime.ts"() {
720
+ var init_dev_namespace = __esm({
721
+ "src/runtime/dev-namespace.ts"() {
1016
722
  "use strict";
1017
- init_types2();
1018
- ELEMENT_TYPE2 = /* @__PURE__ */ Symbol.for("askr.element");
1019
- Fragment2 = /* @__PURE__ */ Symbol.for("askr.fragment");
1020
723
  }
1021
724
  });
1022
725
 
1023
- // src/renderer/dom.ts
1024
- function addTrackedListener(el, eventName, handler) {
1025
- const wrappedHandler = createWrappedHandler(handler, true);
1026
- const options = getPassiveOptions(eventName);
1027
- if (options !== void 0) {
1028
- el.addEventListener(eventName, wrappedHandler, options);
1029
- } else {
1030
- el.addEventListener(eventName, wrappedHandler);
726
+ // src/runtime/fastlane.ts
727
+ function enterBulkCommit() {
728
+ _bulkCommitActive = true;
729
+ _appliedParents = /* @__PURE__ */ new WeakSet();
730
+ try {
731
+ const cleared = globalScheduler.clearPendingSyncTasks?.() ?? 0;
732
+ setDevValue("__ASKR_FASTLANE_CLEARED_TASKS", cleared);
733
+ } catch (err) {
734
+ if (process.env.NODE_ENV !== "production") throw err;
1031
735
  }
1032
- if (!elementListeners.has(el)) {
1033
- elementListeners.set(el, /* @__PURE__ */ new Map());
736
+ }
737
+ function exitBulkCommit() {
738
+ _bulkCommitActive = false;
739
+ _appliedParents = null;
740
+ }
741
+ function isBulkCommitActive2() {
742
+ return _bulkCommitActive;
743
+ }
744
+ function markFastPathApplied(parent) {
745
+ if (!_appliedParents) return;
746
+ try {
747
+ _appliedParents.add(parent);
748
+ } catch (e) {
749
+ void e;
1034
750
  }
1035
- elementListeners.get(el).set(eventName, {
1036
- handler: wrappedHandler,
1037
- original: handler,
1038
- options
1039
- });
1040
751
  }
1041
- function applyPropsToElement(el, props, tagName) {
1042
- for (const key in props) {
1043
- const value = props[key];
1044
- if (isSkippedProp(key)) continue;
1045
- if (value === void 0 || value === null || value === false) continue;
1046
- const eventName = parseEventName(key);
1047
- if (eventName) {
1048
- addTrackedListener(el, eventName, value);
1049
- continue;
752
+ function isFastPathApplied(parent) {
753
+ return !!(_appliedParents && _appliedParents.has(parent));
754
+ }
755
+ function finalizeReadSubscriptions(instance) {
756
+ const newSet = instance._pendingReadStates ?? /* @__PURE__ */ new Set();
757
+ const oldSet = instance._lastReadStates ?? /* @__PURE__ */ new Set();
758
+ const token = instance._currentRenderToken;
759
+ if (token === void 0) return;
760
+ for (const s of oldSet) {
761
+ if (!newSet.has(s)) {
762
+ const readers = s._readers;
763
+ if (readers) readers.delete(instance);
1050
764
  }
1051
- if (key === "class" || key === "className") {
1052
- el.className = String(value);
1053
- } else if (key === "value" || key === "checked") {
1054
- applyFormControlProp(el, key, value, tagName);
1055
- } else {
1056
- el.setAttribute(key, String(value));
765
+ }
766
+ instance.lastRenderToken = token;
767
+ for (const s of newSet) {
768
+ let readers = s._readers;
769
+ if (!readers) {
770
+ readers = /* @__PURE__ */ new Map();
771
+ s._readers = readers;
1057
772
  }
773
+ readers.set(instance, instance.lastRenderToken ?? 0);
1058
774
  }
775
+ instance._lastReadStates = newSet;
776
+ instance._pendingReadStates = /* @__PURE__ */ new Set();
777
+ instance._currentRenderToken = void 0;
1059
778
  }
1060
- function applyFormControlProp(el, key, value, tagName) {
1061
- const tag = tagName.toLowerCase();
1062
- if (key === "value") {
1063
- if (tag === "input" || tag === "textarea" || tag === "select") {
1064
- el.value = String(value);
1065
- el.setAttribute("value", String(value));
1066
- } else {
1067
- el.setAttribute("value", String(value));
1068
- }
1069
- } else if (key === "checked") {
1070
- if (tag === "input") {
1071
- el.checked = Boolean(value);
1072
- el.setAttribute("checked", String(Boolean(value)));
1073
- } else {
1074
- el.setAttribute("checked", String(Boolean(value)));
779
+ function unwrapFragmentForFastPath(vnode) {
780
+ if (!vnode || typeof vnode !== "object" || !("type" in vnode)) return vnode;
781
+ const v = vnode;
782
+ if (typeof v.type === "symbol" && (v.type === Fragment || String(v.type) === "Symbol(askr.fragment)")) {
783
+ const children = v.children || v.props?.children;
784
+ if (Array.isArray(children) && children.length > 0) {
785
+ for (const child of children) {
786
+ if (child && typeof child === "object" && "type" in child) {
787
+ const c = child;
788
+ if (typeof c.type === "string") {
789
+ return child;
790
+ }
791
+ }
792
+ }
1075
793
  }
1076
794
  }
795
+ return vnode;
1077
796
  }
1078
- function materializeKey(el, vnode, props) {
1079
- const vnodeKey = vnode.key ?? props?.key;
1080
- if (vnodeKey !== void 0) {
1081
- el.setAttribute("data-key", String(vnodeKey));
797
+ function classifyUpdate(instance, result) {
798
+ const unwrappedResult = unwrapFragmentForFastPath(result);
799
+ if (!unwrappedResult || typeof unwrappedResult !== "object" || !("type" in unwrappedResult))
800
+ return { useFastPath: false, reason: "not-vnode" };
801
+ const vnode = unwrappedResult;
802
+ if (vnode == null || typeof vnode.type !== "string")
803
+ return { useFastPath: false, reason: "not-intrinsic" };
804
+ const parent = instance.target;
805
+ if (!parent) return { useFastPath: false, reason: "no-root" };
806
+ const firstChild = parent.children[0];
807
+ if (!firstChild) return { useFastPath: false, reason: "no-first-child" };
808
+ if (firstChild.tagName.toLowerCase() !== String(vnode.type).toLowerCase())
809
+ return { useFastPath: false, reason: "root-tag-mismatch" };
810
+ const children = vnode.children || vnode.props?.children;
811
+ if (!Array.isArray(children))
812
+ return { useFastPath: false, reason: "no-children-array" };
813
+ for (const c of children) {
814
+ if (typeof c === "object" && c !== null && "type" in c && typeof c.type === "function") {
815
+ return { useFastPath: false, reason: "component-child-present" };
816
+ }
1082
817
  }
818
+ if (instance.mountOperations.length > 0)
819
+ return { useFastPath: false, reason: "pending-mounts" };
820
+ try {
821
+ populateKeyMapForElement(firstChild);
822
+ } catch {
823
+ }
824
+ const oldKeyMap = getKeyMapForElement(firstChild);
825
+ const decision = isKeyedReorderFastPathEligible(
826
+ firstChild,
827
+ children,
828
+ oldKeyMap
829
+ );
830
+ if (!decision.useFastPath || decision.totalKeyed < 128)
831
+ return { ...decision, useFastPath: false, reason: "renderer-declined" };
832
+ return { ...decision, useFastPath: true };
1083
833
  }
1084
- function warnMissingKeys(children) {
1085
- if (process.env.NODE_ENV === "production") return;
1086
- let hasElements = false;
1087
- let hasKeys = false;
1088
- for (const item of children) {
1089
- if (typeof item === "object" && item !== null && "type" in item) {
1090
- hasElements = true;
1091
- const rawKey = item.key ?? item.props?.key;
1092
- if (rawKey !== void 0) {
1093
- hasKeys = true;
1094
- break;
834
+ function commitReorderOnly(instance, result) {
835
+ const evaluate2 = globalThis.__ASKR_RENDERER?.evaluate;
836
+ if (typeof evaluate2 !== "function") {
837
+ logger.warn(
838
+ "[Tempo][FASTPATH][DEV] renderer.evaluate not available; declining fast-lane"
839
+ );
840
+ return false;
841
+ }
842
+ const schedBefore = process.env.NODE_ENV !== "production" ? globalScheduler.getState() : null;
843
+ enterBulkCommit();
844
+ try {
845
+ globalScheduler.runWithSyncProgress(() => {
846
+ evaluate2(result, instance.target);
847
+ try {
848
+ finalizeReadSubscriptions(instance);
849
+ } catch (e) {
850
+ if (process.env.NODE_ENV !== "production") throw e;
1095
851
  }
852
+ });
853
+ const clearedAfter = globalScheduler.clearPendingSyncTasks?.() ?? 0;
854
+ setDevValue("__FASTLANE_CLEARED_AFTER", clearedAfter);
855
+ if (process.env.NODE_ENV !== "production") {
856
+ validateFastLaneInvariants(instance, schedBefore);
1096
857
  }
858
+ return true;
859
+ } finally {
860
+ exitBulkCommit();
1097
861
  }
1098
- if (hasElements && !hasKeys) {
1099
- try {
1100
- const inst = getCurrentInstance();
1101
- const name = inst?.fn?.name || "<anonymous>";
1102
- logger.warn(
1103
- `Missing keys on dynamic lists in ${name}. Each child in a list should have a unique "key" prop.`
1104
- );
1105
- } catch {
1106
- logger.warn(
1107
- 'Missing keys on dynamic lists. Each child in a list should have a unique "key" prop.'
862
+ if (process.env.NODE_ENV !== "production") {
863
+ if (isBulkCommitActive2()) {
864
+ throw new Error(
865
+ "Fast-lane invariant violated: bulk commit flag still set after commit"
1108
866
  );
1109
867
  }
1110
868
  }
1111
869
  }
1112
- function createDOMNode(node) {
1113
- if (!IS_DOM_AVAILABLE) {
1114
- if (process.env.NODE_ENV !== "production") {
1115
- try {
1116
- logger.warn("[Askr] createDOMNode called in non-DOM environment");
1117
- } catch {
1118
- }
1119
- }
1120
- return null;
870
+ function validateFastLaneInvariants(instance, schedBefore) {
871
+ const commitCount = getDevValue("__LAST_FASTPATH_COMMIT_COUNT") ?? 0;
872
+ const invariants = {
873
+ commitCount,
874
+ mountOps: instance.mountOperations.length,
875
+ cleanupFns: instance.cleanupFns.length
876
+ };
877
+ setDevValue("__LAST_FASTLANE_INVARIANTS", invariants);
878
+ if (commitCount !== 1) {
879
+ console.error(
880
+ "[FASTLANE][INV] commitCount",
881
+ commitCount,
882
+ "diag",
883
+ globalThis.__ASKR_DIAG
884
+ );
885
+ throw new Error(
886
+ "Fast-lane invariant violated: expected exactly one DOM commit during reorder-only commit"
887
+ );
1121
888
  }
1122
- if (typeof node === "string") {
1123
- return document.createTextNode(node);
889
+ if (invariants.mountOps > 0) {
890
+ throw new Error(
891
+ "Fast-lane invariant violated: mount operations were registered during bulk commit"
892
+ );
1124
893
  }
1125
- if (typeof node === "number") {
1126
- return document.createTextNode(String(node));
894
+ if (invariants.cleanupFns > 0) {
895
+ throw new Error(
896
+ "Fast-lane invariant violated: cleanup functions were added during bulk commit"
897
+ );
1127
898
  }
1128
- if (!node) {
1129
- return null;
899
+ const schedAfter = globalScheduler.getState();
900
+ if (schedBefore && schedAfter && schedAfter.taskCount > schedBefore.taskCount) {
901
+ console.error(
902
+ "[FASTLANE] schedBefore, schedAfter",
903
+ schedBefore,
904
+ schedAfter
905
+ );
906
+ console.error("[FASTLANE] enqueue logs", getDevValue("__ENQUEUE_LOGS"));
907
+ throw new Error(
908
+ "Fast-lane invariant violated: scheduler enqueued leftover work during bulk commit"
909
+ );
1130
910
  }
1131
- if (Array.isArray(node)) {
1132
- const fragment = document.createDocumentFragment();
1133
- for (const child of node) {
1134
- const dom = createDOMNode(child);
1135
- if (dom) fragment.appendChild(dom);
911
+ let finalState = globalScheduler.getState();
912
+ const executing = globalScheduler.isExecuting();
913
+ let outstandingAfter = Math.max(
914
+ 0,
915
+ finalState.taskCount - (executing ? 1 : 0)
916
+ );
917
+ if (outstandingAfter !== 0) {
918
+ let attempts = 0;
919
+ while (attempts < 5) {
920
+ const cleared = globalScheduler.clearPendingSyncTasks?.() ?? 0;
921
+ if (cleared === 0) break;
922
+ attempts++;
1136
923
  }
1137
- return fragment;
1138
- }
1139
- if (typeof node === "object" && node !== null && "type" in node) {
1140
- const type = node.type;
1141
- const props = node.props || {};
1142
- if (typeof type === "string") {
1143
- return createIntrinsicElement(node, type, props);
1144
- }
1145
- if (typeof type === "function") {
1146
- return createComponentElement(node, type, props);
1147
- }
1148
- if (typeof type === "symbol" && (type === Fragment2 || String(type) === "Symbol(Fragment)")) {
1149
- return createFragmentElement(node, props);
924
+ finalState = globalScheduler.getState();
925
+ outstandingAfter = Math.max(
926
+ 0,
927
+ finalState.taskCount - (globalScheduler.isExecuting() ? 1 : 0)
928
+ );
929
+ if (outstandingAfter !== 0) {
930
+ console.error(
931
+ "[FASTLANE] Post-commit enqueue logs:",
932
+ getDevValue("__ENQUEUE_LOGS")
933
+ );
934
+ console.error(
935
+ "[FASTLANE] Cleared counts:",
936
+ getDevValue("__FASTLANE_CLEARED_TASKS"),
937
+ getDevValue("__FASTLANE_CLEARED_AFTER")
938
+ );
939
+ throw new Error(
940
+ `Fast-lane invariant violated: scheduler has ${finalState.taskCount} pending task(s) after commit`
941
+ );
1150
942
  }
1151
943
  }
1152
- return null;
1153
944
  }
1154
- function createIntrinsicElement(node, type, props) {
1155
- const el = document.createElement(type);
1156
- materializeKey(el, node, props);
1157
- applyPropsToElement(el, props, type);
1158
- const children = props.children || node.children;
1159
- if (children) {
1160
- if (Array.isArray(children)) {
1161
- warnMissingKeys(children);
1162
- for (const child of children) {
1163
- const dom = createDOMNode(child);
1164
- if (dom) el.appendChild(dom);
1165
- }
1166
- } else {
1167
- const dom = createDOMNode(children);
1168
- if (dom) el.appendChild(dom);
945
+ function tryRuntimeFastLaneSync(instance, result) {
946
+ const cls = classifyUpdate(instance, result);
947
+ if (!cls.useFastPath) {
948
+ setDevValue("__LAST_FASTPATH_STATS", void 0);
949
+ setDevValue("__LAST_FASTPATH_COMMIT_COUNT", 0);
950
+ return false;
951
+ }
952
+ try {
953
+ return commitReorderOnly(instance, result);
954
+ } catch (err) {
955
+ if (process.env.NODE_ENV !== "production") throw err;
956
+ return false;
957
+ }
958
+ }
959
+ var _bulkCommitActive, _appliedParents;
960
+ var init_fastlane = __esm({
961
+ "src/runtime/fastlane.ts"() {
962
+ "use strict";
963
+ init_scheduler();
964
+ init_logger();
965
+ init_keyed();
966
+ init_jsx();
967
+ init_dev_namespace();
968
+ _bulkCommitActive = false;
969
+ _appliedParents = null;
970
+ if (typeof globalThis !== "undefined") {
971
+ globalThis.__ASKR_FASTLANE = {
972
+ isBulkCommitActive: isBulkCommitActive2,
973
+ enterBulkCommit,
974
+ exitBulkCommit,
975
+ tryRuntimeFastLaneSync,
976
+ markFastPathApplied,
977
+ isFastPathApplied
978
+ };
1169
979
  }
1170
980
  }
1171
- return el;
981
+ });
982
+
983
+ // src/common/vnode.ts
984
+ function _isDOMElement(node) {
985
+ return typeof node === "object" && node !== null && "type" in node;
1172
986
  }
1173
- function createComponentElement(node, type, props) {
1174
- const frame = node[CONTEXT_FRAME_SYMBOL];
1175
- const snapshot = frame || getCurrentContextFrame();
1176
- const componentFn = type;
1177
- const isAsync = componentFn.constructor.name === "AsyncFunction";
1178
- if (isAsync) {
1179
- throw new Error(
1180
- "Async components are not supported. Use resource() for async work."
1181
- );
987
+ var init_vnode = __esm({
988
+ "src/common/vnode.ts"() {
989
+ "use strict";
1182
990
  }
1183
- let childInstance = node.__instance;
1184
- if (!childInstance) {
1185
- childInstance = createComponentInstance(
1186
- `comp-${Math.random().toString(36).slice(2, 7)}`,
1187
- componentFn,
1188
- props || {},
1189
- null
1190
- );
1191
- node.__instance = childInstance;
991
+ });
992
+
993
+ // src/renderer/types.ts
994
+ var init_types = __esm({
995
+ "src/renderer/types.ts"() {
996
+ "use strict";
997
+ init_vnode();
1192
998
  }
1193
- if (snapshot) {
1194
- childInstance.ownerFrame = snapshot;
999
+ });
1000
+
1001
+ // src/renderer/cleanup.ts
1002
+ function cleanupSingleInstance(node, errors, strict) {
1003
+ const inst = node.__ASKR_INSTANCE;
1004
+ if (!inst) return;
1005
+ try {
1006
+ cleanupComponent(inst);
1007
+ } catch (err) {
1008
+ if (strict) errors.push(err);
1009
+ else logger.warn("[Askr] cleanupComponent failed:", err);
1195
1010
  }
1196
- const result = withContext(
1197
- snapshot,
1198
- () => renderComponentInline(childInstance)
1199
- );
1200
- if (result instanceof Promise) {
1201
- throw new Error(
1202
- "Async components are not supported. Components must return synchronously."
1203
- );
1011
+ try {
1012
+ delete node.__ASKR_INSTANCE;
1013
+ } catch (e) {
1014
+ if (strict) errors.push(e);
1204
1015
  }
1205
- const dom = withContext(snapshot, () => createDOMNode(result));
1206
- if (dom instanceof Element) {
1207
- mountInstanceInline(childInstance, dom);
1208
- return dom;
1016
+ }
1017
+ function forEachDescendantElement(root, visit) {
1018
+ try {
1019
+ const doc = root.ownerDocument;
1020
+ const createTreeWalker = doc?.createTreeWalker;
1021
+ if (typeof createTreeWalker === "function") {
1022
+ const walker = createTreeWalker.call(doc, root, 1);
1023
+ let n = walker.firstChild();
1024
+ while (n) {
1025
+ visit(n);
1026
+ n = walker.nextNode();
1027
+ }
1028
+ return;
1029
+ }
1030
+ } catch {
1209
1031
  }
1210
- if (!dom) {
1211
- const placeholder = document.createComment("");
1212
- childInstance._placeholder = placeholder;
1213
- childInstance.mounted = true;
1214
- childInstance.notifyUpdate = childInstance._enqueueRun;
1215
- return placeholder;
1032
+ const descendants = root.querySelectorAll("*");
1033
+ for (let i = 0; i < descendants.length; i++) {
1034
+ visit(descendants[i]);
1216
1035
  }
1217
- const host = document.createElement("div");
1218
- host.appendChild(dom);
1219
- mountInstanceInline(childInstance, host);
1220
- return host;
1221
1036
  }
1222
- function createFragmentElement(node, props) {
1223
- const fragment = document.createDocumentFragment();
1224
- const children = props.children || node.children;
1225
- if (children) {
1226
- if (Array.isArray(children)) {
1227
- for (const child of children) {
1228
- const dom = createDOMNode(child);
1229
- if (dom) fragment.appendChild(dom);
1037
+ function cleanupInstanceIfPresent(node, opts) {
1038
+ if (!node || !(node instanceof Element)) return;
1039
+ const strict = opts?.strict ?? false;
1040
+ const errors = strict ? [] : null;
1041
+ try {
1042
+ cleanupSingleInstance(node, errors, strict);
1043
+ } catch (err) {
1044
+ if (strict) errors.push(err);
1045
+ else logger.warn("[Askr] cleanupInstanceIfPresent failed:", err);
1046
+ }
1047
+ try {
1048
+ forEachDescendantElement(node, (d) => {
1049
+ try {
1050
+ cleanupSingleInstance(d, errors, strict);
1051
+ } catch (err) {
1052
+ if (strict) errors.push(err);
1053
+ else
1054
+ logger.warn(
1055
+ "[Askr] cleanupInstanceIfPresent descendant cleanup failed:",
1056
+ err
1057
+ );
1230
1058
  }
1231
- } else {
1232
- const dom = createDOMNode(children);
1233
- if (dom) fragment.appendChild(dom);
1059
+ });
1060
+ } catch (err) {
1061
+ if (strict) errors.push(err);
1062
+ else
1063
+ logger.warn(
1064
+ "[Askr] cleanupInstanceIfPresent descendant query failed:",
1065
+ err
1066
+ );
1067
+ }
1068
+ if (errors && errors.length > 0) {
1069
+ throw new AggregateError(errors, "cleanupInstanceIfPresent failed");
1070
+ }
1071
+ }
1072
+ function cleanupInstancesUnder(node, opts) {
1073
+ cleanupInstanceIfPresent(node, opts);
1074
+ }
1075
+ function removeElementListeners(element) {
1076
+ const map = elementListeners.get(element);
1077
+ if (map) {
1078
+ for (const [eventName, entry] of map) {
1079
+ if (entry.options !== void 0)
1080
+ element.removeEventListener(eventName, entry.handler, entry.options);
1081
+ else element.removeEventListener(eventName, entry.handler);
1234
1082
  }
1083
+ elementListeners.delete(element);
1235
1084
  }
1236
- return fragment;
1237
1085
  }
1238
- function updateElementFromVnode(el, vnode, updateChildren = true) {
1239
- if (!_isDOMElement(vnode)) {
1240
- return;
1086
+ function removeAllListeners(root) {
1087
+ if (!root) return;
1088
+ removeElementListeners(root);
1089
+ forEachDescendantElement(root, removeElementListeners);
1090
+ }
1091
+ var elementListeners;
1092
+ var init_cleanup = __esm({
1093
+ "src/renderer/cleanup.ts"() {
1094
+ "use strict";
1095
+ init_component();
1096
+ init_logger();
1097
+ elementListeners = /* @__PURE__ */ new WeakMap();
1241
1098
  }
1242
- const props = vnode.props || {};
1243
- materializeKey(el, vnode, props);
1244
- const existingListeners = elementListeners.get(el);
1245
- const desiredEventNames = /* @__PURE__ */ new Set();
1099
+ });
1100
+
1101
+ // src/jsx/types.ts
1102
+ var init_types2 = __esm({
1103
+ "src/jsx/types.ts"() {
1104
+ "use strict";
1105
+ init_jsx();
1106
+ }
1107
+ });
1108
+
1109
+ // src/jsx/jsx-runtime.ts
1110
+ var Fragment2;
1111
+ var init_jsx_runtime = __esm({
1112
+ "src/jsx/jsx-runtime.ts"() {
1113
+ "use strict";
1114
+ init_types2();
1115
+ Fragment2 = /* @__PURE__ */ Symbol.for("askr.fragment");
1116
+ }
1117
+ });
1118
+
1119
+ // src/renderer/dom.ts
1120
+ function addTrackedListener(el, eventName, handler) {
1121
+ const wrappedHandler = createWrappedHandler(handler, true);
1122
+ const options = getPassiveOptions(eventName);
1123
+ if (options !== void 0) {
1124
+ el.addEventListener(eventName, wrappedHandler, options);
1125
+ } else {
1126
+ el.addEventListener(eventName, wrappedHandler);
1127
+ }
1128
+ if (!elementListeners.has(el)) {
1129
+ elementListeners.set(el, /* @__PURE__ */ new Map());
1130
+ }
1131
+ elementListeners.get(el).set(eventName, {
1132
+ handler: wrappedHandler,
1133
+ original: handler,
1134
+ options
1135
+ });
1136
+ }
1137
+ function applyPropsToElement(el, props, tagName) {
1246
1138
  for (const key in props) {
1247
1139
  const value = props[key];
1248
1140
  if (isSkippedProp(key)) continue;
1141
+ if (value === void 0 || value === null || value === false) continue;
1249
1142
  const eventName = parseEventName(key);
1250
- if (value === void 0 || value === null || value === false) {
1143
+ if (eventName) {
1144
+ addTrackedListener(el, eventName, value);
1145
+ continue;
1146
+ }
1147
+ if (key === "class" || key === "className") {
1148
+ el.className = String(value);
1149
+ } else if (key === "value" || key === "checked") {
1150
+ applyFormControlProp(el, key, value, tagName);
1151
+ } else {
1152
+ el.setAttribute(key, String(value));
1153
+ }
1154
+ }
1155
+ }
1156
+ function applyFormControlProp(el, key, value, tagName) {
1157
+ if (key === "value") {
1158
+ if (tagNamesEqualIgnoreCase(tagName, "input") || tagNamesEqualIgnoreCase(tagName, "textarea") || tagNamesEqualIgnoreCase(tagName, "select")) {
1159
+ el.value = String(value);
1160
+ el.setAttribute("value", String(value));
1161
+ } else {
1162
+ el.setAttribute("value", String(value));
1163
+ }
1164
+ } else if (key === "checked") {
1165
+ if (tagNamesEqualIgnoreCase(tagName, "input")) {
1166
+ el.checked = Boolean(value);
1167
+ el.setAttribute("checked", String(Boolean(value)));
1168
+ } else {
1169
+ el.setAttribute("checked", String(Boolean(value)));
1170
+ }
1171
+ }
1172
+ }
1173
+ function materializeKey(el, vnode, props) {
1174
+ const vnodeKey = vnode.key ?? props?.key;
1175
+ if (vnodeKey !== void 0) {
1176
+ el.setAttribute("data-key", String(vnodeKey));
1177
+ }
1178
+ }
1179
+ function warnMissingKeys(children) {
1180
+ if (process.env.NODE_ENV === "production") return;
1181
+ let hasElements = false;
1182
+ let hasKeys = false;
1183
+ for (const item of children) {
1184
+ if (typeof item === "object" && item !== null && "type" in item) {
1185
+ hasElements = true;
1186
+ const rawKey = item.key ?? item.props?.key;
1187
+ if (rawKey !== void 0) {
1188
+ hasKeys = true;
1189
+ break;
1190
+ }
1191
+ }
1192
+ }
1193
+ if (hasElements && !hasKeys) {
1194
+ try {
1195
+ const inst = getCurrentInstance();
1196
+ const name = inst?.fn?.name || "<anonymous>";
1197
+ logger.warn(
1198
+ `Missing keys on dynamic lists in ${name}. Each child in a list should have a unique "key" prop.`
1199
+ );
1200
+ } catch {
1201
+ logger.warn(
1202
+ 'Missing keys on dynamic lists. Each child in a list should have a unique "key" prop.'
1203
+ );
1204
+ }
1205
+ }
1206
+ }
1207
+ function createDOMNode(node) {
1208
+ if (!IS_DOM_AVAILABLE) {
1209
+ if (process.env.NODE_ENV !== "production") {
1210
+ try {
1211
+ logger.warn("[Askr] createDOMNode called in non-DOM environment");
1212
+ } catch {
1213
+ }
1214
+ }
1215
+ return null;
1216
+ }
1217
+ if (typeof node === "string") {
1218
+ return document.createTextNode(node);
1219
+ }
1220
+ if (typeof node === "number") {
1221
+ return document.createTextNode(String(node));
1222
+ }
1223
+ if (!node) {
1224
+ return null;
1225
+ }
1226
+ if (Array.isArray(node)) {
1227
+ const fragment = document.createDocumentFragment();
1228
+ for (const child of node) {
1229
+ const dom = createDOMNode(child);
1230
+ if (dom) fragment.appendChild(dom);
1231
+ }
1232
+ return fragment;
1233
+ }
1234
+ if (typeof node === "object" && node !== null && "type" in node) {
1235
+ const type = node.type;
1236
+ const props = node.props || {};
1237
+ if (typeof type === "string") {
1238
+ return createIntrinsicElement(node, type, props);
1239
+ }
1240
+ if (typeof type === "function") {
1241
+ return createComponentElement(node, type, props);
1242
+ }
1243
+ if (typeof type === "symbol" && (type === Fragment2 || String(type) === "Symbol(Fragment)")) {
1244
+ return createFragmentElement(node, props);
1245
+ }
1246
+ }
1247
+ return null;
1248
+ }
1249
+ function createIntrinsicElement(node, type, props) {
1250
+ const el = document.createElement(type);
1251
+ materializeKey(el, node, props);
1252
+ applyPropsToElement(el, props, type);
1253
+ const children = props.children || node.children;
1254
+ if (children) {
1255
+ if (Array.isArray(children)) {
1256
+ warnMissingKeys(children);
1257
+ for (const child of children) {
1258
+ const dom = createDOMNode(child);
1259
+ if (dom) el.appendChild(dom);
1260
+ }
1261
+ } else {
1262
+ const dom = createDOMNode(children);
1263
+ if (dom) el.appendChild(dom);
1264
+ }
1265
+ }
1266
+ return el;
1267
+ }
1268
+ function createComponentElement(node, type, props) {
1269
+ const frame = node[CONTEXT_FRAME_SYMBOL];
1270
+ const snapshot = frame || getCurrentContextFrame();
1271
+ const componentFn = type;
1272
+ const isAsync = componentFn.constructor.name === "AsyncFunction";
1273
+ if (isAsync) {
1274
+ throw new Error(
1275
+ "Async components are not supported. Use resource() for async work."
1276
+ );
1277
+ }
1278
+ let childInstance = node.__instance;
1279
+ if (!childInstance) {
1280
+ childInstance = createComponentInstance(
1281
+ `comp-${Math.random().toString(36).slice(2, 7)}`,
1282
+ componentFn,
1283
+ props || {},
1284
+ null
1285
+ );
1286
+ node.__instance = childInstance;
1287
+ }
1288
+ if (snapshot) {
1289
+ childInstance.ownerFrame = snapshot;
1290
+ }
1291
+ const result = withContext(
1292
+ snapshot,
1293
+ () => renderComponentInline(childInstance)
1294
+ );
1295
+ if (result instanceof Promise) {
1296
+ throw new Error(
1297
+ "Async components are not supported. Components must return synchronously."
1298
+ );
1299
+ }
1300
+ const dom = withContext(snapshot, () => createDOMNode(result));
1301
+ if (dom instanceof Element) {
1302
+ mountInstanceInline(childInstance, dom);
1303
+ return dom;
1304
+ }
1305
+ if (!dom) {
1306
+ const placeholder = document.createComment("");
1307
+ childInstance._placeholder = placeholder;
1308
+ childInstance.mounted = true;
1309
+ childInstance.notifyUpdate = childInstance._enqueueRun;
1310
+ return placeholder;
1311
+ }
1312
+ const host = document.createElement("div");
1313
+ host.appendChild(dom);
1314
+ mountInstanceInline(childInstance, host);
1315
+ return host;
1316
+ }
1317
+ function createFragmentElement(node, props) {
1318
+ const fragment = document.createDocumentFragment();
1319
+ const children = props.children || node.children;
1320
+ if (children) {
1321
+ if (Array.isArray(children)) {
1322
+ for (const child of children) {
1323
+ const dom = createDOMNode(child);
1324
+ if (dom) fragment.appendChild(dom);
1325
+ }
1326
+ } else {
1327
+ const dom = createDOMNode(children);
1328
+ if (dom) fragment.appendChild(dom);
1329
+ }
1330
+ }
1331
+ return fragment;
1332
+ }
1333
+ function updateElementFromVnode(el, vnode, updateChildren = true) {
1334
+ if (!_isDOMElement(vnode)) {
1335
+ return;
1336
+ }
1337
+ const props = vnode.props || {};
1338
+ materializeKey(el, vnode, props);
1339
+ const existingListeners = elementListeners.get(el);
1340
+ let desiredEventNames = null;
1341
+ for (const key in props) {
1342
+ const value = props[key];
1343
+ if (isSkippedProp(key)) continue;
1344
+ const eventName = parseEventName(key);
1345
+ if (value === void 0 || value === null || value === false) {
1251
1346
  if (key === "class" || key === "className") {
1252
1347
  el.className = "";
1253
1348
  } else if (eventName && existingListeners?.has(eventName)) {
@@ -1268,13 +1363,19 @@ function updateElementFromVnode(el, vnode, updateChildren = true) {
1268
1363
  } else if (key === "value" || key === "checked") {
1269
1364
  el[key] = value;
1270
1365
  } else if (eventName) {
1271
- desiredEventNames.add(eventName);
1366
+ if (existingListeners && existingListeners.size > 0) {
1367
+ (desiredEventNames ??= /* @__PURE__ */ new Set()).add(eventName);
1368
+ }
1272
1369
  const existing = existingListeners?.get(eventName);
1273
1370
  if (existing && existing.original === value) {
1274
1371
  continue;
1275
1372
  }
1276
1373
  if (existing) {
1277
- el.removeEventListener(eventName, existing.handler);
1374
+ if (existing.options !== void 0) {
1375
+ el.removeEventListener(eventName, existing.handler, existing.options);
1376
+ } else {
1377
+ el.removeEventListener(eventName, existing.handler);
1378
+ }
1278
1379
  }
1279
1380
  const wrappedHandler = createWrappedHandler(
1280
1381
  value,
@@ -1298,15 +1399,29 @@ function updateElementFromVnode(el, vnode, updateChildren = true) {
1298
1399
  el.setAttribute(key, String(value));
1299
1400
  }
1300
1401
  }
1301
- if (existingListeners) {
1302
- for (const eventName of existingListeners.keys()) {
1303
- if (!desiredEventNames.has(eventName)) {
1304
- const entry = existingListeners.get(eventName);
1305
- el.removeEventListener(eventName, entry.handler);
1306
- existingListeners.delete(eventName);
1402
+ if (existingListeners && existingListeners.size > 0) {
1403
+ if (desiredEventNames === null) {
1404
+ for (const [eventName, entry] of existingListeners) {
1405
+ if (entry.options !== void 0) {
1406
+ el.removeEventListener(eventName, entry.handler, entry.options);
1407
+ } else {
1408
+ el.removeEventListener(eventName, entry.handler);
1409
+ }
1410
+ }
1411
+ elementListeners.delete(el);
1412
+ } else {
1413
+ for (const [eventName, entry] of existingListeners) {
1414
+ if (!desiredEventNames.has(eventName)) {
1415
+ if (entry.options !== void 0) {
1416
+ el.removeEventListener(eventName, entry.handler, entry.options);
1417
+ } else {
1418
+ el.removeEventListener(eventName, entry.handler);
1419
+ }
1420
+ existingListeners.delete(eventName);
1421
+ }
1307
1422
  }
1423
+ if (existingListeners.size === 0) elementListeners.delete(el);
1308
1424
  }
1309
- if (existingListeners.size === 0) elementListeners.delete(el);
1310
1425
  }
1311
1426
  if (updateChildren) {
1312
1427
  const children = vnode.children || props.children;
@@ -1349,47 +1464,47 @@ function updateUnkeyedChildren(parent, newChildren) {
1349
1464
  }
1350
1465
  const max = Math.max(existing.length, newChildren.length);
1351
1466
  for (let i = 0; i < max; i++) {
1352
- const current2 = existing[i];
1467
+ const current = existing[i];
1353
1468
  const next = newChildren[i];
1354
- if (next === void 0 && current2) {
1355
- cleanupInstanceIfPresent(current2);
1356
- current2.remove();
1469
+ if (next === void 0 && current) {
1470
+ cleanupInstanceIfPresent(current);
1471
+ current.remove();
1357
1472
  continue;
1358
1473
  }
1359
- if (!current2 && next !== void 0) {
1474
+ if (!current && next !== void 0) {
1360
1475
  const dom = createDOMNode(next);
1361
1476
  if (dom) parent.appendChild(dom);
1362
1477
  continue;
1363
1478
  }
1364
- if (!current2 || next === void 0) continue;
1479
+ if (!current || next === void 0) continue;
1365
1480
  if (typeof next === "string" || typeof next === "number") {
1366
- current2.textContent = String(next);
1481
+ current.textContent = String(next);
1367
1482
  } else if (_isDOMElement(next)) {
1368
1483
  if (typeof next.type === "string") {
1369
- if (current2.tagName.toLowerCase() === next.type.toLowerCase()) {
1370
- updateElementFromVnode(current2, next);
1484
+ if (tagsEqualIgnoreCase(current.tagName, next.type)) {
1485
+ updateElementFromVnode(current, next);
1371
1486
  } else {
1372
1487
  const dom = createDOMNode(next);
1373
1488
  if (dom) {
1374
- if (current2 instanceof Element) removeAllListeners(current2);
1375
- cleanupInstanceIfPresent(current2);
1376
- parent.replaceChild(dom, current2);
1489
+ if (current instanceof Element) removeAllListeners(current);
1490
+ cleanupInstanceIfPresent(current);
1491
+ parent.replaceChild(dom, current);
1377
1492
  }
1378
1493
  }
1379
1494
  } else {
1380
1495
  const dom = createDOMNode(next);
1381
1496
  if (dom) {
1382
- if (current2 instanceof Element) removeAllListeners(current2);
1383
- cleanupInstanceIfPresent(current2);
1384
- parent.replaceChild(dom, current2);
1497
+ if (current instanceof Element) removeAllListeners(current);
1498
+ cleanupInstanceIfPresent(current);
1499
+ parent.replaceChild(dom, current);
1385
1500
  }
1386
1501
  }
1387
1502
  } else {
1388
1503
  const dom = createDOMNode(next);
1389
1504
  if (dom) {
1390
- if (current2 instanceof Element) removeAllListeners(current2);
1391
- cleanupInstanceIfPresent(current2);
1392
- parent.replaceChild(dom, current2);
1505
+ if (current instanceof Element) removeAllListeners(current);
1506
+ cleanupInstanceIfPresent(current);
1507
+ parent.replaceChild(dom, current);
1393
1508
  }
1394
1509
  }
1395
1510
  }
@@ -1399,31 +1514,38 @@ function performBulkPositionalKeyedTextUpdate(parent, keyedVnodes) {
1399
1514
  let reused = 0;
1400
1515
  let updatedKeys = 0;
1401
1516
  const t0 = now();
1517
+ const debugFastPath = process.env.ASKR_FASTPATH_DEBUG === "1" || process.env.ASKR_FASTPATH_DEBUG === "true";
1402
1518
  for (let i = 0; i < total; i++) {
1403
1519
  const { key, vnode } = keyedVnodes[i];
1404
1520
  const ch = parent.children[i];
1405
1521
  if (ch && _isDOMElement(vnode) && typeof vnode.type === "string") {
1406
1522
  const vnodeType = vnode.type;
1407
- if (ch.tagName.toLowerCase() === vnodeType.toLowerCase()) {
1523
+ if (tagsEqualIgnoreCase(ch.tagName, vnodeType)) {
1408
1524
  const children = vnode.children || vnode.props?.children;
1409
- logFastPathDebug("positional idx", i, {
1410
- chTag: ch.tagName.toLowerCase(),
1411
- vnodeType,
1412
- chChildNodes: ch.childNodes.length,
1413
- childrenType: Array.isArray(children) ? "array" : typeof children
1414
- });
1415
- updateTextContent(ch, children);
1525
+ if (debugFastPath) {
1526
+ logFastPathDebug("positional idx", i, {
1527
+ chTag: ch.tagName,
1528
+ vnodeType,
1529
+ chChildNodes: ch.childNodes.length,
1530
+ childrenType: Array.isArray(children) ? "array" : typeof children
1531
+ });
1532
+ }
1533
+ updateTextContent(ch, children, vnode);
1416
1534
  setDataKey(ch, key, () => updatedKeys++);
1417
1535
  reused++;
1418
1536
  continue;
1419
1537
  } else {
1420
- logFastPathDebug("positional tag mismatch", i, {
1421
- chTag: ch.tagName.toLowerCase(),
1422
- vnodeType
1423
- });
1538
+ if (debugFastPath) {
1539
+ logFastPathDebug("positional tag mismatch", i, {
1540
+ chTag: ch.tagName,
1541
+ vnodeType
1542
+ });
1543
+ }
1424
1544
  }
1425
1545
  } else {
1426
- logFastPathDebug("positional missing or invalid", i, { ch: !!ch });
1546
+ if (debugFastPath) {
1547
+ logFastPathDebug("positional missing or invalid", i, { ch: !!ch });
1548
+ }
1427
1549
  }
1428
1550
  replaceNodeAtPosition(parent, i, vnode);
1429
1551
  }
@@ -1433,14 +1555,46 @@ function performBulkPositionalKeyedTextUpdate(parent, keyedVnodes) {
1433
1555
  recordFastPathStats(stats, "bulkKeyedPositionalHits");
1434
1556
  return stats;
1435
1557
  }
1436
- function updateTextContent(el, children) {
1558
+ function updateTextContent(el, children, vnode) {
1437
1559
  if (typeof children === "string" || typeof children === "number") {
1438
1560
  setTextNodeData(el, String(children));
1439
1561
  } else if (Array.isArray(children) && children.length === 1 && (typeof children[0] === "string" || typeof children[0] === "number")) {
1440
1562
  setTextNodeData(el, String(children[0]));
1441
1563
  } else {
1442
- updateElementFromVnode(el, el);
1564
+ if (!tryUpdateTwoChildTextPattern(el, vnode)) {
1565
+ updateElementFromVnode(el, vnode);
1566
+ }
1567
+ }
1568
+ }
1569
+ function tryUpdateTwoChildTextPattern(parentEl, vnode) {
1570
+ const vnodeChildren = vnode.children || vnode.props?.children;
1571
+ if (!Array.isArray(vnodeChildren) || vnodeChildren.length !== 2) return false;
1572
+ const c0 = vnodeChildren[0];
1573
+ const c1 = vnodeChildren[1];
1574
+ if (!_isDOMElement(c0) || !_isDOMElement(c1)) return false;
1575
+ if (typeof c0.type !== "string" || typeof c1.type !== "string") return false;
1576
+ const el0 = parentEl.children[0];
1577
+ const el1 = parentEl.children[1];
1578
+ if (!el0 || !el1) return false;
1579
+ if (!tagsEqualIgnoreCase(el0.tagName, c0.type)) return false;
1580
+ if (!tagsEqualIgnoreCase(el1.tagName, c1.type)) return false;
1581
+ const t0 = c0.children || c0.props?.children;
1582
+ const t1 = c1.children || c1.props?.children;
1583
+ if (typeof t0 === "string" || typeof t0 === "number") {
1584
+ setTextNodeData(el0, String(t0));
1585
+ } else if (Array.isArray(t0) && t0.length === 1 && (typeof t0[0] === "string" || typeof t0[0] === "number")) {
1586
+ setTextNodeData(el0, String(t0[0]));
1587
+ } else {
1588
+ return false;
1589
+ }
1590
+ if (typeof t1 === "string" || typeof t1 === "number") {
1591
+ setTextNodeData(el1, String(t1));
1592
+ } else if (Array.isArray(t1) && t1.length === 1 && (typeof t1[0] === "string" || typeof t1[0] === "number")) {
1593
+ setTextNodeData(el1, String(t1[0]));
1594
+ } else {
1595
+ return false;
1443
1596
  }
1597
+ return true;
1444
1598
  }
1445
1599
  function setTextNodeData(el, text) {
1446
1600
  if (el.childNodes.length === 1 && el.firstChild?.nodeType === 3) {
@@ -1451,11 +1605,56 @@ function setTextNodeData(el, text) {
1451
1605
  }
1452
1606
  function setDataKey(el, key, onSet) {
1453
1607
  try {
1454
- el.setAttribute("data-key", String(key));
1608
+ const next = String(key);
1609
+ if (el.getAttribute("data-key") === next) return;
1610
+ el.setAttribute("data-key", next);
1455
1611
  onSet();
1456
1612
  } catch {
1457
1613
  }
1458
1614
  }
1615
+ function upperCommonTagName(tag) {
1616
+ switch (tag) {
1617
+ case "div":
1618
+ return "DIV";
1619
+ case "span":
1620
+ return "SPAN";
1621
+ case "p":
1622
+ return "P";
1623
+ case "a":
1624
+ return "A";
1625
+ case "button":
1626
+ return "BUTTON";
1627
+ case "input":
1628
+ return "INPUT";
1629
+ case "ul":
1630
+ return "UL";
1631
+ case "ol":
1632
+ return "OL";
1633
+ case "li":
1634
+ return "LI";
1635
+ default:
1636
+ return null;
1637
+ }
1638
+ }
1639
+ function tagNamesEqualIgnoreCase(a, b) {
1640
+ if (a === b) return true;
1641
+ const len = a.length;
1642
+ if (len !== b.length) return false;
1643
+ for (let i = 0; i < len; i++) {
1644
+ const ac = a.charCodeAt(i);
1645
+ const bc = b.charCodeAt(i);
1646
+ if (ac === bc) continue;
1647
+ const an = ac >= 65 && ac <= 90 ? ac + 32 : ac;
1648
+ const bn = bc >= 65 && bc <= 90 ? bc + 32 : bc;
1649
+ if (an !== bn) return false;
1650
+ }
1651
+ return true;
1652
+ }
1653
+ function tagsEqualIgnoreCase(elementTagName, vnodeType) {
1654
+ const upperCommon = upperCommonTagName(vnodeType);
1655
+ if (upperCommon !== null && elementTagName === upperCommon) return true;
1656
+ return tagNamesEqualIgnoreCase(elementTagName, vnodeType);
1657
+ }
1459
1658
  function replaceNodeAtPosition(parent, index, vnode) {
1460
1659
  const dom = createDOMNode(vnode);
1461
1660
  if (dom) {
@@ -1470,7 +1669,8 @@ function replaceNodeAtPosition(parent, index, vnode) {
1470
1669
  }
1471
1670
  function updateKeyedElementsMap(parent, keyedVnodes) {
1472
1671
  try {
1473
- const newKeyMap = /* @__PURE__ */ new Map();
1672
+ const existing = keyedElements.get(parent);
1673
+ const newKeyMap = existing ? (existing.clear(), existing) : /* @__PURE__ */ new Map();
1474
1674
  for (let i = 0; i < keyedVnodes.length; i++) {
1475
1675
  const k = keyedVnodes[i].key;
1476
1676
  const ch = parent.children[i];
@@ -1492,7 +1692,6 @@ function performBulkTextReplace(parent, newChildren) {
1492
1692
  else if (result === "created") created++;
1493
1693
  }
1494
1694
  const tBuild = now() - t0;
1495
- cleanupRemovedNodes(parent, finalNodes);
1496
1695
  const tCommit = commitBulkReplace(parent, finalNodes);
1497
1696
  keyedElements.delete(parent);
1498
1697
  const stats = {
@@ -1527,7 +1726,7 @@ function processElementVnode(vnode, existingNode, finalNodes) {
1527
1726
  const vnodeObj = vnode;
1528
1727
  if (typeof vnodeObj.type === "string") {
1529
1728
  const tag = vnodeObj.type;
1530
- if (existingNode && existingNode.nodeType === 1 && existingNode.tagName.toLowerCase() === tag.toLowerCase()) {
1729
+ if (existingNode && existingNode.nodeType === 1 && tagsEqualIgnoreCase(existingNode.tagName, tag)) {
1531
1730
  updateElementFromVnode(existingNode, vnode);
1532
1731
  finalNodes.push(existingNode);
1533
1732
  return "reused";
@@ -1540,24 +1739,21 @@ function processElementVnode(vnode, existingNode, finalNodes) {
1540
1739
  }
1541
1740
  return "skipped";
1542
1741
  }
1543
- function cleanupRemovedNodes(parent, keepNodes) {
1544
- try {
1545
- const toRemove = Array.from(parent.childNodes).filter(
1546
- (n) => !keepNodes.includes(n)
1547
- );
1548
- for (const n of toRemove) {
1549
- if (n instanceof Element) removeAllListeners(n);
1550
- cleanupInstanceIfPresent(n);
1551
- }
1552
- } catch {
1553
- }
1554
- }
1555
1742
  function commitBulkReplace(parent, nodes) {
1556
1743
  const fragStart = Date.now();
1557
1744
  const fragment = document.createDocumentFragment();
1558
1745
  for (let i = 0; i < nodes.length; i++) {
1559
1746
  fragment.appendChild(nodes[i]);
1560
1747
  }
1748
+ try {
1749
+ for (let n = parent.firstChild; n; ) {
1750
+ const next = n.nextSibling;
1751
+ if (n instanceof Element) removeAllListeners(n);
1752
+ cleanupInstanceIfPresent(n);
1753
+ n = next;
1754
+ }
1755
+ } catch {
1756
+ }
1561
1757
  recordDOMReplace("bulk-text-replace");
1562
1758
  parent.replaceChildren(fragment);
1563
1759
  return Date.now() - fragStart;
@@ -1687,9 +1883,7 @@ function applyRendererFastPath(parent, keyedVnodes, oldKeyMap, unkeyedVnodes) {
1687
1883
  } else {
1688
1884
  localOldKeyMap = /* @__PURE__ */ new Map();
1689
1885
  try {
1690
- const parentChildren = Array.from(parent.children);
1691
- for (let i = 0; i < parentChildren.length; i++) {
1692
- const ch = parentChildren[i];
1886
+ for (let ch = parent.firstElementChild; ch; ch = ch.nextElementSibling) {
1693
1887
  const k = ch.getAttribute("data-key");
1694
1888
  if (k !== null) {
1695
1889
  localOldKeyMap.set(k, ch);
@@ -1753,11 +1947,11 @@ function applyRendererFastPath(parent, keyedVnodes, oldKeyMap, unkeyedVnodes) {
1753
1947
  fragmentAppendCount++;
1754
1948
  }
1755
1949
  try {
1756
- const existing = Array.from(parent.childNodes);
1757
- const toRemove = existing.filter((n) => !finalNodes.includes(n));
1758
- for (const n of toRemove) {
1950
+ for (let n = parent.firstChild; n; ) {
1951
+ const next = n.nextSibling;
1759
1952
  if (n instanceof Element) removeAllListeners(n);
1760
1953
  cleanupInstanceIfPresent(n);
1954
+ n = next;
1761
1955
  }
1762
1956
  } catch (e) {
1763
1957
  void e;
@@ -1833,14 +2027,26 @@ var init_fastpath = __esm({
1833
2027
  init_cleanup();
1834
2028
  init_diag();
1835
2029
  init_scheduler();
1836
- init_fastlane_shared();
2030
+ init_fastlane();
1837
2031
  }
1838
2032
  });
1839
2033
 
1840
2034
  // src/renderer/reconcile.ts
1841
- function reconcileKeyedChildren(parent, newChildren, oldKeyMap) {
1842
- logReconcileDebug(newChildren);
1843
- const { keyedVnodes, unkeyedVnodes } = partitionChildren(newChildren);
2035
+ function tagNamesEqualIgnoreCase2(a, b) {
2036
+ if (a === b) return true;
2037
+ if (a.length !== b.length) return false;
2038
+ for (let i = 0; i < a.length; i++) {
2039
+ const ca = a.charCodeAt(i);
2040
+ const cb = b.charCodeAt(i);
2041
+ if (ca === cb) continue;
2042
+ const fa = ca >= 65 && ca <= 90 ? ca + 32 : ca;
2043
+ const fb = cb >= 65 && cb <= 90 ? cb + 32 : cb;
2044
+ if (fa !== fb) return false;
2045
+ }
2046
+ return true;
2047
+ }
2048
+ function reconcileKeyedChildren(parent, newChildren, oldKeyMap) {
2049
+ const { keyedVnodes, unkeyedVnodes } = partitionChildren(newChildren);
1844
2050
  const fastPathResult = tryFastPaths(
1845
2051
  parent,
1846
2052
  newChildren,
@@ -1851,20 +2057,6 @@ function reconcileKeyedChildren(parent, newChildren, oldKeyMap) {
1851
2057
  if (fastPathResult) return fastPathResult;
1852
2058
  return performFullReconciliation(parent, newChildren, keyedVnodes, oldKeyMap);
1853
2059
  }
1854
- function logReconcileDebug(newChildren) {
1855
- if (process.env.NODE_ENV !== "production") {
1856
- try {
1857
- logger.warn(
1858
- "[Askr][RECONCILE] reconcileKeyedChildren newChildren sample",
1859
- {
1860
- sample: newChildren && newChildren.length && newChildren[0] || null,
1861
- len: newChildren.length
1862
- }
1863
- );
1864
- } catch {
1865
- }
1866
- }
1867
- }
1868
2060
  function partitionChildren(newChildren) {
1869
2061
  const keyedVnodes = [];
1870
2062
  const unkeyedVnodes = [];
@@ -1922,7 +2114,6 @@ function tryPositionalBulkUpdate(parent, keyedVnodes) {
1922
2114
  const total = keyedVnodes.length;
1923
2115
  if (total < 10) return null;
1924
2116
  const matchCount = countPositionalMatches(parent, keyedVnodes);
1925
- logPositionalCheck(total, matchCount, parent.children.length);
1926
2117
  if (matchCount / total < 0.9) return null;
1927
2118
  if (hasPositionalPropChanges(parent, keyedVnodes)) return null;
1928
2119
  try {
@@ -1943,7 +2134,7 @@ function countPositionalMatches(parent, keyedVnodes) {
1943
2134
  continue;
1944
2135
  const el = parent.children[i];
1945
2136
  if (!el) continue;
1946
- if (el.tagName.toLowerCase() === String(vnode.type).toLowerCase()) {
2137
+ if (tagNamesEqualIgnoreCase2(el.tagName, vnode.type)) {
1947
2138
  matchCount++;
1948
2139
  }
1949
2140
  }
@@ -1951,18 +2142,6 @@ function countPositionalMatches(parent, keyedVnodes) {
1951
2142
  }
1952
2143
  return matchCount;
1953
2144
  }
1954
- function logPositionalCheck(total, matchCount, parentChildren) {
1955
- if (process.env.NODE_ENV !== "production") {
1956
- try {
1957
- logger.warn("[Askr][FASTPATH] positional check", {
1958
- total,
1959
- matchCount,
1960
- parentChildren
1961
- });
1962
- } catch {
1963
- }
1964
- }
1965
- }
1966
2145
  function hasPositionalPropChanges(parent, keyedVnodes) {
1967
2146
  try {
1968
2147
  for (let i = 0; i < keyedVnodes.length; i++) {
@@ -1981,9 +2160,7 @@ function hasPositionalPropChanges(parent, keyedVnodes) {
1981
2160
  function rebuildKeyedMap(parent) {
1982
2161
  try {
1983
2162
  const map = /* @__PURE__ */ new Map();
1984
- const children = Array.from(parent.children);
1985
- for (let i = 0; i < children.length; i++) {
1986
- const el = children[i];
2163
+ for (let el = parent.firstElementChild; el; el = el.nextElementSibling) {
1987
2164
  const k = el.getAttribute("data-key");
1988
2165
  if (k !== null) {
1989
2166
  map.set(k, el);
@@ -2044,18 +2221,19 @@ function createOldElResolver(parent, oldKeyMap, usedOldEls) {
2044
2221
  }
2045
2222
  function scanForElementByKey(parent, k, keyStr, usedOldEls) {
2046
2223
  try {
2047
- const children = Array.from(parent.children);
2048
- for (const ch of children) {
2224
+ for (let ch = parent.firstElementChild; ch; ch = ch.nextElementSibling) {
2049
2225
  if (usedOldEls.has(ch)) continue;
2050
2226
  const attr = ch.getAttribute("data-key");
2051
2227
  if (attr === keyStr) {
2052
2228
  usedOldEls.add(ch);
2053
2229
  return ch;
2054
2230
  }
2055
- const numAttr = Number(attr);
2056
- if (!Number.isNaN(numAttr) && numAttr === k) {
2057
- usedOldEls.add(ch);
2058
- return ch;
2231
+ if (attr !== null) {
2232
+ const numAttr = Number(attr);
2233
+ if (!Number.isNaN(numAttr) && numAttr === k) {
2234
+ usedOldEls.add(ch);
2235
+ return ch;
2236
+ }
2059
2237
  }
2060
2238
  }
2061
2239
  } catch {
@@ -2072,9 +2250,17 @@ function reconcileSingleChild(child, index, parent, resolveOldElOnce, usedOldEls
2072
2250
  function reconcileKeyedChild(child, key, parent, resolveOldElOnce, newKeyMap) {
2073
2251
  const el = resolveOldElOnce(key);
2074
2252
  if (el && el.parentElement === parent) {
2075
- updateElementFromVnode(el, child);
2076
- newKeyMap.set(key, el);
2077
- return el;
2253
+ try {
2254
+ const childObj = child;
2255
+ if (childObj && typeof childObj === "object" && typeof childObj.type === "string") {
2256
+ if (tagNamesEqualIgnoreCase2(el.tagName, childObj.type)) {
2257
+ updateElementFromVnode(el, child);
2258
+ newKeyMap.set(key, el);
2259
+ return el;
2260
+ }
2261
+ }
2262
+ } catch {
2263
+ }
2078
2264
  }
2079
2265
  const dom = createDOMNode(child);
2080
2266
  if (dom) {
@@ -2111,13 +2297,16 @@ function canReuseElement(existing, child) {
2111
2297
  if (typeof child !== "object" || child === null || !("type" in child))
2112
2298
  return false;
2113
2299
  const childObj = child;
2114
- const hasNoKey = existing.getAttribute("data-key") === null || existing.getAttribute("data-key") === void 0;
2115
- return hasNoKey && typeof childObj.type === "string" && existing.tagName.toLowerCase() === String(childObj.type).toLowerCase();
2300
+ const existingKey = existing.getAttribute("data-key");
2301
+ const hasNoKey = existingKey === null || existingKey === void 0;
2302
+ return hasNoKey && typeof childObj.type === "string" && tagNamesEqualIgnoreCase2(existing.tagName, childObj.type);
2116
2303
  }
2117
2304
  function findAvailableUnkeyedElement(parent, usedOldEls) {
2118
- return Array.from(parent.children).find(
2119
- (ch) => !usedOldEls.has(ch) && ch.getAttribute("data-key") === null
2120
- );
2305
+ for (let ch = parent.firstElementChild; ch; ch = ch.nextElementSibling) {
2306
+ if (usedOldEls.has(ch)) continue;
2307
+ if (ch.getAttribute("data-key") === null) return ch;
2308
+ }
2309
+ return void 0;
2121
2310
  }
2122
2311
  function tryReuseElement(avail, child, usedOldEls) {
2123
2312
  if (typeof child === "string" || typeof child === "number") {
@@ -2127,7 +2316,7 @@ function tryReuseElement(avail, child, usedOldEls) {
2127
2316
  }
2128
2317
  if (typeof child === "object" && child !== null && "type" in child) {
2129
2318
  const childObj = child;
2130
- if (typeof childObj.type === "string" && avail.tagName.toLowerCase() === String(childObj.type).toLowerCase()) {
2319
+ if (typeof childObj.type === "string" && tagNamesEqualIgnoreCase2(avail.tagName, childObj.type)) {
2131
2320
  updateElementFromVnode(avail, child);
2132
2321
  usedOldEls.add(avail);
2133
2322
  return avail;
@@ -2141,10 +2330,11 @@ function commitReconciliation(parent, finalNodes) {
2141
2330
  fragment.appendChild(finalNodes[i]);
2142
2331
  }
2143
2332
  try {
2144
- const existing = Array.from(parent.childNodes);
2145
- for (const n of existing) {
2333
+ for (let n = parent.firstChild; n; ) {
2334
+ const next = n.nextSibling;
2146
2335
  if (n instanceof Element) removeAllListeners(n);
2147
2336
  cleanupInstanceIfPresent(n);
2337
+ n = next;
2148
2338
  }
2149
2339
  } catch {
2150
2340
  }
@@ -2157,14 +2347,26 @@ var init_reconcile = __esm({
2157
2347
  init_dom();
2158
2348
  init_keyed();
2159
2349
  init_cleanup();
2160
- init_fastlane_shared();
2350
+ init_fastlane();
2161
2351
  init_fastpath();
2162
- init_logger();
2163
2352
  init_utils();
2164
2353
  }
2165
2354
  });
2166
2355
 
2167
2356
  // src/renderer/evaluate.ts
2357
+ function tagNamesEqualIgnoreCase3(a, b) {
2358
+ if (a === b) return true;
2359
+ if (a.length !== b.length) return false;
2360
+ for (let i = 0; i < a.length; i++) {
2361
+ const ca = a.charCodeAt(i);
2362
+ const cb = b.charCodeAt(i);
2363
+ if (ca === cb) continue;
2364
+ const fa = ca >= 65 && ca <= 90 ? ca + 32 : ca;
2365
+ const fb = cb >= 65 && cb <= 90 ? cb + 32 : cb;
2366
+ if (fa !== fb) return false;
2367
+ }
2368
+ return true;
2369
+ }
2168
2370
  function checkSimpleText(vnodeChildren) {
2169
2371
  if (!Array.isArray(vnodeChildren)) {
2170
2372
  if (typeof vnodeChildren === "string" || typeof vnodeChildren === "number") {
@@ -2187,8 +2389,7 @@ function tryUpdateTextInPlace(element, text) {
2187
2389
  }
2188
2390
  function buildKeyMapFromDOM(parent) {
2189
2391
  const keyMap = /* @__PURE__ */ new Map();
2190
- const children = Array.from(parent.children);
2191
- for (const child of children) {
2392
+ for (let child = parent.firstElementChild; child; child = child.nextElementSibling) {
2192
2393
  const k = child.getAttribute("data-key");
2193
2394
  if (k !== null) {
2194
2395
  keyMap.set(k, child);
@@ -2209,9 +2410,10 @@ function getOrBuildKeyMap(parent) {
2209
2410
  return keyMap.size > 0 ? keyMap : void 0;
2210
2411
  }
2211
2412
  function hasKeyedChildren(children) {
2212
- return children.some(
2213
- (child) => typeof child === "object" && child !== null && "key" in child
2214
- );
2413
+ for (let i = 0; i < children.length; i++) {
2414
+ if (extractKey(children[i]) !== void 0) return true;
2415
+ }
2416
+ return false;
2215
2417
  }
2216
2418
  function trackBulkTextStats(stats) {
2217
2419
  if (process.env.NODE_ENV !== "production") {
@@ -2328,12 +2530,16 @@ function smartUpdateElement(element, vnode) {
2328
2530
  updateElementFromVnode(element, vnode, false);
2329
2531
  }
2330
2532
  function processFragmentChildren(target, childArray) {
2331
- const existingChildren = Array.from(target.children);
2533
+ let existingNode = target.firstElementChild;
2332
2534
  for (let i = 0; i < childArray.length; i++) {
2333
2535
  const childVnode = childArray[i];
2334
- const existingNode = existingChildren[i];
2335
- if (existingNode && _isDOMElement(childVnode) && typeof childVnode.type === "string" && existingNode.tagName.toLowerCase() === childVnode.type.toLowerCase()) {
2536
+ const nextExisting = existingNode ? existingNode.nextElementSibling : null;
2537
+ if (existingNode && _isDOMElement(childVnode) && typeof childVnode.type === "string" && tagNamesEqualIgnoreCase3(
2538
+ existingNode.tagName,
2539
+ childVnode.type
2540
+ )) {
2336
2541
  smartUpdateElement(existingNode, childVnode);
2542
+ existingNode = nextExisting;
2337
2543
  continue;
2338
2544
  }
2339
2545
  const newDom = createDOMNode(childVnode);
@@ -2344,39 +2550,29 @@ function processFragmentChildren(target, childArray) {
2344
2550
  target.appendChild(newDom);
2345
2551
  }
2346
2552
  }
2553
+ existingNode = nextExisting;
2347
2554
  }
2348
- while (target.children.length > childArray.length) {
2349
- target.removeChild(target.lastChild);
2555
+ while (existingNode) {
2556
+ const next = existingNode.nextElementSibling;
2557
+ target.removeChild(existingNode);
2558
+ existingNode = next;
2350
2559
  }
2351
2560
  }
2352
- function createWrappedEventHandler(handler) {
2353
- return (event) => {
2354
- globalScheduler.setInHandler(true);
2355
- try {
2356
- handler(event);
2357
- } catch (error) {
2358
- logger.error("[Askr] Event handler error:", error);
2359
- } finally {
2360
- globalScheduler.setInHandler(false);
2361
- }
2362
- };
2363
- }
2364
2561
  function applyPropsToElement2(el, props) {
2365
2562
  for (const [key, value] of Object.entries(props)) {
2366
2563
  if (key === "children" || key === "key") continue;
2367
2564
  if (value === void 0 || value === null || value === false) continue;
2368
- if (key.startsWith("on") && key.length > 2) {
2369
- const eventName = key.slice(2).charAt(0).toLowerCase() + key.slice(3).toLowerCase();
2370
- const wrappedHandler = createWrappedEventHandler(value);
2371
- const options = eventName === "wheel" || eventName === "scroll" || eventName.startsWith("touch") ? { passive: true } : void 0;
2372
- if (options !== void 0) {
2565
+ const eventName = parseEventName(key);
2566
+ if (eventName) {
2567
+ const wrappedHandler = createWrappedHandler(
2568
+ value,
2569
+ false
2570
+ );
2571
+ const options = getPassiveOptions(eventName);
2572
+ if (options !== void 0)
2373
2573
  el.addEventListener(eventName, wrappedHandler, options);
2374
- } else {
2375
- el.addEventListener(eventName, wrappedHandler);
2376
- }
2377
- if (!elementListeners.has(el)) {
2378
- elementListeners.set(el, /* @__PURE__ */ new Map());
2379
- }
2574
+ else el.addEventListener(eventName, wrappedHandler);
2575
+ if (!elementListeners.has(el)) elementListeners.set(el, /* @__PURE__ */ new Map());
2380
2576
  elementListeners.get(el).set(eventName, {
2381
2577
  handler: wrappedHandler,
2382
2578
  original: value,
@@ -2426,11 +2622,11 @@ function evaluate(node, target, context) {
2426
2622
  }
2427
2623
  if (context && domRanges.has(context)) {
2428
2624
  const range = domRanges.get(context);
2429
- let current2 = range.start.nextSibling;
2430
- while (current2 && current2 !== range.end) {
2431
- const next = current2.nextSibling;
2432
- current2.remove();
2433
- current2 = next;
2625
+ let current = range.start.nextSibling;
2626
+ while (current && current !== range.end) {
2627
+ const next = current.nextSibling;
2628
+ current.remove();
2629
+ current = next;
2434
2630
  }
2435
2631
  const dom = createDOMNode(node);
2436
2632
  if (dom) {
@@ -2458,7 +2654,7 @@ function evaluate(node, target, context) {
2458
2654
  }
2459
2655
  }
2460
2656
  const firstChild = target.children[0];
2461
- if (firstChild && _isDOMElement(vnode) && typeof vnode.type === "string" && firstChild.tagName.toLowerCase() === vnode.type.toLowerCase()) {
2657
+ if (firstChild && _isDOMElement(vnode) && typeof vnode.type === "string" && tagNamesEqualIgnoreCase3(firstChild.tagName, vnode.type)) {
2462
2658
  smartUpdateElement(firstChild, vnode);
2463
2659
  } else {
2464
2660
  target.textContent = "";
@@ -2476,7 +2672,6 @@ var domRanges;
2476
2672
  var init_evaluate = __esm({
2477
2673
  "src/renderer/evaluate.ts"() {
2478
2674
  "use strict";
2479
- init_scheduler();
2480
2675
  init_logger();
2481
2676
  init_cleanup();
2482
2677
  init_keyed();
@@ -2484,7 +2679,8 @@ var init_evaluate = __esm({
2484
2679
  init_types();
2485
2680
  init_dom();
2486
2681
  init_diag();
2487
- init_types2();
2682
+ init_jsx();
2683
+ init_utils();
2488
2684
  domRanges = /* @__PURE__ */ new WeakMap();
2489
2685
  }
2490
2686
  });
@@ -2564,23 +2760,6 @@ function createComponentInstance(id, fn, props, target) {
2564
2760
  function getCurrentComponentInstance() {
2565
2761
  return currentInstance;
2566
2762
  }
2567
- function setCurrentComponentInstance(instance) {
2568
- currentInstance = instance;
2569
- }
2570
- function registerMountOperation(operation) {
2571
- const instance = getCurrentComponentInstance();
2572
- if (instance) {
2573
- if (isBulkCommitActive2()) {
2574
- if (process.env.NODE_ENV !== "production") {
2575
- throw new Error(
2576
- "registerMountOperation called during bulk commit fast-lane"
2577
- );
2578
- }
2579
- return;
2580
- }
2581
- instance.mountOperations.push(operation);
2582
- }
2583
- }
2584
2763
  function executeMountOperations(instance) {
2585
2764
  if (!instance.isRoot) return;
2586
2765
  for (const operation of instance.mountOperations) {
@@ -2633,7 +2812,7 @@ function runComponent(instance) {
2633
2812
  globalScheduler.enqueue(() => {
2634
2813
  if (!instance.target && instance._placeholder) {
2635
2814
  if (result === null || result === void 0) {
2636
- finalizeReadSubscriptions(instance);
2815
+ finalizeReadSubscriptions2(instance);
2637
2816
  return;
2638
2817
  }
2639
2818
  const placeholder = instance._placeholder;
@@ -2653,7 +2832,7 @@ function runComponent(instance) {
2653
2832
  instance.target = host;
2654
2833
  instance._placeholder = void 0;
2655
2834
  host.__ASKR_INSTANCE = instance;
2656
- finalizeReadSubscriptions(instance);
2835
+ finalizeReadSubscriptions2(instance);
2657
2836
  } finally {
2658
2837
  currentInstance = oldInstance;
2659
2838
  }
@@ -2698,7 +2877,7 @@ function runComponent(instance) {
2698
2877
  } finally {
2699
2878
  currentInstance = oldInstance;
2700
2879
  }
2701
- finalizeReadSubscriptions(instance);
2880
+ finalizeReadSubscriptions2(instance);
2702
2881
  instance.mounted = true;
2703
2882
  if (wasFirstMount && instance.mountOperations.length > 0) {
2704
2883
  executeMountOperations(instance);
@@ -2750,7 +2929,7 @@ function renderComponentInline(instance) {
2750
2929
  try {
2751
2930
  const result = executeComponentSync(instance);
2752
2931
  if (!hadToken) {
2753
- finalizeReadSubscriptions(instance);
2932
+ finalizeReadSubscriptions2(instance);
2754
2933
  }
2755
2934
  return result;
2756
2935
  } finally {
@@ -2818,15 +2997,7 @@ function executeComponent(instance) {
2818
2997
  function getCurrentInstance() {
2819
2998
  return currentInstance;
2820
2999
  }
2821
- function getSignal() {
2822
- if (!currentInstance) {
2823
- throw new Error(
2824
- "getSignal() can only be called during component render execution. Ensure you are calling this from inside your component function."
2825
- );
2826
- }
2827
- return currentInstance.abortController.signal;
2828
- }
2829
- function finalizeReadSubscriptions(instance) {
3000
+ function finalizeReadSubscriptions2(instance) {
2830
3001
  const newSet = instance._pendingReadStates ?? /* @__PURE__ */ new Set();
2831
3002
  const oldSet = instance._lastReadStates ?? /* @__PURE__ */ new Set();
2832
3003
  const token = instance._currentRenderToken;
@@ -2897,7 +3068,7 @@ var init_component = __esm({
2897
3068
  init_context();
2898
3069
  init_logger();
2899
3070
  init_diag();
2900
- init_fastlane_shared();
3071
+ init_fastlane();
2901
3072
  init_renderer();
2902
3073
  currentInstance = null;
2903
3074
  stateIndex = 0;
@@ -2905,97 +3076,6 @@ var init_component = __esm({
2905
3076
  }
2906
3077
  });
2907
3078
 
2908
- // src/ssr/errors.ts
2909
- var SSRDataMissingError;
2910
- var init_errors = __esm({
2911
- "src/ssr/errors.ts"() {
2912
- "use strict";
2913
- SSRDataMissingError = class _SSRDataMissingError extends Error {
2914
- constructor(message = "Server-side rendering requires all data to be available synchronously. This component attempted to use async data during SSR.") {
2915
- super(message);
2916
- this.code = "SSR_DATA_MISSING";
2917
- this.name = "SSRDataMissingError";
2918
- Object.setPrototypeOf(this, _SSRDataMissingError.prototype);
2919
- }
2920
- };
2921
- }
2922
- });
2923
-
2924
- // src/ssr/context.ts
2925
- function withSSRContext(ctx, fn) {
2926
- const prev = current;
2927
- current = ctx;
2928
- try {
2929
- return fn();
2930
- } finally {
2931
- current = prev;
2932
- }
2933
- }
2934
- function createRenderContext(seed = 12345) {
2935
- return { seed };
2936
- }
2937
- function getCurrentSSRContext() {
2938
- return currentSSRContext;
2939
- }
2940
- function runWithSSRContext(ctx, fn) {
2941
- const prev = currentSSRContext;
2942
- currentSSRContext = ctx;
2943
- try {
2944
- return fn();
2945
- } finally {
2946
- currentSSRContext = prev;
2947
- }
2948
- }
2949
- function throwSSRDataMissing() {
2950
- throw new SSRDataMissingError();
2951
- }
2952
- var current, currentSSRContext;
2953
- var init_context2 = __esm({
2954
- "src/ssr/context.ts"() {
2955
- "use strict";
2956
- init_errors();
2957
- current = null;
2958
- currentSSRContext = null;
2959
- }
2960
- });
2961
-
2962
- // src/ssr/render-keys.ts
2963
- function getCurrentRenderData() {
2964
- return currentRenderData;
2965
- }
2966
- function resetKeyCounter() {
2967
- keyCounter = 0;
2968
- }
2969
- function getNextKey() {
2970
- return `r:${keyCounter++}`;
2971
- }
2972
- function startRenderPhase(data) {
2973
- currentRenderData = data ?? null;
2974
- resetKeyCounter();
2975
- }
2976
- function stopRenderPhase() {
2977
- currentRenderData = null;
2978
- resetKeyCounter();
2979
- }
2980
- async function resolvePlan(_plan) {
2981
- throw new Error(
2982
- `${PREPASS_REMOVED_MSG}; async resource plans are not supported`
2983
- );
2984
- }
2985
- function collectResources(_opts) {
2986
- throw new Error(`${PREPASS_REMOVED_MSG}; collectResources is disabled`);
2987
- }
2988
- var keyCounter, currentRenderData, PREPASS_REMOVED_MSG, resolveResources;
2989
- var init_render_keys = __esm({
2990
- "src/ssr/render-keys.ts"() {
2991
- "use strict";
2992
- keyCounter = 0;
2993
- currentRenderData = null;
2994
- PREPASS_REMOVED_MSG = "SSR collection/prepass is removed: SSR is strictly synchronous";
2995
- resolveResources = resolvePlan;
2996
- }
2997
- });
2998
-
2999
3079
  // src/router/match.ts
3000
3080
  function match(path, pattern) {
3001
3081
  const normalizedPath = path.endsWith("/") && path !== "/" ? path.slice(0, -1) : path;
@@ -3034,6 +3114,29 @@ var init_match = __esm({
3034
3114
  }
3035
3115
  });
3036
3116
 
3117
+ // src/runtime/execution-model.ts
3118
+ function getExecutionModel() {
3119
+ const g = globalThis;
3120
+ return g[EXECUTION_MODEL_KEY];
3121
+ }
3122
+ function assertExecutionModel(model) {
3123
+ const g = globalThis;
3124
+ const cur = g[EXECUTION_MODEL_KEY];
3125
+ if (cur && cur !== model) {
3126
+ throw new Error(
3127
+ `[Askr] mixing execution models is not allowed (current: ${cur}, attempted: ${model}). Choose exactly one: createSPA, createSSR, or createIslands.`
3128
+ );
3129
+ }
3130
+ if (!cur) g[EXECUTION_MODEL_KEY] = model;
3131
+ }
3132
+ var EXECUTION_MODEL_KEY;
3133
+ var init_execution_model = __esm({
3134
+ "src/runtime/execution-model.ts"() {
3135
+ "use strict";
3136
+ EXECUTION_MODEL_KEY = /* @__PURE__ */ Symbol.for("__ASKR_EXECUTION_MODEL__");
3137
+ }
3138
+ });
3139
+
3037
3140
  // src/router/route.ts
3038
3141
  var route_exports = {};
3039
3142
  __export(route_exports, {
@@ -3050,6 +3153,13 @@ __export(route_exports, {
3050
3153
  setServerLocation: () => setServerLocation,
3051
3154
  unloadNamespace: () => unloadNamespace
3052
3155
  });
3156
+ function setHasRoutes(value) {
3157
+ try {
3158
+ const g = globalThis;
3159
+ g[HAS_ROUTES_KEY] = value;
3160
+ } catch {
3161
+ }
3162
+ }
3053
3163
  function getDepth(path) {
3054
3164
  const normalized = path.endsWith("/") && path !== "/" ? path.slice(0, -1) : path;
3055
3165
  return normalized === "/" ? 0 : normalized.split("/").filter(Boolean).length;
@@ -3168,6 +3278,11 @@ function _unlockRouteRegistrationForTests() {
3168
3278
  registrationLocked = false;
3169
3279
  }
3170
3280
  function route(path, handler, namespace) {
3281
+ if (getExecutionModel() === "islands") {
3282
+ throw new Error(
3283
+ "Routes are not supported with islands. Use createSPA (client) or createSSR (server) instead."
3284
+ );
3285
+ }
3171
3286
  if (typeof path === "undefined") {
3172
3287
  const instance = getCurrentComponentInstance();
3173
3288
  if (!instance) {
@@ -3210,7 +3325,7 @@ function route(path, handler, namespace) {
3210
3325
  }
3211
3326
  if (registrationLocked) {
3212
3327
  throw new Error(
3213
- "Route registration is locked after app startup. Register routes at module load time before calling createIsland()."
3328
+ "Route registration is locked after app startup. Register routes at module load time before calling createSPA or createSSR."
3214
3329
  );
3215
3330
  }
3216
3331
  if (typeof handler !== "function") {
@@ -3220,6 +3335,7 @@ function route(path, handler, namespace) {
3220
3335
  }
3221
3336
  const routeObj = { path, handler, namespace };
3222
3337
  routes.push(routeObj);
3338
+ setHasRoutes(true);
3223
3339
  const depth = getDepth(path);
3224
3340
  let depthRoutes = routesByDepth.get(depth);
3225
3341
  if (!depthRoutes) {
@@ -3243,1373 +3359,303 @@ function unloadNamespace(namespace) {
3243
3359
  if (routes[i].namespace === namespace) {
3244
3360
  const removed = routes[i];
3245
3361
  routes.splice(i, 1);
3246
- const depth = getDepth(removed.path);
3247
- const depthRoutes = routesByDepth.get(depth);
3248
- if (depthRoutes) {
3249
- const idx = depthRoutes.indexOf(removed);
3250
- if (idx >= 0) {
3251
- depthRoutes.splice(idx, 1);
3252
- }
3253
- }
3254
- }
3255
- }
3256
- namespaces.delete(namespace);
3257
- return before - routes.length;
3258
- }
3259
- function clearRoutes() {
3260
- routes.length = 0;
3261
- namespaces.clear();
3262
- routesByDepth.clear();
3263
- }
3264
- function normalizeHandler(handler) {
3265
- if (handler == null) return void 0;
3266
- if (typeof handler === "function") {
3267
- return (params, ctx) => {
3268
- try {
3269
- return handler(params, ctx);
3270
- } catch {
3271
- return handler(params);
3272
- }
3273
- };
3274
- }
3275
- return void 0;
3276
- }
3277
- function registerRoute(path, handler, ...children) {
3278
- const isRelative = !path.startsWith("/");
3279
- const descriptor = {
3280
- path,
3281
- handler,
3282
- children: children.filter(Boolean),
3283
- _isDescriptor: true
3284
- };
3285
- if (!isRelative) {
3286
- const normalized = normalizeHandler(handler);
3287
- if (handler != null && !normalized) {
3288
- throw new Error(
3289
- "registerRoute(path, handler) requires a function handler. Passing JSX elements or VNodes directly is not supported."
3290
- );
3291
- }
3292
- if (normalized) route(path, normalized);
3293
- for (const child of descriptor.children || []) {
3294
- const base = path === "/" ? "" : path.replace(/\/$/, "");
3295
- const childPath = `${base}/${child.path.replace(/^\//, "")}`.replace(
3296
- /\/\//g,
3297
- "/"
3298
- );
3299
- if (child.handler) {
3300
- const childNormalized = normalizeHandler(child.handler);
3301
- if (!childNormalized) {
3302
- throw new Error(
3303
- "registerRoute child handler must be a function. Passing JSX elements directly is not supported."
3304
- );
3305
- }
3306
- if (childNormalized) route(childPath, childNormalized);
3307
- }
3308
- if (child.children && child.children.length) {
3309
- registerRoute(
3310
- childPath,
3311
- null,
3312
- ...child.children
3313
- );
3314
- }
3315
- }
3316
- return descriptor;
3317
- }
3318
- return descriptor;
3319
- }
3320
- function getLoadedNamespaces() {
3321
- return Array.from(namespaces);
3322
- }
3323
- function resolveRoute(pathname) {
3324
- const normalized = pathname.endsWith("/") && pathname !== "/" ? pathname.slice(0, -1) : pathname;
3325
- const depth = normalized === "/" ? 0 : normalized.split("/").filter(Boolean).length;
3326
- const candidates = [];
3327
- const depthRoutes = routesByDepth.get(depth);
3328
- if (depthRoutes) {
3329
- for (const r of depthRoutes) {
3330
- const result = match(pathname, r.path);
3331
- if (result.matched) {
3332
- candidates.push({
3333
- route: r,
3334
- specificity: getSpecificity(r.path),
3335
- params: result.params
3336
- });
3337
- }
3338
- }
3339
- }
3340
- for (const r of routes) {
3341
- if (depthRoutes?.includes(r)) continue;
3342
- const result = match(pathname, r.path);
3343
- if (result.matched) {
3344
- candidates.push({
3345
- route: r,
3346
- specificity: getSpecificity(r.path),
3347
- params: result.params
3348
- });
3349
- }
3350
- }
3351
- candidates.sort((a, b) => b.specificity - a.specificity);
3352
- if (candidates.length > 0) {
3353
- const best = candidates[0];
3354
- return { handler: best.route.handler, params: best.params };
3355
- }
3356
- return null;
3357
- }
3358
- var routes, namespaces, routesByDepth, serverLocation, registrationLocked;
3359
- var init_route = __esm({
3360
- "src/router/route.ts"() {
3361
- "use strict";
3362
- init_match();
3363
- init_component();
3364
- routes = [];
3365
- namespaces = /* @__PURE__ */ new Set();
3366
- routesByDepth = /* @__PURE__ */ new Map();
3367
- serverLocation = null;
3368
- registrationLocked = false;
3369
- }
3370
- });
3371
-
3372
- // src/router/navigate.ts
3373
- var navigate_exports = {};
3374
- __export(navigate_exports, {
3375
- cleanupNavigation: () => cleanupNavigation,
3376
- initializeNavigation: () => initializeNavigation,
3377
- navigate: () => navigate,
3378
- registerAppInstance: () => registerAppInstance
3379
- });
3380
- function registerAppInstance(instance, _path) {
3381
- currentInstance2 = instance;
3382
- if (process.env.NODE_ENV === "production") {
3383
- lockRouteRegistration();
3384
- }
3385
- }
3386
- function navigate(path) {
3387
- if (typeof window === "undefined") {
3388
- return;
3389
- }
3390
- const resolved = resolveRoute(path);
3391
- if (!resolved) {
3392
- if (process.env.NODE_ENV !== "production") {
3393
- logger.warn(`No route found for path: ${path}`);
3394
- }
3395
- return;
3396
- }
3397
- window.history.pushState({ path }, "", path);
3398
- if (currentInstance2) {
3399
- cleanupComponent(currentInstance2);
3400
- currentInstance2.fn = resolved.handler;
3401
- currentInstance2.props = resolved.params;
3402
- currentInstance2.stateValues = [];
3403
- currentInstance2.expectedStateIndices = [];
3404
- currentInstance2.firstRenderComplete = false;
3405
- currentInstance2.stateIndexCheck = -1;
3406
- currentInstance2.evaluationGeneration++;
3407
- currentInstance2.notifyUpdate = null;
3408
- currentInstance2.abortController = new AbortController();
3409
- mountComponent(currentInstance2);
3410
- }
3411
- }
3412
- function handlePopState(_event) {
3413
- const path = window.location.pathname;
3414
- if (!currentInstance2) {
3415
- return;
3416
- }
3417
- const resolved = resolveRoute(path);
3418
- if (resolved) {
3419
- cleanupComponent(currentInstance2);
3420
- currentInstance2.fn = resolved.handler;
3421
- currentInstance2.props = resolved.params;
3422
- currentInstance2.stateValues = [];
3423
- currentInstance2.expectedStateIndices = [];
3424
- currentInstance2.firstRenderComplete = false;
3425
- currentInstance2.stateIndexCheck = -1;
3426
- currentInstance2.evaluationGeneration++;
3427
- currentInstance2.notifyUpdate = null;
3428
- currentInstance2.abortController = new AbortController();
3429
- mountComponent(currentInstance2);
3430
- }
3431
- }
3432
- function initializeNavigation() {
3433
- if (typeof window !== "undefined") {
3434
- window.addEventListener("popstate", handlePopState);
3435
- }
3436
- }
3437
- function cleanupNavigation() {
3438
- if (typeof window !== "undefined") {
3439
- window.removeEventListener("popstate", handlePopState);
3440
- }
3441
- }
3442
- var currentInstance2;
3443
- var init_navigate = __esm({
3444
- "src/router/navigate.ts"() {
3445
- "use strict";
3446
- init_route();
3447
- init_component();
3448
- init_logger();
3449
- currentInstance2 = null;
3450
- }
3451
- });
3452
-
3453
- // src/jsx/utils.ts
3454
- function isElement(value) {
3455
- return typeof value === "object" && value !== null && value.$$typeof === ELEMENT_TYPE;
3456
- }
3457
- function cloneElement(element, props) {
3458
- return {
3459
- ...element,
3460
- props: { ...element.props, ...props }
3461
- };
3462
- }
3463
- var init_utils2 = __esm({
3464
- "src/jsx/utils.ts"() {
3465
- "use strict";
3466
- init_types2();
3467
- }
3468
- });
3469
-
3470
- // src/jsx/index.ts
3471
- var init_jsx = __esm({
3472
- "src/jsx/index.ts"() {
3473
- "use strict";
3474
- init_types2();
3475
- init_utils2();
3476
- }
3477
- });
3478
-
3479
- // src/foundations/portal.tsx
3480
- function definePortal() {
3481
- if (typeof createPortalSlot !== "function") {
3482
- let HostFallback2 = function() {
3483
- hosts = hosts.filter((h) => h.mounted !== false);
3484
- const inst = getCurrentComponentInstance();
3485
- if (inst && !hosts.includes(inst)) hosts.push(inst);
3486
- const owner = hosts.find((h) => h.mounted === true) || null;
3487
- const mountedHosts = hosts.filter((h) => h.mounted === true);
3488
- if (mountedHosts.length > 1) {
3489
- logger.warn(
3490
- "[Portal] multiple hosts are mounted for same portal; first mounted host will be owner"
3491
- );
3492
- }
3493
- if (inst && owner && owner !== inst) {
3494
- logger.debug(
3495
- "[Portal] non-owner reader detected; only owner renders portal content"
3496
- );
3497
- }
3498
- if (process.env.NODE_ENV !== "production") {
3499
- const ns = globalThis.__ASKR__ || (globalThis.__ASKR__ = {});
3500
- ns.__PORTAL_READS = (ns.__PORTAL_READS || 0) + 1;
3501
- }
3502
- if (process.env.NODE_ENV !== "production" && inst && owner && inst === owner) {
3503
- logger.debug("[Portal] owner read ->", inst.id, "pending=", pending);
3504
- const ns = globalThis.__ASKR__ || (globalThis.__ASKR__ = {});
3505
- ns.__PORTAL_HOST_ATTACHED = !!(inst && inst.target);
3506
- ns.__PORTAL_HOST_ID = inst ? inst.id : void 0;
3507
- }
3508
- return inst === owner ? pending : void 0;
3509
- };
3510
- var HostFallback = HostFallback2;
3511
- let hosts = [];
3512
- let pending;
3513
- HostFallback2.render = function RenderFallback(props) {
3514
- hosts = hosts.filter((h) => h.mounted !== false);
3515
- const owner = hosts.find((h) => h.mounted === true) || null;
3516
- if (!owner) {
3517
- logger.debug(
3518
- "[Portal] fallback.write dropped -> no owner or not mounted",
3519
- props?.children
3520
- );
3521
- return null;
3522
- }
3523
- pending = props.children;
3524
- logger.debug("[Portal] fallback.write ->", pending, "owner=", owner.id);
3525
- if (process.env.NODE_ENV !== "production") {
3526
- const ns = globalThis.__ASKR__ || (globalThis.__ASKR__ = {});
3527
- ns.__PORTAL_WRITES = (ns.__PORTAL_WRITES || 0) + 1;
3528
- }
3529
- if (owner && owner.notifyUpdate) {
3530
- if (process.env.NODE_ENV !== "production")
3531
- logger.debug(
3532
- "[Portal] fallback.write notify ->",
3533
- owner.id,
3534
- !!owner.notifyUpdate
3535
- );
3536
- owner.notifyUpdate();
3537
- }
3538
- return null;
3539
- };
3540
- return HostFallback2;
3541
- }
3542
- const slot = createPortalSlot();
3543
- function PortalHost() {
3544
- return slot.read();
3545
- }
3546
- PortalHost.render = function PortalRender(props) {
3547
- logger.debug("[Portal] write ->", props?.children);
3548
- if (process.env.NODE_ENV !== "production") {
3549
- const ns = globalThis.__ASKR__ || (globalThis.__ASKR__ = {});
3550
- ns.__PORTAL_WRITES = (ns.__PORTAL_WRITES || 0) + 1;
3551
- }
3552
- slot.write(props.children);
3553
- return null;
3554
- };
3555
- return PortalHost;
3556
- }
3557
- function _resetDefaultPortal() {
3558
- _defaultPortal = void 0;
3559
- _defaultPortalIsFallback = false;
3560
- }
3561
- function ensureDefaultPortal() {
3562
- logger.debug(
3563
- "[DefaultPortal] ensureDefaultPortal _defaultPortalIsFallback=",
3564
- _defaultPortalIsFallback,
3565
- "createPortalSlot=",
3566
- typeof createPortalSlot === "function"
3567
- );
3568
- if (!_defaultPortal) {
3569
- if (typeof createPortalSlot === "function") {
3570
- _defaultPortal = definePortal();
3571
- _defaultPortalIsFallback = false;
3572
- logger.debug("[DefaultPortal] created real portal");
3573
- } else {
3574
- _defaultPortal = definePortal();
3575
- _defaultPortalIsFallback = true;
3576
- logger.debug("[DefaultPortal] created fallback portal");
3577
- }
3578
- return _defaultPortal;
3579
- }
3580
- if (_defaultPortalIsFallback && typeof createPortalSlot === "function") {
3581
- const real = definePortal();
3582
- _defaultPortal = real;
3583
- _defaultPortalIsFallback = false;
3584
- }
3585
- if (!_defaultPortalIsFallback && typeof createPortalSlot !== "function") {
3586
- const fallback = definePortal();
3587
- _defaultPortal = fallback;
3588
- _defaultPortalIsFallback = true;
3589
- logger.debug("[DefaultPortal] reverted to fallback portal");
3590
- }
3591
- return _defaultPortal;
3592
- }
3593
- var _defaultPortal, _defaultPortalIsFallback, DefaultPortal;
3594
- var init_portal = __esm({
3595
- "src/foundations/portal.tsx"() {
3596
- "use strict";
3597
- init_component();
3598
- init_logger();
3599
- _defaultPortalIsFallback = false;
3600
- DefaultPortal = (() => {
3601
- function Host() {
3602
- const v = ensureDefaultPortal()();
3603
- return v === void 0 ? null : v;
3604
- }
3605
- Host.render = function Render(props) {
3606
- ensureDefaultPortal().render(props);
3607
- return null;
3608
- };
3609
- return Host;
3610
- })();
3611
- }
3612
- });
3613
-
3614
- // src/ssr/escape.ts
3615
- function escapeText(text) {
3616
- const useCache = text.length <= 64;
3617
- if (useCache) {
3618
- const cached = escapeCache.get(text);
3619
- if (cached !== void 0) return cached;
3620
- }
3621
- const str = String(text);
3622
- if (!str.includes("&") && !str.includes("<") && !str.includes(">")) {
3623
- if (useCache && escapeCache.size < MAX_CACHE_SIZE) {
3624
- escapeCache.set(text, str);
3625
- }
3626
- return str;
3627
- }
3628
- const result = str.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
3629
- if (useCache && escapeCache.size < MAX_CACHE_SIZE) {
3630
- escapeCache.set(text, result);
3631
- }
3632
- return result;
3633
- }
3634
- function escapeAttr(value) {
3635
- const str = String(value);
3636
- if (!str.includes("&") && !str.includes('"') && !str.includes("'") && !str.includes("<") && !str.includes(">")) {
3637
- return str;
3638
- }
3639
- return str.replace(/&/g, "&amp;").replace(/"/g, "&quot;").replace(/'/g, "&#x27;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
3640
- }
3641
- function escapeCssValue(value) {
3642
- const str = String(value);
3643
- if (/(?:url|expression|javascript)\s*\(/i.test(str)) {
3644
- return "";
3645
- }
3646
- return str.replace(/[{}<>\\]/g, "");
3647
- }
3648
- function styleObjToCss(value) {
3649
- if (!value || typeof value !== "object") return null;
3650
- const entries = Object.entries(value);
3651
- if (entries.length === 0) return "";
3652
- let out = "";
3653
- for (const [k, v] of entries) {
3654
- if (v === null || v === void 0 || v === false) continue;
3655
- const prop = k.replace(/[A-Z]/g, (m) => `-${m.toLowerCase()}`);
3656
- const safeValue = escapeCssValue(String(v));
3657
- if (safeValue) {
3658
- out += `${prop}:${safeValue};`;
3659
- }
3660
- }
3661
- return out;
3662
- }
3663
- var VOID_ELEMENTS, escapeCache, MAX_CACHE_SIZE;
3664
- var init_escape = __esm({
3665
- "src/ssr/escape.ts"() {
3666
- "use strict";
3667
- VOID_ELEMENTS = /* @__PURE__ */ new Set([
3668
- "area",
3669
- "base",
3670
- "br",
3671
- "col",
3672
- "embed",
3673
- "hr",
3674
- "img",
3675
- "input",
3676
- "link",
3677
- "meta",
3678
- "param",
3679
- "source",
3680
- "track",
3681
- "wbr"
3682
- ]);
3683
- escapeCache = /* @__PURE__ */ new Map();
3684
- MAX_CACHE_SIZE = 256;
3685
- }
3686
- });
3687
-
3688
- // src/ssr/attrs.ts
3689
- function renderAttrs(props, opts) {
3690
- if (!props || typeof props !== "object") {
3691
- return opts?.returnDangerousHtml ? { attrs: "" } : "";
3692
- }
3693
- let result = "";
3694
- let dangerousHtml;
3695
- for (const [key, value] of Object.entries(props)) {
3696
- if (key === "children") continue;
3697
- if (key === "dangerouslySetInnerHTML") {
3698
- if (value && typeof value === "object" && "__html" in value) {
3699
- dangerousHtml = String(value.__html);
3700
- }
3701
- continue;
3702
- }
3703
- if (key.length >= 3 && key[0] === "o" && key[1] === "n" && key[2] >= "A" && key[2] <= "Z") {
3704
- continue;
3705
- }
3706
- if (key.startsWith("_")) continue;
3707
- const attrName = key === "class" || key === "className" ? "class" : key;
3708
- if (attrName === "style") {
3709
- const css = typeof value === "string" ? value : styleObjToCss(value);
3710
- if (css === null || css === "") continue;
3711
- result += ` style="${escapeAttr(css)}"`;
3712
- continue;
3713
- }
3714
- if (value === true) {
3715
- result += ` ${attrName}`;
3716
- } else if (value === false || value === null || value === void 0) {
3717
- continue;
3718
- } else {
3719
- result += ` ${attrName}="${escapeAttr(String(value))}"`;
3720
- }
3721
- }
3722
- if (opts?.returnDangerousHtml) {
3723
- return { attrs: result, dangerousHtml };
3724
- }
3725
- return result;
3726
- }
3727
- var init_attrs = __esm({
3728
- "src/ssr/attrs.ts"() {
3729
- "use strict";
3730
- init_escape();
3731
- }
3732
- });
3733
-
3734
- // src/ssr/sink.ts
3735
- var StringSink, StreamSink;
3736
- var init_sink = __esm({
3737
- "src/ssr/sink.ts"() {
3738
- "use strict";
3739
- StringSink = class {
3740
- constructor() {
3741
- this.chunks = [];
3742
- }
3743
- write(html) {
3744
- if (html) this.chunks.push(html);
3745
- }
3746
- end() {
3747
- }
3748
- toString() {
3749
- return this.chunks.join("");
3750
- }
3751
- };
3752
- StreamSink = class {
3753
- constructor(onChunk, onComplete) {
3754
- this.onChunk = onChunk;
3755
- this.onComplete = onComplete;
3756
- }
3757
- write(html) {
3758
- if (html) this.onChunk(html);
3759
- }
3760
- end() {
3761
- this.onComplete();
3762
- }
3763
- };
3764
- }
3765
- });
3766
-
3767
- // src/ssr/stream-render.ts
3768
- function isVNodeLike(x) {
3769
- return !!x && typeof x === "object" && "type" in x;
3770
- }
3771
- function normalizeChildren(node) {
3772
- const n = node;
3773
- const direct = Array.isArray(n?.children) ? n?.children : null;
3774
- const fromProps = n?.props?.children;
3775
- const raw = direct ?? fromProps;
3776
- if (raw === null || raw === void 0 || raw === false) return [];
3777
- if (Array.isArray(raw)) return raw;
3778
- return [raw];
3779
- }
3780
- function renderChildrenToSink(children, sink, ctx) {
3781
- for (const c of children)
3782
- renderNodeToSink(
3783
- c,
3784
- sink,
3785
- ctx
3786
- );
3787
- }
3788
- function isPromiseLike(x) {
3789
- if (!x || typeof x !== "object") return false;
3790
- const then = x.then;
3791
- return typeof then === "function";
3792
- }
3793
- function executeComponent2(type, props, ctx) {
3794
- const res = type(props ?? {}, { signal: ctx.signal });
3795
- if (isPromiseLike(res)) {
3796
- throwSSRDataMissing();
3797
- }
3798
- return res;
3799
- }
3800
- function renderNodeToSink(node, sink, ctx) {
3801
- if (node === null || node === void 0) return;
3802
- if (typeof node === "string") {
3803
- sink.write(escapeText(node));
3804
- return;
3805
- }
3806
- if (typeof node === "number") {
3807
- sink.write(escapeText(String(node)));
3808
- return;
3809
- }
3810
- if (!isVNodeLike(node)) return;
3811
- const { type, props } = node;
3812
- if (typeof type === "symbol" && (type === Fragment || String(type) === "Symbol(Fragment)")) {
3813
- const children = normalizeChildren(node);
3814
- renderChildrenToSink(children, sink, ctx);
3815
- return;
3816
- }
3817
- if (typeof type === "function") {
3818
- const out = withSSRContext(
3819
- ctx,
3820
- () => executeComponent2(type, props, ctx)
3821
- );
3822
- renderNodeToSink(
3823
- out,
3824
- sink,
3825
- ctx
3826
- );
3827
- return;
3828
- }
3829
- const tag = String(type);
3830
- const { attrs, dangerousHtml } = renderAttrs(props, {
3831
- returnDangerousHtml: true
3832
- });
3833
- if (VOID_ELEMENTS.has(tag)) {
3834
- sink.write(`<${tag}${attrs} />`);
3835
- return;
3836
- }
3837
- sink.write(`<${tag}${attrs}>`);
3838
- if (dangerousHtml !== void 0) {
3839
- sink.write(dangerousHtml);
3840
- } else {
3841
- const children = normalizeChildren(node);
3842
- renderChildrenToSink(children, sink, ctx);
3843
- }
3844
- sink.write(`</${tag}>`);
3845
- }
3846
- var init_stream_render = __esm({
3847
- "src/ssr/stream-render.ts"() {
3848
- "use strict";
3849
- init_jsx();
3850
- init_context2();
3851
- init_escape();
3852
- init_attrs();
3853
- }
3854
- });
3855
-
3856
- // src/ssr/index.ts
3857
- var ssr_exports = {};
3858
- __export(ssr_exports, {
3859
- SSRDataMissingError: () => SSRDataMissingError,
3860
- collectResources: () => collectResources,
3861
- popSSRStrictPurityGuard: () => popSSRStrictPurityGuard,
3862
- pushSSRStrictPurityGuard: () => pushSSRStrictPurityGuard,
3863
- renderToStream: () => renderToStream,
3864
- renderToString: () => renderToString,
3865
- renderToStringSync: () => renderToStringSync,
3866
- renderToStringSyncForUrl: () => renderToStringSyncForUrl,
3867
- resolvePlan: () => resolvePlan,
3868
- resolveResources: () => resolveResources
3869
- });
3870
- function pushSSRStrictPurityGuard() {
3871
- if (process.env.NODE_ENV === "production") return;
3872
- __ssrGuardStack.push({
3873
- random: Reflect.get(Math, "random"),
3874
- now: Reflect.get(Date, "now")
3875
- });
3876
- Reflect.set(Math, "random", () => {
3877
- throw new Error(
3878
- "SSR Strict Purity: Math.random is not allowed during synchronous SSR. Use the provided `ssr` context RNG instead."
3879
- );
3880
- });
3881
- Reflect.set(Date, "now", () => {
3882
- throw new Error(
3883
- "SSR Strict Purity: Date.now is not allowed during synchronous SSR. Pass timestamps explicitly or use deterministic helpers."
3884
- );
3885
- });
3886
- }
3887
- function popSSRStrictPurityGuard() {
3888
- if (process.env.NODE_ENV === "production") return;
3889
- const prev = __ssrGuardStack.pop();
3890
- if (prev) {
3891
- Reflect.set(Math, "random", prev.random);
3892
- Reflect.set(Date, "now", prev.now);
3893
- }
3894
- }
3895
- function renderChildSync(child, ctx) {
3896
- if (typeof child === "string") return escapeText(child);
3897
- if (typeof child === "number") return escapeText(String(child));
3898
- if (child === null || child === void 0 || child === false) return "";
3899
- if (typeof child === "object" && child !== null && "type" in child) {
3900
- return renderNodeSync(child, ctx);
3901
- }
3902
- return "";
3903
- }
3904
- function renderChildrenSync(children, ctx) {
3905
- if (!children || !Array.isArray(children) || children.length === 0) return "";
3906
- let result = "";
3907
- for (const child of children) result += renderChildSync(child, ctx);
3908
- return result;
3909
- }
3910
- function renderNodeSync(node, ctx) {
3911
- const { type, props } = node;
3912
- if (process.env.NODE_ENV !== "production") {
3913
- try {
3914
- logger.warn("[SSR] renderNodeSync type:", typeof type, type);
3915
- } catch {
3916
- }
3917
- }
3918
- if (typeof type === "function") {
3919
- const result = executeComponentSync2(type, props, ctx);
3920
- if (result instanceof Promise) {
3921
- throwSSRDataMissing();
3922
- }
3923
- return renderNodeSync(result, ctx);
3924
- }
3925
- if (typeof type === "symbol") {
3926
- if (type === Fragment) {
3927
- const childrenArr = Array.isArray(node.children) ? node.children : Array.isArray(props?.children) ? props?.children : void 0;
3928
- if (process.env.NODE_ENV !== "production") {
3929
- try {
3930
- logger.warn("[SSR] fragment children length:", childrenArr?.length);
3931
- } catch {
3932
- }
3933
- }
3934
- return renderChildrenSync(childrenArr, ctx);
3935
- }
3936
- throw new Error(
3937
- `renderNodeSync: unsupported VNode symbol type: ${String(type)}`
3938
- );
3939
- }
3940
- const typeStr = type;
3941
- if (VOID_ELEMENTS.has(typeStr)) {
3942
- const attrs2 = renderAttrs(props);
3943
- return `<${typeStr}${attrs2} />`;
3944
- }
3945
- const { attrs, dangerousHtml } = renderAttrs(props, {
3946
- returnDangerousHtml: true
3947
- });
3948
- if (dangerousHtml !== void 0) {
3949
- return `<${typeStr}${attrs}>${dangerousHtml}</${typeStr}>`;
3950
- }
3951
- const children = node.children;
3952
- const childrenHtml = renderChildrenSync(children, ctx);
3953
- return `<${typeStr}${attrs}>${childrenHtml}</${typeStr}>`;
3954
- }
3955
- function executeComponentSync2(component, props, ctx) {
3956
- try {
3957
- if (process.env.NODE_ENV !== "production") {
3958
- pushSSRStrictPurityGuard();
3959
- }
3960
- const prev = getCurrentComponentInstance();
3961
- const temp = createComponentInstance(
3962
- "ssr-temp",
3963
- component,
3964
- props || {},
3965
- null
3966
- );
3967
- temp.ssr = true;
3968
- setCurrentComponentInstance(temp);
3969
- try {
3970
- return runWithSSRContext(ctx, () => {
3971
- const result = component(props || {}, { ssr: ctx });
3972
- if (result instanceof Promise) {
3973
- throwSSRDataMissing();
3974
- }
3975
- if (typeof result === "string" || typeof result === "number" || typeof result === "boolean" || result === null || result === void 0) {
3976
- const inner = result === null || result === void 0 || result === false ? "" : String(result);
3977
- return {
3978
- $$typeof: ELEMENT_TYPE,
3979
- type: Fragment,
3980
- props: { children: inner ? [inner] : [] }
3981
- };
3982
- }
3983
- return result;
3984
- });
3985
- } finally {
3986
- setCurrentComponentInstance(prev);
3987
- }
3988
- } finally {
3989
- if (process.env.NODE_ENV !== "production") popSSRStrictPurityGuard();
3990
- }
3991
- }
3992
- function renderToStringSync(component, props, options) {
3993
- const seed = options?.seed ?? 12345;
3994
- const ctx = createRenderContext(seed);
3995
- startRenderPhase(options?.data ?? null);
3996
- try {
3997
- const wrapped = (p, c) => {
3998
- const out = component(p ?? {}, c);
3999
- const portalVNode = {
4000
- $$typeof: ELEMENT_TYPE,
4001
- type: DefaultPortal,
4002
- props: {},
4003
- key: "__default_portal"
4004
- };
4005
- if (out == null) {
4006
- return {
4007
- $$typeof: ELEMENT_TYPE,
4008
- type: Fragment,
4009
- props: { children: [portalVNode] }
4010
- };
4011
- }
4012
- return {
4013
- $$typeof: ELEMENT_TYPE,
4014
- type: Fragment,
4015
- props: { children: [out, portalVNode] }
4016
- };
4017
- };
4018
- const node = executeComponentSync2(wrapped, props || {}, ctx);
4019
- if (!node) {
4020
- throw new Error("renderToStringSync: wrapped component returned empty");
4021
- }
4022
- return renderNodeSync(node, ctx);
4023
- } finally {
4024
- stopRenderPhase();
4025
- }
4026
- }
4027
- function renderToStringSyncForUrl(opts) {
4028
- const { url, routes: routes2, options } = opts;
4029
- const {
4030
- clearRoutes: clearRoutes2,
4031
- route: route2,
4032
- setServerLocation: setServerLocation2,
4033
- lockRouteRegistration: lockRouteRegistration2,
4034
- resolveRoute: resolveRoute2
4035
- } = route_exports;
4036
- clearRoutes2();
4037
- for (const r of routes2) {
4038
- route2(r.path, r.handler, r.namespace);
4039
- }
4040
- setServerLocation2(url);
4041
- if (process.env.NODE_ENV === "production") lockRouteRegistration2();
4042
- const resolved = resolveRoute2(url);
4043
- if (!resolved)
4044
- throw new Error(`renderToStringSync: no route found for url: ${url}`);
4045
- const seed = options?.seed ?? 12345;
4046
- const ctx = createRenderContext(seed);
4047
- startRenderPhase(options?.data ?? null);
4048
- try {
4049
- const wrapped = (p, c) => {
4050
- const out = resolved.handler(p ?? {}, c);
4051
- const portalVNode = {
4052
- $$typeof: ELEMENT_TYPE,
4053
- type: DefaultPortal,
4054
- props: {},
4055
- key: "__default_portal"
4056
- };
4057
- if (out == null) {
4058
- return {
4059
- $$typeof: ELEMENT_TYPE,
4060
- type: Fragment,
4061
- props: { children: [portalVNode] }
4062
- };
4063
- }
4064
- return {
4065
- $$typeof: ELEMENT_TYPE,
4066
- type: Fragment,
4067
- props: { children: [out, portalVNode] }
4068
- };
4069
- };
4070
- const node = executeComponentSync2(wrapped, resolved.params || {}, ctx);
4071
- return renderNodeSync(node, ctx);
4072
- } finally {
4073
- stopRenderPhase();
4074
- }
4075
- }
4076
- function renderToString(arg) {
4077
- if (typeof arg === "function") {
4078
- return renderToStringSync(
4079
- arg
4080
- );
4081
- }
4082
- const opts = arg;
4083
- const sink = new StringSink();
4084
- renderToSinkInternal({ ...opts, sink });
4085
- sink.end();
4086
- return sink.toString();
4087
- }
4088
- function renderToStream(opts) {
4089
- const sink = new StreamSink(opts.onChunk, opts.onComplete);
4090
- renderToSinkInternal({ ...opts, sink });
4091
- sink.end();
4092
- }
4093
- function renderToSinkInternal(opts) {
4094
- const { url, routes: routes2, seed = 12345, data, sink } = opts;
4095
- const {
4096
- clearRoutes: clearRoutes2,
4097
- route: route2,
4098
- setServerLocation: setServerLocation2,
4099
- lockRouteRegistration: lockRouteRegistration2,
4100
- resolveRoute: resolveRoute2
4101
- } = route_exports;
4102
- clearRoutes2();
4103
- for (const r of routes2) route2(r.path, r.handler, r.namespace);
4104
- setServerLocation2(url);
4105
- if (process.env.NODE_ENV === "production") lockRouteRegistration2();
4106
- const resolved = resolveRoute2(url);
4107
- if (!resolved) throw new Error(`SSR: no route found for url: ${url}`);
4108
- const ctx = {
4109
- url,
4110
- seed,
4111
- data,
4112
- params: resolved.params,
4113
- signal: void 0
4114
- };
4115
- const node = resolved.handler(resolved.params);
4116
- startRenderPhase(data || null);
4117
- try {
4118
- renderNodeToSink(node, sink, ctx);
4119
- } finally {
4120
- stopRenderPhase();
4121
- }
4122
- }
4123
- var __ssrGuardStack;
4124
- var init_ssr = __esm({
4125
- "src/ssr/index.ts"() {
4126
- "use strict";
4127
- init_route();
4128
- init_jsx();
4129
- init_portal();
4130
- init_context2();
4131
- init_component();
4132
- init_escape();
4133
- init_attrs();
4134
- init_logger();
4135
- init_context2();
4136
- init_sink();
4137
- init_stream_render();
4138
- init_render_keys();
4139
- __ssrGuardStack = [];
4140
- }
4141
- });
4142
-
4143
- // src/index.ts
4144
- var index_exports = {};
4145
- __export(index_exports, {
4146
- DefaultPortal: () => DefaultPortal,
4147
- Fragment: () => Fragment2,
4148
- Link: () => Link,
4149
- Slot: () => Slot,
4150
- _resetDefaultPortal: () => _resetDefaultPortal,
4151
- cleanupApp: () => cleanupApp,
4152
- clearRoutes: () => clearRoutes,
4153
- collectResources: () => collectResources,
4154
- createIsland: () => createIsland,
4155
- createSPA: () => createSPA,
4156
- defineContext: () => defineContext,
4157
- definePortal: () => definePortal,
4158
- derive: () => derive,
4159
- getLoadedNamespaces: () => getLoadedNamespaces,
4160
- getNamespaceRoutes: () => getNamespaceRoutes,
4161
- getRoutes: () => getRoutes,
4162
- getSignal: () => getSignal,
4163
- hasApp: () => hasApp,
4164
- hydrateSPA: () => hydrateSPA,
4165
- jsx: () => jsx,
4166
- jsxs: () => jsxs,
4167
- layout: () => layout,
4168
- navigate: () => navigate,
4169
- readContext: () => readContext,
4170
- renderToStream: () => renderToStream,
4171
- renderToString: () => renderToString,
4172
- renderToStringSync: () => renderToStringSync,
4173
- renderToStringSyncForUrl: () => renderToStringSyncForUrl,
4174
- resolveResources: () => resolveResources,
4175
- resource: () => resource,
4176
- route: () => route,
4177
- scheduleEventHandler: () => scheduleEventHandler,
4178
- setServerLocation: () => setServerLocation,
4179
- state: () => state,
4180
- task: () => task,
4181
- unloadNamespace: () => unloadNamespace
4182
- });
4183
- module.exports = __toCommonJS(index_exports);
4184
-
4185
- // src/runtime/state.ts
4186
- init_scheduler();
4187
- init_component();
4188
- init_invariant();
4189
- init_fastlane_shared();
4190
- function state(initialValue) {
4191
- const instance = getCurrentInstance();
4192
- if (!instance) {
4193
- throw new Error(
4194
- "state() can only be called during component render execution. Move state() calls to the top level of your component function."
4195
- );
4196
- }
4197
- const index = getNextStateIndex();
4198
- const stateValues = instance.stateValues;
4199
- if (index < instance.stateIndexCheck) {
4200
- throw new Error(
4201
- `State index violation: state() call at index ${index}, but previously saw index ${instance.stateIndexCheck}. This happens when state() is called conditionally (inside if/for/etc). Move all state() calls to the top level of your component function, before any conditionals.`
4202
- );
4203
- }
4204
- invariant(
4205
- index >= instance.stateIndexCheck,
4206
- "[State] State indices must increase monotonically"
4207
- );
4208
- instance.stateIndexCheck = index;
4209
- if (instance.firstRenderComplete) {
4210
- if (!instance.expectedStateIndices.includes(index)) {
4211
- throw new Error(
4212
- `Hook order violation: state() called at index ${index}, but this index was not in the first render's sequence [${instance.expectedStateIndices.join(", ")}]. This usually means state() is inside a conditional or loop. Move all state() calls to the top level of your component function.`
4213
- );
4214
- }
4215
- } else {
4216
- instance.expectedStateIndices.push(index);
4217
- }
4218
- if (stateValues[index]) {
4219
- const existing = stateValues[index];
4220
- if (existing._owner !== instance) {
4221
- throw new Error(
4222
- `State ownership violation: state() called at index ${index} is owned by a different component instance. State ownership is positional and immutable.`
4223
- );
4224
- }
4225
- return existing;
4226
- }
4227
- const cell = createStateCell(initialValue, instance);
4228
- stateValues[index] = cell;
4229
- return cell;
4230
- }
4231
- function createStateCell(initialValue, instance) {
4232
- let value = initialValue;
4233
- const readers = /* @__PURE__ */ new Map();
4234
- function read() {
4235
- read._hasBeenRead = true;
4236
- const inst = getCurrentInstance();
4237
- if (inst && inst._currentRenderToken !== void 0) {
4238
- if (!inst._pendingReadStates) inst._pendingReadStates = /* @__PURE__ */ new Set();
4239
- inst._pendingReadStates.add(read);
4240
- }
4241
- return value;
4242
- }
4243
- read._readers = readers;
4244
- read._owner = instance;
4245
- read.set = (newValueOrUpdater) => {
4246
- const currentInst = getCurrentInstance();
4247
- if (currentInst !== null && process.env.NODE_ENV !== "production") {
4248
- throw new Error(
4249
- `[Askr] state.set() cannot be called during component render. State mutations during render break the actor model and cause infinite loops. Move state updates to event handlers or use conditional rendering instead.`
4250
- );
4251
- }
4252
- if (currentInst !== null && process.env.NODE_ENV === "production") {
4253
- if (typeof console !== "undefined" && console.warn) {
4254
- console.warn(
4255
- "[Askr] state.set() called during render - update skipped. Move state updates to event handlers."
4256
- );
4257
- }
4258
- return;
4259
- }
4260
- let newValue;
4261
- if (typeof newValueOrUpdater === "function") {
4262
- const updater = newValueOrUpdater;
4263
- newValue = updater(value);
4264
- } else {
4265
- newValue = newValueOrUpdater;
4266
- }
4267
- if (Object.is(value, newValue)) return;
4268
- if (isBulkCommitActive2()) {
4269
- value = newValue;
4270
- return;
4271
- }
4272
- value = newValue;
4273
- const readersMap = read._readers;
4274
- if (readersMap) {
4275
- for (const [subInst, token] of readersMap) {
4276
- if (subInst.lastRenderToken !== token) continue;
4277
- if (!subInst.hasPendingUpdate) {
4278
- subInst.hasPendingUpdate = true;
4279
- const subTask = subInst._pendingFlushTask;
4280
- if (subTask) globalScheduler.enqueue(subTask);
4281
- else
4282
- globalScheduler.enqueue(() => {
4283
- subInst.hasPendingUpdate = false;
4284
- subInst.notifyUpdate?.();
4285
- });
4286
- }
4287
- }
4288
- }
4289
- const readersMapForOwner = readersMap;
4290
- const ownerRecordedToken = readersMapForOwner?.get(instance);
4291
- const ownerShouldEnqueue = (
4292
- // Normal case: owner read this state in last committed render
4293
- ownerRecordedToken !== void 0 && instance.lastRenderToken === ownerRecordedToken
4294
- );
4295
- if (ownerShouldEnqueue && !instance.hasPendingUpdate) {
4296
- instance.hasPendingUpdate = true;
4297
- const task2 = instance._pendingFlushTask;
4298
- if (task2) globalScheduler.enqueue(task2);
4299
- else
4300
- globalScheduler.enqueue(() => {
4301
- instance.hasPendingUpdate = false;
4302
- instance.notifyUpdate?.();
4303
- });
3362
+ const depth = getDepth(removed.path);
3363
+ const depthRoutes = routesByDepth.get(depth);
3364
+ if (depthRoutes) {
3365
+ const idx = depthRoutes.indexOf(removed);
3366
+ if (idx >= 0) {
3367
+ depthRoutes.splice(idx, 1);
3368
+ }
3369
+ }
4304
3370
  }
4305
- };
4306
- return read;
3371
+ }
3372
+ namespaces.delete(namespace);
3373
+ return before - routes.length;
4307
3374
  }
4308
-
4309
- // src/index.ts
4310
- init_component();
4311
- init_scheduler();
4312
- init_context();
4313
-
4314
- // src/runtime/operations.ts
4315
- init_component();
4316
- init_context();
4317
-
4318
- // src/runtime/resource-cell.ts
4319
- init_context();
4320
- init_logger();
4321
- init_context2();
4322
- var ResourceCell = class {
4323
- constructor(fn, deps, resourceFrame) {
4324
- this.value = null;
4325
- this.pending = true;
4326
- this.error = null;
4327
- this.generation = 0;
4328
- this.controller = null;
4329
- this.deps = null;
4330
- this.resourceFrame = null;
4331
- this.subscribers = /* @__PURE__ */ new Set();
4332
- this.fn = fn;
4333
- this.deps = deps ? deps.slice() : null;
4334
- this.resourceFrame = resourceFrame;
4335
- this.snapshot = {
4336
- value: null,
4337
- pending: true,
4338
- error: null,
4339
- refresh: () => this.refresh()
3375
+ function clearRoutes() {
3376
+ routes.length = 0;
3377
+ namespaces.clear();
3378
+ routesByDepth.clear();
3379
+ registrationLocked = false;
3380
+ setHasRoutes(false);
3381
+ }
3382
+ function normalizeHandler(handler) {
3383
+ if (handler == null) return void 0;
3384
+ if (typeof handler === "function") {
3385
+ return (params, ctx) => {
3386
+ try {
3387
+ return handler(params, ctx);
3388
+ } catch {
3389
+ return handler(params);
3390
+ }
4340
3391
  };
4341
3392
  }
4342
- subscribe(cb) {
4343
- this.subscribers.add(cb);
4344
- return () => this.subscribers.delete(cb);
4345
- }
4346
- notifySubscribers() {
4347
- this.snapshot.value = this.value;
4348
- this.snapshot.pending = this.pending;
4349
- this.snapshot.error = this.error;
4350
- for (const cb of this.subscribers) cb();
4351
- }
4352
- start(ssr = false, notify = true) {
4353
- const generation = this.generation;
4354
- this.controller?.abort();
4355
- const controller = new AbortController();
4356
- this.controller = controller;
4357
- this.pending = true;
4358
- this.error = null;
4359
- if (notify) this.notifySubscribers();
4360
- let result;
4361
- try {
4362
- result = withAsyncResourceContext(
4363
- this.resourceFrame,
4364
- () => this.fn({ signal: controller.signal })
3393
+ return void 0;
3394
+ }
3395
+ function registerRoute(path, handler, ...children) {
3396
+ const isRelative = !path.startsWith("/");
3397
+ const descriptor = {
3398
+ path,
3399
+ handler,
3400
+ children: children.filter(Boolean),
3401
+ _isDescriptor: true
3402
+ };
3403
+ if (!isRelative) {
3404
+ const normalized = normalizeHandler(handler);
3405
+ if (handler != null && !normalized) {
3406
+ throw new Error(
3407
+ "registerRoute(path, handler) requires a function handler. Passing JSX elements or VNodes directly is not supported."
4365
3408
  );
4366
- } catch (err) {
4367
- this.pending = false;
4368
- this.error = err;
4369
- if (notify) this.notifySubscribers();
4370
- return;
4371
- }
4372
- if (!(result instanceof Promise)) {
4373
- this.value = result;
4374
- this.pending = false;
4375
- this.error = null;
4376
- if (notify) this.notifySubscribers();
4377
- return;
4378
3409
  }
4379
- if (ssr) {
4380
- throwSSRDataMissing();
4381
- }
4382
- result.then((val) => {
4383
- if (this.generation !== generation) return;
4384
- if (this.controller !== controller) return;
4385
- this.value = val;
4386
- this.pending = false;
4387
- this.error = null;
4388
- this.notifySubscribers();
4389
- }).catch((err) => {
4390
- if (this.generation !== generation) return;
4391
- this.pending = false;
4392
- this.error = err;
4393
- try {
4394
- if (this.ownerName) {
4395
- logger.error(
4396
- `[Askr] Async resource error in ${this.ownerName}:`,
4397
- err
3410
+ if (normalized) route(path, normalized);
3411
+ for (const child of descriptor.children || []) {
3412
+ const base = path === "/" ? "" : path.replace(/\/$/, "");
3413
+ const childPath = `${base}/${child.path.replace(/^\//, "")}`.replace(
3414
+ /\/\//g,
3415
+ "/"
3416
+ );
3417
+ if (child.handler) {
3418
+ const childNormalized = normalizeHandler(child.handler);
3419
+ if (!childNormalized) {
3420
+ throw new Error(
3421
+ "registerRoute child handler must be a function. Passing JSX elements directly is not supported."
4398
3422
  );
4399
- } else {
4400
- logger.error("[Askr] Async resource error:", err);
4401
3423
  }
4402
- } catch {
3424
+ if (childNormalized) route(childPath, childNormalized);
4403
3425
  }
4404
- this.notifySubscribers();
4405
- });
3426
+ if (child.children && child.children.length) {
3427
+ registerRoute(
3428
+ childPath,
3429
+ null,
3430
+ ...child.children
3431
+ );
3432
+ }
3433
+ }
3434
+ return descriptor;
3435
+ }
3436
+ return descriptor;
3437
+ }
3438
+ function getLoadedNamespaces() {
3439
+ return Array.from(namespaces);
3440
+ }
3441
+ function resolveRoute(pathname) {
3442
+ const normalized = pathname.endsWith("/") && pathname !== "/" ? pathname.slice(0, -1) : pathname;
3443
+ const depth = normalized === "/" ? 0 : normalized.split("/").filter(Boolean).length;
3444
+ const candidates = [];
3445
+ const depthRoutes = routesByDepth.get(depth);
3446
+ if (depthRoutes) {
3447
+ for (const r of depthRoutes) {
3448
+ const result = match(pathname, r.path);
3449
+ if (result.matched) {
3450
+ candidates.push({
3451
+ route: r,
3452
+ specificity: getSpecificity(r.path),
3453
+ params: result.params
3454
+ });
3455
+ }
3456
+ }
4406
3457
  }
4407
- refresh() {
4408
- this.generation++;
4409
- this.controller?.abort();
4410
- this.start();
3458
+ for (const r of routes) {
3459
+ if (depthRoutes?.includes(r)) continue;
3460
+ const result = match(pathname, r.path);
3461
+ if (result.matched) {
3462
+ candidates.push({
3463
+ route: r,
3464
+ specificity: getSpecificity(r.path),
3465
+ params: result.params
3466
+ });
3467
+ }
4411
3468
  }
4412
- abort() {
4413
- this.controller?.abort();
3469
+ candidates.sort((a, b) => b.specificity - a.specificity);
3470
+ if (candidates.length > 0) {
3471
+ const best = candidates[0];
3472
+ return { handler: best.route.handler, params: best.params };
4414
3473
  }
4415
- };
3474
+ return null;
3475
+ }
3476
+ var routes, namespaces, HAS_ROUTES_KEY, routesByDepth, serverLocation, registrationLocked;
3477
+ var init_route = __esm({
3478
+ "src/router/route.ts"() {
3479
+ "use strict";
3480
+ init_match();
3481
+ init_component();
3482
+ init_execution_model();
3483
+ routes = [];
3484
+ namespaces = /* @__PURE__ */ new Set();
3485
+ HAS_ROUTES_KEY = /* @__PURE__ */ Symbol.for("__ASKR_HAS_ROUTES__");
3486
+ setHasRoutes(false);
3487
+ routesByDepth = /* @__PURE__ */ new Map();
3488
+ serverLocation = null;
3489
+ registrationLocked = false;
3490
+ }
3491
+ });
4416
3492
 
4417
- // src/shared/derive-cache.ts
4418
- var deriveCacheMap = /* @__PURE__ */ new WeakMap();
4419
- function getDeriveCache(instance) {
4420
- let cache = deriveCacheMap.get(instance);
4421
- if (!cache) {
4422
- cache = /* @__PURE__ */ new Map();
4423
- deriveCacheMap.set(instance, cache);
3493
+ // src/router/navigate.ts
3494
+ function registerAppInstance(instance, _path) {
3495
+ currentInstance2 = instance;
3496
+ if (process.env.NODE_ENV === "production") {
3497
+ lockRouteRegistration();
3498
+ }
3499
+ }
3500
+ function handlePopState(_event) {
3501
+ const path = window.location.pathname;
3502
+ if (!currentInstance2) {
3503
+ return;
3504
+ }
3505
+ const resolved = resolveRoute(path);
3506
+ if (resolved) {
3507
+ cleanupComponent(currentInstance2);
3508
+ currentInstance2.fn = resolved.handler;
3509
+ currentInstance2.props = resolved.params;
3510
+ currentInstance2.stateValues = [];
3511
+ currentInstance2.expectedStateIndices = [];
3512
+ currentInstance2.firstRenderComplete = false;
3513
+ currentInstance2.stateIndexCheck = -1;
3514
+ currentInstance2.evaluationGeneration++;
3515
+ currentInstance2.notifyUpdate = null;
3516
+ currentInstance2.abortController = new AbortController();
3517
+ mountComponent(currentInstance2);
3518
+ }
3519
+ }
3520
+ function initializeNavigation() {
3521
+ if (typeof window !== "undefined") {
3522
+ window.addEventListener("popstate", handlePopState);
4424
3523
  }
4425
- return cache;
4426
3524
  }
3525
+ var currentInstance2;
3526
+ var init_navigate = __esm({
3527
+ "src/router/navigate.ts"() {
3528
+ "use strict";
3529
+ init_route();
3530
+ init_component();
3531
+ currentInstance2 = null;
3532
+ }
3533
+ });
4427
3534
 
4428
- // src/runtime/operations.ts
4429
- init_context2();
4430
- init_render_keys();
4431
- function resource(fn, deps = []) {
4432
- const instance = getCurrentComponentInstance();
4433
- const inst = instance;
4434
- if (!instance) {
4435
- const renderData2 = getCurrentRenderData();
4436
- if (renderData2) {
4437
- const key = getNextKey();
4438
- if (!(key in renderData2)) {
4439
- throwSSRDataMissing();
4440
- }
4441
- const val = renderData2[key];
4442
- return {
4443
- value: val,
4444
- pending: false,
4445
- error: null,
4446
- refresh: () => {
4447
- }
4448
- };
4449
- }
4450
- const ssrCtx = getCurrentSSRContext();
4451
- if (ssrCtx) {
4452
- throwSSRDataMissing();
4453
- }
4454
- return {
4455
- value: null,
4456
- pending: true,
4457
- error: null,
4458
- refresh: () => {
4459
- }
4460
- };
3535
+ // src/jsx/index.ts
3536
+ var init_jsx2 = __esm({
3537
+ "src/jsx/index.ts"() {
3538
+ "use strict";
3539
+ init_types2();
4461
3540
  }
4462
- const renderData = getCurrentRenderData();
4463
- if (renderData) {
4464
- const key = getNextKey();
4465
- if (!(key in renderData)) {
4466
- throwSSRDataMissing();
4467
- }
4468
- const val = renderData[key];
4469
- const holder2 = state({
4470
- cell: void 0,
4471
- snapshot: {
4472
- value: val,
4473
- pending: false,
4474
- error: null,
4475
- refresh: () => {
4476
- }
4477
- }
4478
- });
4479
- const h2 = holder2();
4480
- h2.snapshot.value = val;
4481
- h2.snapshot.pending = false;
4482
- h2.snapshot.error = null;
4483
- holder2.set(h2);
4484
- return h2.snapshot;
4485
- }
4486
- const holder = state({
4487
- cell: void 0,
4488
- snapshot: {
4489
- value: null,
4490
- pending: true,
4491
- error: null,
4492
- refresh: () => {
3541
+ });
3542
+
3543
+ // src/foundations/portal.tsx
3544
+ function definePortal() {
3545
+ if (typeof createPortalSlot !== "function") {
3546
+ let HostFallback2 = function() {
3547
+ if (owner && owner.mounted === false) {
3548
+ owner = null;
3549
+ pending = void 0;
4493
3550
  }
4494
- }
4495
- });
4496
- const h = holder();
4497
- if (!h.cell) {
4498
- const frame = getCurrentContextFrame();
4499
- const cell2 = new ResourceCell(fn, deps, frame);
4500
- cell2.ownerName = inst.fn?.name || "<anonymous>";
4501
- h.cell = cell2;
4502
- h.snapshot = cell2.snapshot;
4503
- const unsubscribe = cell2.subscribe(() => {
4504
- const cur = holder();
4505
- cur.snapshot.value = cell2.snapshot.value;
4506
- cur.snapshot.pending = cell2.snapshot.pending;
4507
- cur.snapshot.error = cell2.snapshot.error;
4508
- holder.set(cur);
4509
- try {
4510
- inst._enqueueRun?.();
4511
- } catch {
3551
+ const inst = getCurrentComponentInstance();
3552
+ if (!owner && inst) owner = inst;
3553
+ if (process.env.NODE_ENV !== "production") {
3554
+ const ns = globalThis.__ASKR__ || (globalThis.__ASKR__ = {});
3555
+ ns.__PORTAL_READS = (ns.__PORTAL_READS || 0) + 1;
4512
3556
  }
4513
- });
4514
- inst.cleanupFns.push(() => {
4515
- unsubscribe();
4516
- cell2.abort();
4517
- });
4518
- try {
4519
- cell2.start(inst.ssr ?? false, false);
4520
- if (!cell2.pending) {
4521
- const cur = holder();
4522
- cur.snapshot.value = cell2.value;
4523
- cur.snapshot.pending = cell2.pending;
4524
- cur.snapshot.error = cell2.error;
3557
+ if (process.env.NODE_ENV !== "production") {
3558
+ if (inst && owner && inst !== owner && inst.mounted === true) {
3559
+ logger.warn(
3560
+ "[Portal] multiple mounted hosts detected; first mounted host is owner"
3561
+ );
3562
+ }
4525
3563
  }
4526
- } catch (err) {
4527
- if (err instanceof SSRDataMissingError) throw err;
4528
- cell2.error = err;
4529
- cell2.pending = false;
4530
- const cur = holder();
4531
- cur.snapshot.value = cell2.value;
4532
- cur.snapshot.pending = cell2.pending;
4533
- cur.snapshot.error = cell2.error;
4534
- }
4535
- }
4536
- const cell = h.cell;
4537
- const depsChanged = !cell.deps || cell.deps.length !== deps.length || cell.deps.some((d, i) => d !== deps[i]);
4538
- if (depsChanged) {
4539
- cell.deps = deps.slice();
4540
- cell.generation++;
4541
- cell.pending = true;
4542
- cell.error = null;
4543
- try {
4544
- cell.start(inst.ssr ?? false, false);
4545
- if (!cell.pending) {
4546
- const cur = holder();
4547
- cur.snapshot.value = cell.value;
4548
- cur.snapshot.pending = cell.pending;
4549
- cur.snapshot.error = cell.error;
3564
+ return inst && owner && inst === owner ? pending : void 0;
3565
+ };
3566
+ var HostFallback = HostFallback2;
3567
+ let owner = null;
3568
+ let pending;
3569
+ HostFallback2.render = function RenderFallback(props) {
3570
+ if (!owner || owner.mounted !== true) return null;
3571
+ if (process.env.NODE_ENV !== "production") {
3572
+ const ns = globalThis.__ASKR__ || (globalThis.__ASKR__ = {});
3573
+ ns.__PORTAL_WRITES = (ns.__PORTAL_WRITES || 0) + 1;
4550
3574
  }
4551
- } catch (err) {
4552
- if (err instanceof SSRDataMissingError) throw err;
4553
- cell.error = err;
4554
- cell.pending = false;
4555
- const cur = holder();
4556
- cur.snapshot.value = cell.value;
4557
- cur.snapshot.pending = cell.pending;
4558
- cur.snapshot.error = cell.error;
4559
- }
3575
+ pending = props.children;
3576
+ if (owner.notifyUpdate) owner.notifyUpdate();
3577
+ return null;
3578
+ };
3579
+ return HostFallback2;
3580
+ }
3581
+ const slot = createPortalSlot();
3582
+ function PortalHost() {
3583
+ return slot.read();
4560
3584
  }
4561
- return h.snapshot;
3585
+ PortalHost.render = function PortalRender(props) {
3586
+ if (process.env.NODE_ENV !== "production") {
3587
+ const ns = globalThis.__ASKR__ || (globalThis.__ASKR__ = {});
3588
+ ns.__PORTAL_WRITES = (ns.__PORTAL_WRITES || 0) + 1;
3589
+ }
3590
+ slot.write(props.children);
3591
+ return null;
3592
+ };
3593
+ return PortalHost;
4562
3594
  }
4563
- function derive(source, map) {
4564
- if (map === void 0 && typeof source === "function") {
4565
- const value2 = source();
4566
- if (value2 == null) return null;
4567
- const instance2 = getCurrentComponentInstance();
4568
- if (!instance2) {
4569
- return value2;
3595
+ function ensureDefaultPortal() {
3596
+ if (!_defaultPortal) {
3597
+ if (typeof createPortalSlot === "function") {
3598
+ _defaultPortal = definePortal();
3599
+ _defaultPortalIsFallback = false;
3600
+ } else {
3601
+ _defaultPortal = definePortal();
3602
+ _defaultPortalIsFallback = true;
4570
3603
  }
4571
- const cache2 = getDeriveCache(instance2);
4572
- if (cache2.has(value2)) return cache2.get(value2);
4573
- cache2.set(value2, value2);
4574
- return value2;
4575
- }
4576
- let value;
4577
- if (typeof source === "function" && !("value" in source)) {
4578
- value = source();
4579
- } else {
4580
- value = source?.value ?? source;
3604
+ return _defaultPortal;
4581
3605
  }
4582
- if (value == null) return null;
4583
- const instance = getCurrentComponentInstance();
4584
- if (!instance) {
4585
- return map(value);
3606
+ if (_defaultPortalIsFallback && typeof createPortalSlot === "function") {
3607
+ const real = definePortal();
3608
+ _defaultPortal = real;
3609
+ _defaultPortalIsFallback = false;
4586
3610
  }
4587
- const cache = getDeriveCache(instance);
4588
- if (cache.has(value)) {
4589
- return cache.get(value);
3611
+ if (!_defaultPortalIsFallback && typeof createPortalSlot !== "function") {
3612
+ const fallback = definePortal();
3613
+ _defaultPortal = fallback;
3614
+ _defaultPortalIsFallback = true;
4590
3615
  }
4591
- const result = map(value);
4592
- cache.set(value, result);
4593
- return result;
4594
- }
4595
- function task(fn) {
4596
- const ownerIsRoot = getCurrentComponentInstance()?.isRoot ?? false;
4597
- registerMountOperation(async () => {
4598
- if (!ownerIsRoot) {
4599
- throw new Error("[Askr] task() may only be used in root components");
4600
- }
4601
- return await fn();
4602
- });
3616
+ return _defaultPortal;
4603
3617
  }
3618
+ var _defaultPortal, _defaultPortalIsFallback, DefaultPortal;
3619
+ var init_portal = __esm({
3620
+ "src/foundations/portal.tsx"() {
3621
+ "use strict";
3622
+ init_component();
3623
+ init_logger();
3624
+ _defaultPortalIsFallback = false;
3625
+ DefaultPortal = (() => {
3626
+ function Host() {
3627
+ const v = ensureDefaultPortal()();
3628
+ return v === void 0 ? null : v;
3629
+ }
3630
+ Host.render = function Render(props) {
3631
+ ensureDefaultPortal().render(props);
3632
+ return null;
3633
+ };
3634
+ return Host;
3635
+ })();
3636
+ }
3637
+ });
3638
+
3639
+ // src/index.ts
3640
+ var index_exports = {};
3641
+ __export(index_exports, {
3642
+ createIsland: () => createIsland,
3643
+ createSPA: () => createSPA,
3644
+ derive: () => derive,
3645
+ state: () => state
3646
+ });
3647
+ module.exports = __toCommonJS(index_exports);
4604
3648
 
4605
3649
  // src/boot/index.ts
4606
3650
  init_component();
4607
3651
  init_scheduler();
4608
3652
  init_logger();
4609
3653
  init_navigate();
4610
- init_jsx();
3654
+ init_execution_model();
3655
+ init_jsx2();
4611
3656
  init_portal();
4612
3657
  init_renderer();
3658
+ var HAS_ROUTES_KEY2 = /* @__PURE__ */ Symbol.for("__ASKR_HAS_ROUTES__");
4613
3659
  var componentIdCounter = 0;
4614
3660
  var instancesByRoot = /* @__PURE__ */ new WeakMap();
4615
3661
  var CLEANUP_SYMBOL = /* @__PURE__ */ Symbol.for("__tempoCleanup__");
@@ -4749,6 +3795,7 @@ function mountOrUpdate(rootElement, componentFn, options) {
4749
3795
  globalScheduler.flush();
4750
3796
  }
4751
3797
  function createIsland(config) {
3798
+ assertExecutionModel("islands");
4752
3799
  if (!config || typeof config !== "object") {
4753
3800
  throw new Error("createIsland requires a config object");
4754
3801
  }
@@ -4759,433 +3806,230 @@ function createIsland(config) {
4759
3806
  if (!rootElement) throw new Error(`Root element not found: ${config.root}`);
4760
3807
  if ("routes" in config) {
4761
3808
  throw new Error(
4762
- "createIsland does not accept routes; use createSPA for routed apps"
4763
- );
4764
- }
4765
- mountOrUpdate(rootElement, config.component, {
4766
- cleanupStrict: config.cleanupStrict
4767
- });
4768
- }
4769
- async function createSPA(config) {
4770
- if (!config || typeof config !== "object") {
4771
- throw new Error("createSPA requires a config object");
4772
- }
4773
- if (!Array.isArray(config.routes) || config.routes.length === 0) {
4774
- throw new Error(
4775
- "createSPA requires a route table. If you are enhancing existing HTML, use createIsland instead."
4776
- );
4777
- }
4778
- const rootElement = typeof config.root === "string" ? document.getElementById(config.root) : config.root;
4779
- if (!rootElement) throw new Error(`Root element not found: ${config.root}`);
4780
- const { clearRoutes: clearRoutes2, route: route2, lockRouteRegistration: lockRouteRegistration2, resolveRoute: resolveRoute2 } = await Promise.resolve().then(() => (init_route(), route_exports));
4781
- clearRoutes2();
4782
- for (const r of config.routes) {
4783
- route2(r.path, r.handler, r.namespace);
4784
- }
4785
- if (process.env.NODE_ENV === "production") lockRouteRegistration2();
4786
- const path = typeof window !== "undefined" ? window.location.pathname : "/";
4787
- const resolved = resolveRoute2(path);
4788
- if (!resolved) {
4789
- if (process.env.NODE_ENV !== "production") {
4790
- logger.warn(
4791
- `createSPA: no route found for current path (${path}). Mounting empty placeholder; navigation will activate routes when requested.`
4792
- );
4793
- }
4794
- mountOrUpdate(rootElement, () => ({ type: "div", children: [] }), {
4795
- cleanupStrict: false
4796
- });
4797
- const instance2 = instancesByRoot.get(rootElement);
4798
- if (!instance2) throw new Error("Internal error: app instance missing");
4799
- registerAppInstance(instance2, path);
4800
- initializeNavigation();
4801
- return;
4802
- }
4803
- mountOrUpdate(rootElement, resolved.handler, {
4804
- cleanupStrict: false
4805
- });
4806
- const instance = instancesByRoot.get(rootElement);
4807
- if (!instance) throw new Error("Internal error: app instance missing");
4808
- registerAppInstance(instance, path);
4809
- initializeNavigation();
4810
- }
4811
- async function hydrateSPA(config) {
4812
- if (!config || typeof config !== "object") {
4813
- throw new Error("hydrateSPA requires a config object");
4814
- }
4815
- if (!Array.isArray(config.routes) || config.routes.length === 0) {
4816
- throw new Error(
4817
- "hydrateSPA requires a route table. If you are enhancing existing HTML, use createIsland instead."
4818
- );
4819
- }
4820
- const rootElement = typeof config.root === "string" ? document.getElementById(config.root) : config.root;
4821
- if (!rootElement) throw new Error(`Root element not found: ${config.root}`);
4822
- const serverHTML = rootElement.innerHTML;
4823
- const {
4824
- clearRoutes: clearRoutes2,
4825
- route: route2,
4826
- setServerLocation: setServerLocation2,
4827
- lockRouteRegistration: lockRouteRegistration2,
4828
- resolveRoute: resolveRoute2
4829
- } = await Promise.resolve().then(() => (init_route(), route_exports));
4830
- clearRoutes2();
4831
- for (const r of config.routes) {
4832
- route2(r.path, r.handler, r.namespace);
4833
- }
4834
- const path = typeof window !== "undefined" ? window.location.pathname : "/";
4835
- setServerLocation2(path);
4836
- if (process.env.NODE_ENV === "production") lockRouteRegistration2();
4837
- const resolved = resolveRoute2(path);
4838
- if (!resolved) {
4839
- throw new Error(`hydrateSPA: no route found for current path (${path}).`);
4840
- }
4841
- const { renderToStringSync: renderToStringSync2 } = await Promise.resolve().then(() => (init_ssr(), ssr_exports));
4842
- const expectedHTML = renderToStringSync2(() => {
4843
- const out = resolved.handler(resolved.params);
4844
- return out ?? {
4845
- type: "div",
4846
- children: []
4847
- };
4848
- });
4849
- const serverContainer = document.createElement("div");
4850
- serverContainer.innerHTML = serverHTML;
4851
- const expectedContainer = document.createElement("div");
4852
- expectedContainer.innerHTML = expectedHTML;
4853
- if (!serverContainer.isEqualNode(expectedContainer)) {
4854
- throw new Error(
4855
- "[Askr] Hydration mismatch detected. Server HTML does not match expected server-render output."
4856
- );
4857
- }
4858
- mountOrUpdate(rootElement, resolved.handler, {
4859
- cleanupStrict: false
4860
- });
4861
- const { registerAppInstance: registerAppInstance2, initializeNavigation: initializeNavigation2 } = await Promise.resolve().then(() => (init_navigate(), navigate_exports));
4862
- const instance = instancesByRoot.get(rootElement);
4863
- if (!instance) throw new Error("Internal error: app instance missing");
4864
- registerAppInstance2(instance, path);
4865
- initializeNavigation2();
4866
- }
4867
- function cleanupApp(root) {
4868
- const rootElement = typeof root === "string" ? document.getElementById(root) : root;
4869
- if (!rootElement) return;
4870
- const cleanupFn = rootElement[CLEANUP_SYMBOL];
4871
- if (typeof cleanupFn === "function") {
4872
- cleanupFn();
4873
- }
4874
- instancesByRoot.delete(rootElement);
4875
- }
4876
- function hasApp(root) {
4877
- const rootElement = typeof root === "string" ? document.getElementById(root) : root;
4878
- if (!rootElement) return false;
4879
- return instancesByRoot.has(rootElement);
4880
- }
4881
-
4882
- // src/index.ts
4883
- init_route();
4884
- init_route();
4885
- init_navigate();
4886
-
4887
- // src/components/Link.tsx
4888
- init_navigate();
4889
- function Link({ href, children }) {
4890
- return {
4891
- type: "a",
4892
- props: {
4893
- href,
4894
- children,
4895
- onClick: (e) => {
4896
- const event = e;
4897
- const button = event.button ?? 0;
4898
- if (button !== 0 || // not left-click
4899
- event.ctrlKey || // Ctrl/Cmd+click
4900
- event.metaKey || // Cmd on Mac
4901
- event.shiftKey || // Shift+click
4902
- event.altKey) {
4903
- return;
4904
- }
4905
- event.preventDefault();
4906
- navigate(href);
4907
- }
4908
- }
4909
- };
4910
- }
4911
-
4912
- // src/foundations/layout.tsx
4913
- function layout(Layout) {
4914
- return (children, props) => Layout({ ...props, children });
4915
- }
4916
-
4917
- // src/foundations/slot.tsx
4918
- init_logger();
4919
- init_jsx();
4920
- function Slot(props) {
4921
- if (props.asChild) {
4922
- const { children, ...rest } = props;
4923
- if (isElement(children)) {
4924
- return cloneElement(children, rest);
4925
- }
4926
- logger.warn("<Slot asChild> expects a single JSX element child.");
4927
- return null;
4928
- }
4929
- return {
4930
- $$typeof: ELEMENT_TYPE,
4931
- type: Fragment,
4932
- props: { children: props.children }
4933
- };
4934
- }
4935
-
4936
- // src/index.ts
4937
- init_portal();
4938
- init_ssr();
4939
- init_jsx_runtime();
4940
- init_route();
4941
- init_navigate();
4942
-
4943
- // src/runtime/fastlane.ts
4944
- init_scheduler();
4945
- init_logger();
4946
- init_component();
4947
- init_renderer();
4948
- init_fastlane_shared();
4949
- init_types2();
4950
- init_dev_namespace();
4951
- function unwrapFragmentForFastPath(vnode) {
4952
- if (!vnode || typeof vnode !== "object" || !("type" in vnode)) return vnode;
4953
- const v = vnode;
4954
- if (typeof v.type === "symbol" && (v.type === Fragment || String(v.type) === "Symbol(askr.fragment)")) {
4955
- const children = v.children || v.props?.children;
4956
- if (Array.isArray(children) && children.length > 0) {
4957
- for (const child of children) {
4958
- if (child && typeof child === "object" && "type" in child) {
4959
- const c = child;
4960
- if (typeof c.type === "string") {
4961
- return child;
4962
- }
4963
- }
4964
- }
4965
- }
4966
- }
4967
- return vnode;
4968
- }
4969
- function classifyUpdate(instance, result) {
4970
- const unwrappedResult = unwrapFragmentForFastPath(result);
4971
- if (!unwrappedResult || typeof unwrappedResult !== "object" || !("type" in unwrappedResult))
4972
- return { useFastPath: false, reason: "not-vnode" };
4973
- const vnode = unwrappedResult;
4974
- if (vnode == null || typeof vnode.type !== "string")
4975
- return { useFastPath: false, reason: "not-intrinsic" };
4976
- const parent = instance.target;
4977
- if (!parent) return { useFastPath: false, reason: "no-root" };
4978
- const firstChild = parent.children[0];
4979
- if (!firstChild) return { useFastPath: false, reason: "no-first-child" };
4980
- if (firstChild.tagName.toLowerCase() !== String(vnode.type).toLowerCase())
4981
- return { useFastPath: false, reason: "root-tag-mismatch" };
4982
- const children = vnode.children || vnode.props?.children;
4983
- if (!Array.isArray(children))
4984
- return { useFastPath: false, reason: "no-children-array" };
4985
- for (const c of children) {
4986
- if (typeof c === "object" && c !== null && "type" in c && typeof c.type === "function") {
4987
- return { useFastPath: false, reason: "component-child-present" };
4988
- }
4989
- }
4990
- if (instance.mountOperations.length > 0)
4991
- return { useFastPath: false, reason: "pending-mounts" };
4992
- try {
4993
- populateKeyMapForElement(firstChild);
4994
- } catch {
4995
- }
4996
- const oldKeyMap = getKeyMapForElement(firstChild);
4997
- const decision = isKeyedReorderFastPathEligible(
4998
- firstChild,
4999
- children,
5000
- oldKeyMap
5001
- );
5002
- if (!decision.useFastPath || decision.totalKeyed < 128)
5003
- return { ...decision, useFastPath: false, reason: "renderer-declined" };
5004
- return { ...decision, useFastPath: true };
5005
- }
5006
- function commitReorderOnly(instance, result) {
5007
- const evaluate2 = globalThis.__ASKR_RENDERER?.evaluate;
5008
- if (typeof evaluate2 !== "function") {
5009
- logger.warn(
5010
- "[Tempo][FASTPATH][DEV] renderer.evaluate not available; declining fast-lane"
5011
- );
5012
- return false;
5013
- }
5014
- const schedBefore = process.env.NODE_ENV !== "production" ? globalScheduler.getState() : null;
5015
- enterBulkCommit();
5016
- try {
5017
- globalScheduler.runWithSyncProgress(() => {
5018
- evaluate2(result, instance.target);
5019
- try {
5020
- finalizeReadSubscriptions(instance);
5021
- } catch (e) {
5022
- if (process.env.NODE_ENV !== "production") throw e;
5023
- }
5024
- });
5025
- const clearedAfter = globalScheduler.clearPendingSyncTasks?.() ?? 0;
5026
- setDevValue("__FASTLANE_CLEARED_AFTER", clearedAfter);
5027
- if (process.env.NODE_ENV !== "production") {
5028
- validateFastLaneInvariants(instance, schedBefore);
5029
- }
5030
- return true;
5031
- } finally {
5032
- exitBulkCommit();
3809
+ "createIsland does not accept routes; use createSPA for routed apps"
3810
+ );
5033
3811
  }
5034
- if (process.env.NODE_ENV !== "production") {
5035
- if (isBulkCommitActive2()) {
3812
+ try {
3813
+ const g = globalThis;
3814
+ if (g[HAS_ROUTES_KEY2]) {
5036
3815
  throw new Error(
5037
- "Fast-lane invariant violated: bulk commit flag still set after commit"
3816
+ "Routes are not supported with islands. Use createSPA (client) or createSSR (server) instead."
5038
3817
  );
5039
3818
  }
3819
+ } catch {
5040
3820
  }
3821
+ mountOrUpdate(rootElement, config.component, {
3822
+ cleanupStrict: config.cleanupStrict
3823
+ });
5041
3824
  }
5042
- function validateFastLaneInvariants(instance, schedBefore) {
5043
- const commitCount = getDevValue("__LAST_FASTPATH_COMMIT_COUNT") ?? 0;
5044
- const invariants = {
5045
- commitCount,
5046
- mountOps: instance.mountOperations.length,
5047
- cleanupFns: instance.cleanupFns.length
5048
- };
5049
- setDevValue("__LAST_FASTLANE_INVARIANTS", invariants);
5050
- if (commitCount !== 1) {
5051
- console.error(
5052
- "[FASTLANE][INV] commitCount",
5053
- commitCount,
5054
- "diag",
5055
- globalThis.__ASKR_DIAG
5056
- );
5057
- throw new Error(
5058
- "Fast-lane invariant violated: expected exactly one DOM commit during reorder-only commit"
5059
- );
3825
+ async function createSPA(config) {
3826
+ assertExecutionModel("spa");
3827
+ if (!config || typeof config !== "object") {
3828
+ throw new Error("createSPA requires a config object");
5060
3829
  }
5061
- if (invariants.mountOps > 0) {
3830
+ if (!Array.isArray(config.routes) || config.routes.length === 0) {
5062
3831
  throw new Error(
5063
- "Fast-lane invariant violated: mount operations were registered during bulk commit"
3832
+ "createSPA requires a route table. If you are enhancing existing HTML, use createIsland instead."
5064
3833
  );
5065
3834
  }
5066
- if (invariants.cleanupFns > 0) {
3835
+ const rootElement = typeof config.root === "string" ? document.getElementById(config.root) : config.root;
3836
+ if (!rootElement) throw new Error(`Root element not found: ${config.root}`);
3837
+ const { clearRoutes: clearRoutes2, route: route2, lockRouteRegistration: lockRouteRegistration2, resolveRoute: resolveRoute2 } = await Promise.resolve().then(() => (init_route(), route_exports));
3838
+ clearRoutes2();
3839
+ for (const r of config.routes) {
3840
+ route2(r.path, r.handler, r.namespace);
3841
+ }
3842
+ if (process.env.NODE_ENV === "production") lockRouteRegistration2();
3843
+ const path = typeof window !== "undefined" ? window.location.pathname : "/";
3844
+ const resolved = resolveRoute2(path);
3845
+ if (!resolved) {
3846
+ if (process.env.NODE_ENV !== "production") {
3847
+ logger.warn(
3848
+ `createSPA: no route found for current path (${path}). Mounting empty placeholder; navigation will activate routes when requested.`
3849
+ );
3850
+ }
3851
+ mountOrUpdate(rootElement, () => ({ type: "div", children: [] }), {
3852
+ cleanupStrict: false
3853
+ });
3854
+ const instance2 = instancesByRoot.get(rootElement);
3855
+ if (!instance2) throw new Error("Internal error: app instance missing");
3856
+ registerAppInstance(instance2, path);
3857
+ initializeNavigation();
3858
+ return;
3859
+ }
3860
+ mountOrUpdate(rootElement, resolved.handler, {
3861
+ cleanupStrict: false
3862
+ });
3863
+ const instance = instancesByRoot.get(rootElement);
3864
+ if (!instance) throw new Error("Internal error: app instance missing");
3865
+ registerAppInstance(instance, path);
3866
+ initializeNavigation();
3867
+ }
3868
+
3869
+ // src/runtime/state.ts
3870
+ init_scheduler();
3871
+ init_component();
3872
+ init_invariant();
3873
+ init_fastlane();
3874
+ function state(initialValue) {
3875
+ const instance = getCurrentInstance();
3876
+ if (!instance) {
5067
3877
  throw new Error(
5068
- "Fast-lane invariant violated: cleanup functions were added during bulk commit"
3878
+ "state() can only be called during component render execution. Move state() calls to the top level of your component function."
5069
3879
  );
5070
3880
  }
5071
- const schedAfter = globalScheduler.getState();
5072
- if (schedBefore && schedAfter && schedAfter.taskCount > schedBefore.taskCount) {
5073
- console.error(
5074
- "[FASTLANE] schedBefore, schedAfter",
5075
- schedBefore,
5076
- schedAfter
5077
- );
5078
- console.error("[FASTLANE] enqueue logs", getDevValue("__ENQUEUE_LOGS"));
3881
+ const index = getNextStateIndex();
3882
+ const stateValues = instance.stateValues;
3883
+ if (index < instance.stateIndexCheck) {
5079
3884
  throw new Error(
5080
- "Fast-lane invariant violated: scheduler enqueued leftover work during bulk commit"
3885
+ `State index violation: state() call at index ${index}, but previously saw index ${instance.stateIndexCheck}. This happens when state() is called conditionally (inside if/for/etc). Move all state() calls to the top level of your component function, before any conditionals.`
5081
3886
  );
5082
3887
  }
5083
- let finalState = globalScheduler.getState();
5084
- const executing = globalScheduler.isExecuting();
5085
- let outstandingAfter = Math.max(
5086
- 0,
5087
- finalState.taskCount - (executing ? 1 : 0)
3888
+ invariant(
3889
+ index >= instance.stateIndexCheck,
3890
+ "[State] State indices must increase monotonically"
5088
3891
  );
5089
- if (outstandingAfter !== 0) {
5090
- let attempts = 0;
5091
- while (attempts < 5) {
5092
- const cleared = globalScheduler.clearPendingSyncTasks?.() ?? 0;
5093
- if (cleared === 0) break;
5094
- attempts++;
5095
- }
5096
- finalState = globalScheduler.getState();
5097
- outstandingAfter = Math.max(
5098
- 0,
5099
- finalState.taskCount - (globalScheduler.isExecuting() ? 1 : 0)
5100
- );
5101
- if (outstandingAfter !== 0) {
5102
- console.error(
5103
- "[FASTLANE] Post-commit enqueue logs:",
5104
- getDevValue("__ENQUEUE_LOGS")
5105
- );
5106
- console.error(
5107
- "[FASTLANE] Cleared counts:",
5108
- getDevValue("__FASTLANE_CLEARED_TASKS"),
5109
- getDevValue("__FASTLANE_CLEARED_AFTER")
3892
+ instance.stateIndexCheck = index;
3893
+ if (instance.firstRenderComplete) {
3894
+ if (!instance.expectedStateIndices.includes(index)) {
3895
+ throw new Error(
3896
+ `Hook order violation: state() called at index ${index}, but this index was not in the first render's sequence [${instance.expectedStateIndices.join(", ")}]. This usually means state() is inside a conditional or loop. Move all state() calls to the top level of your component function.`
5110
3897
  );
3898
+ }
3899
+ } else {
3900
+ instance.expectedStateIndices.push(index);
3901
+ }
3902
+ if (stateValues[index]) {
3903
+ const existing = stateValues[index];
3904
+ if (existing._owner !== instance) {
5111
3905
  throw new Error(
5112
- `Fast-lane invariant violated: scheduler has ${finalState.taskCount} pending task(s) after commit`
3906
+ `State ownership violation: state() called at index ${index} is owned by a different component instance. State ownership is positional and immutable.`
5113
3907
  );
5114
3908
  }
3909
+ return existing;
5115
3910
  }
3911
+ const cell = createStateCell(initialValue, instance);
3912
+ stateValues[index] = cell;
3913
+ return cell;
5116
3914
  }
5117
- function tryRuntimeFastLaneSync(instance, result) {
5118
- const cls = classifyUpdate(instance, result);
5119
- if (!cls.useFastPath) {
5120
- setDevValue("__LAST_FASTPATH_STATS", void 0);
5121
- setDevValue("__LAST_FASTPATH_COMMIT_COUNT", 0);
5122
- return false;
5123
- }
5124
- try {
5125
- return commitReorderOnly(instance, result);
5126
- } catch (err) {
5127
- if (process.env.NODE_ENV !== "production") throw err;
5128
- return false;
3915
+ function createStateCell(initialValue, instance) {
3916
+ let value = initialValue;
3917
+ const readers = /* @__PURE__ */ new Map();
3918
+ function read() {
3919
+ read._hasBeenRead = true;
3920
+ const inst = getCurrentInstance();
3921
+ if (inst && inst._currentRenderToken !== void 0) {
3922
+ if (!inst._pendingReadStates) inst._pendingReadStates = /* @__PURE__ */ new Set();
3923
+ inst._pendingReadStates.add(read);
3924
+ }
3925
+ return value;
5129
3926
  }
5130
- }
5131
- if (typeof globalThis !== "undefined") {
5132
- globalThis.__ASKR_FASTLANE = {
5133
- isBulkCommitActive: isBulkCommitActive2,
5134
- enterBulkCommit,
5135
- exitBulkCommit,
5136
- tryRuntimeFastLaneSync,
5137
- markFastPathApplied,
5138
- isFastPathApplied
3927
+ read._readers = readers;
3928
+ read._owner = instance;
3929
+ read.set = (newValueOrUpdater) => {
3930
+ const currentInst = getCurrentInstance();
3931
+ if (currentInst !== null) {
3932
+ throw new Error(
3933
+ `[Askr] state.set() cannot be called during component render. State mutations during render break the actor model and cause infinite loops. Move state updates to event handlers or use conditional rendering instead.`
3934
+ );
3935
+ }
3936
+ let newValue;
3937
+ if (typeof newValueOrUpdater === "function") {
3938
+ const updater = newValueOrUpdater;
3939
+ newValue = updater(value);
3940
+ } else {
3941
+ newValue = newValueOrUpdater;
3942
+ }
3943
+ if (Object.is(value, newValue)) return;
3944
+ if (isBulkCommitActive2()) {
3945
+ value = newValue;
3946
+ return;
3947
+ }
3948
+ value = newValue;
3949
+ const readersMap = read._readers;
3950
+ if (readersMap) {
3951
+ for (const [subInst, token] of readersMap) {
3952
+ if (subInst.lastRenderToken !== token) continue;
3953
+ if (!subInst.hasPendingUpdate) {
3954
+ subInst.hasPendingUpdate = true;
3955
+ const subTask = subInst._pendingFlushTask;
3956
+ if (subTask) globalScheduler.enqueue(subTask);
3957
+ else
3958
+ globalScheduler.enqueue(() => {
3959
+ subInst.hasPendingUpdate = false;
3960
+ subInst.notifyUpdate?.();
3961
+ });
3962
+ }
3963
+ }
3964
+ }
3965
+ const readersMapForOwner = readersMap;
3966
+ const ownerRecordedToken = readersMapForOwner?.get(instance);
3967
+ const ownerShouldEnqueue = (
3968
+ // Normal case: owner read this state in last committed render
3969
+ ownerRecordedToken !== void 0 && instance.lastRenderToken === ownerRecordedToken
3970
+ );
3971
+ if (ownerShouldEnqueue && !instance.hasPendingUpdate) {
3972
+ instance.hasPendingUpdate = true;
3973
+ const task = instance._pendingFlushTask;
3974
+ if (task) globalScheduler.enqueue(task);
3975
+ else
3976
+ globalScheduler.enqueue(() => {
3977
+ instance.hasPendingUpdate = false;
3978
+ instance.notifyUpdate?.();
3979
+ });
3980
+ }
5139
3981
  };
3982
+ return read;
5140
3983
  }
5141
3984
 
5142
- // src/index.ts
5143
- if (typeof globalThis !== "undefined") {
5144
- const g = globalThis;
5145
- if (!g.createIsland) g.createIsland = createIsland;
5146
- if (!g.createSPA) g.createSPA = createSPA;
5147
- if (!g.hydrateSPA) g.hydrateSPA = hydrateSPA;
5148
- if (!g.route) g.route = route;
5149
- if (!g.getRoutes) g.getRoutes = getRoutes;
5150
- if (!g.navigate) g.navigate = navigate;
3985
+ // src/runtime/derive.ts
3986
+ init_component();
3987
+ var deriveCaches = /* @__PURE__ */ new WeakMap();
3988
+ function getDeriveCache(instance) {
3989
+ let cache = deriveCaches.get(instance);
3990
+ if (!cache) {
3991
+ cache = /* @__PURE__ */ new Map();
3992
+ deriveCaches.set(instance, cache);
3993
+ }
3994
+ return cache;
3995
+ }
3996
+ function derive(source, map) {
3997
+ if (map === void 0 && typeof source === "function") {
3998
+ const value2 = source();
3999
+ if (value2 == null) return null;
4000
+ const instance2 = getCurrentComponentInstance();
4001
+ if (!instance2) {
4002
+ return value2;
4003
+ }
4004
+ const cache2 = getDeriveCache(instance2);
4005
+ if (cache2.has(value2)) return cache2.get(value2);
4006
+ cache2.set(value2, value2);
4007
+ return value2;
4008
+ }
4009
+ let value;
4010
+ if (typeof source === "function" && !("value" in source)) {
4011
+ value = source();
4012
+ } else {
4013
+ value = source?.value ?? source;
4014
+ }
4015
+ if (value == null) return null;
4016
+ const instance = getCurrentComponentInstance();
4017
+ if (!instance) {
4018
+ return map(value);
4019
+ }
4020
+ const cache = getDeriveCache(instance);
4021
+ if (cache.has(value)) {
4022
+ return cache.get(value);
4023
+ }
4024
+ const result = map(value);
4025
+ cache.set(value, result);
4026
+ return result;
5151
4027
  }
5152
4028
  // Annotate the CommonJS export names for ESM import in node:
5153
4029
  0 && (module.exports = {
5154
- DefaultPortal,
5155
- Fragment,
5156
- Link,
5157
- Slot,
5158
- _resetDefaultPortal,
5159
- cleanupApp,
5160
- clearRoutes,
5161
- collectResources,
5162
4030
  createIsland,
5163
4031
  createSPA,
5164
- defineContext,
5165
- definePortal,
5166
4032
  derive,
5167
- getLoadedNamespaces,
5168
- getNamespaceRoutes,
5169
- getRoutes,
5170
- getSignal,
5171
- hasApp,
5172
- hydrateSPA,
5173
- jsx,
5174
- jsxs,
5175
- layout,
5176
- navigate,
5177
- readContext,
5178
- renderToStream,
5179
- renderToString,
5180
- renderToStringSync,
5181
- renderToStringSyncForUrl,
5182
- resolveResources,
5183
- resource,
5184
- route,
5185
- scheduleEventHandler,
5186
- setServerLocation,
5187
- state,
5188
- task,
5189
- unloadNamespace
4033
+ state
5190
4034
  });
5191
4035
  //# sourceMappingURL=index.cjs.map