@askrjs/askr 0.0.1 → 0.0.2

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 (50) hide show
  1. package/README.md +18 -10
  2. package/dist/chunk-KR6HG7HF.js +38 -0
  3. package/dist/chunk-KR6HG7HF.js.map +1 -0
  4. package/dist/{chunk-L7RL4LYV.js → chunk-MIPES65F.js} +1486 -1905
  5. package/dist/chunk-MIPES65F.js.map +1 -0
  6. package/dist/{chunk-HIWJVOS4.js → chunk-PFOLLB6A.js} +38 -17
  7. package/dist/chunk-PFOLLB6A.js.map +1 -0
  8. package/dist/chunk-QECQ2TF6.js +28 -0
  9. package/dist/chunk-QECQ2TF6.js.map +1 -0
  10. package/dist/{chunk-UUM5W2RM.js → chunk-RJWOOUYV.js} +2 -2
  11. package/dist/chunk-RJWOOUYV.js.map +1 -0
  12. package/dist/index.cjs +1760 -1972
  13. package/dist/index.cjs.map +1 -1
  14. package/dist/index.d.cts +52 -40
  15. package/dist/index.d.ts +52 -40
  16. package/dist/index.js +226 -52
  17. package/dist/index.js.map +1 -1
  18. package/dist/jsx/jsx-dev-runtime.cjs +9 -3
  19. package/dist/jsx/jsx-dev-runtime.cjs.map +1 -1
  20. package/dist/jsx/jsx-dev-runtime.d.cts +4 -9
  21. package/dist/jsx/jsx-dev-runtime.d.ts +4 -9
  22. package/dist/jsx/jsx-dev-runtime.js +10 -4
  23. package/dist/jsx/jsx-dev-runtime.js.map +1 -1
  24. package/dist/jsx/jsx-runtime.cjs +14 -5
  25. package/dist/jsx/jsx-runtime.cjs.map +1 -1
  26. package/dist/jsx/jsx-runtime.d.cts +9 -6
  27. package/dist/jsx/jsx-runtime.d.ts +9 -6
  28. package/dist/jsx/jsx-runtime.js +6 -2
  29. package/dist/{navigate-NLQOZQGM.js → navigate-SDZNA2ZE.js} +5 -5
  30. package/dist/{route-TVYWYCEJ.js → route-P5YQBT4T.js} +4 -4
  31. package/dist/{ssr-4ELUFK65.js → ssr-65K3IJ6B.js} +9 -5
  32. package/dist/{types-DUDmnzD8.d.cts → types-DLTViI21.d.cts} +15 -3
  33. package/dist/{types-DUDmnzD8.d.ts → types-DLTViI21.d.ts} +15 -3
  34. package/package.json +5 -3
  35. package/src/jsx/index.ts +4 -0
  36. package/src/jsx/jsx-dev-runtime.ts +7 -10
  37. package/src/jsx/jsx-runtime.ts +23 -11
  38. package/src/jsx/types.ts +22 -3
  39. package/src/jsx/utils.ts +19 -0
  40. package/dist/chunk-4CV4JOE5.js +0 -27
  41. package/dist/chunk-HIWJVOS4.js.map +0 -1
  42. package/dist/chunk-L7RL4LYV.js.map +0 -1
  43. package/dist/chunk-UUM5W2RM.js.map +0 -1
  44. package/dist/chunk-YNH3D4KW.js +0 -29
  45. package/dist/chunk-YNH3D4KW.js.map +0 -1
  46. package/dist/ssr-4ELUFK65.js.map +0 -1
  47. package/src/jsx/react-jsx-runtime.d.ts +0 -0
  48. /package/dist/{chunk-4CV4JOE5.js.map → navigate-SDZNA2ZE.js.map} +0 -0
  49. /package/dist/{navigate-NLQOZQGM.js.map → route-P5YQBT4T.js.map} +0 -0
  50. /package/dist/{route-TVYWYCEJ.js.map → ssr-65K3IJ6B.js.map} +0 -0
package/dist/index.cjs CHANGED
@@ -136,9 +136,9 @@ var init_scheduler = __esm({
136
136
  // Keep a lightweight taskCount for compatibility/diagnostics
137
137
  this.taskCount = 0;
138
138
  }
139
- enqueue(task) {
139
+ enqueue(task2) {
140
140
  assertSchedulingPrecondition(
141
- typeof task === "function",
141
+ typeof task2 === "function",
142
142
  "enqueue() requires a function"
143
143
  );
144
144
  if (isBulkCommitActive() && !this.allowSyncProgress) {
@@ -149,7 +149,7 @@ var init_scheduler = __esm({
149
149
  }
150
150
  return;
151
151
  }
152
- this.q.push(task);
152
+ this.q.push(task2);
153
153
  this.taskCount++;
154
154
  if (!this.running && !this.kickScheduled && !this.inHandler && !isBulkCommitActive()) {
155
155
  this.kickScheduled = true;
@@ -190,10 +190,10 @@ var init_scheduler = __esm({
190
190
  `[Scheduler] exceeded MAX_FLUSH_DEPTH (${MAX_FLUSH_DEPTH}). Likely infinite update loop.`
191
191
  );
192
192
  }
193
- const task = this.q[this.head++];
193
+ const task2 = this.q[this.head++];
194
194
  try {
195
195
  this.executionDepth++;
196
- task();
196
+ task2();
197
197
  this.executionDepth--;
198
198
  } catch (err) {
199
199
  if (this.executionDepth > 0) this.executionDepth = 0;
@@ -279,17 +279,14 @@ var init_scheduler = __esm({
279
279
  if (this.flushVersion >= target) return Promise.resolve();
280
280
  return new Promise((resolve, reject) => {
281
281
  const timer = setTimeout(() => {
282
+ const ns = globalThis.__ASKR__ || {};
282
283
  const diag = {
283
284
  flushVersion: this.flushVersion,
284
285
  queueLen: this.q.length - this.head,
285
286
  running: this.running,
286
287
  inHandler: this.inHandler,
287
288
  bulk: isBulkCommitActive(),
288
- globals: {
289
- __ASKR_LAST_FASTPATH_STATS: globalThis.__ASKR_LAST_FASTPATH_STATS,
290
- __ASKR_LAST_BULK_TEXT_FASTPATH_STATS: globalThis.__ASKR_LAST_BULK_TEXT_FASTPATH_STATS,
291
- __ASKR_FASTPATH_COUNTERS: globalThis.__ASKR_FASTPATH_COUNTERS
292
- }
289
+ namespace: ns
293
290
  };
294
291
  reject(
295
292
  new Error(
@@ -366,267 +363,6 @@ var init_scheduler = __esm({
366
363
  }
367
364
  });
368
365
 
369
- // src/runtime/fastlane.ts
370
- function enterBulkCommit() {
371
- _bulkCommitActive = true;
372
- _appliedParents = /* @__PURE__ */ new WeakSet();
373
- try {
374
- const cleared = globalScheduler.clearPendingSyncTasks?.() ?? 0;
375
- if (process.env.NODE_ENV !== "production") {
376
- const _g = globalThis;
377
- _g.__ASKR_FASTLANE_CLEARED_TASKS = cleared;
378
- }
379
- } catch (err) {
380
- if (process.env.NODE_ENV !== "production") throw err;
381
- }
382
- }
383
- function exitBulkCommit() {
384
- _bulkCommitActive = false;
385
- _appliedParents = null;
386
- }
387
- function isBulkCommitActive2() {
388
- return _bulkCommitActive;
389
- }
390
- function markFastPathApplied(parent) {
391
- if (!_appliedParents) return;
392
- try {
393
- _appliedParents.add(parent);
394
- } catch (e) {
395
- void e;
396
- }
397
- }
398
- function isFastPathApplied(parent) {
399
- return !!(_appliedParents && _appliedParents.has(parent));
400
- }
401
- function classifyUpdate(instance, result) {
402
- if (!result || typeof result !== "object" || !("type" in result))
403
- return { useFastPath: false, reason: "not-vnode" };
404
- const vnode = result;
405
- if (vnode == null || typeof vnode.type !== "string")
406
- return { useFastPath: false, reason: "not-intrinsic" };
407
- const parent = instance.target;
408
- if (!parent) return { useFastPath: false, reason: "no-root" };
409
- const firstChild = parent.children[0];
410
- if (!firstChild) return { useFastPath: false, reason: "no-first-child" };
411
- if (firstChild.tagName.toLowerCase() !== String(vnode.type).toLowerCase())
412
- return { useFastPath: false, reason: "root-tag-mismatch" };
413
- const children = vnode.children || vnode.props?.children;
414
- if (!Array.isArray(children))
415
- return { useFastPath: false, reason: "no-children-array" };
416
- for (let i = 0; i < children.length; i++) {
417
- const c = children[i];
418
- if (typeof c === "object" && c !== null && "type" in c && typeof c.type === "function") {
419
- return { useFastPath: false, reason: "component-child-present" };
420
- }
421
- }
422
- if (instance.mountOperations.length > 0)
423
- return { useFastPath: false, reason: "pending-mounts" };
424
- const oldKeyMap = getKeyMapForElement(firstChild);
425
- const decision = isKeyedReorderFastPathEligible(
426
- firstChild,
427
- children,
428
- oldKeyMap
429
- );
430
- if (!decision.useFastPath || decision.totalKeyed < 128)
431
- return { ...decision, useFastPath: false, reason: "renderer-declined" };
432
- return { ...decision, useFastPath: true };
433
- }
434
- function commitReorderOnly(instance, result) {
435
- const evaluate2 = globalThis.__ASKR_RENDERER?.evaluate;
436
- if (typeof evaluate2 !== "function") {
437
- logger.warn(
438
- "[Tempo][FASTPATH][DEV] renderer.evaluate not available; declining fast-lane"
439
- );
440
- return false;
441
- }
442
- const schedBefore = process.env.NODE_ENV !== "production" ? globalScheduler.getState() : null;
443
- enterBulkCommit();
444
- try {
445
- globalScheduler.runWithSyncProgress(() => {
446
- evaluate2(result, instance.target);
447
- try {
448
- const comp = (init_component(), __toCommonJS(component_exports));
449
- if (typeof comp?.finalizeReadSubscriptions === "function") {
450
- try {
451
- comp.finalizeReadSubscriptions(instance);
452
- } catch (e) {
453
- if (process.env.NODE_ENV !== "production") throw e;
454
- }
455
- }
456
- } catch (e) {
457
- void e;
458
- }
459
- });
460
- try {
461
- const clearedAfter = globalScheduler.clearPendingSyncTasks?.() ?? 0;
462
- if (process.env.NODE_ENV !== "production") {
463
- const _g = globalThis;
464
- _g.__ASKR_FASTLANE_CLEARED_AFTER = clearedAfter;
465
- }
466
- } catch (err) {
467
- if (process.env.NODE_ENV !== "production") throw err;
468
- }
469
- if (process.env.NODE_ENV !== "production") {
470
- const _g = globalThis;
471
- const commitCount = _g.__ASKR_LAST_FASTPATH_COMMIT_COUNT ?? 0;
472
- const invariants = {
473
- commitCount,
474
- mountOps: instance.mountOperations.length,
475
- cleanupFns: instance.cleanupFns.length
476
- };
477
- _g.__ASKR_LAST_FASTLANE_INVARIANTS = invariants;
478
- if (commitCount !== 1) {
479
- throw new Error(
480
- "Fast-lane invariant violated: expected exactly one DOM commit during reorder-only commit"
481
- );
482
- }
483
- if (invariants.mountOps > 0) {
484
- throw new Error(
485
- "Fast-lane invariant violated: mount operations were registered during bulk commit"
486
- );
487
- }
488
- if (invariants.cleanupFns > 0) {
489
- throw new Error(
490
- "Fast-lane invariant violated: cleanup functions were added during bulk commit"
491
- );
492
- }
493
- const schedAfter = globalScheduler.getState();
494
- if (schedBefore && schedAfter && // Only fail if outstanding tasks increased — consuming existing tasks is allowed
495
- schedAfter.taskCount > schedBefore.taskCount) {
496
- try {
497
- console.error(
498
- "[FASTLANE] schedBefore, schedAfter",
499
- schedBefore,
500
- schedAfter
501
- );
502
- console.error(
503
- "[FASTLANE] enqueue logs",
504
- globalThis.__ASKR_ENQUEUE_LOGS
505
- );
506
- } catch (e) {
507
- void e;
508
- }
509
- throw new Error(
510
- "Fast-lane invariant violated: scheduler enqueued leftover work during bulk commit"
511
- );
512
- }
513
- let finalState = globalScheduler.getState();
514
- const executing = globalScheduler.isExecuting();
515
- const outstandingAfter = Math.max(
516
- 0,
517
- finalState.taskCount - (executing ? 1 : 0)
518
- );
519
- if (outstandingAfter !== 0) {
520
- if (process.env.NODE_ENV !== "production") {
521
- let attempts = 0;
522
- while (attempts < 5) {
523
- const cleared = globalScheduler.clearPendingSyncTasks?.() ?? 0;
524
- if (cleared === 0) break;
525
- attempts++;
526
- }
527
- finalState = globalScheduler.getState();
528
- const outstandingAfter2 = Math.max(
529
- 0,
530
- finalState.taskCount - (globalScheduler.isExecuting() ? 1 : 0)
531
- );
532
- if (outstandingAfter2 !== 0) {
533
- try {
534
- const _g2 = globalThis;
535
- console.error(
536
- "[FASTLANE] Post-commit enqueue logs:",
537
- _g2.__ASKR_ENQUEUE_LOGS
538
- );
539
- console.error(
540
- "[FASTLANE] Cleared counts:",
541
- _g2.__ASKR_FASTLANE_CLEARED_TASKS,
542
- _g2.__ASKR_FASTLANE_CLEARED_AFTER
543
- );
544
- } catch (err) {
545
- void err;
546
- }
547
- throw new Error(
548
- `Fast-lane invariant violated: scheduler has ${finalState.taskCount} pending task(s) after commit`
549
- );
550
- }
551
- } else {
552
- globalScheduler.clearPendingSyncTasks?.();
553
- }
554
- }
555
- }
556
- return true;
557
- } finally {
558
- exitBulkCommit();
559
- if (process.env.NODE_ENV !== "production") {
560
- try {
561
- const _g = globalThis;
562
- _g.__ASKR_FASTLANE_BULK_FLAG_CHECK = isBulkCommitActive2();
563
- } catch (e) {
564
- void e;
565
- }
566
- }
567
- }
568
- if (process.env.NODE_ENV !== "production") {
569
- const _g = globalThis;
570
- if (_g.__ASKR_FASTLANE_BULK_FLAG_CHECK) {
571
- delete _g.__ASKR_FASTLANE_BULK_FLAG_CHECK;
572
- throw new Error(
573
- "Fast-lane invariant violated: bulk commit flag still set after commit"
574
- );
575
- }
576
- }
577
- }
578
- function tryRuntimeFastLaneSync(instance, result) {
579
- const cls = classifyUpdate(instance, result);
580
- if (!cls.useFastPath) return false;
581
- try {
582
- return commitReorderOnly(instance, result);
583
- } catch (err) {
584
- if (process.env.NODE_ENV !== "production") throw err;
585
- return false;
586
- }
587
- }
588
- var _bulkCommitActive, _appliedParents;
589
- var init_fastlane = __esm({
590
- "src/runtime/fastlane.ts"() {
591
- "use strict";
592
- init_scheduler();
593
- init_logger();
594
- init_dom();
595
- _bulkCommitActive = false;
596
- _appliedParents = null;
597
- if (typeof globalThis !== "undefined") {
598
- const _g = globalThis;
599
- _g.__ASKR_FASTLANE = {
600
- isBulkCommitActive: isBulkCommitActive2,
601
- enterBulkCommit,
602
- exitBulkCommit,
603
- tryRuntimeFastLaneSync,
604
- markFastPathApplied,
605
- isFastPathApplied
606
- };
607
- }
608
- }
609
- });
610
-
611
- // src/jsx/jsx-runtime.ts
612
- function jsx(type, props, key) {
613
- return {
614
- type,
615
- props: props || {},
616
- key
617
- };
618
- }
619
- function jsxs(type, props, key) {
620
- return jsx(type, props, key);
621
- }
622
- var Fragment;
623
- var init_jsx_runtime = __esm({
624
- "src/jsx/jsx-runtime.ts"() {
625
- "use strict";
626
- Fragment = /* @__PURE__ */ Symbol.for("@askrjs/askr.Fragment");
627
- }
628
- });
629
-
630
366
  // src/runtime/context.ts
631
367
  function withContext(frame, fn) {
632
368
  const oldFrame = currentContextFrame;
@@ -652,9 +388,10 @@ function defineContext(defaultValue) {
652
388
  key,
653
389
  defaultValue,
654
390
  Scope: (props) => {
391
+ const value = props.value;
655
392
  return {
656
393
  type: ContextScopeComponent,
657
- props: { key, value: props.value, children: props.children }
394
+ props: { key, value, children: props.children }
658
395
  };
659
396
  }
660
397
  };
@@ -690,29 +427,29 @@ function ContextScopeComponent(props) {
690
427
  parent: parentFrame,
691
428
  values: /* @__PURE__ */ new Map([[key, value]])
692
429
  };
430
+ function createFunctionChildInvoker(fn, frame, owner) {
431
+ return {
432
+ type: ContextFunctionChildInvoker,
433
+ props: { fn, __frame: frame, __owner: owner }
434
+ };
435
+ }
693
436
  if (Array.isArray(children)) {
694
437
  return children.map((child) => {
695
438
  if (typeof child === "function") {
696
- return {
697
- type: ContextFunctionChildInvoker,
698
- props: {
699
- fn: child,
700
- __frame: newFrame,
701
- __owner: getCurrentComponentInstance()
702
- }
703
- };
439
+ return createFunctionChildInvoker(
440
+ child,
441
+ newFrame,
442
+ getCurrentComponentInstance()
443
+ );
704
444
  }
705
445
  return markWithFrame(child, newFrame);
706
446
  });
707
447
  } else if (typeof children === "function") {
708
- return {
709
- type: ContextFunctionChildInvoker,
710
- props: {
711
- fn: children,
712
- __frame: newFrame,
713
- __owner: getCurrentComponentInstance()
714
- }
715
- };
448
+ return createFunctionChildInvoker(
449
+ children,
450
+ newFrame,
451
+ getCurrentComponentInstance()
452
+ );
716
453
  } else if (children) {
717
454
  return markWithFrame(children, newFrame);
718
455
  }
@@ -756,22 +493,126 @@ var init_context = __esm({
756
493
  }
757
494
  });
758
495
 
759
- // src/renderer/dom.ts
760
- function cleanupInstanceIfPresent(node) {
496
+ // src/renderer/diag/index.ts
497
+ function getDiagMap() {
498
+ try {
499
+ const root = globalThis;
500
+ if (!root.__ASKR_DIAG) root.__ASKR_DIAG = {};
501
+ return root.__ASKR_DIAG;
502
+ } catch (e) {
503
+ void e;
504
+ return {};
505
+ }
506
+ }
507
+ function __ASKR_set(key, value) {
508
+ try {
509
+ const g = getDiagMap();
510
+ g[key] = value;
511
+ try {
512
+ const root = globalThis;
513
+ try {
514
+ const ns = root.__ASKR__ || (root.__ASKR__ = {});
515
+ try {
516
+ ns[key] = value;
517
+ } catch (e) {
518
+ void e;
519
+ }
520
+ } catch (e) {
521
+ void e;
522
+ }
523
+ } catch (e) {
524
+ void e;
525
+ }
526
+ } catch (e) {
527
+ void e;
528
+ }
529
+ }
530
+ function __ASKR_incCounter(key) {
531
+ try {
532
+ const g = getDiagMap();
533
+ const prev = typeof g[key] === "number" ? g[key] : 0;
534
+ const next = prev + 1;
535
+ g[key] = next;
536
+ try {
537
+ const root = globalThis;
538
+ const ns = root.__ASKR__ || (root.__ASKR__ = {});
539
+ try {
540
+ const nsPrev = typeof ns[key] === "number" ? ns[key] : 0;
541
+ ns[key] = nsPrev + 1;
542
+ } catch (e) {
543
+ void e;
544
+ }
545
+ } catch (e) {
546
+ void e;
547
+ }
548
+ } catch (e) {
549
+ void e;
550
+ }
551
+ }
552
+ var init_diag = __esm({
553
+ "src/renderer/diag/index.ts"() {
554
+ "use strict";
555
+ }
556
+ });
557
+
558
+ // src/runtime/fastlane-shared.ts
559
+ function isBulkCommitActive2() {
560
+ return _bulkCommitActive;
561
+ }
562
+ function markFastPathApplied(parent) {
563
+ if (!_appliedParents) return;
564
+ try {
565
+ _appliedParents.add(parent);
566
+ } catch (e) {
567
+ void e;
568
+ }
569
+ }
570
+ var _bulkCommitActive, _appliedParents;
571
+ var init_fastlane_shared = __esm({
572
+ "src/runtime/fastlane-shared.ts"() {
573
+ "use strict";
574
+ _bulkCommitActive = false;
575
+ _appliedParents = null;
576
+ }
577
+ });
578
+
579
+ // src/renderer/types.ts
580
+ function _isDOMElement(node) {
581
+ return typeof node === "object" && node !== null && "type" in node;
582
+ }
583
+ var init_types = __esm({
584
+ "src/renderer/types.ts"() {
585
+ "use strict";
586
+ }
587
+ });
588
+
589
+ // src/renderer/cleanup.ts
590
+ function cleanupInstanceIfPresent(node, opts) {
761
591
  if (!node) return;
762
592
  if (!(node instanceof Element)) return;
593
+ const errors = [];
763
594
  try {
764
595
  const inst = node.__ASKR_INSTANCE;
765
596
  if (inst) {
766
- cleanupComponent(inst);
597
+ try {
598
+ cleanupComponent(inst);
599
+ } catch (err) {
600
+ if (opts?.strict) errors.push(err);
601
+ else if (process.env.NODE_ENV !== "production")
602
+ logger.warn("[Askr] cleanupComponent failed:", err);
603
+ }
767
604
  try {
768
605
  delete node.__ASKR_INSTANCE;
769
606
  } catch (e) {
770
- void e;
607
+ if (opts?.strict) errors.push(e);
608
+ else void e;
771
609
  }
772
610
  }
773
611
  } catch (err) {
774
- void err;
612
+ if (opts?.strict) errors.push(err);
613
+ else if (process.env.NODE_ENV !== "production") {
614
+ logger.warn("[Askr] cleanupInstanceIfPresent failed:", err);
615
+ }
775
616
  }
776
617
  try {
777
618
  const descendants = node.querySelectorAll("*");
@@ -779,32 +620,57 @@ function cleanupInstanceIfPresent(node) {
779
620
  try {
780
621
  const inst = d.__ASKR_INSTANCE;
781
622
  if (inst) {
782
- cleanupComponent(inst);
623
+ try {
624
+ cleanupComponent(inst);
625
+ } catch (err) {
626
+ if (opts?.strict) errors.push(err);
627
+ else if (process.env.NODE_ENV !== "production") {
628
+ logger.warn(
629
+ "[Askr] cleanupInstanceIfPresent descendant cleanup failed:",
630
+ err
631
+ );
632
+ }
633
+ }
783
634
  try {
784
635
  delete d.__ASKR_INSTANCE;
785
636
  } catch (e) {
786
- void e;
637
+ if (opts?.strict) errors.push(e);
638
+ else void e;
787
639
  }
788
640
  }
789
641
  } catch (err) {
790
- void err;
642
+ if (opts?.strict) errors.push(err);
643
+ else if (process.env.NODE_ENV !== "production") {
644
+ logger.warn(
645
+ "[Askr] cleanupInstanceIfPresent descendant cleanup failed:",
646
+ err
647
+ );
648
+ }
791
649
  }
792
650
  }
793
651
  } catch (err) {
794
- void err;
652
+ if (opts?.strict) errors.push(err);
653
+ else if (process.env.NODE_ENV !== "production") {
654
+ logger.warn(
655
+ "[Askr] cleanupInstanceIfPresent descendant query failed:",
656
+ err
657
+ );
658
+ }
659
+ }
660
+ if (errors.length > 0) {
661
+ throw new AggregateError(errors, "cleanupInstanceIfPresent failed");
795
662
  }
796
663
  }
797
- function _isDOMElement(node) {
798
- return typeof node === "object" && node !== null && "type" in node;
799
- }
800
- function getKeyMapForElement(el) {
801
- return keyedElements.get(el);
664
+ function cleanupInstancesUnder(node, opts) {
665
+ cleanupInstanceIfPresent(node, opts);
802
666
  }
803
667
  function removeElementListeners(element) {
804
668
  const map = elementListeners.get(element);
805
669
  if (map) {
806
670
  for (const [eventName, entry] of map) {
807
- element.removeEventListener(eventName, entry.handler);
671
+ if (entry.options !== void 0)
672
+ element.removeEventListener(eventName, entry.handler, entry.options);
673
+ else element.removeEventListener(eventName, entry.handler);
808
674
  }
809
675
  elementListeners.delete(element);
810
676
  }
@@ -817,252 +683,32 @@ function removeAllListeners(root) {
817
683
  removeElementListeners(children[i]);
818
684
  }
819
685
  }
820
- function evaluate(node, target, context) {
821
- if (!target) return;
822
- if (context && domRanges.has(context)) {
823
- const range = domRanges.get(context);
824
- let current2 = range.start.nextSibling;
825
- while (current2 && current2 !== range.end) {
826
- const next = current2.nextSibling;
827
- current2.remove();
828
- current2 = next;
829
- }
830
- const dom = createDOMNode(node);
831
- if (dom) {
832
- target.insertBefore(dom, range.end);
833
- }
834
- } else if (context) {
835
- const start = document.createComment("component-start");
836
- const end = document.createComment("component-end");
837
- target.appendChild(start);
838
- target.appendChild(end);
839
- domRanges.set(context, { start, end });
840
- const dom = createDOMNode(node);
841
- if (dom) {
842
- target.insertBefore(dom, end);
843
- }
844
- } else {
845
- const vnode = node;
846
- const firstChild = target.children[0];
847
- if (firstChild && _isDOMElement(vnode) && typeof vnode.type === "string" && firstChild.tagName.toLowerCase() === vnode.type.toLowerCase()) {
848
- const vnodeChildren = vnode.children || vnode.props?.children;
849
- let isSimpleTextVNode = false;
850
- let textContent;
851
- if (!Array.isArray(vnodeChildren)) {
852
- if (typeof vnodeChildren === "string" || typeof vnodeChildren === "number") {
853
- isSimpleTextVNode = true;
854
- textContent = String(vnodeChildren);
855
- }
856
- } else if (vnodeChildren.length === 1) {
857
- const child = vnodeChildren[0];
858
- if (typeof child === "string" || typeof child === "number") {
859
- isSimpleTextVNode = true;
860
- textContent = String(child);
861
- }
862
- }
863
- if (isSimpleTextVNode && firstChild.childNodes.length === 1 && firstChild.firstChild?.nodeType === 3) {
864
- firstChild.firstChild.data = textContent;
865
- } else {
866
- if (vnodeChildren) {
867
- if (Array.isArray(vnodeChildren)) {
868
- const hasKeys = vnodeChildren.some(
869
- (child) => typeof child === "object" && child !== null && "key" in child
870
- );
871
- if (hasKeys) {
872
- let oldKeyMap = keyedElements.get(firstChild);
873
- if (!oldKeyMap) {
874
- oldKeyMap = /* @__PURE__ */ new Map();
875
- }
876
- try {
877
- if (process.env.ASKR_FORCE_BULK_POSREUSE === "1") {
878
- try {
879
- const keyedVnodes = [];
880
- for (let i = 0; i < vnodeChildren.length; i++) {
881
- const c = vnodeChildren[i];
882
- if (_isDOMElement(c) && c.key !== void 0) {
883
- keyedVnodes.push({
884
- key: c.key,
885
- vnode: c
886
- });
887
- }
888
- }
889
- if (keyedVnodes.length > 0 && keyedVnodes.length === vnodeChildren.length) {
890
- logger.warn(
891
- "[Askr][FASTPATH] forced positional bulk keyed reuse (evaluate-level)"
892
- );
893
- const stats = performBulkPositionalKeyedTextUpdate(
894
- firstChild,
895
- keyedVnodes
896
- );
897
- if (process.env.NODE_ENV !== "production" || process.env.ASKR_FASTPATH_DEBUG === "1") {
898
- try {
899
- const gl = globalThis;
900
- gl.__ASKR_LAST_FASTPATH_STATS = stats;
901
- gl.__ASKR_LAST_FASTPATH_COMMIT_COUNT = 1;
902
- const counters = gl.__ASKR_FASTPATH_COUNTERS || {};
903
- counters.bulkKeyedPositionalForced = (counters.bulkKeyedPositionalForced || 0) + 1;
904
- gl.__ASKR_FASTPATH_COUNTERS = counters;
905
- } catch (e) {
906
- void e;
907
- }
908
- }
909
- try {
910
- const map = /* @__PURE__ */ new Map();
911
- const children = Array.from(firstChild.children);
912
- for (let i = 0; i < children.length; i++) {
913
- const el = children[i];
914
- const k = el.getAttribute("data-key");
915
- if (k !== null) {
916
- map.set(k, el);
917
- const n = Number(k);
918
- if (!Number.isNaN(n)) map.set(n, el);
919
- }
920
- }
921
- keyedElements.set(firstChild, map);
922
- } catch (e) {
923
- void e;
924
- }
925
- } else {
926
- const newKeyMap = reconcileKeyedChildren(
927
- firstChild,
928
- vnodeChildren,
929
- oldKeyMap
930
- );
931
- keyedElements.set(firstChild, newKeyMap);
932
- }
933
- } catch (err) {
934
- logger.warn(
935
- "[Askr][FASTPATH] forced bulk path failed, falling back",
936
- err
937
- );
938
- const newKeyMap = reconcileKeyedChildren(
939
- firstChild,
940
- vnodeChildren,
941
- oldKeyMap
942
- );
943
- keyedElements.set(firstChild, newKeyMap);
944
- }
945
- } else {
946
- const newKeyMap = reconcileKeyedChildren(
947
- firstChild,
948
- vnodeChildren,
949
- oldKeyMap
950
- );
951
- keyedElements.set(firstChild, newKeyMap);
952
- }
953
- } catch (e) {
954
- void e;
955
- const newKeyMap = reconcileKeyedChildren(
956
- firstChild,
957
- vnodeChildren,
958
- oldKeyMap
959
- );
960
- keyedElements.set(firstChild, newKeyMap);
961
- }
962
- } else {
963
- if (isBulkTextFastPathEligible(firstChild, vnodeChildren)) {
964
- const stats = performBulkTextReplace(firstChild, vnodeChildren);
965
- if (process.env.NODE_ENV !== "production") {
966
- try {
967
- const gl = globalThis;
968
- gl.__ASKR_LAST_BULK_TEXT_FASTPATH_STATS = stats;
969
- const counters = gl.__ASKR_FASTPATH_COUNTERS || {};
970
- counters.bulkTextHits = (counters.bulkTextHits || 0) + 1;
971
- gl.__ASKR_FASTPATH_COUNTERS = counters;
972
- } catch (e) {
973
- void e;
974
- }
975
- }
976
- } else {
977
- if (process.env.NODE_ENV !== "production") {
978
- try {
979
- const gl = globalThis;
980
- const counters = gl.__ASKR_FASTPATH_COUNTERS || {};
981
- counters.bulkTextMisses = (counters.bulkTextMisses || 0) + 1;
982
- gl.__ASKR_FASTPATH_COUNTERS = counters;
983
- } catch (e) {
984
- void e;
985
- }
986
- }
987
- updateUnkeyedChildren(firstChild, vnodeChildren);
988
- keyedElements.delete(firstChild);
989
- }
990
- }
991
- } else {
992
- firstChild.textContent = "";
993
- const dom = createDOMNode(vnodeChildren);
994
- if (dom) firstChild.appendChild(dom);
995
- keyedElements.delete(firstChild);
996
- }
997
- } else {
998
- firstChild.textContent = "";
999
- keyedElements.delete(firstChild);
1000
- }
1001
- }
1002
- updateElementFromVnode(firstChild, vnode, false);
1003
- } else {
1004
- target.textContent = "";
1005
- if (_isDOMElement(vnode) && typeof vnode.type === "string") {
1006
- const children = vnode.children;
1007
- if (Array.isArray(children) && children.some(
1008
- (child) => typeof child === "object" && child !== null && "key" in child
1009
- )) {
1010
- const el = document.createElement(vnode.type);
1011
- target.appendChild(el);
1012
- const props = vnode.props || {};
1013
- for (const [key, value] of Object.entries(props)) {
1014
- if (key === "children" || key === "key") continue;
1015
- if (value === void 0 || value === null || value === false)
1016
- continue;
1017
- if (key.startsWith("on") && key.length > 2) {
1018
- const eventName = key.slice(2).charAt(0).toLowerCase() + key.slice(3).toLowerCase();
1019
- const wrappedHandler = (event) => {
1020
- globalScheduler.setInHandler(true);
1021
- try {
1022
- value(event);
1023
- } catch (error) {
1024
- logger.error("[Askr] Event handler error:", error);
1025
- } finally {
1026
- globalScheduler.setInHandler(false);
1027
- }
1028
- };
1029
- el.addEventListener(eventName, wrappedHandler);
1030
- if (!elementListeners.has(el)) {
1031
- elementListeners.set(el, /* @__PURE__ */ new Map());
1032
- }
1033
- elementListeners.get(el).set(eventName, {
1034
- handler: wrappedHandler,
1035
- original: value
1036
- });
1037
- continue;
1038
- }
1039
- if (key === "class" || key === "className") {
1040
- el.className = String(value);
1041
- } else if (key === "value" || key === "checked") {
1042
- el[key] = value;
1043
- } else {
1044
- el.setAttribute(key, String(value));
1045
- }
1046
- }
1047
- const newKeyMap = reconcileKeyedChildren(el, children, void 0);
1048
- keyedElements.set(el, newKeyMap);
1049
- return;
1050
- return;
1051
- }
1052
- }
1053
- const dom = createDOMNode(vnode);
1054
- if (dom) {
1055
- target.appendChild(dom);
1056
- }
1057
- }
1058
- }
1059
- }
1060
- function isKeyedReorderFastPathEligible(parent, newChildren, oldKeyMap) {
1061
- const keyedVnodes = [];
1062
- for (let i = 0; i < newChildren.length; i++) {
1063
- const child = newChildren[i];
1064
- if (_isDOMElement(child) && child.key !== void 0) {
1065
- keyedVnodes.push({ key: child.key, vnode: child });
686
+ var elementListeners;
687
+ var init_cleanup = __esm({
688
+ "src/renderer/cleanup.ts"() {
689
+ "use strict";
690
+ init_component();
691
+ init_logger();
692
+ elementListeners = /* @__PURE__ */ new WeakMap();
693
+ }
694
+ });
695
+
696
+ // src/renderer/keyed.ts
697
+ function getKeyMapForElement(el) {
698
+ return keyedElements.get(el);
699
+ }
700
+ function isKeyedReorderFastPathEligible(parent, newChildren, oldKeyMap) {
701
+ const keyedVnodes = [];
702
+ for (let i = 0; i < newChildren.length; i++) {
703
+ const child = newChildren[i];
704
+ if (typeof child === "object" && child !== null && "type" in child) {
705
+ const childObj = child;
706
+ if (childObj.key !== void 0) {
707
+ keyedVnodes.push({
708
+ key: childObj.key,
709
+ vnode: child
710
+ });
711
+ }
1066
712
  }
1067
713
  }
1068
714
  const totalKeyed = keyedVnodes.length;
@@ -1113,8 +759,9 @@ function isKeyedReorderFastPathEligible(parent, newChildren, oldKeyMap) {
1113
759
  let hasPropsPresent = false;
1114
760
  for (let i = 0; i < keyedVnodes.length; i++) {
1115
761
  const vnode = keyedVnodes[i].vnode;
1116
- if (!_isDOMElement(vnode)) continue;
1117
- const props = vnode.props || {};
762
+ if (typeof vnode !== "object" || vnode === null) continue;
763
+ const vnodeObj = vnode;
764
+ const props = vnodeObj.props || {};
1118
765
  for (const k of Object.keys(props)) {
1119
766
  if (k === "children" || k === "key") continue;
1120
767
  if (k.startsWith("on") && k.length > 2) continue;
@@ -1128,8 +775,9 @@ function isKeyedReorderFastPathEligible(parent, newChildren, oldKeyMap) {
1128
775
  for (let i = 0; i < keyedVnodes.length; i++) {
1129
776
  const { key, vnode } = keyedVnodes[i];
1130
777
  const el = oldKeyMap?.get(key);
1131
- if (!el || !_isDOMElement(vnode)) continue;
1132
- const props = vnode.props || {};
778
+ if (!el || typeof vnode !== "object" || vnode === null) continue;
779
+ const vnodeObj = vnode;
780
+ const props = vnodeObj.props || {};
1133
781
  for (const k of Object.keys(props)) {
1134
782
  if (k === "children" || k === "key") continue;
1135
783
  if (k.startsWith("on") && k.length > 2) continue;
@@ -1158,8 +806,9 @@ function isKeyedReorderFastPathEligible(parent, newChildren, oldKeyMap) {
1158
806
  break;
1159
807
  }
1160
808
  }
1161
- } catch {
809
+ } catch (e) {
1162
810
  hasPropChanges = true;
811
+ void e;
1163
812
  break;
1164
813
  }
1165
814
  }
@@ -1174,1024 +823,421 @@ function isKeyedReorderFastPathEligible(parent, newChildren, oldKeyMap) {
1174
823
  hasPropChanges
1175
824
  };
1176
825
  }
1177
- function reconcileKeyedChildren(parent, newChildren, oldKeyMap) {
1178
- const newKeyMap = /* @__PURE__ */ new Map();
1179
- const keyedVnodes = [];
1180
- const unkeyedVnodes = [];
1181
- for (let i = 0; i < newChildren.length; i++) {
1182
- const child = newChildren[i];
1183
- if (_isDOMElement(child) && child.key !== void 0) {
1184
- keyedVnodes.push({ key: child.key, vnode: child });
1185
- } else {
1186
- unkeyedVnodes.push(child);
1187
- }
826
+ var keyedElements, _reconcilerRecordedParents;
827
+ var init_keyed = __esm({
828
+ "src/renderer/keyed.ts"() {
829
+ "use strict";
830
+ keyedElements = /* @__PURE__ */ new WeakMap();
831
+ _reconcilerRecordedParents = /* @__PURE__ */ new WeakSet();
1188
832
  }
1189
- const totalKeyed = keyedVnodes.length;
1190
- const newKeyOrder = keyedVnodes.map((kv) => kv.key);
1191
- const oldKeyOrder = oldKeyMap ? Array.from(oldKeyMap.keys()) : [];
1192
- let moveCount = 0;
1193
- for (let i = 0; i < newKeyOrder.length; i++) {
1194
- const k2 = newKeyOrder[i];
1195
- if (i >= oldKeyOrder.length || oldKeyOrder[i] !== k2 || !oldKeyMap?.has(k2)) {
1196
- moveCount++;
1197
- }
833
+ });
834
+
835
+ // src/jsx/types.ts
836
+ var ELEMENT_TYPE, Fragment;
837
+ var init_types2 = __esm({
838
+ "src/jsx/types.ts"() {
839
+ "use strict";
840
+ ELEMENT_TYPE = /* @__PURE__ */ Symbol.for("askr.element");
841
+ Fragment = /* @__PURE__ */ Symbol.for("askr.fragment");
1198
842
  }
1199
- let hasPropChanges = false;
1200
- for (let i = 0; i < keyedVnodes.length; i++) {
1201
- const { key, vnode } = keyedVnodes[i];
1202
- const el = oldKeyMap?.get(key);
1203
- if (!el || !_isDOMElement(vnode)) continue;
1204
- const props = vnode.props || {};
1205
- for (const k2 of Object.keys(props)) {
1206
- if (k2 === "children" || k2 === "key") continue;
1207
- if (k2.startsWith("on") && k2.length > 2) {
1208
- continue;
1209
- }
1210
- const v = props[k2];
1211
- try {
1212
- if (k2 === "class" || k2 === "className") {
1213
- if (el.className !== String(v)) {
1214
- logger.warn("[Askr][FASTPATH][DEV] prop mismatch", {
1215
- key,
1216
- prop: k2,
1217
- expected: String(v),
1218
- actual: el.className
1219
- });
1220
- hasPropChanges = true;
1221
- break;
1222
- }
1223
- } else if (k2 === "value" || k2 === "checked") {
1224
- if (el[k2] !== v) {
1225
- logger.warn("[Askr][FASTPATH][DEV] prop mismatch", {
1226
- key,
1227
- prop: k2,
1228
- expected: v,
1229
- actual: el[k2]
1230
- });
1231
- hasPropChanges = true;
1232
- break;
1233
- }
1234
- } else {
1235
- const attr = el.getAttribute(k2);
1236
- if (v === void 0 || v === null || v === false) {
1237
- if (attr !== null) {
1238
- logger.warn(
1239
- "[Askr][FASTPATH][DEV] prop mismatch (missing attr)",
1240
- {
1241
- key,
1242
- prop: k2,
1243
- expected: v,
1244
- actual: attr
1245
- }
1246
- );
1247
- hasPropChanges = true;
1248
- break;
1249
- }
1250
- } else if (String(v) !== attr) {
1251
- logger.warn("[Askr][FASTPATH][DEV] prop mismatch (attr diff)", {
1252
- key,
1253
- prop: k2,
1254
- expected: String(v),
1255
- actual: attr
1256
- });
1257
- hasPropChanges = true;
1258
- break;
1259
- }
1260
- }
1261
- } catch {
1262
- hasPropChanges = true;
1263
- break;
1264
- }
1265
- }
1266
- if (hasPropChanges) break;
843
+ });
844
+
845
+ // src/jsx/jsx-runtime.ts
846
+ function jsxDEV(type, props, key) {
847
+ return {
848
+ $$typeof: ELEMENT_TYPE2,
849
+ type,
850
+ props: props ?? {},
851
+ key: key ?? null
852
+ };
853
+ }
854
+ function jsx(type, props, key) {
855
+ return jsxDEV(type, props, key);
856
+ }
857
+ function jsxs(type, props, key) {
858
+ return jsxDEV(type, props, key);
859
+ }
860
+ var ELEMENT_TYPE2, Fragment2;
861
+ var init_jsx_runtime = __esm({
862
+ "src/jsx/jsx-runtime.ts"() {
863
+ "use strict";
864
+ init_types2();
865
+ ELEMENT_TYPE2 = /* @__PURE__ */ Symbol.for("askr.element");
866
+ Fragment2 = /* @__PURE__ */ Symbol.for("askr.fragment");
1267
867
  }
1268
- const decision = isKeyedReorderFastPathEligible(
1269
- parent,
1270
- newChildren,
1271
- oldKeyMap
1272
- );
1273
- const useFastPath = decision.useFastPath;
1274
- logger.warn("[Askr][FASTPATH][DEV] decision", decision);
1275
- try {
1276
- const hugeThreshold = Number(process.env.ASKR_BULK_HUGE_THRESHOLD) || 2048;
1277
- if (!useFastPath && keyedVnodes.length >= hugeThreshold) {
1278
- let allSimple = true;
1279
- for (let i = 0; i < keyedVnodes.length; i++) {
1280
- const vnode = keyedVnodes[i].vnode;
1281
- if (!_isDOMElement(vnode)) {
1282
- allSimple = false;
1283
- break;
1284
- }
1285
- const dv = vnode;
1286
- if (typeof dv.type !== "string") {
1287
- allSimple = false;
1288
- break;
1289
- }
1290
- const ch = dv.children || dv.props?.children;
1291
- if (ch === void 0) continue;
1292
- if (Array.isArray(ch)) {
1293
- if (ch.length !== 1 || typeof ch[0] !== "string" && typeof ch[0] !== "number") {
1294
- allSimple = false;
1295
- break;
1296
- }
1297
- } else if (typeof ch !== "string" && typeof ch !== "number") {
1298
- allSimple = false;
1299
- break;
1300
- }
1301
- }
1302
- if (allSimple) {
1303
- logger.warn("[Askr][FASTPATH] applying huge-list positional fallback");
1304
- try {
1305
- if (isBulkCommitActive2()) markFastPathApplied(parent);
1306
- } catch (e) {
1307
- void e;
1308
- }
1309
- const stats = performBulkPositionalKeyedTextUpdate(parent, keyedVnodes);
1310
- if (process.env.NODE_ENV !== "production" || process.env.ASKR_FASTPATH_DEBUG === "1") {
1311
- try {
1312
- const gl = globalThis;
1313
- gl.__ASKR_LAST_FASTPATH_STATS = stats;
1314
- const counters = gl.__ASKR_FASTPATH_COUNTERS || {};
1315
- counters.bulkKeyedHugeFallback = (counters.bulkKeyedHugeFallback || 0) + 1;
1316
- gl.__ASKR_FASTPATH_COUNTERS = counters;
1317
- gl.__ASKR_BULK_DIAG = {
1318
- phase: "bulk-keyed-huge-fallback",
1319
- stats
1320
- };
1321
- } catch (e) {
1322
- void e;
1323
- }
1324
- }
1325
- return newKeyMap;
868
+ });
869
+
870
+ // src/renderer/dom.ts
871
+ function createDOMNode(node) {
872
+ if (!IS_DOM_AVAILABLE) {
873
+ if (process.env.NODE_ENV !== "production") {
874
+ try {
875
+ logger.warn("[Askr] createDOMNode called in non-DOM environment");
876
+ } catch (e) {
877
+ void e;
1326
878
  }
1327
879
  }
1328
- } catch (e) {
1329
- void e;
880
+ return null;
1330
881
  }
1331
- if (process.env.NODE_ENV !== "production" || process.env.ASKR_FASTPATH_DEBUG === "1") {
1332
- try {
1333
- const gl = globalThis;
1334
- gl.__ASKR_BULK_DIAG = {
1335
- phase: "keyed-decision",
1336
- decision,
1337
- totalKeyed,
1338
- oldKeyMapSize: oldKeyMap?.size ?? 0
1339
- };
1340
- } catch (e) {
1341
- void e;
1342
- }
882
+ if (typeof node === "string") {
883
+ return document.createTextNode(node);
1343
884
  }
1344
- let allSimpleText = false;
1345
- try {
1346
- if (keyedVnodes.length >= (Number(process.env.ASKR_BULK_TEXT_THRESHOLD) || 1024)) {
1347
- let missing = 0;
1348
- try {
1349
- const present = /* @__PURE__ */ new Set();
1350
- const parentChildren2 = Array.from(parent.children);
1351
- for (let i = 0; i < parentChildren2.length; i++) {
1352
- const attr = parentChildren2[i].getAttribute("data-key");
1353
- if (attr !== null) {
1354
- present.add(attr);
1355
- const n = Number(attr);
1356
- if (!Number.isNaN(n)) present.add(n);
1357
- }
1358
- }
1359
- for (let i = 0; i < keyedVnodes.length; i++) {
1360
- const k2 = keyedVnodes[i].key;
1361
- if (!present.has(k2)) missing++;
1362
- }
1363
- } catch {
1364
- missing = 0;
1365
- }
1366
- allSimpleText = keyedVnodes.length > 0 && keyedVnodes.every(({ vnode }) => {
1367
- if (!_isDOMElement(vnode)) return false;
1368
- const dv = vnode;
1369
- if (typeof dv.type !== "string") return false;
1370
- const ch = dv.children || dv.props?.children;
1371
- if (ch === void 0) return true;
1372
- if (Array.isArray(ch)) {
1373
- return ch.length === 1 && (typeof ch[0] === "string" || typeof ch[0] === "number");
1374
- }
1375
- return typeof ch === "string" || typeof ch === "number";
1376
- });
1377
- let hasPropsPresent = false;
1378
- for (let i = 0; i < keyedVnodes.length; i++) {
1379
- const vnode = keyedVnodes[i].vnode;
1380
- if (!_isDOMElement(vnode)) continue;
1381
- const props = vnode.props || {};
1382
- for (const k2 of Object.keys(props)) {
1383
- if (k2 === "children" || k2 === "key") continue;
1384
- if (k2.startsWith("on") && k2.length > 2) continue;
1385
- if (k2.startsWith("data-")) continue;
1386
- hasPropsPresent = true;
1387
- break;
1388
- }
1389
- if (hasPropsPresent) break;
1390
- }
1391
- const missingRatio = missing / Math.max(1, keyedVnodes.length);
1392
- if (missingRatio > 0.5 && allSimpleText && !hasPropsPresent) {
1393
- logger.warn(
1394
- "[Askr][FASTPATH] switching to positional bulk keyed fast-path due to missing keys ratio",
1395
- missingRatio
1396
- );
1397
- try {
1398
- if (isBulkCommitActive2()) markFastPathApplied(parent);
1399
- } catch (e) {
1400
- void e;
1401
- }
1402
- const stats = performBulkPositionalKeyedTextUpdate(parent, keyedVnodes);
1403
- if (process.env.NODE_ENV !== "production") {
1404
- try {
1405
- const gl = globalThis;
1406
- gl.__ASKR_LAST_FASTPATH_STATS = stats;
1407
- try {
1408
- _reconcilerRecordedParents.add(parent);
1409
- } catch (e) {
1410
- void e;
1411
- }
1412
- gl.__ASKR_LAST_FASTPATH_COMMIT_COUNT = 1;
1413
- const counters = gl.__ASKR_FASTPATH_COUNTERS || {};
1414
- counters.bulkKeyedPositionalHits = (counters.bulkKeyedPositionalHits || 0) + 1;
1415
- gl.__ASKR_FASTPATH_COUNTERS = counters;
1416
- } catch (e) {
1417
- void e;
1418
- }
1419
- }
1420
- return newKeyMap;
1421
- }
1422
- }
1423
- } catch (e) {
1424
- void e;
885
+ if (typeof node === "number") {
886
+ return document.createTextNode(String(node));
1425
887
  }
1426
- if (!useFastPath && typeof globalThis !== "undefined") {
1427
- try {
1428
- let parentFastpathApplied = false;
1429
- try {
1430
- parentFastpathApplied = isFastPathApplied(parent);
1431
- } catch {
1432
- parentFastpathApplied = false;
1433
- }
1434
- if (!parentFastpathApplied) {
1435
- const gl = globalThis;
1436
- try {
1437
- if (_reconcilerRecordedParents.has(parent)) {
1438
- _reconcilerRecordedParents.delete(parent);
1439
- } else {
1440
- delete gl.__ASKR_LAST_FASTPATH_STATS;
1441
- delete gl.__ASKR_LAST_FASTPATH_REUSED;
1442
- }
1443
- } catch {
1444
- delete gl.__ASKR_LAST_FASTPATH_STATS;
1445
- delete gl.__ASKR_LAST_FASTPATH_REUSED;
1446
- }
1447
- }
1448
- } catch (e) {
1449
- void e;
888
+ if (!node) {
889
+ return null;
890
+ }
891
+ if (Array.isArray(node)) {
892
+ const fragment = document.createDocumentFragment();
893
+ for (let i = 0; i < node.length; i++) {
894
+ const dom = createDOMNode(node[i]);
895
+ if (dom) fragment.appendChild(dom);
1450
896
  }
897
+ return fragment;
1451
898
  }
1452
- let _triedPositionalReuse = false;
1453
- try {
1454
- const bulkTextThreshold = Number(process.env.ASKR_BULK_TEXT_THRESHOLD) || 1024;
1455
- if (!useFastPath && keyedVnodes.length >= bulkTextThreshold && allSimpleText && !hasPropChanges) {
1456
- try {
1457
- if (isFastPathApplied(parent)) {
1458
- logger.warn(
1459
- "[Askr][FASTPATH] fast-path already applied on parent; skipping"
1460
- );
1461
- return newKeyMap;
1462
- }
1463
- } catch (e) {
1464
- void e;
1465
- }
1466
- let stable = true;
1467
- try {
1468
- const parentChildren2 = Array.from(parent.children);
1469
- if (parentChildren2.length === keyedVnodes.length) {
1470
- const allSimple = keyedVnodes.every(({ vnode }) => {
1471
- if (!_isDOMElement(vnode)) return false;
1472
- const dv = vnode;
1473
- if (typeof dv.type !== "string") return false;
1474
- const ch = dv.children || dv.props?.children;
1475
- if (ch === void 0) return true;
1476
- if (Array.isArray(ch)) {
1477
- return ch.length === 1 && (typeof ch[0] === "string" || typeof ch[0] === "number");
1478
- }
1479
- return typeof ch === "string" || typeof ch === "number";
1480
- });
1481
- if (allSimple || process.env.ASKR_FORCE_BULK_POSREUSE === "1") {
1482
- logger.warn(
1483
- "[Askr][FASTPATH] len-match heuristic triggered (positional bulk)"
1484
- );
1485
- if (process.env.NODE_ENV !== "production" || process.env.ASKR_FASTPATH_DEBUG === "1") {
1486
- try {
1487
- const gl = globalThis;
1488
- gl.__ASKR_BULK_DIAG = {
1489
- phase: "bulk-keyed-positional-trigger-lenmatch-early",
1490
- totalKeyed: keyedVnodes.length,
1491
- allSimple,
1492
- forced: process.env.ASKR_FORCE_BULK_POSREUSE === "1"
1493
- };
1494
- } catch (e) {
1495
- void e;
1496
- }
1497
- }
899
+ if (typeof node === "object" && node !== null && "type" in node) {
900
+ const type = node.type;
901
+ const props = node.props || {};
902
+ if (typeof type === "string") {
903
+ const el = document.createElement(type);
904
+ for (const key in props) {
905
+ const value = props[key];
906
+ if (key === "children" || key === "key") continue;
907
+ if (value === void 0 || value === null || value === false) continue;
908
+ if (key.startsWith("on") && key.length > 2) {
909
+ const eventName = key.slice(2).charAt(0).toLowerCase() + key.slice(3).toLowerCase();
910
+ const wrappedHandler = (event) => {
911
+ globalScheduler.setInHandler(true);
1498
912
  try {
1499
- if (isBulkCommitActive2()) markFastPathApplied(parent);
1500
- } catch (e) {
1501
- void e;
1502
- }
1503
- const stats = performBulkPositionalKeyedTextUpdate(
1504
- parent,
1505
- keyedVnodes
1506
- );
1507
- if (process.env.NODE_ENV !== "production" || process.env.ASKR_FASTPATH_DEBUG === "1") {
1508
- try {
1509
- const gl = globalThis;
1510
- gl.__ASKR_LAST_FASTPATH_STATS = stats;
1511
- gl.__ASKR_BULK_DIAG = {
1512
- phase: "bulk-keyed-positional-applied",
1513
- stats
1514
- };
1515
- const counters = gl.__ASKR_FASTPATH_COUNTERS || {};
1516
- counters.bulkKeyedPositionalHits = (counters.bulkKeyedPositionalHits || 0) + 1;
1517
- if (process.env.ASKR_FORCE_BULK_POSREUSE === "1")
1518
- counters.bulkKeyedPositionalForced = (counters.bulkKeyedPositionalForced || 0) + 1;
1519
- gl.__ASKR_FASTPATH_COUNTERS = counters;
1520
- } catch (e) {
1521
- void e;
913
+ value(event);
914
+ } catch (error) {
915
+ logger.error("[Askr] Event handler error:", error);
916
+ } finally {
917
+ globalScheduler.setInHandler(false);
918
+ const state2 = globalScheduler.getState();
919
+ if ((state2.queueLength ?? 0) > 0 && !state2.running) {
920
+ queueMicrotask(() => {
921
+ try {
922
+ if (!globalScheduler.isExecuting()) globalScheduler.flush();
923
+ } catch (err) {
924
+ queueMicrotask(() => {
925
+ throw err;
926
+ });
927
+ }
928
+ });
1522
929
  }
1523
930
  }
1524
- return newKeyMap;
931
+ };
932
+ const options = eventName === "wheel" || eventName === "scroll" || eventName.startsWith("touch") ? { passive: true } : void 0;
933
+ if (options !== void 0)
934
+ el.addEventListener(eventName, wrappedHandler, options);
935
+ else el.addEventListener(eventName, wrappedHandler);
936
+ if (!elementListeners.has(el)) {
937
+ elementListeners.set(el, /* @__PURE__ */ new Map());
1525
938
  }
1526
- }
1527
- if (parentChildren2.length !== keyedVnodes.length) stable = false;
1528
- else {
1529
- let keyMismatches = 0;
1530
- for (let i = 0; i < keyedVnodes.length; i++) {
1531
- const k2 = keyedVnodes[i].key;
1532
- const ch = parentChildren2[i];
1533
- if (!ch) {
1534
- stable = false;
1535
- break;
1536
- }
1537
- const attr = ch.getAttribute("data-key");
1538
- if (attr === null) {
1539
- stable = false;
1540
- break;
939
+ elementListeners.get(el).set(eventName, {
940
+ handler: wrappedHandler,
941
+ original: value,
942
+ options
943
+ });
944
+ } else if (key === "class" || key === "className") {
945
+ el.className = String(value);
946
+ } else if (key === "value" || key === "checked") {
947
+ const tag = type.toLowerCase();
948
+ if (key === "value") {
949
+ if (tag === "input" || tag === "textarea" || tag === "select") {
950
+ el.value = String(value);
951
+ el.setAttribute("value", String(value));
952
+ } else {
953
+ el.setAttribute("value", String(value));
1541
954
  }
1542
- if (String(k2) !== attr && String(Number(attr)) !== String(k2)) {
1543
- keyMismatches++;
955
+ } else {
956
+ if (tag === "input") {
957
+ el.checked = Boolean(value);
958
+ el.setAttribute("checked", String(Boolean(value)));
959
+ } else {
960
+ el.setAttribute("checked", String(Boolean(value)));
1544
961
  }
1545
962
  }
1546
- if (stable) {
1547
- if (keyMismatches === 0) {
1548
- logger.warn(
1549
- "[Askr][FASTPATH] applying bulk keyed text fast-path (stable keys)"
1550
- );
1551
- if (process.env.NODE_ENV !== "production" || process.env.ASKR_FASTPATH_DEBUG === "1") {
1552
- try {
1553
- const gl = globalThis;
1554
- gl.__ASKR_BULK_DIAG = {
1555
- phase: "bulk-keyed-stable-trigger",
1556
- totalKeyed: keyedVnodes.length,
1557
- hasPropChanges
1558
- };
1559
- } catch (e) {
1560
- void e;
1561
- }
1562
- }
1563
- try {
1564
- if (isBulkCommitActive2()) markFastPathApplied(parent);
1565
- } catch (e) {
1566
- void e;
1567
- }
1568
- const stats = performBulkKeyedTextReplace(
1569
- parent,
1570
- keyedVnodes,
1571
- oldKeyMap
1572
- );
1573
- if (process.env.NODE_ENV !== "production" || process.env.ASKR_FASTPATH_DEBUG === "1") {
1574
- try {
1575
- const gl = globalThis;
1576
- gl.__ASKR_LAST_FASTPATH_STATS = stats;
1577
- gl.__ASKR_BULK_DIAG = {
1578
- phase: "bulk-keyed-applied",
1579
- stats
1580
- };
1581
- const counters = gl.__ASKR_FASTPATH_COUNTERS || {};
1582
- counters.bulkKeyedTextHits = (counters.bulkKeyedTextHits || 0) + 1;
1583
- gl.__ASKR_FASTPATH_COUNTERS = counters;
1584
- } catch (e) {
1585
- void e;
963
+ } else {
964
+ el.setAttribute(key, String(value));
965
+ }
966
+ }
967
+ const vnodeKey = node.key ?? props?.key;
968
+ if (vnodeKey !== void 0) {
969
+ el.setAttribute("data-key", String(vnodeKey));
970
+ }
971
+ const children = props.children || node.children;
972
+ if (children) {
973
+ if (Array.isArray(children)) {
974
+ if (process.env.NODE_ENV !== "production") {
975
+ let hasElements = false;
976
+ let hasKeys = false;
977
+ for (let i = 0; i < children.length; i++) {
978
+ const item = children[i];
979
+ if (typeof item === "object" && item !== null && "type" in item) {
980
+ hasElements = true;
981
+ const itemProps = item.props || {};
982
+ if ("key" in itemProps) {
983
+ hasKeys = true;
984
+ break;
1586
985
  }
1587
986
  }
1588
- return newKeyMap;
1589
987
  }
1590
- if (parentChildren2.length === keyedVnodes.length) {
1591
- logger.warn(
1592
- "[Askr][FASTPATH] applying bulk keyed positional text fast-path (len-match)"
1593
- );
1594
- if (process.env.NODE_ENV !== "production" || process.env.ASKR_FASTPATH_DEBUG === "1") {
988
+ if (hasElements && !hasKeys) {
989
+ if (typeof console !== "undefined") {
1595
990
  try {
1596
- const gl = globalThis;
1597
- gl.__ASKR_BULK_DIAG = {
1598
- phase: "bulk-keyed-positional-trigger-lenmatch",
1599
- totalKeyed: keyedVnodes.length,
1600
- keyMismatches
1601
- };
991
+ const inst = getCurrentInstance();
992
+ const name = inst?.fn?.name || "<anonymous>";
993
+ logger.warn(
994
+ `Missing keys on dynamic lists in ${name}. Each child in a list should have a unique "key" prop.`
995
+ );
1602
996
  } catch (e) {
997
+ logger.warn(
998
+ 'Missing keys on dynamic lists. Each child in a list should have a unique "key" prop.'
999
+ );
1603
1000
  void e;
1604
1001
  }
1605
1002
  }
1606
- try {
1607
- if (isBulkCommitActive2()) markFastPathApplied(parent);
1608
- } catch (e) {
1609
- void e;
1610
- }
1611
- const stats = performBulkPositionalKeyedTextUpdate(
1612
- parent,
1613
- keyedVnodes
1614
- );
1615
- if (process.env.NODE_ENV !== "production" || process.env.ASKR_FASTPATH_DEBUG === "1") {
1616
- try {
1617
- const gl = globalThis;
1618
- gl.__ASKR_LAST_FASTPATH_STATS = stats;
1619
- gl.__ASKR_BULK_DIAG = {
1620
- phase: "bulk-keyed-positional-applied",
1621
- stats
1622
- };
1623
- const counters = gl.__ASKR_FASTPATH_COUNTERS || {};
1624
- counters.bulkKeyedPositionalHits = (counters.bulkKeyedPositionalHits || 0) + 1;
1625
- gl.__ASKR_FASTPATH_COUNTERS = counters;
1626
- } catch (e) {
1627
- void e;
1628
- }
1629
- }
1630
- return newKeyMap;
1631
- }
1632
- const mismatchRatio = keyMismatches / keyedVnodes.length;
1633
- const POSITIONAL_THRESHOLD = 0.5;
1634
- if (mismatchRatio > POSITIONAL_THRESHOLD) {
1635
- logger.warn(
1636
- "[Askr][FASTPATH] applying bulk keyed positional text fast-path"
1637
- );
1638
- if (process.env.NODE_ENV !== "production" || process.env.ASKR_FASTPATH_DEBUG === "1") {
1639
- try {
1640
- globalThis.__ASKR_BULK_DIAG = {
1641
- phase: "bulk-keyed-positional-trigger",
1642
- totalKeyed: keyedVnodes.length,
1643
- keyMismatches
1644
- };
1645
- } catch (e) {
1646
- void e;
1647
- }
1648
- }
1649
- try {
1650
- if (isBulkCommitActive2()) markFastPathApplied(parent);
1651
- } catch (e) {
1652
- void e;
1653
- }
1654
- const stats = performBulkPositionalKeyedTextUpdate(
1655
- parent,
1656
- keyedVnodes
1657
- );
1658
- if (process.env.NODE_ENV !== "production" || process.env.ASKR_FASTPATH_DEBUG === "1") {
1659
- try {
1660
- const gl = globalThis;
1661
- gl.__ASKR_LAST_FASTPATH_STATS = stats;
1662
- gl.__ASKR_BULK_DIAG = {
1663
- phase: "bulk-keyed-positional-applied",
1664
- stats
1665
- };
1666
- const counters = gl.__ASKR_FASTPATH_COUNTERS || {};
1667
- counters.bulkKeyedPositionalHits = (counters.bulkKeyedPositionalHits || 0) + 1;
1668
- gl.__ASKR_FASTPATH_COUNTERS = counters;
1669
- } catch (e) {
1670
- void e;
1671
- }
1672
- }
1673
- return newKeyMap;
1674
- }
1675
- }
1676
- }
1677
- } catch {
1678
- stable = false;
1679
- }
1680
- }
1681
- } catch (e) {
1682
- void e;
1683
- }
1684
- if (!useFastPath) {
1685
- const totalKeyed2 = keyedVnodes.length;
1686
- const candidates = newChildren.filter(
1687
- (c) => _isDOMElement(c) && typeof c.type === "string"
1688
- );
1689
- const smallListPositionalReuseEligible = candidates.length > 0 && candidates.length <= 64 && parent.children.length === candidates.length && candidates.every((vnode) => {
1690
- const children = vnode.children || vnode.props?.children;
1691
- if (Array.isArray(children)) {
1692
- return children.length === 1 && (typeof children[0] === "string" || typeof children[0] === "number");
1693
- }
1694
- return children === void 0 || typeof children === "string" || typeof children === "number";
1695
- });
1696
- try {
1697
- if (smallListPositionalReuseEligible) {
1698
- let eligible = true;
1699
- for (let i = 0; i < totalKeyed2; i++) {
1700
- const vnode = keyedVnodes[i].vnode;
1701
- if (!_isDOMElement(vnode) || typeof vnode.type !== "string") {
1702
- eligible = false;
1703
- break;
1704
- }
1705
- const children = vnode.children || vnode.props?.children;
1706
- if (Array.isArray(children)) {
1707
- if (children.length !== 1) {
1708
- eligible = false;
1709
- break;
1710
- }
1711
- const c = children[0];
1712
- if (typeof c !== "string" && typeof c !== "number") {
1713
- eligible = false;
1714
- break;
1715
- }
1716
- } else if (children !== void 0 && typeof children !== "string" && typeof children !== "number") {
1717
- eligible = false;
1718
- break;
1719
- }
1720
- }
1721
- const anyKeyMatches = !!oldKeyMap && keyedVnodes.some((kv) => oldKeyMap.has(kv.key));
1722
- if (anyKeyMatches) {
1723
- eligible = false;
1724
- }
1725
- if (eligible || process.env.ASKR_FORCE_POSREUSE === "1") {
1726
- _triedPositionalReuse = true;
1727
- if (process.env.ASKR_FORCE_POSREUSE === "1") {
1728
- logger.warn(
1729
- "[Askr][POSREUSE][FORCED] forcing positional reuse path for testing"
1730
- );
1731
- } else {
1732
- logger.warn("[Askr][POSREUSE] positional reuse heuristic applied");
1733
- }
1734
- const existingChildren = parent.children;
1735
- for (let i = 0; i < totalKeyed2; i++) {
1736
- const { key, vnode } = keyedVnodes[i];
1737
- const current2 = existingChildren[i];
1738
- if (current2 && _isDOMElement(vnode)) {
1739
- const vnodeType = vnode.type;
1740
- if (current2.tagName.toLowerCase() === vnodeType.toLowerCase()) {
1741
- updateElementFromVnode(current2, vnode);
1742
- newKeyMap.set(key, current2);
1743
- continue;
1744
- }
1745
1003
  }
1746
- const newEl = createDOMNode(vnode);
1747
- if (newEl instanceof Element) {
1748
- if (current2) {
1749
- cleanupInstanceIfPresent(current2);
1750
- parent.replaceChild(newEl, current2);
1751
- } else parent.appendChild(newEl);
1752
- newKeyMap.set(key, newEl);
1753
- }
1754
- }
1755
- for (const vnode of unkeyedVnodes) {
1756
- const newEl = createDOMNode(vnode);
1757
- if (newEl) parent.appendChild(newEl);
1758
- }
1759
- return newKeyMap;
1760
- }
1761
- }
1762
- } catch {
1763
- }
1764
- }
1765
- if (useFastPath) {
1766
- if (!isSchedulerExecuting()) {
1767
- logger.warn(
1768
- "[Askr][FASTPATH][DEV] Fast-path reconciliation invoked outside scheduler execution"
1769
- );
1770
- }
1771
- let parentChildrenArr;
1772
- let localOldKeyMap;
1773
- if (totalKeyed <= 20) {
1774
- try {
1775
- const pc = parent.children;
1776
- parentChildrenArr = new Array(pc.length);
1777
- for (let i = 0; i < pc.length; i++)
1778
- parentChildrenArr[i] = pc[i];
1779
- } catch {
1780
- parentChildrenArr = void 0;
1781
- }
1782
- } else {
1783
- localOldKeyMap = /* @__PURE__ */ new Map();
1784
- try {
1785
- const parentChildren2 = Array.from(parent.children);
1786
- for (let i = 0; i < parentChildren2.length; i++) {
1787
- const ch = parentChildren2[i];
1788
- const k2 = ch.getAttribute("data-key");
1789
- if (k2 !== null) {
1790
- localOldKeyMap.set(k2, ch);
1791
- const n = Number(k2);
1792
- if (!Number.isNaN(n)) localOldKeyMap.set(n, ch);
1793
1004
  }
1794
- }
1795
- } catch {
1796
- localOldKeyMap = void 0;
1797
- }
1798
- }
1799
- logger.warn(
1800
- "[Askr][FASTPATH] oldKeyMap size:",
1801
- oldKeyMap?.size ?? 0,
1802
- "localOldKeyMap size:",
1803
- localOldKeyMap?.size
1804
- );
1805
- const tLookupStart = typeof performance !== "undefined" && performance.now ? performance.now() : Date.now();
1806
- const finalNodes = [];
1807
- let mapLookups = 0;
1808
- let createdNodes = 0;
1809
- let reusedCount = 0;
1810
- for (let i = 0; i < keyedVnodes.length; i++) {
1811
- const { key, vnode } = keyedVnodes[i];
1812
- mapLookups++;
1813
- let el;
1814
- if (totalKeyed <= 20 && parentChildrenArr) {
1815
- const ks = String(key);
1816
- for (let j = 0; j < parentChildrenArr.length; j++) {
1817
- const ch = parentChildrenArr[j];
1818
- const k2 = ch.getAttribute("data-key");
1819
- if (k2 !== null && (k2 === ks || Number(k2) === key)) {
1820
- el = ch;
1821
- break;
1005
+ for (let i = 0; i < children.length; i++) {
1006
+ const dom = createDOMNode(children[i]);
1007
+ if (dom) el.appendChild(dom);
1822
1008
  }
1009
+ } else {
1010
+ const dom = createDOMNode(children);
1011
+ if (dom) el.appendChild(dom);
1823
1012
  }
1824
- if (!el) el = oldKeyMap?.get(key);
1825
- } else {
1826
- el = localOldKeyMap?.get(key) ?? oldKeyMap?.get(key);
1827
- }
1828
- if (el) {
1829
- finalNodes.push(el);
1830
- reusedCount++;
1831
- } else {
1832
- const newEl = createDOMNode(vnode);
1833
- if (newEl) {
1834
- finalNodes.push(newEl);
1835
- createdNodes++;
1836
- }
1837
- }
1838
- }
1839
- for (const vnode of unkeyedVnodes) {
1840
- const newEl = createDOMNode(vnode);
1841
- if (newEl) {
1842
- finalNodes.push(newEl);
1843
- createdNodes++;
1844
1013
  }
1014
+ return el;
1845
1015
  }
1846
- const t_lookup = typeof performance !== "undefined" && performance.now ? performance.now() - tLookupStart : 0;
1847
- if (process.env.ASKR_FASTPATH_GUARD === "1") {
1848
- const replaceChildrenCount = 0;
1849
- const otherMutationCount = 0;
1850
- const orig = {};
1851
- const elProto = Element.prototype;
1852
- const nodeProto = Node.prototype;
1853
- const removeFn = (() => {
1854
- if (typeof document === "undefined" || typeof document.createElement !== "function")
1855
- return void 0;
1856
- try {
1857
- const el = document.createElement("div");
1858
- return typeof el.remove === "function" ? el.remove : void 0;
1859
- } catch {
1860
- return void 0;
1861
- }
1862
- })();
1863
- if (elProto.replaceChildren)
1864
- orig.replaceChildren = elProto.replaceChildren;
1865
- if (nodeProto.appendChild) orig.appendChild = nodeProto.appendChild;
1866
- if (nodeProto.insertBefore) orig.insertBefore = nodeProto.insertBefore;
1867
- if (nodeProto.removeChild) orig.removeChild = nodeProto.removeChild;
1868
- if (nodeProto.replaceChild) orig.replaceChild = nodeProto.replaceChild;
1869
- if (removeFn) orig.remove = removeFn;
1870
- let violation = false;
1871
- try {
1872
- const fragment = document.createDocumentFragment();
1873
- for (let i = 0; i < finalNodes.length; i++)
1874
- fragment.appendChild(finalNodes[i]);
1875
- let commitCount = 0;
1876
- commitCount++;
1877
- parent.replaceChildren(fragment);
1878
- if (typeof globalThis !== "undefined") {
1879
- globalThis["__ASKR_LAST_FASTPATH_COMMIT_COUNT"] = commitCount;
1880
- }
1881
- } finally {
1882
- violation = otherMutationCount !== 0 || replaceChildrenCount < 1;
1883
- }
1884
- if (violation) {
1885
- logger.error(
1886
- "[Askr][DEV] Fast-path structural mutation invariant violated:",
1887
- {
1888
- replaceChildrenCount,
1889
- otherMutationCount
1890
- }
1891
- );
1016
+ if (typeof type === "function") {
1017
+ const frame = node[CONTEXT_FRAME_SYMBOL];
1018
+ const snapshot = frame || getCurrentContextFrame();
1019
+ const componentFn = type;
1020
+ const isAsync = componentFn.constructor.name === "AsyncFunction";
1021
+ if (isAsync) {
1892
1022
  throw new Error(
1893
- "Fast-path must perform a single structural replacement (replaceChildren) and no other structural mutations"
1023
+ "Async components are not supported. Use resource() for async work."
1894
1024
  );
1895
1025
  }
1896
- } else {
1897
- const tFragmentStart = Date.now();
1898
- const fragment = document.createDocumentFragment();
1899
- let fragmentAppendCount = 0;
1900
- for (let i = 0; i < finalNodes.length; i++) {
1901
- fragment.appendChild(finalNodes[i]);
1902
- fragmentAppendCount++;
1903
- }
1904
- const t_fragment = Date.now() - tFragmentStart;
1905
- const schedBefore = process.env.NODE_ENV !== "production" ? globalScheduler.getState() : null;
1906
- const wasExecuting = process.env.NODE_ENV !== "production" ? isSchedulerExecuting() : false;
1907
- const tCommitStart = Date.now();
1908
- if (process.env.NODE_ENV !== "production") {
1909
- }
1910
- let commitCount = 0;
1911
- commitCount++;
1912
- try {
1913
- const existing = Array.from(parent.childNodes);
1914
- for (const n of existing) cleanupInstanceIfPresent(n);
1915
- } catch (e) {
1916
- void e;
1026
+ const vnodeAny = node;
1027
+ let childInstance = vnodeAny.__instance;
1028
+ if (!childInstance) {
1029
+ childInstance = createComponentInstance(
1030
+ `comp-${Math.random().toString(36).slice(2, 7)}`,
1031
+ componentFn,
1032
+ props || {},
1033
+ null
1034
+ );
1035
+ vnodeAny.__instance = childInstance;
1917
1036
  }
1918
- try {
1919
- const existing = Array.from(parent.childNodes);
1920
- for (const n of existing) cleanupInstanceIfPresent(n);
1921
- } catch (e) {
1922
- void e;
1037
+ if (snapshot) {
1038
+ childInstance.ownerFrame = snapshot;
1923
1039
  }
1924
- parent.replaceChildren(fragment);
1925
- if (typeof globalThis !== "undefined") {
1926
- globalThis["__ASKR_LAST_FASTPATH_COMMIT_COUNT"] = commitCount;
1040
+ const result = withContext(
1041
+ snapshot,
1042
+ () => renderComponentInline(childInstance)
1043
+ );
1044
+ if (result instanceof Promise) {
1045
+ throw new Error(
1046
+ "Async components are not supported. Components must return synchronously."
1047
+ );
1927
1048
  }
1928
- const t_commit = Date.now() - tCommitStart;
1929
- if (process.env.NODE_ENV !== "production") {
1930
- const schedAfter = globalScheduler.getState();
1931
- if (!wasExecuting) {
1932
- logger.warn(
1933
- "[Askr][FASTPATH][DEV] Fast-path commit invoked outside scheduler execution"
1934
- );
1935
- }
1936
- if (schedBefore && schedAfter) {
1937
- if (schedBefore.taskCount !== schedAfter.taskCount) {
1938
- logger.error(
1939
- "[Askr][FASTPATH][DEV] Scheduler tasks were enqueued during fast-path commit",
1940
- {
1941
- before: schedBefore,
1942
- after: schedAfter
1943
- }
1944
- );
1945
- throw new Error("Fast-path must not enqueue scheduler tasks");
1946
- }
1947
- }
1948
- const parentNodes = Array.from(parent.childNodes);
1949
- if (parentNodes.length !== finalNodes.length) {
1950
- logger.error("[Askr][FASTPATH][DEV] Parent child count mismatch", {
1951
- parentCount: parentNodes.length,
1952
- expected: finalNodes.length
1953
- });
1954
- throw new Error(
1955
- "Fast-path must perform a single structural replacement"
1956
- );
1957
- }
1958
- for (let i = 0; i < finalNodes.length; i++) {
1959
- if (parentNodes[i] !== finalNodes[i]) {
1960
- logger.error(
1961
- "[Askr][FASTPATH][DEV] Final DOM order mismatch at index",
1962
- i,
1963
- {
1964
- expected: finalNodes[i],
1965
- found: parentNodes[i]
1966
- }
1967
- );
1968
- throw new Error(
1969
- "Fast-path final DOM order does not match expected nodes"
1970
- );
1971
- }
1972
- }
1049
+ const dom = withContext(snapshot, () => createDOMNode(result));
1050
+ if (dom instanceof Element) {
1051
+ mountInstanceInline(childInstance, dom);
1052
+ return dom;
1973
1053
  }
1974
- const tBookkeepingStart = Date.now();
1975
- for (let i = 0; i < keyedVnodes.length; i++) {
1976
- const key = keyedVnodes[i].key;
1977
- const node = finalNodes[i];
1978
- if (node instanceof Element) newKeyMap.set(key, node);
1054
+ const host = document.createElement("div");
1055
+ if (dom instanceof DocumentFragment) {
1056
+ host.appendChild(dom);
1057
+ } else if (dom) {
1058
+ host.appendChild(dom);
1979
1059
  }
1980
- const t_bookkeeping = Date.now() - tBookkeepingStart;
1981
- if (process.env.ASKR_FASTPATH_TRACE === "1" || process.env.NODE_ENV !== "production") {
1982
- const stats = {
1983
- n: totalKeyed,
1984
- moves: moveCount,
1985
- lisLen: 0,
1986
- t_lookup,
1987
- t_fragment,
1988
- t_commit,
1989
- t_bookkeeping,
1990
- fragmentAppendCount,
1991
- mapLookups,
1992
- createdNodes,
1993
- reusedCount
1994
- };
1995
- if (typeof globalThis !== "undefined") {
1996
- const _g = globalThis;
1997
- _g["__ASKR_LAST_FASTPATH_STATS"] = stats;
1998
- _g["__ASKR_LAST_FASTPATH_REUSED"] = reusedCount > 0;
1999
- const historyKey = "__ASKR_LAST_FASTPATH_HISTORY";
2000
- let hist = _g[historyKey];
2001
- if (!hist) {
2002
- hist = [];
2003
- _g[historyKey] = hist;
1060
+ mountInstanceInline(childInstance, host);
1061
+ return host;
1062
+ }
1063
+ if (typeof type === "symbol" && (type === Fragment2 || String(type) === "Symbol(Fragment)")) {
1064
+ const fragment = document.createDocumentFragment();
1065
+ const children = props.children || node.children;
1066
+ if (children) {
1067
+ if (Array.isArray(children)) {
1068
+ for (let i = 0; i < children.length; i++) {
1069
+ const dom = createDOMNode(children[i]);
1070
+ if (dom) fragment.appendChild(dom);
2004
1071
  }
2005
- hist.push(stats);
1072
+ } else {
1073
+ const dom = createDOMNode(children);
1074
+ if (dom) fragment.appendChild(dom);
2006
1075
  }
2007
- logger.warn("[Askr][FASTPATH]", JSON.stringify(stats));
2008
1076
  }
1077
+ return fragment;
2009
1078
  }
2010
- return newKeyMap;
2011
- }
2012
- const parentChildren = Array.from(parent.children);
2013
- const positions = new Array(keyedVnodes.length).fill(-1);
2014
- for (let i = 0; i < keyedVnodes.length; i++) {
2015
- const key = keyedVnodes[i].key;
2016
- const el = oldKeyMap?.get(key);
2017
- if (el && el.parentElement === parent) {
2018
- positions[i] = parentChildren.indexOf(el);
2019
- }
2020
- }
2021
- const tailsStart = typeof performance !== "undefined" && performance.now ? performance.now() : Date.now();
2022
- const keepSet = /* @__PURE__ */ new Set();
2023
- const tails = [];
2024
- const tailsIdx = [];
2025
- const prev = new Array(positions.length).fill(-1);
2026
- for (let i = 0; i < positions.length; i++) {
2027
- const pos = positions[i];
2028
- if (pos === -1) continue;
2029
- let lo = 0;
2030
- let hi = tails.length;
2031
- while (lo < hi) {
2032
- const mid = lo + hi >> 1;
2033
- if (tails[mid] < pos) lo = mid + 1;
2034
- else hi = mid;
2035
- }
2036
- if (lo === tails.length) {
2037
- tails.push(pos);
2038
- tailsIdx.push(i);
2039
- } else {
2040
- tails[lo] = pos;
2041
- tailsIdx[lo] = i;
2042
- }
2043
- prev[i] = lo > 0 ? tailsIdx[lo - 1] : -1;
2044
1079
  }
2045
- let k = tailsIdx.length ? tailsIdx[tailsIdx.length - 1] : -1;
2046
- while (k !== -1) {
2047
- keepSet.add(k);
2048
- k = prev[k];
1080
+ return null;
1081
+ }
1082
+ function updateElementFromVnode(el, vnode, updateChildren = true) {
1083
+ if (!_isDOMElement(vnode)) {
1084
+ return;
2049
1085
  }
2050
- const tLIS = (typeof performance !== "undefined" && performance.now ? performance.now() : Date.now()) - tailsStart;
2051
- if (process.env.NODE_ENV !== "production" || process.env.ASKR_FASTPATH_DEBUG === "1") {
2052
- try {
2053
- const prev2 = globalThis.__ASKR_BULK_DIAG;
2054
- globalThis.__ASKR_BULK_DIAG = {
2055
- phase: "keyed-fallback-lis",
2056
- positionsFound: positions.filter((p) => p !== -1).length,
2057
- keepCount: keepSet.size,
2058
- tLIS,
2059
- previousDecision: prev2?.decision
2060
- };
2061
- } catch (e) {
2062
- void e;
2063
- }
1086
+ const props = vnode.props || {};
1087
+ const vnodeKey = vnode.key ?? vnode.props?.key;
1088
+ if (vnodeKey !== void 0) {
1089
+ el.setAttribute("data-key", String(vnodeKey));
2064
1090
  }
2065
- let anchor = parent.firstChild;
2066
- for (let i = 0; i < keyedVnodes.length; i++) {
2067
- const { key, vnode } = keyedVnodes[i];
2068
- const el = oldKeyMap?.get(key);
2069
- if (el && el.parentElement === parent) {
2070
- if (keepSet.has(i)) {
2071
- if (anchor === el) {
2072
- anchor = el.nextSibling;
1091
+ const existingListeners = elementListeners.get(el);
1092
+ const desiredEventNames = /* @__PURE__ */ new Set();
1093
+ for (const key in props) {
1094
+ const value = props[key];
1095
+ if (key === "children" || key === "key") continue;
1096
+ if (value === void 0 || value === null || value === false) {
1097
+ if (key === "class" || key === "className") {
1098
+ el.className = "";
1099
+ } else if (key.startsWith("on") && key.length > 2) {
1100
+ const eventName = key.slice(2).charAt(0).toLowerCase() + key.slice(3).toLowerCase();
1101
+ if (existingListeners && existingListeners.has(eventName)) {
1102
+ const entry = existingListeners.get(eventName);
1103
+ if (entry.options !== void 0)
1104
+ el.removeEventListener(eventName, entry.handler, entry.options);
1105
+ else el.removeEventListener(eventName, entry.handler);
1106
+ existingListeners.delete(eventName);
2073
1107
  }
2074
- updateElementFromVnode(el, vnode);
2075
- newKeyMap.set(key, el);
1108
+ continue;
2076
1109
  } else {
2077
- parent.insertBefore(el, anchor);
2078
- updateElementFromVnode(el, vnode);
2079
- newKeyMap.set(key, el);
2080
- anchor = el.nextSibling;
1110
+ el.removeAttribute(key);
2081
1111
  }
2082
- } else {
2083
- const newEl = createDOMNode(vnode);
2084
- if (newEl instanceof Element) {
2085
- parent.insertBefore(newEl, anchor);
2086
- newKeyMap.set(key, newEl);
2087
- anchor = newEl.nextSibling;
1112
+ continue;
1113
+ }
1114
+ if (key === "class" || key === "className") {
1115
+ el.className = String(value);
1116
+ } else if (key === "value" || key === "checked") {
1117
+ el[key] = value;
1118
+ } else if (key.startsWith("on") && key.length > 2) {
1119
+ const eventName = key.slice(2).charAt(0).toLowerCase() + key.slice(3).toLowerCase();
1120
+ desiredEventNames.add(eventName);
1121
+ const existing = existingListeners?.get(eventName);
1122
+ if (existing && existing.original === value) {
1123
+ continue;
1124
+ }
1125
+ if (existing) {
1126
+ el.removeEventListener(eventName, existing.handler);
1127
+ }
1128
+ const wrappedHandler = (event) => {
1129
+ globalScheduler.setInHandler(true);
1130
+ try {
1131
+ value(event);
1132
+ } catch (error) {
1133
+ logger.error("[Askr] Event handler error:", error);
1134
+ } finally {
1135
+ globalScheduler.setInHandler(false);
1136
+ }
1137
+ };
1138
+ const options = eventName === "wheel" || eventName === "scroll" || eventName.startsWith("touch") ? { passive: true } : void 0;
1139
+ if (options !== void 0)
1140
+ el.addEventListener(eventName, wrappedHandler, options);
1141
+ else el.addEventListener(eventName, wrappedHandler);
1142
+ if (!elementListeners.has(el)) {
1143
+ elementListeners.set(el, /* @__PURE__ */ new Map());
2088
1144
  }
1145
+ elementListeners.get(el).set(eventName, {
1146
+ handler: wrappedHandler,
1147
+ original: value,
1148
+ options
1149
+ });
1150
+ } else {
1151
+ el.setAttribute(key, String(value));
2089
1152
  }
2090
1153
  }
2091
- for (const vnode of unkeyedVnodes) {
2092
- const newEl = createDOMNode(vnode);
2093
- if (newEl) {
2094
- parent.appendChild(newEl);
1154
+ if (existingListeners) {
1155
+ for (const eventName of existingListeners.keys()) {
1156
+ const entry = existingListeners.get(eventName);
1157
+ if (!desiredEventNames.has(eventName)) {
1158
+ el.removeEventListener(eventName, entry.handler);
1159
+ existingListeners.delete(eventName);
1160
+ }
2095
1161
  }
1162
+ if (existingListeners.size === 0) elementListeners.delete(el);
2096
1163
  }
2097
- return newKeyMap;
2098
- }
2099
- function performBulkKeyedTextReplace(parent, keyedVnodes, oldKeyMap) {
2100
- const total = keyedVnodes.length;
2101
- const finalNodes = [];
2102
- let reused = 0;
2103
- let created = 0;
2104
- const t0 = typeof performance !== "undefined" && performance.now ? performance.now() : Date.now();
2105
- for (let i = 0; i < total; i++) {
2106
- const { key, vnode } = keyedVnodes[i];
2107
- const el = oldKeyMap?.get(key);
2108
- if (el && _isDOMElement(vnode) && typeof vnode.type === "string") {
2109
- const children = vnode.children || vnode.props?.children;
2110
- if (typeof children === "string" || typeof children === "number") {
2111
- if (el.childNodes.length === 1 && el.firstChild?.nodeType === 3) {
2112
- el.firstChild.data = String(children);
2113
- } else {
2114
- el.textContent = String(children);
2115
- }
2116
- } else if (Array.isArray(children) && children.length === 1 && (typeof children[0] === "string" || typeof children[0] === "number")) {
2117
- if (el.childNodes.length === 1 && el.firstChild?.nodeType === 3) {
2118
- el.firstChild.data = String(children[0]);
2119
- } else {
2120
- el.textContent = String(children[0]);
2121
- }
2122
- } else {
2123
- updateElementFromVnode(el, vnode);
2124
- }
2125
- finalNodes.push(el);
2126
- reused++;
1164
+ if (updateChildren) {
1165
+ const children = vnode.children || props.children;
1166
+ updateElementChildren(el, children);
1167
+ }
1168
+ }
1169
+ function updateElementChildren(el, children) {
1170
+ if (!children) {
1171
+ el.textContent = "";
1172
+ return;
1173
+ }
1174
+ if (!Array.isArray(children) && (typeof children === "string" || typeof children === "number")) {
1175
+ if (el.childNodes.length === 1 && el.firstChild?.nodeType === 3) {
1176
+ el.firstChild.data = String(children);
2127
1177
  } else {
2128
- const dom = createDOMNode(vnode);
2129
- if (dom) {
2130
- finalNodes.push(dom);
2131
- created++;
2132
- }
1178
+ el.textContent = String(children);
2133
1179
  }
1180
+ return;
2134
1181
  }
2135
- try {
2136
- const toRemove = Array.from(parent.childNodes).filter(
2137
- (n) => !finalNodes.includes(n)
2138
- );
2139
- for (const n of toRemove) cleanupInstanceIfPresent(n);
2140
- } catch (e) {
2141
- void e;
1182
+ if (Array.isArray(children)) {
1183
+ updateUnkeyedChildren(el, children);
1184
+ return;
2142
1185
  }
2143
- const existingChildren = Array.from(parent.children);
2144
- const listenerSnapshots = new Array(existingChildren.length).fill(void 0);
2145
- try {
2146
- for (let i = 0; i < existingChildren.length; i++) {
2147
- const ch = existingChildren[i];
2148
- if (ch) {
2149
- const map = elementListeners.get(ch);
2150
- if (map && map.size > 0) {
2151
- const clone = /* @__PURE__ */ new Map();
2152
- for (const [k, v] of map) clone.set(k, v);
2153
- listenerSnapshots[i] = clone;
2154
- }
2155
- }
2156
- }
2157
- } catch (e) {
2158
- void e;
1186
+ el.textContent = "";
1187
+ const dom = createDOMNode(children);
1188
+ if (dom) el.appendChild(dom);
1189
+ }
1190
+ function updateUnkeyedChildren(parent, newChildren) {
1191
+ const existing = Array.from(parent.children);
1192
+ if (existing.length === 0 && parent.childNodes.length > 0) {
1193
+ parent.textContent = "";
2159
1194
  }
2160
- const fragment = document.createDocumentFragment();
2161
- for (let i = 0; i < finalNodes.length; i++)
2162
- fragment.appendChild(finalNodes[i]);
2163
- parent.replaceChildren(fragment);
2164
- try {
2165
- for (let i = 0; i < finalNodes.length; i++) {
2166
- const newNode = finalNodes[i];
2167
- const snapshot = listenerSnapshots[i];
2168
- if (snapshot && newNode instanceof Element) {
2169
- for (const [eventName, entry] of snapshot) {
2170
- const existing = elementListeners.get(newNode)?.get(eventName);
2171
- if (existing && existing.original === entry.original) continue;
2172
- newNode.addEventListener(eventName, entry.handler);
2173
- if (!elementListeners.has(newNode))
2174
- elementListeners.set(newNode, /* @__PURE__ */ new Map());
2175
- elementListeners.get(newNode).set(eventName, entry);
1195
+ const max = Math.max(existing.length, newChildren.length);
1196
+ for (let i = 0; i < max; i++) {
1197
+ const current2 = existing[i];
1198
+ const next = newChildren[i];
1199
+ if (next === void 0 && current2) {
1200
+ cleanupInstanceIfPresent(current2);
1201
+ current2.remove();
1202
+ continue;
1203
+ }
1204
+ if (!current2 && next !== void 0) {
1205
+ const dom = createDOMNode(next);
1206
+ if (dom) parent.appendChild(dom);
1207
+ continue;
1208
+ }
1209
+ if (!current2 || next === void 0) continue;
1210
+ if (typeof next === "string" || typeof next === "number") {
1211
+ current2.textContent = String(next);
1212
+ } else if (_isDOMElement(next)) {
1213
+ if (typeof next.type === "string") {
1214
+ if (current2.tagName.toLowerCase() === next.type.toLowerCase()) {
1215
+ updateElementFromVnode(current2, next);
1216
+ } else {
1217
+ const dom = createDOMNode(next);
1218
+ if (dom) {
1219
+ if (current2 instanceof Element) removeAllListeners(current2);
1220
+ cleanupInstanceIfPresent(current2);
1221
+ parent.replaceChild(dom, current2);
1222
+ }
1223
+ }
1224
+ } else {
1225
+ const dom = createDOMNode(next);
1226
+ if (dom) {
1227
+ if (current2 instanceof Element) removeAllListeners(current2);
1228
+ cleanupInstanceIfPresent(current2);
1229
+ parent.replaceChild(dom, current2);
2176
1230
  }
2177
1231
  }
1232
+ } else {
1233
+ const dom = createDOMNode(next);
1234
+ if (dom) {
1235
+ if (current2 instanceof Element) removeAllListeners(current2);
1236
+ cleanupInstanceIfPresent(current2);
1237
+ parent.replaceChild(dom, current2);
1238
+ }
2178
1239
  }
2179
- } catch (e) {
2180
- void e;
2181
- }
2182
- try {
2183
- keyedElements.delete(parent);
2184
- } catch (e) {
2185
- void e;
2186
1240
  }
2187
- const t = typeof performance !== "undefined" && performance.now ? performance.now() - t0 : 0;
2188
- const stats = {
2189
- n: total,
2190
- reused,
2191
- created,
2192
- t
2193
- };
2194
- return stats;
2195
1241
  }
2196
1242
  function performBulkPositionalKeyedTextUpdate(parent, keyedVnodes) {
2197
1243
  const total = keyedVnodes.length;
@@ -2205,6 +1251,18 @@ function performBulkPositionalKeyedTextUpdate(parent, keyedVnodes) {
2205
1251
  const vnodeType = vnode.type;
2206
1252
  if (ch.tagName.toLowerCase() === vnodeType.toLowerCase()) {
2207
1253
  const children = vnode.children || vnode.props?.children;
1254
+ try {
1255
+ if (process.env.ASKR_FASTPATH_DEBUG === "1" || process.env.ASKR_FASTPATH_DEBUG === "true") {
1256
+ logger.warn("[Askr][FASTPATH] positional idx", i, {
1257
+ chTag: ch.tagName.toLowerCase(),
1258
+ vnodeType,
1259
+ chChildNodes: ch.childNodes.length,
1260
+ childrenType: Array.isArray(children) ? "array" : typeof children
1261
+ });
1262
+ }
1263
+ } catch (e) {
1264
+ void e;
1265
+ }
2208
1266
  if (typeof children === "string" || typeof children === "number") {
2209
1267
  if (ch.childNodes.length === 1 && ch.firstChild?.nodeType === 3) {
2210
1268
  ch.firstChild.data = String(children);
@@ -2228,6 +1286,27 @@ function performBulkPositionalKeyedTextUpdate(parent, keyedVnodes) {
2228
1286
  }
2229
1287
  reused++;
2230
1288
  continue;
1289
+ } else {
1290
+ try {
1291
+ if (process.env.ASKR_FASTPATH_DEBUG === "1" || process.env.ASKR_FASTPATH_DEBUG === "true") {
1292
+ logger.warn("[Askr][FASTPATH] positional tag mismatch", i, {
1293
+ chTag: ch.tagName.toLowerCase(),
1294
+ vnodeType
1295
+ });
1296
+ }
1297
+ } catch (e) {
1298
+ void e;
1299
+ }
1300
+ }
1301
+ } else {
1302
+ try {
1303
+ if (process.env.ASKR_FASTPATH_DEBUG === "1" || process.env.ASKR_FASTPATH_DEBUG === "true") {
1304
+ logger.warn("[Askr][FASTPATH] positional missing or invalid", i, {
1305
+ ch: !!ch
1306
+ });
1307
+ }
1308
+ } catch (e) {
1309
+ void e;
2231
1310
  }
2232
1311
  }
2233
1312
  const dom = createDOMNode(vnode);
@@ -2251,12 +1330,99 @@ function performBulkPositionalKeyedTextUpdate(parent, keyedVnodes) {
2251
1330
  } catch (e) {
2252
1331
  void e;
2253
1332
  }
1333
+ const stats = { n: total, reused, updatedKeys, t };
1334
+ try {
1335
+ if (process.env.ASKR_FASTPATH_DEBUG === "1" || process.env.ASKR_FASTPATH_DEBUG === "true") {
1336
+ logger.warn("[Askr][FASTPATH] bulk positional stats", stats);
1337
+ }
1338
+ __ASKR_set("__LAST_FASTPATH_STATS", stats);
1339
+ __ASKR_set("__LAST_FASTPATH_COMMIT_COUNT", 1);
1340
+ __ASKR_incCounter("bulkKeyedPositionalHits");
1341
+ } catch (e) {
1342
+ void e;
1343
+ }
1344
+ return stats;
1345
+ }
1346
+ function performBulkTextReplace(parent, newChildren) {
1347
+ const t0 = typeof performance !== "undefined" && performance.now ? performance.now() : Date.now();
1348
+ const existing = Array.from(parent.childNodes);
1349
+ const finalNodes = [];
1350
+ let reused = 0;
1351
+ let created = 0;
1352
+ for (let i = 0; i < newChildren.length; i++) {
1353
+ const vnode = newChildren[i];
1354
+ const existingNode = existing[i];
1355
+ if (typeof vnode === "string" || typeof vnode === "number") {
1356
+ const text = String(vnode);
1357
+ if (existingNode && existingNode.nodeType === 3) {
1358
+ existingNode.data = text;
1359
+ finalNodes.push(existingNode);
1360
+ reused++;
1361
+ } else {
1362
+ finalNodes.push(document.createTextNode(text));
1363
+ created++;
1364
+ }
1365
+ continue;
1366
+ }
1367
+ if (typeof vnode === "object" && vnode !== null && "type" in vnode) {
1368
+ const vnodeObj = vnode;
1369
+ if (typeof vnodeObj.type === "string") {
1370
+ const tag = vnodeObj.type;
1371
+ if (existingNode && existingNode.nodeType === 1 && existingNode.tagName.toLowerCase() === tag.toLowerCase()) {
1372
+ updateElementFromVnode(existingNode, vnode);
1373
+ finalNodes.push(existingNode);
1374
+ reused++;
1375
+ continue;
1376
+ }
1377
+ }
1378
+ const dom = createDOMNode(vnode);
1379
+ if (dom) {
1380
+ finalNodes.push(dom);
1381
+ created++;
1382
+ continue;
1383
+ }
1384
+ }
1385
+ }
1386
+ const tBuild = (typeof performance !== "undefined" && performance.now ? performance.now() : Date.now()) - t0;
1387
+ try {
1388
+ const toRemove = Array.from(parent.childNodes).filter(
1389
+ (n) => !finalNodes.includes(n)
1390
+ );
1391
+ for (const n of toRemove) {
1392
+ if (n instanceof Element) removeAllListeners(n);
1393
+ cleanupInstanceIfPresent(n);
1394
+ }
1395
+ } catch (e) {
1396
+ void e;
1397
+ }
1398
+ const fragStart = Date.now();
1399
+ const fragment = document.createDocumentFragment();
1400
+ for (let i = 0; i < finalNodes.length; i++)
1401
+ fragment.appendChild(finalNodes[i]);
1402
+ try {
1403
+ __ASKR_incCounter("__DOM_REPLACE_COUNT");
1404
+ __ASKR_set("__LAST_DOM_REPLACE_STACK_DOM", new Error().stack);
1405
+ } catch (e) {
1406
+ void e;
1407
+ }
1408
+ parent.replaceChildren(fragment);
1409
+ const tCommit = Date.now() - fragStart;
1410
+ keyedElements.delete(parent);
2254
1411
  const stats = {
2255
- n: total,
1412
+ n: newChildren.length,
2256
1413
  reused,
2257
- updatedKeys,
2258
- t
1414
+ created,
1415
+ tBuild,
1416
+ tCommit
2259
1417
  };
1418
+ try {
1419
+ __ASKR_set("__LAST_BULK_TEXT_FASTPATH_STATS", stats);
1420
+ __ASKR_set("__LAST_FASTPATH_STATS", stats);
1421
+ __ASKR_set("__LAST_FASTPATH_COMMIT_COUNT", 1);
1422
+ __ASKR_incCounter("bulkTextFastpathHits");
1423
+ } catch (e) {
1424
+ void e;
1425
+ }
2260
1426
  return stats;
2261
1427
  }
2262
1428
  function isBulkTextFastPathEligible(parent, newChildren) {
@@ -2266,12 +1432,12 @@ function isBulkTextFastPathEligible(parent, newChildren) {
2266
1432
  if (total < threshold) {
2267
1433
  if (process.env.NODE_ENV !== "production" || process.env.ASKR_FASTPATH_DEBUG === "1") {
2268
1434
  try {
2269
- globalThis.__ASKR_BULK_DIAG = {
1435
+ __ASKR_set("__BULK_DIAG", {
2270
1436
  phase: "bulk-unkeyed-eligible",
2271
1437
  reason: "too-small",
2272
1438
  total,
2273
1439
  threshold
2274
- };
1440
+ });
2275
1441
  } catch (e) {
2276
1442
  void e;
2277
1443
  }
@@ -2290,11 +1456,11 @@ function isBulkTextFastPathEligible(parent, newChildren) {
2290
1456
  if (typeof dv.type === "function") {
2291
1457
  if (process.env.NODE_ENV !== "production" || process.env.ASKR_FASTPATH_DEBUG === "1") {
2292
1458
  try {
2293
- globalThis.__ASKR_BULK_DIAG = {
1459
+ __ASKR_set("__BULK_DIAG", {
2294
1460
  phase: "bulk-unkeyed-eligible",
2295
1461
  reason: "component-child",
2296
1462
  index: i
2297
- };
1463
+ });
2298
1464
  } catch (e) {
2299
1465
  void e;
2300
1466
  }
@@ -2323,438 +1489,806 @@ function isBulkTextFastPathEligible(parent, newChildren) {
2323
1489
  const eligible = fraction >= requiredFraction && parent.childNodes.length >= total;
2324
1490
  if (process.env.NODE_ENV !== "production" || process.env.ASKR_FASTPATH_DEBUG === "1") {
2325
1491
  try {
2326
- globalThis.__ASKR_BULK_DIAG = {
1492
+ __ASKR_set("__BULK_DIAG", {
2327
1493
  phase: "bulk-unkeyed-eligible",
2328
1494
  total,
2329
1495
  simple,
2330
1496
  fraction,
2331
1497
  requiredFraction,
2332
1498
  eligible
2333
- };
1499
+ });
2334
1500
  } catch (e) {
2335
1501
  void e;
2336
1502
  }
2337
1503
  }
2338
1504
  return eligible;
2339
1505
  }
2340
- function performBulkTextReplace(parent, newChildren) {
2341
- const t0 = typeof performance !== "undefined" && performance.now ? performance.now() : Date.now();
2342
- const existing = Array.from(parent.childNodes);
2343
- const finalNodes = [];
2344
- let reused = 0;
2345
- let created = 0;
2346
- for (let i = 0; i < newChildren.length; i++) {
2347
- const vnode = newChildren[i];
2348
- const existingNode = existing[i];
2349
- if (typeof vnode === "string" || typeof vnode === "number") {
2350
- const text = String(vnode);
2351
- if (existingNode && existingNode.nodeType === 3) {
2352
- existingNode.data = text;
2353
- finalNodes.push(existingNode);
2354
- reused++;
2355
- } else {
2356
- finalNodes.push(document.createTextNode(text));
2357
- created++;
1506
+ var IS_DOM_AVAILABLE;
1507
+ var init_dom = __esm({
1508
+ "src/renderer/dom.ts"() {
1509
+ "use strict";
1510
+ init_scheduler();
1511
+ init_logger();
1512
+ init_jsx_runtime();
1513
+ init_context();
1514
+ init_component();
1515
+ init_cleanup();
1516
+ init_diag();
1517
+ init_types();
1518
+ init_keyed();
1519
+ IS_DOM_AVAILABLE = typeof document !== "undefined";
1520
+ }
1521
+ });
1522
+
1523
+ // src/renderer/fastpath.ts
1524
+ function applyRendererFastPath(parent, keyedVnodes, oldKeyMap, unkeyedVnodes) {
1525
+ if (typeof document === "undefined") return null;
1526
+ const totalKeyed = keyedVnodes.length;
1527
+ if (totalKeyed === 0 && (!unkeyedVnodes || unkeyedVnodes.length === 0))
1528
+ return null;
1529
+ if (!isSchedulerExecuting()) {
1530
+ logger.warn(
1531
+ "[Askr][FASTPATH][DEV] Fast-path reconciliation invoked outside scheduler execution"
1532
+ );
1533
+ }
1534
+ let parentChildrenArr;
1535
+ let localOldKeyMap;
1536
+ if (totalKeyed <= 20) {
1537
+ try {
1538
+ const pc = parent.children;
1539
+ parentChildrenArr = new Array(pc.length);
1540
+ for (let i = 0; i < pc.length; i++)
1541
+ parentChildrenArr[i] = pc[i];
1542
+ } catch (e) {
1543
+ parentChildrenArr = void 0;
1544
+ void e;
1545
+ }
1546
+ } else {
1547
+ localOldKeyMap = /* @__PURE__ */ new Map();
1548
+ try {
1549
+ const parentChildren = Array.from(parent.children);
1550
+ for (let i = 0; i < parentChildren.length; i++) {
1551
+ const ch = parentChildren[i];
1552
+ const k = ch.getAttribute("data-key");
1553
+ if (k !== null) {
1554
+ localOldKeyMap.set(k, ch);
1555
+ const n = Number(k);
1556
+ if (!Number.isNaN(n)) localOldKeyMap.set(n, ch);
1557
+ }
2358
1558
  }
2359
- continue;
1559
+ } catch (e) {
1560
+ localOldKeyMap = void 0;
1561
+ void e;
2360
1562
  }
2361
- if (typeof vnode === "object" && vnode !== null && "type" in vnode) {
2362
- if (typeof vnode.type === "string") {
2363
- const vtype = vnode.type;
2364
- if (existingNode && existingNode.nodeType === 1 && existingNode.tagName.toLowerCase() === vtype.toLowerCase()) {
2365
- const el = existingNode;
2366
- const children = vnode.children || vnode.props?.children;
2367
- if (typeof children === "string" || typeof children === "number") {
2368
- if (el.childNodes.length === 1 && el.firstChild?.nodeType === 3) {
2369
- el.firstChild.data = String(children);
2370
- } else {
2371
- el.textContent = String(children);
2372
- }
2373
- } else if (Array.isArray(children) && children.length === 1 && (typeof children[0] === "string" || typeof children[0] === "number")) {
2374
- if (el.childNodes.length === 1 && el.firstChild?.nodeType === 3) {
2375
- el.firstChild.data = String(children[0]);
2376
- } else {
2377
- el.textContent = String(children[0]);
1563
+ }
1564
+ const finalNodes = [];
1565
+ let mapLookups = 0;
1566
+ let createdNodes = 0;
1567
+ let reusedCount = 0;
1568
+ for (let i = 0; i < keyedVnodes.length; i++) {
1569
+ const { key, vnode } = keyedVnodes[i];
1570
+ mapLookups++;
1571
+ let el;
1572
+ if (totalKeyed <= 20 && parentChildrenArr) {
1573
+ const ks = String(key);
1574
+ for (let j = 0; j < parentChildrenArr.length; j++) {
1575
+ const ch = parentChildrenArr[j];
1576
+ const k = ch.getAttribute("data-key");
1577
+ if (k !== null && (k === ks || Number(k) === key)) {
1578
+ el = ch;
1579
+ break;
1580
+ }
1581
+ }
1582
+ if (!el) el = oldKeyMap?.get(key);
1583
+ } else {
1584
+ el = localOldKeyMap?.get(key) ?? oldKeyMap?.get(key);
1585
+ }
1586
+ if (el) {
1587
+ finalNodes.push(el);
1588
+ reusedCount++;
1589
+ } else {
1590
+ const newEl = createDOMNode(vnode);
1591
+ if (newEl) {
1592
+ finalNodes.push(newEl);
1593
+ createdNodes++;
1594
+ }
1595
+ }
1596
+ }
1597
+ if (unkeyedVnodes && unkeyedVnodes.length) {
1598
+ for (const vnode of unkeyedVnodes) {
1599
+ const newEl = createDOMNode(vnode);
1600
+ if (newEl) {
1601
+ finalNodes.push(newEl);
1602
+ createdNodes++;
1603
+ }
1604
+ }
1605
+ }
1606
+ try {
1607
+ const tFragmentStart = Date.now();
1608
+ const fragment = document.createDocumentFragment();
1609
+ let fragmentAppendCount = 0;
1610
+ for (let i = 0; i < finalNodes.length; i++) {
1611
+ fragment.appendChild(finalNodes[i]);
1612
+ fragmentAppendCount++;
1613
+ }
1614
+ try {
1615
+ const existing = Array.from(parent.childNodes);
1616
+ const toRemove = existing.filter((n) => !finalNodes.includes(n));
1617
+ for (const n of toRemove) {
1618
+ if (n instanceof Element) removeAllListeners(n);
1619
+ cleanupInstanceIfPresent(n);
1620
+ }
1621
+ } catch (e) {
1622
+ void e;
1623
+ }
1624
+ try {
1625
+ __ASKR_incCounter("__DOM_REPLACE_COUNT");
1626
+ __ASKR_set("__LAST_DOM_REPLACE_STACK_FASTPATH", new Error().stack);
1627
+ } catch (e) {
1628
+ void e;
1629
+ }
1630
+ parent.replaceChildren(fragment);
1631
+ try {
1632
+ __ASKR_set("__LAST_FASTPATH_COMMIT_COUNT", 1);
1633
+ } catch (e) {
1634
+ void e;
1635
+ }
1636
+ try {
1637
+ if (isBulkCommitActive2()) markFastPathApplied(parent);
1638
+ } catch (e) {
1639
+ void e;
1640
+ }
1641
+ const newKeyMap = /* @__PURE__ */ new Map();
1642
+ for (let i = 0; i < keyedVnodes.length; i++) {
1643
+ const key = keyedVnodes[i].key;
1644
+ const node = finalNodes[i];
1645
+ if (node instanceof Element) newKeyMap.set(key, node);
1646
+ }
1647
+ try {
1648
+ const stats = {
1649
+ n: totalKeyed,
1650
+ moves: 0,
1651
+ lisLen: 0,
1652
+ t_lookup: 0,
1653
+ t_fragment: Date.now() - tFragmentStart,
1654
+ t_commit: 0,
1655
+ t_bookkeeping: 0,
1656
+ fragmentAppendCount,
1657
+ mapLookups,
1658
+ createdNodes,
1659
+ reusedCount
1660
+ };
1661
+ if (typeof globalThis !== "undefined") {
1662
+ __ASKR_set("__LAST_FASTPATH_STATS", stats);
1663
+ __ASKR_set("__LAST_FASTPATH_REUSED", reusedCount > 0);
1664
+ __ASKR_incCounter("fastpathHistoryPush");
1665
+ }
1666
+ if (process.env.ASKR_FASTPATH_DEBUG === "1" || process.env.ASKR_FASTPATH_DEBUG === "true") {
1667
+ logger.warn(
1668
+ "[Askr][FASTPATH]",
1669
+ JSON.stringify({ n: totalKeyed, createdNodes, reusedCount })
1670
+ );
1671
+ }
1672
+ } catch (e) {
1673
+ void e;
1674
+ }
1675
+ try {
1676
+ _reconcilerRecordedParents.add(parent);
1677
+ } catch (e) {
1678
+ void e;
1679
+ }
1680
+ return newKeyMap;
1681
+ } catch (e) {
1682
+ void e;
1683
+ return null;
1684
+ }
1685
+ }
1686
+ var init_fastpath = __esm({
1687
+ "src/renderer/fastpath.ts"() {
1688
+ "use strict";
1689
+ init_dom();
1690
+ init_keyed();
1691
+ init_logger();
1692
+ init_cleanup();
1693
+ init_diag();
1694
+ init_scheduler();
1695
+ init_fastlane_shared();
1696
+ }
1697
+ });
1698
+
1699
+ // src/renderer/reconcile.ts
1700
+ function reconcileKeyedChildren(parent, newChildren, oldKeyMap) {
1701
+ const newKeyMap = /* @__PURE__ */ new Map();
1702
+ const keyedVnodes = [];
1703
+ const unkeyedVnodes = [];
1704
+ for (let i = 0; i < newChildren.length; i++) {
1705
+ const child = newChildren[i];
1706
+ if (typeof child === "object" && child !== null && "type" in child) {
1707
+ const childObj = child;
1708
+ const rawKey = childObj.key ?? childObj.props?.key;
1709
+ if (rawKey !== void 0) {
1710
+ const key = typeof rawKey === "symbol" ? String(rawKey) : rawKey;
1711
+ keyedVnodes.push({ key, vnode: child });
1712
+ } else {
1713
+ unkeyedVnodes.push(child);
1714
+ }
1715
+ } else {
1716
+ unkeyedVnodes.push(child);
1717
+ }
1718
+ }
1719
+ try {
1720
+ const decision = isKeyedReorderFastPathEligible(
1721
+ parent,
1722
+ newChildren,
1723
+ oldKeyMap
1724
+ );
1725
+ if (decision.useFastPath && keyedVnodes.length >= 128 || // If we're executing inside a runtime bulk commit (fastlane), prefer the
1726
+ // renderer fast-path to ensure the single-commit invariant is preserved.
1727
+ isBulkCommitActive2()) {
1728
+ try {
1729
+ const map = applyRendererFastPath(
1730
+ parent,
1731
+ keyedVnodes,
1732
+ oldKeyMap,
1733
+ unkeyedVnodes
1734
+ );
1735
+ if (map) {
1736
+ try {
1737
+ keyedElements.set(parent, map);
1738
+ } catch (e) {
1739
+ void e;
1740
+ }
1741
+ return map;
1742
+ }
1743
+ } catch (e) {
1744
+ void e;
1745
+ }
1746
+ }
1747
+ try {
1748
+ const total = keyedVnodes.length;
1749
+ if (total >= 10) {
1750
+ let matchCount = 0;
1751
+ try {
1752
+ for (let i = 0; i < total; i++) {
1753
+ const vnode = keyedVnodes[i].vnode;
1754
+ if (!vnode || typeof vnode !== "object" || typeof vnode.type !== "string")
1755
+ continue;
1756
+ const el = parent.children[i];
1757
+ if (!el) continue;
1758
+ if (el.tagName.toLowerCase() === String(vnode.type).toLowerCase())
1759
+ matchCount++;
1760
+ }
1761
+ } catch (e) {
1762
+ void e;
1763
+ }
1764
+ if (matchCount / total >= 0.9) {
1765
+ let hasPropChanges = false;
1766
+ try {
1767
+ for (let i = 0; i < total; i++) {
1768
+ const vnode = keyedVnodes[i].vnode;
1769
+ const el = parent.children[i];
1770
+ if (!el || !vnode || typeof vnode !== "object") continue;
1771
+ const props = vnode.props || {};
1772
+ for (const k of Object.keys(props)) {
1773
+ if (k === "children" || k === "key") continue;
1774
+ if (k.startsWith("on") && k.length > 2) continue;
1775
+ if (k.startsWith("data-")) continue;
1776
+ const v = props[k];
1777
+ try {
1778
+ if (k === "class" || k === "className") {
1779
+ if (el.className !== String(v)) {
1780
+ hasPropChanges = true;
1781
+ break;
1782
+ }
1783
+ } else if (k === "value" || k === "checked") {
1784
+ if (el[k] !== v) {
1785
+ hasPropChanges = true;
1786
+ break;
1787
+ }
1788
+ } else {
1789
+ const attr = el.getAttribute(k);
1790
+ if (v === void 0 || v === null || v === false) {
1791
+ if (attr !== null) {
1792
+ hasPropChanges = true;
1793
+ break;
1794
+ }
1795
+ } else if (String(v) !== attr) {
1796
+ hasPropChanges = true;
1797
+ break;
1798
+ }
1799
+ }
1800
+ } catch (e) {
1801
+ hasPropChanges = true;
1802
+ void e;
1803
+ break;
1804
+ }
1805
+ }
1806
+ if (hasPropChanges) break;
2378
1807
  }
1808
+ } catch (e) {
1809
+ void e;
1810
+ }
1811
+ if (hasPropChanges) {
2379
1812
  } else {
2380
- updateElementFromVnode(el, vnode);
1813
+ try {
1814
+ const stats = performBulkPositionalKeyedTextUpdate(
1815
+ parent,
1816
+ keyedVnodes
1817
+ );
1818
+ if (process.env.NODE_ENV !== "production" || process.env.ASKR_FASTPATH_DEBUG === "1") {
1819
+ try {
1820
+ __ASKR_set("__LAST_FASTPATH_STATS", stats);
1821
+ __ASKR_set("__LAST_FASTPATH_COMMIT_COUNT", 1);
1822
+ __ASKR_incCounter("bulkKeyedPositionalHits");
1823
+ } catch (e) {
1824
+ void e;
1825
+ }
1826
+ }
1827
+ try {
1828
+ const map = /* @__PURE__ */ new Map();
1829
+ const children = Array.from(parent.children);
1830
+ for (let i = 0; i < children.length; i++) {
1831
+ const el = children[i];
1832
+ const k = el.getAttribute("data-key");
1833
+ if (k !== null) {
1834
+ map.set(k, el);
1835
+ const n = Number(k);
1836
+ if (!Number.isNaN(n)) map.set(n, el);
1837
+ }
1838
+ }
1839
+ keyedElements.set(parent, map);
1840
+ } catch (e) {
1841
+ void e;
1842
+ }
1843
+ return keyedElements.get(parent);
1844
+ } catch (e) {
1845
+ void e;
1846
+ }
2381
1847
  }
1848
+ }
1849
+ }
1850
+ } catch (e) {
1851
+ void e;
1852
+ }
1853
+ } catch (e) {
1854
+ void e;
1855
+ }
1856
+ const finalNodes = [];
1857
+ const usedOldEls = /* @__PURE__ */ new WeakSet();
1858
+ const resolveOldElOnce = (k) => {
1859
+ if (!oldKeyMap) return void 0;
1860
+ const direct = oldKeyMap.get(k);
1861
+ if (direct && !usedOldEls.has(direct)) {
1862
+ usedOldEls.add(direct);
1863
+ return direct;
1864
+ }
1865
+ const s = String(k);
1866
+ const byString = oldKeyMap.get(s);
1867
+ if (byString && !usedOldEls.has(byString)) {
1868
+ usedOldEls.add(byString);
1869
+ return byString;
1870
+ }
1871
+ const n = Number(String(k));
1872
+ if (!Number.isNaN(n)) {
1873
+ const byNum = oldKeyMap.get(n);
1874
+ if (byNum && !usedOldEls.has(byNum)) {
1875
+ usedOldEls.add(byNum);
1876
+ return byNum;
1877
+ }
1878
+ }
1879
+ try {
1880
+ const children = Array.from(parent.children);
1881
+ for (const ch of children) {
1882
+ if (usedOldEls.has(ch)) continue;
1883
+ const attr = ch.getAttribute("data-key");
1884
+ if (attr === s) {
1885
+ usedOldEls.add(ch);
1886
+ return ch;
1887
+ }
1888
+ const numAttr = Number(attr);
1889
+ if (!Number.isNaN(numAttr) && numAttr === k) {
1890
+ usedOldEls.add(ch);
1891
+ return ch;
1892
+ }
1893
+ }
1894
+ } catch (e) {
1895
+ void e;
1896
+ }
1897
+ return void 0;
1898
+ };
1899
+ for (let i = 0; i < newChildren.length; i++) {
1900
+ const child = newChildren[i];
1901
+ if (typeof child === "object" && child !== null && "type" in child) {
1902
+ const childObj = child;
1903
+ const rawKey = childObj.key ?? childObj.props?.key;
1904
+ if (rawKey !== void 0) {
1905
+ const key = typeof rawKey === "symbol" ? String(rawKey) : rawKey;
1906
+ const el = resolveOldElOnce(key);
1907
+ if (el && el.parentElement === parent) {
1908
+ updateElementFromVnode(el, child);
2382
1909
  finalNodes.push(el);
2383
- reused++;
1910
+ newKeyMap.set(key, el);
2384
1911
  continue;
2385
1912
  }
2386
- const dom2 = createDOMNode(vnode);
1913
+ const dom2 = createDOMNode(child);
2387
1914
  if (dom2) {
2388
1915
  finalNodes.push(dom2);
2389
- created++;
1916
+ if (dom2 instanceof Element) newKeyMap.set(key, dom2);
2390
1917
  }
2391
1918
  continue;
2392
1919
  }
2393
1920
  }
2394
- const dom = createDOMNode(vnode);
2395
- if (dom) {
2396
- finalNodes.push(dom);
2397
- created++;
1921
+ try {
1922
+ const existing = parent.children[i];
1923
+ if (existing && (typeof child === "string" || typeof child === "number") && existing.nodeType === 1) {
1924
+ existing.textContent = String(child);
1925
+ finalNodes.push(existing);
1926
+ usedOldEls.add(existing);
1927
+ continue;
1928
+ }
1929
+ if (existing && typeof child === "object" && child !== null && "type" in child && (existing.getAttribute("data-key") === null || existing.getAttribute("data-key") === void 0) && typeof child.type === "string" && existing.tagName.toLowerCase() === String(child.type).toLowerCase()) {
1930
+ updateElementFromVnode(existing, child);
1931
+ finalNodes.push(existing);
1932
+ usedOldEls.add(existing);
1933
+ continue;
1934
+ }
1935
+ try {
1936
+ const avail = Array.from(parent.children).find(
1937
+ (ch) => !usedOldEls.has(ch) && ch.getAttribute("data-key") === null
1938
+ );
1939
+ if (avail) {
1940
+ if (typeof child === "string" || typeof child === "number") {
1941
+ avail.textContent = String(child);
1942
+ } else if (typeof child === "object" && child !== null && "type" in child && typeof child.type === "string" && avail.tagName.toLowerCase() === String(child.type).toLowerCase()) {
1943
+ updateElementFromVnode(avail, child);
1944
+ } else {
1945
+ const dom2 = createDOMNode(child);
1946
+ if (dom2) {
1947
+ finalNodes.push(dom2);
1948
+ continue;
1949
+ }
1950
+ }
1951
+ usedOldEls.add(avail);
1952
+ finalNodes.push(avail);
1953
+ continue;
1954
+ }
1955
+ } catch (e) {
1956
+ void e;
1957
+ }
1958
+ } catch (e) {
1959
+ void e;
1960
+ }
1961
+ const dom = createDOMNode(child);
1962
+ if (dom) finalNodes.push(dom);
1963
+ }
1964
+ if (typeof document === "undefined") return newKeyMap;
1965
+ const fragment = document.createDocumentFragment();
1966
+ for (let i = 0; i < finalNodes.length; i++)
1967
+ fragment.appendChild(finalNodes[i]);
1968
+ try {
1969
+ const existing = Array.from(parent.childNodes);
1970
+ for (const n of existing) {
1971
+ if (n instanceof Element) removeAllListeners(n);
1972
+ cleanupInstanceIfPresent(n);
2398
1973
  }
1974
+ } catch (e) {
1975
+ void e;
2399
1976
  }
2400
- const tBuild = typeof performance !== "undefined" && performance.now ? performance.now() - t0 : 0;
2401
1977
  try {
2402
- const toRemove = Array.from(parent.childNodes).filter(
2403
- (n) => !finalNodes.includes(n)
2404
- );
2405
- for (const n of toRemove) cleanupInstanceIfPresent(n);
1978
+ __ASKR_incCounter("__DOM_REPLACE_COUNT");
1979
+ __ASKR_set("__LAST_DOM_REPLACE_STACK_RECONCILE", new Error().stack);
2406
1980
  } catch (e) {
2407
1981
  void e;
2408
1982
  }
2409
- const fragStart = Date.now();
2410
- const fragment = document.createDocumentFragment();
2411
- for (let i = 0; i < finalNodes.length; i++)
2412
- fragment.appendChild(finalNodes[i]);
2413
1983
  parent.replaceChildren(fragment);
2414
- const tCommit = Date.now() - fragStart;
2415
1984
  keyedElements.delete(parent);
2416
- const stats = {
2417
- n: newChildren.length,
2418
- reused,
2419
- created,
2420
- tBuild,
2421
- tCommit
2422
- };
2423
- return stats;
1985
+ return newKeyMap;
2424
1986
  }
2425
- function updateElementFromVnode(el, vnode, updateChildren = true) {
2426
- if (!_isDOMElement(vnode)) {
2427
- return;
2428
- }
2429
- const props = vnode.props || {};
2430
- if (vnode.key !== void 0) {
2431
- el.setAttribute("data-key", String(vnode.key));
1987
+ var init_reconcile = __esm({
1988
+ "src/renderer/reconcile.ts"() {
1989
+ "use strict";
1990
+ init_dom();
1991
+ init_keyed();
1992
+ init_cleanup();
1993
+ init_fastlane_shared();
1994
+ init_diag();
1995
+ init_fastpath();
2432
1996
  }
2433
- const existingListeners = elementListeners.get(el);
2434
- const desiredEventNames = /* @__PURE__ */ new Set();
2435
- for (const key in props) {
2436
- const value = props[key];
2437
- if (key === "children" || key === "key") continue;
2438
- if (value === void 0 || value === null || value === false) {
2439
- if (key === "class" || key === "className") {
2440
- el.className = "";
2441
- } else if (key.startsWith("on") && key.length > 2) {
2442
- const eventName = key.slice(2).charAt(0).toLowerCase() + key.slice(3).toLowerCase();
2443
- if (existingListeners && existingListeners.has(eventName)) {
2444
- const entry = existingListeners.get(eventName);
2445
- el.removeEventListener(eventName, entry.handler);
2446
- existingListeners.delete(eventName);
2447
- }
2448
- continue;
2449
- } else {
2450
- el.removeAttribute(key);
1997
+ });
1998
+
1999
+ // src/renderer/evaluate.ts
2000
+ function evaluate(node, target, context) {
2001
+ if (!target) return;
2002
+ if (typeof document === "undefined") {
2003
+ if (process.env.NODE_ENV !== "production") {
2004
+ try {
2005
+ console.warn("[Askr] evaluate() called in non-DOM environment; no-op.");
2006
+ } catch (e) {
2007
+ void e;
2451
2008
  }
2452
- continue;
2453
2009
  }
2454
- if (key === "class" || key === "className") {
2455
- el.className = String(value);
2456
- } else if (key === "value" || key === "checked") {
2457
- el[key] = value;
2458
- } else if (key.startsWith("on") && key.length > 2) {
2459
- const eventName = key.slice(2).charAt(0).toLowerCase() + key.slice(3).toLowerCase();
2460
- desiredEventNames.add(eventName);
2461
- const existing = existingListeners?.get(eventName);
2462
- if (existing && existing.original === value) {
2463
- continue;
2464
- }
2465
- if (existing) {
2466
- el.removeEventListener(eventName, existing.handler);
2467
- }
2468
- const wrappedHandler = (event) => {
2469
- globalScheduler.setInHandler(true);
2470
- try {
2471
- value(event);
2472
- } catch (error) {
2473
- logger.error("[Askr] Event handler error:", error);
2474
- } finally {
2475
- globalScheduler.setInHandler(false);
2476
- }
2477
- };
2478
- el.addEventListener(eventName, wrappedHandler);
2479
- if (!elementListeners.has(el)) {
2480
- elementListeners.set(el, /* @__PURE__ */ new Map());
2481
- }
2482
- elementListeners.get(el).set(eventName, {
2483
- handler: wrappedHandler,
2484
- original: value
2485
- });
2486
- } else {
2487
- el.setAttribute(key, String(value));
2488
- }
2489
- }
2490
- if (existingListeners) {
2491
- for (const eventName of existingListeners.keys()) {
2492
- const entry = existingListeners.get(eventName);
2493
- if (!desiredEventNames.has(eventName)) {
2494
- el.removeEventListener(eventName, entry.handler);
2495
- existingListeners.delete(eventName);
2496
- }
2497
- }
2498
- if (existingListeners.size === 0) elementListeners.delete(el);
2499
- }
2500
- if (updateChildren) {
2501
- const children = vnode.children || props.children;
2502
- updateElementChildren(el, children);
2503
- }
2504
- }
2505
- function updateElementChildren(el, children) {
2506
- if (!children) {
2507
- el.textContent = "";
2508
2010
  return;
2509
2011
  }
2510
- if (!Array.isArray(children) && (typeof children === "string" || typeof children === "number")) {
2511
- if (el.childNodes.length === 1 && el.firstChild?.nodeType === 3) {
2512
- el.firstChild.data = String(children);
2513
- } else {
2514
- el.textContent = String(children);
2515
- }
2516
- return;
2517
- }
2518
- if (Array.isArray(children)) {
2519
- updateUnkeyedChildren(el, children);
2520
- return;
2521
- }
2522
- el.textContent = "";
2523
- const dom = createDOMNode(children);
2524
- if (dom) el.appendChild(dom);
2525
- }
2526
- function updateUnkeyedChildren(parent, newChildren) {
2527
- const existing = Array.from(parent.children);
2528
- if (existing.length === 0 && parent.childNodes.length > 0) {
2529
- parent.textContent = "";
2530
- }
2531
- const max = Math.max(existing.length, newChildren.length);
2532
- for (let i = 0; i < max; i++) {
2533
- const current2 = existing[i];
2534
- const next = newChildren[i];
2535
- if (next === void 0 && current2) {
2536
- cleanupInstanceIfPresent(current2);
2012
+ if (context && domRanges.has(context)) {
2013
+ const range = domRanges.get(context);
2014
+ let current2 = range.start.nextSibling;
2015
+ while (current2 && current2 !== range.end) {
2016
+ const next = current2.nextSibling;
2537
2017
  current2.remove();
2538
- continue;
2018
+ current2 = next;
2539
2019
  }
2540
- if (!current2 && next !== void 0) {
2541
- const dom = createDOMNode(next);
2542
- if (dom) parent.appendChild(dom);
2543
- continue;
2020
+ const dom = createDOMNode(node);
2021
+ if (dom) {
2022
+ target.insertBefore(dom, range.end);
2544
2023
  }
2545
- if (!current2 || next === void 0) continue;
2546
- if (typeof next === "string" || typeof next === "number") {
2547
- current2.textContent = String(next);
2548
- } else if (_isDOMElement(next)) {
2549
- if (typeof next.type === "string") {
2550
- if (current2.tagName.toLowerCase() === next.type.toLowerCase()) {
2551
- updateElementFromVnode(current2, next);
2552
- } else {
2553
- const dom = createDOMNode(next);
2554
- if (dom) {
2555
- cleanupInstanceIfPresent(current2);
2556
- parent.replaceChild(dom, current2);
2557
- }
2024
+ } else if (context) {
2025
+ const start = document.createComment("component-start");
2026
+ const end = document.createComment("component-end");
2027
+ target.appendChild(start);
2028
+ target.appendChild(end);
2029
+ domRanges.set(context, { start, end });
2030
+ const dom = createDOMNode(node);
2031
+ if (dom) {
2032
+ target.insertBefore(dom, end);
2033
+ }
2034
+ } else {
2035
+ const vnode = node;
2036
+ const firstChild = target.children[0];
2037
+ if (firstChild && _isDOMElement(vnode) && typeof vnode.type === "string" && firstChild.tagName.toLowerCase() === vnode.type.toLowerCase()) {
2038
+ const vnodeChildren = vnode.children || vnode.props?.children;
2039
+ let isSimpleTextVNode = false;
2040
+ let textContent;
2041
+ if (!Array.isArray(vnodeChildren)) {
2042
+ if (typeof vnodeChildren === "string" || typeof vnodeChildren === "number") {
2043
+ isSimpleTextVNode = true;
2044
+ textContent = String(vnodeChildren);
2558
2045
  }
2559
- } else {
2560
- const dom = createDOMNode(next);
2561
- if (dom) {
2562
- cleanupInstanceIfPresent(current2);
2563
- parent.replaceChild(dom, current2);
2046
+ } else if (vnodeChildren.length === 1) {
2047
+ const child = vnodeChildren[0];
2048
+ if (typeof child === "string" || typeof child === "number") {
2049
+ isSimpleTextVNode = true;
2050
+ textContent = String(child);
2564
2051
  }
2565
2052
  }
2566
- } else {
2567
- const dom = createDOMNode(next);
2568
- if (dom) {
2569
- cleanupInstanceIfPresent(current2);
2570
- parent.replaceChild(dom, current2);
2571
- }
2572
- }
2573
- }
2574
- }
2575
- function createDOMNode(node) {
2576
- if (typeof node === "string") {
2577
- return document.createTextNode(node);
2578
- }
2579
- if (typeof node === "number") {
2580
- return document.createTextNode(String(node));
2581
- }
2582
- if (!node) {
2583
- return null;
2584
- }
2585
- if (Array.isArray(node)) {
2586
- const fragment = document.createDocumentFragment();
2587
- for (let i = 0; i < node.length; i++) {
2588
- const dom = createDOMNode(node[i]);
2589
- if (dom) fragment.appendChild(dom);
2590
- }
2591
- return fragment;
2592
- }
2593
- if (typeof node === "object" && node !== null && "type" in node) {
2594
- const type = node.type;
2595
- const props = node.props || {};
2596
- if (typeof type === "string") {
2597
- const el = document.createElement(type);
2598
- for (const key in props) {
2599
- const value = props[key];
2600
- if (key === "children" || key === "key") continue;
2601
- if (value === void 0 || value === null || value === false) continue;
2602
- if (key.startsWith("on") && key.length > 2) {
2603
- const eventName = key.slice(2).charAt(0).toLowerCase() + key.slice(3).toLowerCase();
2604
- const wrappedHandler = (event) => {
2605
- globalScheduler.setInHandler(true);
2606
- try {
2607
- value(event);
2608
- } catch (error) {
2609
- logger.error("[Askr] Event handler error:", error);
2610
- } finally {
2611
- globalScheduler.setInHandler(false);
2612
- const state2 = globalScheduler.getState();
2613
- if ((state2.queueLength ?? 0) > 0 && !state2.running) {
2614
- queueMicrotask(() => {
2053
+ if (isSimpleTextVNode && firstChild.childNodes.length === 1 && firstChild.firstChild?.nodeType === 3) {
2054
+ firstChild.firstChild.data = textContent;
2055
+ } else {
2056
+ if (vnodeChildren) {
2057
+ if (Array.isArray(vnodeChildren)) {
2058
+ const hasKeys = vnodeChildren.some(
2059
+ (child) => typeof child === "object" && child !== null && "key" in child
2060
+ );
2061
+ if (hasKeys) {
2062
+ let oldKeyMap = keyedElements.get(firstChild);
2063
+ if (!oldKeyMap) {
2064
+ oldKeyMap = /* @__PURE__ */ new Map();
2065
+ try {
2066
+ const children = Array.from(firstChild.children);
2067
+ for (let i = 0; i < children.length; i++) {
2068
+ const ch = children[i];
2069
+ const k = ch.getAttribute("data-key");
2070
+ if (k !== null) {
2071
+ oldKeyMap.set(k, ch);
2072
+ const n = Number(k);
2073
+ if (!Number.isNaN(n)) oldKeyMap.set(n, ch);
2074
+ }
2075
+ }
2076
+ if (oldKeyMap.size > 0)
2077
+ keyedElements.set(firstChild, oldKeyMap);
2078
+ } catch (e) {
2079
+ void e;
2080
+ }
2081
+ }
2082
+ try {
2083
+ if (process.env.ASKR_FORCE_BULK_POSREUSE === "1") {
2615
2084
  try {
2616
- if (!globalScheduler.isExecuting()) globalScheduler.flush();
2085
+ const keyedVnodes = [];
2086
+ for (let i = 0; i < vnodeChildren.length; i++) {
2087
+ const c = vnodeChildren[i];
2088
+ if (_isDOMElement(c) && c.key !== void 0) {
2089
+ keyedVnodes.push({
2090
+ key: c.key,
2091
+ vnode: c
2092
+ });
2093
+ }
2094
+ }
2095
+ if (keyedVnodes.length > 0 && keyedVnodes.length === vnodeChildren.length) {
2096
+ if (process.env.ASKR_FASTPATH_DEBUG === "1" || process.env.ASKR_FASTPATH_DEBUG === "true") {
2097
+ logger.warn(
2098
+ "[Askr][FASTPATH] forced positional bulk keyed reuse (evaluate-level)"
2099
+ );
2100
+ }
2101
+ const stats = performBulkPositionalKeyedTextUpdate(
2102
+ firstChild,
2103
+ keyedVnodes
2104
+ );
2105
+ if (process.env.NODE_ENV !== "production" || process.env.ASKR_FASTPATH_DEBUG === "1") {
2106
+ try {
2107
+ __ASKR_set("__LAST_FASTPATH_STATS", stats);
2108
+ __ASKR_set("__LAST_FASTPATH_COMMIT_COUNT", 1);
2109
+ __ASKR_incCounter("bulkKeyedPositionalForced");
2110
+ } catch (e) {
2111
+ void e;
2112
+ }
2113
+ }
2114
+ try {
2115
+ const map = /* @__PURE__ */ new Map();
2116
+ const children = Array.from(firstChild.children);
2117
+ for (let i = 0; i < children.length; i++) {
2118
+ const el = children[i];
2119
+ const k = el.getAttribute("data-key");
2120
+ if (k !== null) {
2121
+ map.set(k, el);
2122
+ const n = Number(k);
2123
+ if (!Number.isNaN(n)) map.set(n, el);
2124
+ }
2125
+ }
2126
+ keyedElements.set(firstChild, map);
2127
+ } catch (e) {
2128
+ void e;
2129
+ }
2130
+ } else {
2131
+ const newKeyMap = reconcileKeyedChildren(
2132
+ firstChild,
2133
+ vnodeChildren,
2134
+ oldKeyMap
2135
+ );
2136
+ keyedElements.set(firstChild, newKeyMap);
2137
+ }
2617
2138
  } catch (err) {
2618
- setTimeout(() => {
2619
- throw err;
2620
- });
2139
+ if (process.env.ASKR_FASTPATH_DEBUG === "1" || process.env.ASKR_FASTPATH_DEBUG === "true") {
2140
+ logger.warn(
2141
+ "[Askr][FASTPATH] forced bulk path failed, falling back",
2142
+ err
2143
+ );
2144
+ }
2145
+ const newKeyMap = reconcileKeyedChildren(
2146
+ firstChild,
2147
+ vnodeChildren,
2148
+ oldKeyMap
2149
+ );
2150
+ keyedElements.set(firstChild, newKeyMap);
2621
2151
  }
2622
- });
2152
+ } else {
2153
+ const newKeyMap = reconcileKeyedChildren(
2154
+ firstChild,
2155
+ vnodeChildren,
2156
+ oldKeyMap
2157
+ );
2158
+ keyedElements.set(firstChild, newKeyMap);
2159
+ }
2160
+ } catch (e) {
2161
+ void e;
2162
+ const newKeyMap = reconcileKeyedChildren(
2163
+ firstChild,
2164
+ vnodeChildren,
2165
+ oldKeyMap
2166
+ );
2167
+ keyedElements.set(firstChild, newKeyMap);
2168
+ }
2169
+ } else {
2170
+ if (isBulkTextFastPathEligible(firstChild, vnodeChildren)) {
2171
+ const stats = performBulkTextReplace(firstChild, vnodeChildren);
2172
+ if (process.env.NODE_ENV !== "production") {
2173
+ try {
2174
+ __ASKR_set("__LAST_BULK_TEXT_FASTPATH_STATS", stats);
2175
+ __ASKR_incCounter("bulkTextHits");
2176
+ } catch (e) {
2177
+ void e;
2178
+ }
2179
+ }
2180
+ } else {
2181
+ if (process.env.NODE_ENV !== "production") {
2182
+ try {
2183
+ __ASKR_incCounter("bulkTextMisses");
2184
+ } catch (e) {
2185
+ void e;
2186
+ }
2187
+ }
2188
+ updateUnkeyedChildren(firstChild, vnodeChildren);
2189
+ keyedElements.delete(firstChild);
2623
2190
  }
2624
2191
  }
2625
- };
2626
- el.addEventListener(eventName, wrappedHandler);
2627
- if (!elementListeners.has(el)) {
2628
- elementListeners.set(el, /* @__PURE__ */ new Map());
2192
+ } else {
2193
+ firstChild.textContent = "";
2194
+ const dom = createDOMNode(vnodeChildren);
2195
+ if (dom) firstChild.appendChild(dom);
2196
+ keyedElements.delete(firstChild);
2629
2197
  }
2630
- elementListeners.get(el).set(eventName, {
2631
- handler: wrappedHandler,
2632
- original: value
2633
- });
2634
- } else if (key === "class" || key === "className") {
2635
- el.className = String(value);
2636
- } else if (key === "value" || key === "checked") {
2637
- el[key] = value;
2638
- el.setAttribute(key, String(value));
2639
2198
  } else {
2640
- el.setAttribute(key, String(value));
2199
+ firstChild.textContent = "";
2200
+ keyedElements.delete(firstChild);
2641
2201
  }
2642
2202
  }
2643
- const vnodeKey = node.key;
2644
- if (vnodeKey !== void 0) {
2645
- el.setAttribute("data-key", String(vnodeKey));
2646
- }
2647
- const children = props.children || node.children;
2648
- if (children) {
2649
- if (Array.isArray(children)) {
2650
- if (process.env.NODE_ENV !== "production") {
2651
- let hasElements = false;
2652
- let hasKeys = false;
2653
- for (let i = 0; i < children.length; i++) {
2654
- const item = children[i];
2655
- if (typeof item === "object" && item !== null && "type" in item) {
2656
- hasElements = true;
2657
- const itemProps = item.props || {};
2658
- if ("key" in itemProps) {
2659
- hasKeys = true;
2660
- break;
2203
+ updateElementFromVnode(firstChild, vnode, false);
2204
+ } else {
2205
+ target.textContent = "";
2206
+ if (_isDOMElement(vnode) && typeof vnode.type === "string") {
2207
+ const children = vnode.children;
2208
+ if (Array.isArray(children) && children.some(
2209
+ (child) => typeof child === "object" && child !== null && "key" in child
2210
+ )) {
2211
+ const el = document.createElement(vnode.type);
2212
+ target.appendChild(el);
2213
+ const props = vnode.props || {};
2214
+ for (const [key, value] of Object.entries(props)) {
2215
+ if (key === "children" || key === "key") continue;
2216
+ if (value === void 0 || value === null || value === false)
2217
+ continue;
2218
+ if (key.startsWith("on") && key.length > 2) {
2219
+ const eventName = key.slice(2).charAt(0).toLowerCase() + key.slice(3).toLowerCase();
2220
+ const wrappedHandler = (event) => {
2221
+ globalScheduler.setInHandler(true);
2222
+ try {
2223
+ value(event);
2224
+ } catch (error) {
2225
+ logger.error("[Askr] Event handler error:", error);
2226
+ } finally {
2227
+ globalScheduler.setInHandler(false);
2661
2228
  }
2229
+ };
2230
+ const options = eventName === "wheel" || eventName === "scroll" || eventName.startsWith("touch") ? { passive: true } : void 0;
2231
+ if (options !== void 0)
2232
+ el.addEventListener(eventName, wrappedHandler, options);
2233
+ else el.addEventListener(eventName, wrappedHandler);
2234
+ if (!elementListeners.has(el)) {
2235
+ elementListeners.set(el, /* @__PURE__ */ new Map());
2662
2236
  }
2237
+ elementListeners.get(el).set(eventName, {
2238
+ handler: wrappedHandler,
2239
+ original: value,
2240
+ options
2241
+ });
2242
+ continue;
2663
2243
  }
2664
- if (hasElements && !hasKeys) {
2665
- if (typeof console !== "undefined") {
2666
- logger.warn(
2667
- 'Missing keys on dynamic lists. Each child in a list should have a unique "key" prop.'
2668
- );
2669
- }
2244
+ if (key === "class" || key === "className") {
2245
+ el.className = String(value);
2246
+ } else if (key === "value" || key === "checked") {
2247
+ el[key] = value;
2248
+ } else {
2249
+ el.setAttribute(key, String(value));
2670
2250
  }
2671
2251
  }
2672
- for (let i = 0; i < children.length; i++) {
2673
- const dom = createDOMNode(children[i]);
2674
- if (dom) el.appendChild(dom);
2675
- }
2676
- } else {
2677
- const dom = createDOMNode(children);
2678
- if (dom) el.appendChild(dom);
2252
+ const newKeyMap = reconcileKeyedChildren(el, children, void 0);
2253
+ keyedElements.set(el, newKeyMap);
2254
+ return;
2255
+ return;
2679
2256
  }
2680
2257
  }
2681
- return el;
2682
- }
2683
- if (typeof type === "function") {
2684
- const frame = node[CONTEXT_FRAME_SYMBOL];
2685
- const snapshot = frame || getCurrentContextFrame();
2686
- const componentFn = type;
2687
- const isAsync = componentFn.constructor.name === "AsyncFunction";
2688
- if (isAsync) {
2689
- throw new Error(
2690
- "Async components are not supported. Use resource() for async work."
2691
- );
2692
- }
2693
- const vnodeAny = node;
2694
- let childInstance = vnodeAny.__instance;
2695
- if (!childInstance) {
2696
- childInstance = createComponentInstance(
2697
- `comp-${Math.random().toString(36).slice(2, 7)}`,
2698
- componentFn,
2699
- props || {},
2700
- null
2701
- );
2702
- vnodeAny.__instance = childInstance;
2703
- }
2704
- if (snapshot) {
2705
- childInstance.ownerFrame = snapshot;
2706
- }
2707
- const result = withContext(
2708
- snapshot,
2709
- () => renderComponentInline(childInstance)
2710
- );
2711
- if (result instanceof Promise) {
2712
- throw new Error(
2713
- "Async components are not supported. Components must return synchronously."
2714
- );
2715
- }
2716
- const dom = withContext(snapshot, () => createDOMNode(result));
2717
- if (dom instanceof Element) {
2718
- mountInstanceInline(childInstance, dom);
2719
- } else {
2720
- const host = document.createElement("div");
2721
- mountInstanceInline(childInstance, host);
2722
- }
2723
- return dom;
2724
- }
2725
- if (typeof type === "symbol" && (type === Fragment || String(type) === "Symbol(Fragment)")) {
2726
- const fragment = document.createDocumentFragment();
2727
- const children = props.children || node.children;
2728
- if (children) {
2729
- if (Array.isArray(children)) {
2730
- for (let i = 0; i < children.length; i++) {
2731
- const dom = createDOMNode(children[i]);
2732
- if (dom) fragment.appendChild(dom);
2733
- }
2734
- } else {
2735
- const dom = createDOMNode(children);
2736
- if (dom) fragment.appendChild(dom);
2737
- }
2258
+ const dom = createDOMNode(vnode);
2259
+ if (dom) {
2260
+ target.appendChild(dom);
2738
2261
  }
2739
- return fragment;
2740
2262
  }
2741
2263
  }
2742
- return null;
2743
2264
  }
2744
- var domRanges, elementListeners, keyedElements, _reconcilerRecordedParents;
2745
- var init_dom = __esm({
2746
- "src/renderer/dom.ts"() {
2265
+ var domRanges;
2266
+ var init_evaluate = __esm({
2267
+ "src/renderer/evaluate.ts"() {
2747
2268
  "use strict";
2748
2269
  init_scheduler();
2749
- init_fastlane();
2750
2270
  init_logger();
2751
- init_jsx_runtime();
2752
- init_context();
2753
- init_component();
2271
+ init_cleanup();
2272
+ init_keyed();
2273
+ init_reconcile();
2274
+ init_types();
2275
+ init_dom();
2276
+ init_diag();
2754
2277
  domRanges = /* @__PURE__ */ new WeakMap();
2755
- elementListeners = /* @__PURE__ */ new WeakMap();
2756
- keyedElements = /* @__PURE__ */ new WeakMap();
2757
- _reconcilerRecordedParents = /* @__PURE__ */ new WeakSet();
2278
+ }
2279
+ });
2280
+
2281
+ // src/renderer/index.ts
2282
+ var init_renderer = __esm({
2283
+ "src/renderer/index.ts"() {
2284
+ "use strict";
2285
+ init_types();
2286
+ init_cleanup();
2287
+ init_keyed();
2288
+ init_dom();
2289
+ init_evaluate();
2290
+ init_evaluate();
2291
+ init_keyed();
2758
2292
  if (typeof globalThis !== "undefined") {
2759
2293
  const _g = globalThis;
2760
2294
  _g.__ASKR_RENDERER = {
@@ -2767,22 +2301,6 @@ var init_dom = __esm({
2767
2301
  });
2768
2302
 
2769
2303
  // src/runtime/component.ts
2770
- var component_exports = {};
2771
- __export(component_exports, {
2772
- cleanupComponent: () => cleanupComponent,
2773
- createComponentInstance: () => createComponentInstance,
2774
- executeComponent: () => executeComponent,
2775
- finalizeReadSubscriptions: () => finalizeReadSubscriptions,
2776
- getCurrentComponentInstance: () => getCurrentComponentInstance,
2777
- getCurrentInstance: () => getCurrentInstance,
2778
- getNextStateIndex: () => getNextStateIndex,
2779
- getSignal: () => getSignal,
2780
- mountComponent: () => mountComponent,
2781
- mountInstanceInline: () => mountInstanceInline,
2782
- registerMountOperation: () => registerMountOperation,
2783
- renderComponentInline: () => renderComponentInline,
2784
- setCurrentComponentInstance: () => setCurrentComponentInstance
2785
- });
2786
2304
  function createComponentInstance(id, fn, props, target) {
2787
2305
  const instance = {
2788
2306
  id,
@@ -2808,6 +2326,7 @@ function createComponentInstance(id, fn, props, target) {
2808
2326
  ownerFrame: null,
2809
2327
  // Will be set by renderer when vnode is marked
2810
2328
  ssr: false,
2329
+ cleanupStrict: false,
2811
2330
  isRoot: false,
2812
2331
  // Render-tracking (for precise state subscriptions)
2813
2332
  _currentRenderToken: void 0,
@@ -2902,12 +2421,41 @@ function runComponent(instance) {
2902
2421
  }
2903
2422
  globalScheduler.enqueue(() => {
2904
2423
  if (instance.target) {
2424
+ let oldChildren = [];
2905
2425
  try {
2906
2426
  const wasFirstMount = !instance.mounted;
2907
2427
  const oldInstance = currentInstance;
2908
2428
  currentInstance = instance;
2429
+ oldChildren = Array.from(instance.target.childNodes);
2909
2430
  try {
2910
2431
  evaluate(result, instance.target);
2432
+ } catch (e) {
2433
+ try {
2434
+ const newChildren = Array.from(instance.target.childNodes);
2435
+ for (const n of newChildren) {
2436
+ try {
2437
+ cleanupInstancesUnder(n);
2438
+ } catch (err) {
2439
+ logger.warn(
2440
+ "[Askr] error cleaning up failed commit children:",
2441
+ err
2442
+ );
2443
+ }
2444
+ }
2445
+ } catch (_err) {
2446
+ void _err;
2447
+ }
2448
+ try {
2449
+ __ASKR_incCounter("__DOM_REPLACE_COUNT");
2450
+ __ASKR_set(
2451
+ "__LAST_DOM_REPLACE_STACK_COMPONENT_RESTORE",
2452
+ new Error().stack
2453
+ );
2454
+ } catch (e2) {
2455
+ void e2;
2456
+ }
2457
+ instance.target.replaceChildren(...oldChildren);
2458
+ throw e;
2911
2459
  } finally {
2912
2460
  currentInstance = oldInstance;
2913
2461
  }
@@ -2917,7 +2465,35 @@ function runComponent(instance) {
2917
2465
  executeMountOperations(instance);
2918
2466
  }
2919
2467
  } catch (renderError) {
2920
- instance.target.innerHTML = domSnapshot;
2468
+ try {
2469
+ const currentChildren = Array.from(instance.target.childNodes);
2470
+ for (const n of currentChildren) {
2471
+ try {
2472
+ cleanupInstancesUnder(n);
2473
+ } catch (err) {
2474
+ logger.warn(
2475
+ "[Askr] error cleaning up partial children during rollback:",
2476
+ err
2477
+ );
2478
+ }
2479
+ }
2480
+ } catch (_err) {
2481
+ void _err;
2482
+ }
2483
+ try {
2484
+ try {
2485
+ __ASKR_incCounter("__DOM_REPLACE_COUNT");
2486
+ __ASKR_set(
2487
+ "__LAST_DOM_REPLACE_STACK_COMPONENT_ROLLBACK",
2488
+ new Error().stack
2489
+ );
2490
+ } catch (e) {
2491
+ void e;
2492
+ }
2493
+ instance.target.replaceChildren(...oldChildren);
2494
+ } catch {
2495
+ instance.target.innerHTML = domSnapshot;
2496
+ }
2921
2497
  throw renderError;
2922
2498
  }
2923
2499
  }
@@ -2966,21 +2542,24 @@ function executeComponentSync(instance) {
2966
2542
  executionFrame,
2967
2543
  () => instance.fn(instance.props, context)
2968
2544
  );
2969
- if (process.env.NODE_ENV !== "production") {
2970
- const renderTime = Date.now() - renderStartTime;
2971
- if (renderTime > 5) {
2972
- logger.warn(
2973
- `[askr] Slow render detected: ${renderTime}ms. Consider optimizing component performance.`
2974
- );
2975
- }
2545
+ const renderTime = Date.now() - renderStartTime;
2546
+ if (renderTime > 5) {
2547
+ logger.warn(
2548
+ `[askr] Slow render detected: ${renderTime}ms. Consider optimizing component performance.`
2549
+ );
2976
2550
  }
2977
2551
  if (!instance.firstRenderComplete) {
2978
2552
  instance.firstRenderComplete = true;
2979
2553
  }
2980
- if (process.env.NODE_ENV !== "production") {
2981
- for (let i = 0; i < instance.stateValues.length; i++) {
2982
- const state2 = instance.stateValues[i];
2983
- if (state2 && !state2._hasBeenRead) {
2554
+ for (let i = 0; i < instance.stateValues.length; i++) {
2555
+ const state2 = instance.stateValues[i];
2556
+ if (state2 && !state2._hasBeenRead) {
2557
+ try {
2558
+ const name = instance.fn?.name || "<anonymous>";
2559
+ logger.warn(
2560
+ `[askr] Unused state variable detected in ${name} at index ${i}. State should be read during render or removed.`
2561
+ );
2562
+ } catch {
2984
2563
  logger.warn(
2985
2564
  `[askr] Unused state variable detected. State should be read during render or removed.`
2986
2565
  );
@@ -3015,13 +2594,13 @@ function finalizeReadSubscriptions(instance) {
3015
2594
  if (token === void 0) return;
3016
2595
  for (const s of oldSet) {
3017
2596
  if (!newSet.has(s)) {
3018
- const readers = s?._readers;
2597
+ const readers = s._readers;
3019
2598
  if (readers) readers.delete(instance);
3020
2599
  }
3021
2600
  }
3022
2601
  instance.lastRenderToken = token;
3023
2602
  for (const s of newSet) {
3024
- let readers = s?._readers;
2603
+ let readers = s._readers;
3025
2604
  if (!readers) {
3026
2605
  readers = /* @__PURE__ */ new Map();
3027
2606
  s._readers = readers;
@@ -3039,13 +2618,30 @@ function mountComponent(instance) {
3039
2618
  executeComponent(instance);
3040
2619
  }
3041
2620
  function cleanupComponent(instance) {
2621
+ const cleanupErrors = [];
3042
2622
  for (const cleanup of instance.cleanupFns) {
3043
- cleanup();
2623
+ try {
2624
+ cleanup();
2625
+ } catch (err) {
2626
+ if (instance.cleanupStrict) {
2627
+ cleanupErrors.push(err);
2628
+ } else {
2629
+ if (process.env.NODE_ENV !== "production") {
2630
+ logger.warn("[Askr] cleanup function threw:", err);
2631
+ }
2632
+ }
2633
+ }
3044
2634
  }
3045
2635
  instance.cleanupFns = [];
2636
+ if (cleanupErrors.length > 0) {
2637
+ throw new AggregateError(
2638
+ cleanupErrors,
2639
+ `Cleanup failed for component ${instance.id}`
2640
+ );
2641
+ }
3046
2642
  if (instance._lastReadStates) {
3047
2643
  for (const s of instance._lastReadStates) {
3048
- const readers = s?._readers;
2644
+ const readers = s._readers;
3049
2645
  if (readers) readers.delete(instance);
3050
2646
  }
3051
2647
  instance._lastReadStates = /* @__PURE__ */ new Set();
@@ -3056,11 +2652,12 @@ var currentInstance, stateIndex, _globalRenderCounter;
3056
2652
  var init_component = __esm({
3057
2653
  "src/runtime/component.ts"() {
3058
2654
  "use strict";
3059
- init_dom();
3060
2655
  init_scheduler();
3061
2656
  init_context();
3062
2657
  init_logger();
3063
- init_fastlane();
2658
+ init_diag();
2659
+ init_fastlane_shared();
2660
+ init_renderer();
3064
2661
  currentInstance = null;
3065
2662
  stateIndex = 0;
3066
2663
  _globalRenderCounter = 0;
@@ -3387,7 +2984,7 @@ function route(path, handler, namespace) {
3387
2984
  }
3388
2985
  if (registrationLocked) {
3389
2986
  throw new Error(
3390
- "Route registration is locked after app startup. Register routes at module load time before calling createApp()."
2987
+ "Route registration is locked after app startup. Register routes at module load time before calling createIsland()."
3391
2988
  );
3392
2989
  }
3393
2990
  if (typeof handler !== "function") {
@@ -3737,9 +3334,14 @@ function renderChildrenToSink(children, sink, ctx) {
3737
3334
  ctx
3738
3335
  );
3739
3336
  }
3337
+ function isPromiseLike(x) {
3338
+ if (!x || typeof x !== "object") return false;
3339
+ const then = x.then;
3340
+ return typeof then === "function";
3341
+ }
3740
3342
  function executeComponent2(type, props, ctx) {
3741
3343
  const res = type(props ?? {}, { signal: ctx.signal });
3742
- if (res && typeof res === "object" && "then" in res && typeof res.then === "function") {
3344
+ if (isPromiseLike(res)) {
3743
3345
  throwSSRDataMissing();
3744
3346
  }
3745
3347
  return res;
@@ -3809,6 +3411,8 @@ var ssr_exports = {};
3809
3411
  __export(ssr_exports, {
3810
3412
  SSRDataMissingError: () => SSRDataMissingError,
3811
3413
  collectResources: () => collectResources,
3414
+ popSSRStrictPurityGuard: () => popSSRStrictPurityGuard,
3415
+ pushSSRStrictPurityGuard: () => pushSSRStrictPurityGuard,
3812
3416
  renderToStream: () => renderToStream,
3813
3417
  renderToString: () => renderToString,
3814
3418
  renderToStringSync: () => renderToStringSync,
@@ -3816,6 +3420,31 @@ __export(ssr_exports, {
3816
3420
  resolvePlan: () => resolvePlan,
3817
3421
  resolveResources: () => resolveResources
3818
3422
  });
3423
+ function pushSSRStrictPurityGuard() {
3424
+ if (process.env.NODE_ENV === "production") return;
3425
+ __ssrGuardStack.push({
3426
+ random: Reflect.get(Math, "random"),
3427
+ now: Reflect.get(Date, "now")
3428
+ });
3429
+ Reflect.set(Math, "random", () => {
3430
+ throw new Error(
3431
+ "SSR Strict Purity: Math.random is not allowed during synchronous SSR. Use the provided `ssr` context RNG instead."
3432
+ );
3433
+ });
3434
+ Reflect.set(Date, "now", () => {
3435
+ throw new Error(
3436
+ "SSR Strict Purity: Date.now is not allowed during synchronous SSR. Pass timestamps explicitly or use deterministic helpers."
3437
+ );
3438
+ });
3439
+ }
3440
+ function popSSRStrictPurityGuard() {
3441
+ if (process.env.NODE_ENV === "production") return;
3442
+ const prev = __ssrGuardStack.pop();
3443
+ if (prev) {
3444
+ Reflect.set(Math, "random", prev.random);
3445
+ Reflect.set(Date, "now", prev.now);
3446
+ }
3447
+ }
3819
3448
  function escapeText2(text) {
3820
3449
  const cached = escapeCache2.get(text);
3821
3450
  if (cached) return cached;
@@ -3893,20 +3522,9 @@ function renderNodeSync(node, ctx) {
3893
3522
  return `<${typeStr}${attrs}>${childrenHtml}</${typeStr}>`;
3894
3523
  }
3895
3524
  function executeComponentSync2(component, props, ctx) {
3896
- const originalRandom = Math.random;
3897
- const originalDateNow = Date.now;
3898
3525
  try {
3899
3526
  if (process.env.NODE_ENV !== "production") {
3900
- Math.random = () => {
3901
- throw new Error(
3902
- "SSR Strict Purity: Math.random is not allowed during synchronous SSR. Use the provided `ssr` context RNG instead."
3903
- );
3904
- };
3905
- Date.now = () => {
3906
- throw new Error(
3907
- "SSR Strict Purity: Date.now is not allowed during synchronous SSR. Pass timestamps explicitly or use deterministic helpers."
3908
- );
3909
- };
3527
+ pushSSRStrictPurityGuard();
3910
3528
  }
3911
3529
  const prev = getCurrentComponentInstance();
3912
3530
  const temp = createComponentInstance(
@@ -3929,8 +3547,7 @@ function executeComponentSync2(component, props, ctx) {
3929
3547
  setCurrentComponentInstance(prev);
3930
3548
  }
3931
3549
  } finally {
3932
- Math.random = originalRandom;
3933
- Date.now = originalDateNow;
3550
+ if (process.env.NODE_ENV !== "production") popSSRStrictPurityGuard();
3934
3551
  }
3935
3552
  }
3936
3553
  function renderToStringSync(component, props, options) {
@@ -4023,7 +3640,7 @@ function renderToSinkInternal(opts) {
4023
3640
  stopRenderPhase();
4024
3641
  }
4025
3642
  }
4026
- var VOID_ELEMENTS2, escapeCache2;
3643
+ var VOID_ELEMENTS2, escapeCache2, __ssrGuardStack;
4027
3644
  var init_ssr = __esm({
4028
3645
  "src/ssr/index.ts"() {
4029
3646
  "use strict";
@@ -4051,21 +3668,24 @@ var init_ssr = __esm({
4051
3668
  "wbr"
4052
3669
  ]);
4053
3670
  escapeCache2 = /* @__PURE__ */ new Map();
3671
+ __ssrGuardStack = [];
4054
3672
  }
4055
3673
  });
4056
3674
 
4057
3675
  // src/index.ts
4058
3676
  var index_exports = {};
4059
3677
  __export(index_exports, {
4060
- Fragment: () => Fragment,
3678
+ Fragment: () => Fragment2,
4061
3679
  Link: () => Link,
3680
+ Slot: () => Slot,
4062
3681
  cleanupApp: () => cleanupApp,
4063
3682
  clearRoutes: () => clearRoutes,
4064
3683
  collectResources: () => collectResources,
4065
- createApp: () => createApp,
4066
3684
  createIsland: () => createIsland,
4067
3685
  createSPA: () => createSPA,
4068
3686
  defineContext: () => defineContext,
3687
+ definePortal: () => definePortal,
3688
+ derive: () => derive,
4069
3689
  getLoadedNamespaces: () => getLoadedNamespaces,
4070
3690
  getNamespaceRoutes: () => getNamespaceRoutes,
4071
3691
  getRoutes: () => getRoutes,
@@ -4087,6 +3707,7 @@ __export(index_exports, {
4087
3707
  scheduleEventHandler: () => scheduleEventHandler,
4088
3708
  setServerLocation: () => setServerLocation,
4089
3709
  state: () => state,
3710
+ task: () => task,
4090
3711
  unloadNamespace: () => unloadNamespace
4091
3712
  });
4092
3713
  module.exports = __toCommonJS(index_exports);
@@ -4095,7 +3716,7 @@ module.exports = __toCommonJS(index_exports);
4095
3716
  init_scheduler();
4096
3717
  init_component();
4097
3718
  init_invariant();
4098
- init_fastlane();
3719
+ init_fastlane_shared();
4099
3720
  function state(initialValue) {
4100
3721
  const instance = getCurrentInstance();
4101
3722
  if (!instance) {
@@ -4151,7 +3772,7 @@ function createStateCell(initialValue, instance) {
4151
3772
  }
4152
3773
  read._readers = readers;
4153
3774
  read._owner = instance;
4154
- read.set = (newValue) => {
3775
+ read.set = (newValueOrUpdater) => {
4155
3776
  const currentInst = getCurrentInstance();
4156
3777
  if (currentInst !== null && process.env.NODE_ENV !== "production") {
4157
3778
  throw new Error(
@@ -4161,6 +3782,13 @@ function createStateCell(initialValue, instance) {
4161
3782
  if (currentInst !== null && process.env.NODE_ENV === "production") {
4162
3783
  return;
4163
3784
  }
3785
+ let newValue;
3786
+ if (typeof newValueOrUpdater === "function") {
3787
+ const updater = newValueOrUpdater;
3788
+ newValue = updater(value);
3789
+ } else {
3790
+ newValue = newValueOrUpdater;
3791
+ }
4164
3792
  if (Object.is(value, newValue)) return;
4165
3793
  if (isBulkCommitActive2()) {
4166
3794
  value = newValue;
@@ -4191,8 +3819,8 @@ function createStateCell(initialValue, instance) {
4191
3819
  );
4192
3820
  if (ownerShouldEnqueue && !instance.hasPendingUpdate) {
4193
3821
  instance.hasPendingUpdate = true;
4194
- const task = instance._pendingFlushTask;
4195
- if (task) globalScheduler.enqueue(task);
3822
+ const task2 = instance._pendingFlushTask;
3823
+ if (task2) globalScheduler.enqueue(task2);
4196
3824
  else
4197
3825
  globalScheduler.enqueue(() => {
4198
3826
  instance.hasPendingUpdate = false;
@@ -4288,7 +3916,14 @@ var ResourceCell = class {
4288
3916
  this.pending = false;
4289
3917
  this.error = err;
4290
3918
  try {
4291
- logger.error("[Askr] Async resource error:", err);
3919
+ if (this.ownerName) {
3920
+ logger.error(
3921
+ `[Askr] Async resource error in ${this.ownerName}:`,
3922
+ err
3923
+ );
3924
+ } else {
3925
+ logger.error("[Askr] Async resource error:", err);
3926
+ }
4292
3927
  } catch {
4293
3928
  }
4294
3929
  this.notifySubscribers();
@@ -4304,6 +3939,17 @@ var ResourceCell = class {
4304
3939
  }
4305
3940
  };
4306
3941
 
3942
+ // src/shared/derive_cache.ts
3943
+ var deriveCacheMap = /* @__PURE__ */ new WeakMap();
3944
+ function getDeriveCache(instance) {
3945
+ let cache = deriveCacheMap.get(instance);
3946
+ if (!cache) {
3947
+ cache = /* @__PURE__ */ new Map();
3948
+ deriveCacheMap.set(instance, cache);
3949
+ }
3950
+ return cache;
3951
+ }
3952
+
4307
3953
  // src/runtime/operations.ts
4308
3954
  init_context2();
4309
3955
  init_data();
@@ -4376,6 +4022,7 @@ function resource(fn, deps = []) {
4376
4022
  if (!h.cell) {
4377
4023
  const frame = getCurrentContextFrame();
4378
4024
  const cell2 = new ResourceCell(fn, deps, frame);
4025
+ cell2.ownerName = inst.fn?.name || "<anonymous>";
4379
4026
  h.cell = cell2;
4380
4027
  h.snapshot = cell2.snapshot;
4381
4028
  const unsubscribe = cell2.subscribe(() => {
@@ -4438,35 +4085,101 @@ function resource(fn, deps = []) {
4438
4085
  }
4439
4086
  return h.snapshot;
4440
4087
  }
4088
+ function derive(source, map) {
4089
+ if (map === void 0 && typeof source === "function") {
4090
+ const value2 = source();
4091
+ if (value2 == null) return null;
4092
+ const instance2 = getCurrentComponentInstance();
4093
+ if (!instance2) {
4094
+ return value2;
4095
+ }
4096
+ const cache2 = getDeriveCache(instance2);
4097
+ if (cache2.has(value2)) return cache2.get(value2);
4098
+ cache2.set(value2, value2);
4099
+ return value2;
4100
+ }
4101
+ let value;
4102
+ if (typeof source === "function" && !("value" in source)) {
4103
+ value = source();
4104
+ } else {
4105
+ value = source?.value ?? source;
4106
+ }
4107
+ if (value == null) return null;
4108
+ const instance = getCurrentComponentInstance();
4109
+ if (!instance) {
4110
+ return map(value);
4111
+ }
4112
+ const cache = getDeriveCache(instance);
4113
+ if (cache.has(value)) {
4114
+ return cache.get(value);
4115
+ }
4116
+ const result = map(value);
4117
+ cache.set(value, result);
4118
+ return result;
4119
+ }
4120
+ function task(fn) {
4121
+ const ownerIsRoot = getCurrentComponentInstance()?.isRoot ?? false;
4122
+ registerMountOperation(async () => {
4123
+ if (!ownerIsRoot) {
4124
+ throw new Error("[Askr] task() may only be used in root components");
4125
+ }
4126
+ return await fn();
4127
+ });
4128
+ }
4441
4129
 
4442
- // src/app/createApp.ts
4130
+ // src/boot/index.ts
4443
4131
  init_component();
4444
4132
  init_scheduler();
4445
4133
  init_logger();
4446
- init_dom();
4447
4134
  init_navigate();
4135
+ init_renderer();
4448
4136
  var componentIdCounter = 0;
4449
4137
  var instancesByRoot = /* @__PURE__ */ new WeakMap();
4450
4138
  var CLEANUP_SYMBOL = /* @__PURE__ */ Symbol.for("__tempoCleanup__");
4451
- function createApp(config) {
4452
- if (!config || typeof config !== "object") {
4453
- throw new Error("createApp requires a config object");
4454
- }
4455
- if ("routes" in config) {
4456
- throw new Error(
4457
- "The `createApp` API is removed. Use `createSPA({ root, routes })` for routed apps, or `hydrateSPA({ root, routes })` for SSR hydration."
4458
- );
4459
- }
4460
- const appCfg = config;
4461
- createIsland({
4462
- root: appCfg.root,
4463
- component: appCfg.component
4464
- });
4465
- }
4466
4139
  function attachCleanupForRoot(rootElement, instance) {
4467
4140
  rootElement[CLEANUP_SYMBOL] = () => {
4468
- removeAllListeners(rootElement);
4469
- cleanupComponent(instance);
4141
+ const errors = [];
4142
+ try {
4143
+ removeAllListeners(rootElement);
4144
+ } catch (e) {
4145
+ errors.push(e);
4146
+ }
4147
+ try {
4148
+ const descendants = rootElement.querySelectorAll("*");
4149
+ for (const d of Array.from(descendants)) {
4150
+ try {
4151
+ const inst = d.__ASKR_INSTANCE;
4152
+ if (inst) {
4153
+ try {
4154
+ cleanupComponent(inst);
4155
+ } catch (err) {
4156
+ errors.push(err);
4157
+ }
4158
+ try {
4159
+ delete d.__ASKR_INSTANCE;
4160
+ } catch (err) {
4161
+ errors.push(err);
4162
+ }
4163
+ }
4164
+ } catch (err) {
4165
+ errors.push(err);
4166
+ }
4167
+ }
4168
+ } catch (e) {
4169
+ errors.push(e);
4170
+ }
4171
+ try {
4172
+ cleanupComponent(instance);
4173
+ } catch (e) {
4174
+ errors.push(e);
4175
+ }
4176
+ if (errors.length > 0) {
4177
+ if (instance.cleanupStrict) {
4178
+ throw new AggregateError(errors, `cleanup failed for app root`);
4179
+ } else if (process.env.NODE_ENV !== "production") {
4180
+ for (const err of errors) logger.warn("[Askr] cleanup error:", err);
4181
+ }
4182
+ }
4470
4183
  };
4471
4184
  try {
4472
4185
  const descriptor = Object.getOwnPropertyDescriptor(rootElement, "innerHTML") || Object.getOwnPropertyDescriptor(
@@ -4480,8 +4193,20 @@ function attachCleanupForRoot(rootElement, instance) {
4480
4193
  } : void 0,
4481
4194
  set: function(value) {
4482
4195
  if (value === "" && instancesByRoot.get(this) === instance) {
4483
- removeAllListeners(rootElement);
4484
- cleanupComponent(instance);
4196
+ try {
4197
+ removeAllListeners(rootElement);
4198
+ } catch (e) {
4199
+ if (instance.cleanupStrict) throw e;
4200
+ if (process.env.NODE_ENV !== "production")
4201
+ logger.warn("[Askr] cleanup error:", e);
4202
+ }
4203
+ try {
4204
+ cleanupComponent(instance);
4205
+ } catch (e) {
4206
+ if (instance.cleanupStrict) throw e;
4207
+ if (process.env.NODE_ENV !== "production")
4208
+ logger.warn("[Askr] cleanup error:", e);
4209
+ }
4485
4210
  }
4486
4211
  if (descriptor.set) {
4487
4212
  return descriptor.set.call(this, value);
@@ -4493,19 +4218,27 @@ function attachCleanupForRoot(rootElement, instance) {
4493
4218
  } catch {
4494
4219
  }
4495
4220
  }
4496
- function mountOrUpdate(rootElement, componentFn) {
4221
+ function mountOrUpdate(rootElement, componentFn, options) {
4497
4222
  const existingCleanup = rootElement[CLEANUP_SYMBOL];
4498
4223
  if (existingCleanup) existingCleanup();
4499
4224
  let instance = instancesByRoot.get(rootElement);
4500
4225
  if (instance) {
4501
4226
  removeAllListeners(rootElement);
4502
- cleanupComponent(instance);
4227
+ try {
4228
+ cleanupComponent(instance);
4229
+ } catch (e) {
4230
+ if (process.env.NODE_ENV !== "production")
4231
+ logger.warn("[Askr] prior cleanup threw:", e);
4232
+ }
4503
4233
  instance.fn = componentFn;
4504
4234
  instance.evaluationGeneration++;
4505
4235
  instance.mounted = false;
4506
4236
  instance.expectedStateIndices = [];
4507
4237
  instance.firstRenderComplete = false;
4508
4238
  instance.isRoot = true;
4239
+ if (options && typeof options.cleanupStrict === "boolean") {
4240
+ instance.cleanupStrict = options.cleanupStrict;
4241
+ }
4509
4242
  } else {
4510
4243
  const componentId = String(++componentIdCounter);
4511
4244
  instance = createComponentInstance(
@@ -4516,6 +4249,9 @@ function mountOrUpdate(rootElement, componentFn) {
4516
4249
  );
4517
4250
  instancesByRoot.set(rootElement, instance);
4518
4251
  instance.isRoot = true;
4252
+ if (options && typeof options.cleanupStrict === "boolean") {
4253
+ instance.cleanupStrict = options.cleanupStrict;
4254
+ }
4519
4255
  }
4520
4256
  attachCleanupForRoot(rootElement, instance);
4521
4257
  mountComponent(instance);
@@ -4535,7 +4271,9 @@ function createIsland(config) {
4535
4271
  "createIsland does not accept routes; use createSPA for routed apps"
4536
4272
  );
4537
4273
  }
4538
- mountOrUpdate(rootElement, config.component);
4274
+ mountOrUpdate(rootElement, config.component, {
4275
+ cleanupStrict: config.cleanupStrict
4276
+ });
4539
4277
  }
4540
4278
  async function createSPA(config) {
4541
4279
  if (!config || typeof config !== "object") {
@@ -4562,14 +4300,18 @@ async function createSPA(config) {
4562
4300
  `createSPA: no route found for current path (${path}). Mounting empty placeholder; navigation will activate routes when requested.`
4563
4301
  );
4564
4302
  }
4565
- mountOrUpdate(rootElement, () => ({ type: "div", children: [] }));
4303
+ mountOrUpdate(rootElement, () => ({ type: "div", children: [] }), {
4304
+ cleanupStrict: false
4305
+ });
4566
4306
  const instance2 = instancesByRoot.get(rootElement);
4567
4307
  if (!instance2) throw new Error("Internal error: app instance missing");
4568
4308
  registerAppInstance(instance2, path);
4569
4309
  initializeNavigation();
4570
4310
  return;
4571
4311
  }
4572
- mountOrUpdate(rootElement, resolved.handler);
4312
+ mountOrUpdate(rootElement, resolved.handler, {
4313
+ cleanupStrict: false
4314
+ });
4573
4315
  const instance = instancesByRoot.get(rootElement);
4574
4316
  if (!instance) throw new Error("Internal error: app instance missing");
4575
4317
  registerAppInstance(instance, path);
@@ -4622,7 +4364,9 @@ async function hydrateSPA(config) {
4622
4364
  "[Askr] Hydration mismatch detected. Server HTML does not match expected server-render output."
4623
4365
  );
4624
4366
  }
4625
- mountOrUpdate(rootElement, resolved.handler);
4367
+ mountOrUpdate(rootElement, resolved.handler, {
4368
+ cleanupStrict: false
4369
+ });
4626
4370
  const { registerAppInstance: registerAppInstance2, initializeNavigation: initializeNavigation2 } = await Promise.resolve().then(() => (init_navigate(), navigate_exports));
4627
4371
  const instance = instancesByRoot.get(rootElement);
4628
4372
  if (!instance) throw new Error("Internal error: app instance missing");
@@ -4646,13 +4390,6 @@ function hasApp(root) {
4646
4390
 
4647
4391
  // src/index.ts
4648
4392
  init_route();
4649
-
4650
- // src/router/layouts.ts
4651
- function layout(Layout) {
4652
- return (children) => Layout({ children });
4653
- }
4654
-
4655
- // src/index.ts
4656
4393
  init_route();
4657
4394
  init_navigate();
4658
4395
 
@@ -4681,6 +4418,55 @@ function Link({ href, children }) {
4681
4418
  };
4682
4419
  }
4683
4420
 
4421
+ // src/foundations/layout.ts
4422
+ function layout(Layout) {
4423
+ return (children, props) => Layout({ ...props, children });
4424
+ }
4425
+
4426
+ // src/foundations/slot.ts
4427
+ init_logger();
4428
+
4429
+ // src/jsx/index.ts
4430
+ init_types2();
4431
+
4432
+ // src/jsx/utils.ts
4433
+ init_types2();
4434
+ function isElement(value) {
4435
+ return typeof value === "object" && value !== null && value.$$typeof === ELEMENT_TYPE;
4436
+ }
4437
+ function cloneElement(element, props) {
4438
+ return {
4439
+ ...element,
4440
+ props: { ...element.props, ...props }
4441
+ };
4442
+ }
4443
+
4444
+ // src/foundations/slot.ts
4445
+ function Slot(props) {
4446
+ if (props.asChild) {
4447
+ const { children, ...rest } = props;
4448
+ if (isElement(children)) {
4449
+ return cloneElement(children, rest);
4450
+ }
4451
+ logger.warn("<Slot asChild> expects a single JSX element child.");
4452
+ return null;
4453
+ }
4454
+ return { type: Fragment, props: { children: props.children } };
4455
+ }
4456
+
4457
+ // src/foundations/portal.ts
4458
+ function definePortal() {
4459
+ const slot = createPortalSlot();
4460
+ function PortalHost() {
4461
+ return slot.read();
4462
+ }
4463
+ PortalHost.render = function PortalRender(props) {
4464
+ slot.write(props.children);
4465
+ return null;
4466
+ };
4467
+ return PortalHost;
4468
+ }
4469
+
4684
4470
  // src/index.ts
4685
4471
  init_ssr();
4686
4472
  init_jsx_runtime();
@@ -4688,7 +4474,6 @@ init_route();
4688
4474
  init_navigate();
4689
4475
  if (typeof globalThis !== "undefined") {
4690
4476
  const g = globalThis;
4691
- if (!g.createApp) g.createApp = createApp;
4692
4477
  if (!g.createIsland) g.createIsland = createIsland;
4693
4478
  if (!g.createSPA) g.createSPA = createSPA;
4694
4479
  if (!g.hydrateSPA) g.hydrateSPA = hydrateSPA;
@@ -4700,13 +4485,15 @@ if (typeof globalThis !== "undefined") {
4700
4485
  0 && (module.exports = {
4701
4486
  Fragment,
4702
4487
  Link,
4488
+ Slot,
4703
4489
  cleanupApp,
4704
4490
  clearRoutes,
4705
4491
  collectResources,
4706
- createApp,
4707
4492
  createIsland,
4708
4493
  createSPA,
4709
4494
  defineContext,
4495
+ definePortal,
4496
+ derive,
4710
4497
  getLoadedNamespaces,
4711
4498
  getNamespaceRoutes,
4712
4499
  getRoutes,
@@ -4728,6 +4515,7 @@ if (typeof globalThis !== "undefined") {
4728
4515
  scheduleEventHandler,
4729
4516
  setServerLocation,
4730
4517
  state,
4518
+ task,
4731
4519
  unloadNamespace
4732
4520
  });
4733
4521
  //# sourceMappingURL=index.cjs.map