@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
@@ -1,12 +1,11 @@
1
1
  import {
2
2
  Fragment,
3
3
  init_jsx_runtime
4
- } from "./chunk-YNH3D4KW.js";
4
+ } from "./chunk-KR6HG7HF.js";
5
5
  import {
6
6
  __esm,
7
- __export,
8
- __toCommonJS
9
- } from "./chunk-4CV4JOE5.js";
7
+ __export
8
+ } from "./chunk-QECQ2TF6.js";
10
9
 
11
10
  // src/dev/invariant.ts
12
11
  function invariant(condition, message, context) {
@@ -267,17 +266,14 @@ var init_scheduler = __esm({
267
266
  if (this.flushVersion >= target) return Promise.resolve();
268
267
  return new Promise((resolve, reject) => {
269
268
  const timer = setTimeout(() => {
269
+ const ns = globalThis.__ASKR__ || {};
270
270
  const diag = {
271
271
  flushVersion: this.flushVersion,
272
272
  queueLen: this.q.length - this.head,
273
273
  running: this.running,
274
274
  inHandler: this.inHandler,
275
275
  bulk: isBulkCommitActive(),
276
- globals: {
277
- __ASKR_LAST_FASTPATH_STATS: globalThis.__ASKR_LAST_FASTPATH_STATS,
278
- __ASKR_LAST_BULK_TEXT_FASTPATH_STATS: globalThis.__ASKR_LAST_BULK_TEXT_FASTPATH_STATS,
279
- __ASKR_FASTPATH_COUNTERS: globalThis.__ASKR_FASTPATH_COUNTERS
280
- }
276
+ namespace: ns
281
277
  };
282
278
  reject(
283
279
  new Error(
@@ -354,248 +350,6 @@ var init_scheduler = __esm({
354
350
  }
355
351
  });
356
352
 
357
- // src/runtime/fastlane.ts
358
- function enterBulkCommit() {
359
- _bulkCommitActive = true;
360
- _appliedParents = /* @__PURE__ */ new WeakSet();
361
- try {
362
- const cleared = globalScheduler.clearPendingSyncTasks?.() ?? 0;
363
- if (process.env.NODE_ENV !== "production") {
364
- const _g = globalThis;
365
- _g.__ASKR_FASTLANE_CLEARED_TASKS = cleared;
366
- }
367
- } catch (err) {
368
- if (process.env.NODE_ENV !== "production") throw err;
369
- }
370
- }
371
- function exitBulkCommit() {
372
- _bulkCommitActive = false;
373
- _appliedParents = null;
374
- }
375
- function isBulkCommitActive2() {
376
- return _bulkCommitActive;
377
- }
378
- function markFastPathApplied(parent) {
379
- if (!_appliedParents) return;
380
- try {
381
- _appliedParents.add(parent);
382
- } catch (e) {
383
- void e;
384
- }
385
- }
386
- function isFastPathApplied(parent) {
387
- return !!(_appliedParents && _appliedParents.has(parent));
388
- }
389
- function classifyUpdate(instance, result) {
390
- if (!result || typeof result !== "object" || !("type" in result))
391
- return { useFastPath: false, reason: "not-vnode" };
392
- const vnode = result;
393
- if (vnode == null || typeof vnode.type !== "string")
394
- return { useFastPath: false, reason: "not-intrinsic" };
395
- const parent = instance.target;
396
- if (!parent) return { useFastPath: false, reason: "no-root" };
397
- const firstChild = parent.children[0];
398
- if (!firstChild) return { useFastPath: false, reason: "no-first-child" };
399
- if (firstChild.tagName.toLowerCase() !== String(vnode.type).toLowerCase())
400
- return { useFastPath: false, reason: "root-tag-mismatch" };
401
- const children = vnode.children || vnode.props?.children;
402
- if (!Array.isArray(children))
403
- return { useFastPath: false, reason: "no-children-array" };
404
- for (let i = 0; i < children.length; i++) {
405
- const c = children[i];
406
- if (typeof c === "object" && c !== null && "type" in c && typeof c.type === "function") {
407
- return { useFastPath: false, reason: "component-child-present" };
408
- }
409
- }
410
- if (instance.mountOperations.length > 0)
411
- return { useFastPath: false, reason: "pending-mounts" };
412
- const oldKeyMap = getKeyMapForElement(firstChild);
413
- const decision = isKeyedReorderFastPathEligible(
414
- firstChild,
415
- children,
416
- oldKeyMap
417
- );
418
- if (!decision.useFastPath || decision.totalKeyed < 128)
419
- return { ...decision, useFastPath: false, reason: "renderer-declined" };
420
- return { ...decision, useFastPath: true };
421
- }
422
- function commitReorderOnly(instance, result) {
423
- const evaluate2 = globalThis.__ASKR_RENDERER?.evaluate;
424
- if (typeof evaluate2 !== "function") {
425
- logger.warn(
426
- "[Tempo][FASTPATH][DEV] renderer.evaluate not available; declining fast-lane"
427
- );
428
- return false;
429
- }
430
- const schedBefore = process.env.NODE_ENV !== "production" ? globalScheduler.getState() : null;
431
- enterBulkCommit();
432
- try {
433
- globalScheduler.runWithSyncProgress(() => {
434
- evaluate2(result, instance.target);
435
- try {
436
- const comp = (init_component(), __toCommonJS(component_exports));
437
- if (typeof comp?.finalizeReadSubscriptions === "function") {
438
- try {
439
- comp.finalizeReadSubscriptions(instance);
440
- } catch (e) {
441
- if (process.env.NODE_ENV !== "production") throw e;
442
- }
443
- }
444
- } catch (e) {
445
- void e;
446
- }
447
- });
448
- try {
449
- const clearedAfter = globalScheduler.clearPendingSyncTasks?.() ?? 0;
450
- if (process.env.NODE_ENV !== "production") {
451
- const _g = globalThis;
452
- _g.__ASKR_FASTLANE_CLEARED_AFTER = clearedAfter;
453
- }
454
- } catch (err) {
455
- if (process.env.NODE_ENV !== "production") throw err;
456
- }
457
- if (process.env.NODE_ENV !== "production") {
458
- const _g = globalThis;
459
- const commitCount = _g.__ASKR_LAST_FASTPATH_COMMIT_COUNT ?? 0;
460
- const invariants = {
461
- commitCount,
462
- mountOps: instance.mountOperations.length,
463
- cleanupFns: instance.cleanupFns.length
464
- };
465
- _g.__ASKR_LAST_FASTLANE_INVARIANTS = invariants;
466
- if (commitCount !== 1) {
467
- throw new Error(
468
- "Fast-lane invariant violated: expected exactly one DOM commit during reorder-only commit"
469
- );
470
- }
471
- if (invariants.mountOps > 0) {
472
- throw new Error(
473
- "Fast-lane invariant violated: mount operations were registered during bulk commit"
474
- );
475
- }
476
- if (invariants.cleanupFns > 0) {
477
- throw new Error(
478
- "Fast-lane invariant violated: cleanup functions were added during bulk commit"
479
- );
480
- }
481
- const schedAfter = globalScheduler.getState();
482
- if (schedBefore && schedAfter && // Only fail if outstanding tasks increased — consuming existing tasks is allowed
483
- schedAfter.taskCount > schedBefore.taskCount) {
484
- try {
485
- console.error(
486
- "[FASTLANE] schedBefore, schedAfter",
487
- schedBefore,
488
- schedAfter
489
- );
490
- console.error(
491
- "[FASTLANE] enqueue logs",
492
- globalThis.__ASKR_ENQUEUE_LOGS
493
- );
494
- } catch (e) {
495
- void e;
496
- }
497
- throw new Error(
498
- "Fast-lane invariant violated: scheduler enqueued leftover work during bulk commit"
499
- );
500
- }
501
- let finalState = globalScheduler.getState();
502
- const executing = globalScheduler.isExecuting();
503
- const outstandingAfter = Math.max(
504
- 0,
505
- finalState.taskCount - (executing ? 1 : 0)
506
- );
507
- if (outstandingAfter !== 0) {
508
- if (process.env.NODE_ENV !== "production") {
509
- let attempts = 0;
510
- while (attempts < 5) {
511
- const cleared = globalScheduler.clearPendingSyncTasks?.() ?? 0;
512
- if (cleared === 0) break;
513
- attempts++;
514
- }
515
- finalState = globalScheduler.getState();
516
- const outstandingAfter2 = Math.max(
517
- 0,
518
- finalState.taskCount - (globalScheduler.isExecuting() ? 1 : 0)
519
- );
520
- if (outstandingAfter2 !== 0) {
521
- try {
522
- const _g2 = globalThis;
523
- console.error(
524
- "[FASTLANE] Post-commit enqueue logs:",
525
- _g2.__ASKR_ENQUEUE_LOGS
526
- );
527
- console.error(
528
- "[FASTLANE] Cleared counts:",
529
- _g2.__ASKR_FASTLANE_CLEARED_TASKS,
530
- _g2.__ASKR_FASTLANE_CLEARED_AFTER
531
- );
532
- } catch (err) {
533
- void err;
534
- }
535
- throw new Error(
536
- `Fast-lane invariant violated: scheduler has ${finalState.taskCount} pending task(s) after commit`
537
- );
538
- }
539
- } else {
540
- globalScheduler.clearPendingSyncTasks?.();
541
- }
542
- }
543
- }
544
- return true;
545
- } finally {
546
- exitBulkCommit();
547
- if (process.env.NODE_ENV !== "production") {
548
- try {
549
- const _g = globalThis;
550
- _g.__ASKR_FASTLANE_BULK_FLAG_CHECK = isBulkCommitActive2();
551
- } catch (e) {
552
- void e;
553
- }
554
- }
555
- }
556
- if (process.env.NODE_ENV !== "production") {
557
- const _g = globalThis;
558
- if (_g.__ASKR_FASTLANE_BULK_FLAG_CHECK) {
559
- delete _g.__ASKR_FASTLANE_BULK_FLAG_CHECK;
560
- throw new Error(
561
- "Fast-lane invariant violated: bulk commit flag still set after commit"
562
- );
563
- }
564
- }
565
- }
566
- function tryRuntimeFastLaneSync(instance, result) {
567
- const cls = classifyUpdate(instance, result);
568
- if (!cls.useFastPath) return false;
569
- try {
570
- return commitReorderOnly(instance, result);
571
- } catch (err) {
572
- if (process.env.NODE_ENV !== "production") throw err;
573
- return false;
574
- }
575
- }
576
- var _bulkCommitActive, _appliedParents;
577
- var init_fastlane = __esm({
578
- "src/runtime/fastlane.ts"() {
579
- "use strict";
580
- init_scheduler();
581
- init_logger();
582
- init_dom();
583
- _bulkCommitActive = false;
584
- _appliedParents = null;
585
- if (typeof globalThis !== "undefined") {
586
- const _g = globalThis;
587
- _g.__ASKR_FASTLANE = {
588
- isBulkCommitActive: isBulkCommitActive2,
589
- enterBulkCommit,
590
- exitBulkCommit,
591
- tryRuntimeFastLaneSync,
592
- markFastPathApplied,
593
- isFastPathApplied
594
- };
595
- }
596
- }
597
- });
598
-
599
353
  // src/runtime/context.ts
600
354
  function withContext(frame, fn) {
601
355
  const oldFrame = currentContextFrame;
@@ -621,9 +375,10 @@ function defineContext(defaultValue) {
621
375
  key,
622
376
  defaultValue,
623
377
  Scope: (props) => {
378
+ const value = props.value;
624
379
  return {
625
380
  type: ContextScopeComponent,
626
- props: { key, value: props.value, children: props.children }
381
+ props: { key, value, children: props.children }
627
382
  };
628
383
  }
629
384
  };
@@ -659,29 +414,29 @@ function ContextScopeComponent(props) {
659
414
  parent: parentFrame,
660
415
  values: /* @__PURE__ */ new Map([[key, value]])
661
416
  };
417
+ function createFunctionChildInvoker(fn, frame, owner) {
418
+ return {
419
+ type: ContextFunctionChildInvoker,
420
+ props: { fn, __frame: frame, __owner: owner }
421
+ };
422
+ }
662
423
  if (Array.isArray(children)) {
663
424
  return children.map((child) => {
664
425
  if (typeof child === "function") {
665
- return {
666
- type: ContextFunctionChildInvoker,
667
- props: {
668
- fn: child,
669
- __frame: newFrame,
670
- __owner: getCurrentComponentInstance()
671
- }
672
- };
426
+ return createFunctionChildInvoker(
427
+ child,
428
+ newFrame,
429
+ getCurrentComponentInstance()
430
+ );
673
431
  }
674
432
  return markWithFrame(child, newFrame);
675
433
  });
676
434
  } else if (typeof children === "function") {
677
- return {
678
- type: ContextFunctionChildInvoker,
679
- props: {
680
- fn: children,
681
- __frame: newFrame,
682
- __owner: getCurrentComponentInstance()
683
- }
684
- };
435
+ return createFunctionChildInvoker(
436
+ children,
437
+ newFrame,
438
+ getCurrentComponentInstance()
439
+ );
685
440
  } else if (children) {
686
441
  return markWithFrame(children, newFrame);
687
442
  }
@@ -725,22 +480,126 @@ var init_context = __esm({
725
480
  }
726
481
  });
727
482
 
728
- // src/renderer/dom.ts
729
- function cleanupInstanceIfPresent(node) {
483
+ // src/renderer/diag/index.ts
484
+ function getDiagMap() {
485
+ try {
486
+ const root = globalThis;
487
+ if (!root.__ASKR_DIAG) root.__ASKR_DIAG = {};
488
+ return root.__ASKR_DIAG;
489
+ } catch (e) {
490
+ void e;
491
+ return {};
492
+ }
493
+ }
494
+ function __ASKR_set(key, value) {
495
+ try {
496
+ const g = getDiagMap();
497
+ g[key] = value;
498
+ try {
499
+ const root = globalThis;
500
+ try {
501
+ const ns = root.__ASKR__ || (root.__ASKR__ = {});
502
+ try {
503
+ ns[key] = value;
504
+ } catch (e) {
505
+ void e;
506
+ }
507
+ } catch (e) {
508
+ void e;
509
+ }
510
+ } catch (e) {
511
+ void e;
512
+ }
513
+ } catch (e) {
514
+ void e;
515
+ }
516
+ }
517
+ function __ASKR_incCounter(key) {
518
+ try {
519
+ const g = getDiagMap();
520
+ const prev = typeof g[key] === "number" ? g[key] : 0;
521
+ const next = prev + 1;
522
+ g[key] = next;
523
+ try {
524
+ const root = globalThis;
525
+ const ns = root.__ASKR__ || (root.__ASKR__ = {});
526
+ try {
527
+ const nsPrev = typeof ns[key] === "number" ? ns[key] : 0;
528
+ ns[key] = nsPrev + 1;
529
+ } catch (e) {
530
+ void e;
531
+ }
532
+ } catch (e) {
533
+ void e;
534
+ }
535
+ } catch (e) {
536
+ void e;
537
+ }
538
+ }
539
+ var init_diag = __esm({
540
+ "src/renderer/diag/index.ts"() {
541
+ "use strict";
542
+ }
543
+ });
544
+
545
+ // src/runtime/fastlane-shared.ts
546
+ function isBulkCommitActive2() {
547
+ return _bulkCommitActive;
548
+ }
549
+ function markFastPathApplied(parent) {
550
+ if (!_appliedParents) return;
551
+ try {
552
+ _appliedParents.add(parent);
553
+ } catch (e) {
554
+ void e;
555
+ }
556
+ }
557
+ var _bulkCommitActive, _appliedParents;
558
+ var init_fastlane_shared = __esm({
559
+ "src/runtime/fastlane-shared.ts"() {
560
+ "use strict";
561
+ _bulkCommitActive = false;
562
+ _appliedParents = null;
563
+ }
564
+ });
565
+
566
+ // src/renderer/types.ts
567
+ function _isDOMElement(node) {
568
+ return typeof node === "object" && node !== null && "type" in node;
569
+ }
570
+ var init_types = __esm({
571
+ "src/renderer/types.ts"() {
572
+ "use strict";
573
+ }
574
+ });
575
+
576
+ // src/renderer/cleanup.ts
577
+ function cleanupInstanceIfPresent(node, opts) {
730
578
  if (!node) return;
731
579
  if (!(node instanceof Element)) return;
580
+ const errors = [];
732
581
  try {
733
582
  const inst = node.__ASKR_INSTANCE;
734
583
  if (inst) {
735
- cleanupComponent(inst);
584
+ try {
585
+ cleanupComponent(inst);
586
+ } catch (err) {
587
+ if (opts?.strict) errors.push(err);
588
+ else if (process.env.NODE_ENV !== "production")
589
+ logger.warn("[Askr] cleanupComponent failed:", err);
590
+ }
736
591
  try {
737
592
  delete node.__ASKR_INSTANCE;
738
593
  } catch (e) {
739
- void e;
594
+ if (opts?.strict) errors.push(e);
595
+ else void e;
740
596
  }
741
597
  }
742
598
  } catch (err) {
743
- void err;
599
+ if (opts?.strict) errors.push(err);
600
+ else if (process.env.NODE_ENV !== "production") {
601
+ logger.warn("[Askr] cleanupInstanceIfPresent failed:", err);
602
+ }
744
603
  }
745
604
  try {
746
605
  const descendants = node.querySelectorAll("*");
@@ -748,32 +607,57 @@ function cleanupInstanceIfPresent(node) {
748
607
  try {
749
608
  const inst = d.__ASKR_INSTANCE;
750
609
  if (inst) {
751
- cleanupComponent(inst);
610
+ try {
611
+ cleanupComponent(inst);
612
+ } catch (err) {
613
+ if (opts?.strict) errors.push(err);
614
+ else if (process.env.NODE_ENV !== "production") {
615
+ logger.warn(
616
+ "[Askr] cleanupInstanceIfPresent descendant cleanup failed:",
617
+ err
618
+ );
619
+ }
620
+ }
752
621
  try {
753
622
  delete d.__ASKR_INSTANCE;
754
623
  } catch (e) {
755
- void e;
624
+ if (opts?.strict) errors.push(e);
625
+ else void e;
756
626
  }
757
627
  }
758
628
  } catch (err) {
759
- void err;
629
+ if (opts?.strict) errors.push(err);
630
+ else if (process.env.NODE_ENV !== "production") {
631
+ logger.warn(
632
+ "[Askr] cleanupInstanceIfPresent descendant cleanup failed:",
633
+ err
634
+ );
635
+ }
760
636
  }
761
637
  }
762
638
  } catch (err) {
763
- void err;
639
+ if (opts?.strict) errors.push(err);
640
+ else if (process.env.NODE_ENV !== "production") {
641
+ logger.warn(
642
+ "[Askr] cleanupInstanceIfPresent descendant query failed:",
643
+ err
644
+ );
645
+ }
646
+ }
647
+ if (errors.length > 0) {
648
+ throw new AggregateError(errors, "cleanupInstanceIfPresent failed");
764
649
  }
765
650
  }
766
- function _isDOMElement(node) {
767
- return typeof node === "object" && node !== null && "type" in node;
768
- }
769
- function getKeyMapForElement(el) {
770
- return keyedElements.get(el);
651
+ function cleanupInstancesUnder(node, opts) {
652
+ cleanupInstanceIfPresent(node, opts);
771
653
  }
772
654
  function removeElementListeners(element) {
773
655
  const map = elementListeners.get(element);
774
656
  if (map) {
775
657
  for (const [eventName, entry] of map) {
776
- element.removeEventListener(eventName, entry.handler);
658
+ if (entry.options !== void 0)
659
+ element.removeEventListener(eventName, entry.handler, entry.options);
660
+ else element.removeEventListener(eventName, entry.handler);
777
661
  }
778
662
  elementListeners.delete(element);
779
663
  }
@@ -786,262 +670,42 @@ function removeAllListeners(root) {
786
670
  removeElementListeners(children[i]);
787
671
  }
788
672
  }
789
- function evaluate(node, target, context) {
790
- if (!target) return;
791
- if (context && domRanges.has(context)) {
792
- const range = domRanges.get(context);
793
- let current = range.start.nextSibling;
794
- while (current && current !== range.end) {
795
- const next = current.nextSibling;
796
- current.remove();
797
- current = next;
673
+ var elementListeners;
674
+ var init_cleanup = __esm({
675
+ "src/renderer/cleanup.ts"() {
676
+ "use strict";
677
+ init_component();
678
+ init_logger();
679
+ elementListeners = /* @__PURE__ */ new WeakMap();
680
+ }
681
+ });
682
+
683
+ // src/renderer/keyed.ts
684
+ function getKeyMapForElement(el) {
685
+ return keyedElements.get(el);
686
+ }
687
+ function isKeyedReorderFastPathEligible(parent, newChildren, oldKeyMap) {
688
+ const keyedVnodes = [];
689
+ for (let i = 0; i < newChildren.length; i++) {
690
+ const child = newChildren[i];
691
+ if (typeof child === "object" && child !== null && "type" in child) {
692
+ const childObj = child;
693
+ if (childObj.key !== void 0) {
694
+ keyedVnodes.push({
695
+ key: childObj.key,
696
+ vnode: child
697
+ });
698
+ }
798
699
  }
799
- const dom = createDOMNode(node);
800
- if (dom) {
801
- target.insertBefore(dom, range.end);
802
- }
803
- } else if (context) {
804
- const start = document.createComment("component-start");
805
- const end = document.createComment("component-end");
806
- target.appendChild(start);
807
- target.appendChild(end);
808
- domRanges.set(context, { start, end });
809
- const dom = createDOMNode(node);
810
- if (dom) {
811
- target.insertBefore(dom, end);
812
- }
813
- } else {
814
- const vnode = node;
815
- const firstChild = target.children[0];
816
- if (firstChild && _isDOMElement(vnode) && typeof vnode.type === "string" && firstChild.tagName.toLowerCase() === vnode.type.toLowerCase()) {
817
- const vnodeChildren = vnode.children || vnode.props?.children;
818
- let isSimpleTextVNode = false;
819
- let textContent;
820
- if (!Array.isArray(vnodeChildren)) {
821
- if (typeof vnodeChildren === "string" || typeof vnodeChildren === "number") {
822
- isSimpleTextVNode = true;
823
- textContent = String(vnodeChildren);
824
- }
825
- } else if (vnodeChildren.length === 1) {
826
- const child = vnodeChildren[0];
827
- if (typeof child === "string" || typeof child === "number") {
828
- isSimpleTextVNode = true;
829
- textContent = String(child);
830
- }
831
- }
832
- if (isSimpleTextVNode && firstChild.childNodes.length === 1 && firstChild.firstChild?.nodeType === 3) {
833
- firstChild.firstChild.data = textContent;
834
- } else {
835
- if (vnodeChildren) {
836
- if (Array.isArray(vnodeChildren)) {
837
- const hasKeys = vnodeChildren.some(
838
- (child) => typeof child === "object" && child !== null && "key" in child
839
- );
840
- if (hasKeys) {
841
- let oldKeyMap = keyedElements.get(firstChild);
842
- if (!oldKeyMap) {
843
- oldKeyMap = /* @__PURE__ */ new Map();
844
- }
845
- try {
846
- if (process.env.ASKR_FORCE_BULK_POSREUSE === "1") {
847
- try {
848
- const keyedVnodes = [];
849
- for (let i = 0; i < vnodeChildren.length; i++) {
850
- const c = vnodeChildren[i];
851
- if (_isDOMElement(c) && c.key !== void 0) {
852
- keyedVnodes.push({
853
- key: c.key,
854
- vnode: c
855
- });
856
- }
857
- }
858
- if (keyedVnodes.length > 0 && keyedVnodes.length === vnodeChildren.length) {
859
- logger.warn(
860
- "[Askr][FASTPATH] forced positional bulk keyed reuse (evaluate-level)"
861
- );
862
- const stats = performBulkPositionalKeyedTextUpdate(
863
- firstChild,
864
- keyedVnodes
865
- );
866
- if (process.env.NODE_ENV !== "production" || process.env.ASKR_FASTPATH_DEBUG === "1") {
867
- try {
868
- const gl = globalThis;
869
- gl.__ASKR_LAST_FASTPATH_STATS = stats;
870
- gl.__ASKR_LAST_FASTPATH_COMMIT_COUNT = 1;
871
- const counters = gl.__ASKR_FASTPATH_COUNTERS || {};
872
- counters.bulkKeyedPositionalForced = (counters.bulkKeyedPositionalForced || 0) + 1;
873
- gl.__ASKR_FASTPATH_COUNTERS = counters;
874
- } catch (e) {
875
- void e;
876
- }
877
- }
878
- try {
879
- const map = /* @__PURE__ */ new Map();
880
- const children = Array.from(firstChild.children);
881
- for (let i = 0; i < children.length; i++) {
882
- const el = children[i];
883
- const k = el.getAttribute("data-key");
884
- if (k !== null) {
885
- map.set(k, el);
886
- const n = Number(k);
887
- if (!Number.isNaN(n)) map.set(n, el);
888
- }
889
- }
890
- keyedElements.set(firstChild, map);
891
- } catch (e) {
892
- void e;
893
- }
894
- } else {
895
- const newKeyMap = reconcileKeyedChildren(
896
- firstChild,
897
- vnodeChildren,
898
- oldKeyMap
899
- );
900
- keyedElements.set(firstChild, newKeyMap);
901
- }
902
- } catch (err) {
903
- logger.warn(
904
- "[Askr][FASTPATH] forced bulk path failed, falling back",
905
- err
906
- );
907
- const newKeyMap = reconcileKeyedChildren(
908
- firstChild,
909
- vnodeChildren,
910
- oldKeyMap
911
- );
912
- keyedElements.set(firstChild, newKeyMap);
913
- }
914
- } else {
915
- const newKeyMap = reconcileKeyedChildren(
916
- firstChild,
917
- vnodeChildren,
918
- oldKeyMap
919
- );
920
- keyedElements.set(firstChild, newKeyMap);
921
- }
922
- } catch (e) {
923
- void e;
924
- const newKeyMap = reconcileKeyedChildren(
925
- firstChild,
926
- vnodeChildren,
927
- oldKeyMap
928
- );
929
- keyedElements.set(firstChild, newKeyMap);
930
- }
931
- } else {
932
- if (isBulkTextFastPathEligible(firstChild, vnodeChildren)) {
933
- const stats = performBulkTextReplace(firstChild, vnodeChildren);
934
- if (process.env.NODE_ENV !== "production") {
935
- try {
936
- const gl = globalThis;
937
- gl.__ASKR_LAST_BULK_TEXT_FASTPATH_STATS = stats;
938
- const counters = gl.__ASKR_FASTPATH_COUNTERS || {};
939
- counters.bulkTextHits = (counters.bulkTextHits || 0) + 1;
940
- gl.__ASKR_FASTPATH_COUNTERS = counters;
941
- } catch (e) {
942
- void e;
943
- }
944
- }
945
- } else {
946
- if (process.env.NODE_ENV !== "production") {
947
- try {
948
- const gl = globalThis;
949
- const counters = gl.__ASKR_FASTPATH_COUNTERS || {};
950
- counters.bulkTextMisses = (counters.bulkTextMisses || 0) + 1;
951
- gl.__ASKR_FASTPATH_COUNTERS = counters;
952
- } catch (e) {
953
- void e;
954
- }
955
- }
956
- updateUnkeyedChildren(firstChild, vnodeChildren);
957
- keyedElements.delete(firstChild);
958
- }
959
- }
960
- } else {
961
- firstChild.textContent = "";
962
- const dom = createDOMNode(vnodeChildren);
963
- if (dom) firstChild.appendChild(dom);
964
- keyedElements.delete(firstChild);
965
- }
966
- } else {
967
- firstChild.textContent = "";
968
- keyedElements.delete(firstChild);
969
- }
970
- }
971
- updateElementFromVnode(firstChild, vnode, false);
972
- } else {
973
- target.textContent = "";
974
- if (_isDOMElement(vnode) && typeof vnode.type === "string") {
975
- const children = vnode.children;
976
- if (Array.isArray(children) && children.some(
977
- (child) => typeof child === "object" && child !== null && "key" in child
978
- )) {
979
- const el = document.createElement(vnode.type);
980
- target.appendChild(el);
981
- const props = vnode.props || {};
982
- for (const [key, value] of Object.entries(props)) {
983
- if (key === "children" || key === "key") continue;
984
- if (value === void 0 || value === null || value === false)
985
- continue;
986
- if (key.startsWith("on") && key.length > 2) {
987
- const eventName = key.slice(2).charAt(0).toLowerCase() + key.slice(3).toLowerCase();
988
- const wrappedHandler = (event) => {
989
- globalScheduler.setInHandler(true);
990
- try {
991
- value(event);
992
- } catch (error) {
993
- logger.error("[Askr] Event handler error:", error);
994
- } finally {
995
- globalScheduler.setInHandler(false);
996
- }
997
- };
998
- el.addEventListener(eventName, wrappedHandler);
999
- if (!elementListeners.has(el)) {
1000
- elementListeners.set(el, /* @__PURE__ */ new Map());
1001
- }
1002
- elementListeners.get(el).set(eventName, {
1003
- handler: wrappedHandler,
1004
- original: value
1005
- });
1006
- continue;
1007
- }
1008
- if (key === "class" || key === "className") {
1009
- el.className = String(value);
1010
- } else if (key === "value" || key === "checked") {
1011
- el[key] = value;
1012
- } else {
1013
- el.setAttribute(key, String(value));
1014
- }
1015
- }
1016
- const newKeyMap = reconcileKeyedChildren(el, children, void 0);
1017
- keyedElements.set(el, newKeyMap);
1018
- return;
1019
- return;
1020
- }
1021
- }
1022
- const dom = createDOMNode(vnode);
1023
- if (dom) {
1024
- target.appendChild(dom);
1025
- }
1026
- }
1027
- }
1028
- }
1029
- function isKeyedReorderFastPathEligible(parent, newChildren, oldKeyMap) {
1030
- const keyedVnodes = [];
1031
- for (let i = 0; i < newChildren.length; i++) {
1032
- const child = newChildren[i];
1033
- if (_isDOMElement(child) && child.key !== void 0) {
1034
- keyedVnodes.push({ key: child.key, vnode: child });
1035
- }
1036
- }
1037
- const totalKeyed = keyedVnodes.length;
1038
- const newKeyOrder = keyedVnodes.map((kv) => kv.key);
1039
- const oldKeyOrder = oldKeyMap ? Array.from(oldKeyMap.keys()) : [];
1040
- let moveCount = 0;
1041
- for (let i = 0; i < newKeyOrder.length; i++) {
1042
- const k = newKeyOrder[i];
1043
- if (i >= oldKeyOrder.length || oldKeyOrder[i] !== k || !oldKeyMap?.has(k)) {
1044
- moveCount++;
700
+ }
701
+ const totalKeyed = keyedVnodes.length;
702
+ const newKeyOrder = keyedVnodes.map((kv) => kv.key);
703
+ const oldKeyOrder = oldKeyMap ? Array.from(oldKeyMap.keys()) : [];
704
+ let moveCount = 0;
705
+ for (let i = 0; i < newKeyOrder.length; i++) {
706
+ const k = newKeyOrder[i];
707
+ if (i >= oldKeyOrder.length || oldKeyOrder[i] !== k || !oldKeyMap?.has(k)) {
708
+ moveCount++;
1045
709
  }
1046
710
  }
1047
711
  const FAST_MOVE_THRESHOLD_ABS = 64;
@@ -1082,8 +746,9 @@ function isKeyedReorderFastPathEligible(parent, newChildren, oldKeyMap) {
1082
746
  let hasPropsPresent = false;
1083
747
  for (let i = 0; i < keyedVnodes.length; i++) {
1084
748
  const vnode = keyedVnodes[i].vnode;
1085
- if (!_isDOMElement(vnode)) continue;
1086
- const props = vnode.props || {};
749
+ if (typeof vnode !== "object" || vnode === null) continue;
750
+ const vnodeObj = vnode;
751
+ const props = vnodeObj.props || {};
1087
752
  for (const k of Object.keys(props)) {
1088
753
  if (k === "children" || k === "key") continue;
1089
754
  if (k.startsWith("on") && k.length > 2) continue;
@@ -1097,8 +762,9 @@ function isKeyedReorderFastPathEligible(parent, newChildren, oldKeyMap) {
1097
762
  for (let i = 0; i < keyedVnodes.length; i++) {
1098
763
  const { key, vnode } = keyedVnodes[i];
1099
764
  const el = oldKeyMap?.get(key);
1100
- if (!el || !_isDOMElement(vnode)) continue;
1101
- const props = vnode.props || {};
765
+ if (!el || typeof vnode !== "object" || vnode === null) continue;
766
+ const vnodeObj = vnode;
767
+ const props = vnodeObj.props || {};
1102
768
  for (const k of Object.keys(props)) {
1103
769
  if (k === "children" || k === "key") continue;
1104
770
  if (k.startsWith("on") && k.length > 2) continue;
@@ -1127,8 +793,9 @@ function isKeyedReorderFastPathEligible(parent, newChildren, oldKeyMap) {
1127
793
  break;
1128
794
  }
1129
795
  }
1130
- } catch {
796
+ } catch (e) {
1131
797
  hasPropChanges = true;
798
+ void e;
1132
799
  break;
1133
800
  }
1134
801
  }
@@ -1143,1024 +810,386 @@ function isKeyedReorderFastPathEligible(parent, newChildren, oldKeyMap) {
1143
810
  hasPropChanges
1144
811
  };
1145
812
  }
1146
- function reconcileKeyedChildren(parent, newChildren, oldKeyMap) {
1147
- const newKeyMap = /* @__PURE__ */ new Map();
1148
- const keyedVnodes = [];
1149
- const unkeyedVnodes = [];
1150
- for (let i = 0; i < newChildren.length; i++) {
1151
- const child = newChildren[i];
1152
- if (_isDOMElement(child) && child.key !== void 0) {
1153
- keyedVnodes.push({ key: child.key, vnode: child });
1154
- } else {
1155
- unkeyedVnodes.push(child);
1156
- }
813
+ var keyedElements, _reconcilerRecordedParents;
814
+ var init_keyed = __esm({
815
+ "src/renderer/keyed.ts"() {
816
+ "use strict";
817
+ keyedElements = /* @__PURE__ */ new WeakMap();
818
+ _reconcilerRecordedParents = /* @__PURE__ */ new WeakSet();
1157
819
  }
1158
- const totalKeyed = keyedVnodes.length;
1159
- const newKeyOrder = keyedVnodes.map((kv) => kv.key);
1160
- const oldKeyOrder = oldKeyMap ? Array.from(oldKeyMap.keys()) : [];
1161
- let moveCount = 0;
1162
- for (let i = 0; i < newKeyOrder.length; i++) {
1163
- const k2 = newKeyOrder[i];
1164
- if (i >= oldKeyOrder.length || oldKeyOrder[i] !== k2 || !oldKeyMap?.has(k2)) {
1165
- moveCount++;
820
+ });
821
+
822
+ // src/renderer/dom.ts
823
+ function createDOMNode(node) {
824
+ if (!IS_DOM_AVAILABLE) {
825
+ if (process.env.NODE_ENV !== "production") {
826
+ try {
827
+ logger.warn("[Askr] createDOMNode called in non-DOM environment");
828
+ } catch (e) {
829
+ void e;
830
+ }
1166
831
  }
832
+ return null;
1167
833
  }
1168
- let hasPropChanges = false;
1169
- for (let i = 0; i < keyedVnodes.length; i++) {
1170
- const { key, vnode } = keyedVnodes[i];
1171
- const el = oldKeyMap?.get(key);
1172
- if (!el || !_isDOMElement(vnode)) continue;
1173
- const props = vnode.props || {};
1174
- for (const k2 of Object.keys(props)) {
1175
- if (k2 === "children" || k2 === "key") continue;
1176
- if (k2.startsWith("on") && k2.length > 2) {
1177
- continue;
1178
- }
1179
- const v = props[k2];
1180
- try {
1181
- if (k2 === "class" || k2 === "className") {
1182
- if (el.className !== String(v)) {
1183
- logger.warn("[Askr][FASTPATH][DEV] prop mismatch", {
1184
- key,
1185
- prop: k2,
1186
- expected: String(v),
1187
- actual: el.className
1188
- });
1189
- hasPropChanges = true;
1190
- break;
1191
- }
1192
- } else if (k2 === "value" || k2 === "checked") {
1193
- if (el[k2] !== v) {
1194
- logger.warn("[Askr][FASTPATH][DEV] prop mismatch", {
1195
- key,
1196
- prop: k2,
1197
- expected: v,
1198
- actual: el[k2]
1199
- });
1200
- hasPropChanges = true;
1201
- break;
1202
- }
1203
- } else {
1204
- const attr = el.getAttribute(k2);
1205
- if (v === void 0 || v === null || v === false) {
1206
- if (attr !== null) {
1207
- logger.warn(
1208
- "[Askr][FASTPATH][DEV] prop mismatch (missing attr)",
1209
- {
1210
- key,
1211
- prop: k2,
1212
- expected: v,
1213
- actual: attr
1214
- }
1215
- );
1216
- hasPropChanges = true;
1217
- break;
1218
- }
1219
- } else if (String(v) !== attr) {
1220
- logger.warn("[Askr][FASTPATH][DEV] prop mismatch (attr diff)", {
1221
- key,
1222
- prop: k2,
1223
- expected: String(v),
1224
- actual: attr
1225
- });
1226
- hasPropChanges = true;
1227
- break;
1228
- }
1229
- }
1230
- } catch {
1231
- hasPropChanges = true;
1232
- break;
1233
- }
1234
- }
1235
- if (hasPropChanges) break;
1236
- }
1237
- const decision = isKeyedReorderFastPathEligible(
1238
- parent,
1239
- newChildren,
1240
- oldKeyMap
1241
- );
1242
- const useFastPath = decision.useFastPath;
1243
- logger.warn("[Askr][FASTPATH][DEV] decision", decision);
1244
- try {
1245
- const hugeThreshold = Number(process.env.ASKR_BULK_HUGE_THRESHOLD) || 2048;
1246
- if (!useFastPath && keyedVnodes.length >= hugeThreshold) {
1247
- let allSimple = true;
1248
- for (let i = 0; i < keyedVnodes.length; i++) {
1249
- const vnode = keyedVnodes[i].vnode;
1250
- if (!_isDOMElement(vnode)) {
1251
- allSimple = false;
1252
- break;
1253
- }
1254
- const dv = vnode;
1255
- if (typeof dv.type !== "string") {
1256
- allSimple = false;
1257
- break;
1258
- }
1259
- const ch = dv.children || dv.props?.children;
1260
- if (ch === void 0) continue;
1261
- if (Array.isArray(ch)) {
1262
- if (ch.length !== 1 || typeof ch[0] !== "string" && typeof ch[0] !== "number") {
1263
- allSimple = false;
1264
- break;
1265
- }
1266
- } else if (typeof ch !== "string" && typeof ch !== "number") {
1267
- allSimple = false;
1268
- break;
1269
- }
1270
- }
1271
- if (allSimple) {
1272
- logger.warn("[Askr][FASTPATH] applying huge-list positional fallback");
1273
- try {
1274
- if (isBulkCommitActive2()) markFastPathApplied(parent);
1275
- } catch (e) {
1276
- void e;
1277
- }
1278
- const stats = performBulkPositionalKeyedTextUpdate(parent, keyedVnodes);
1279
- if (process.env.NODE_ENV !== "production" || process.env.ASKR_FASTPATH_DEBUG === "1") {
1280
- try {
1281
- const gl = globalThis;
1282
- gl.__ASKR_LAST_FASTPATH_STATS = stats;
1283
- const counters = gl.__ASKR_FASTPATH_COUNTERS || {};
1284
- counters.bulkKeyedHugeFallback = (counters.bulkKeyedHugeFallback || 0) + 1;
1285
- gl.__ASKR_FASTPATH_COUNTERS = counters;
1286
- gl.__ASKR_BULK_DIAG = {
1287
- phase: "bulk-keyed-huge-fallback",
1288
- stats
1289
- };
1290
- } catch (e) {
1291
- void e;
1292
- }
1293
- }
1294
- return newKeyMap;
1295
- }
1296
- }
1297
- } catch (e) {
1298
- void e;
834
+ if (typeof node === "string") {
835
+ return document.createTextNode(node);
1299
836
  }
1300
- if (process.env.NODE_ENV !== "production" || process.env.ASKR_FASTPATH_DEBUG === "1") {
1301
- try {
1302
- const gl = globalThis;
1303
- gl.__ASKR_BULK_DIAG = {
1304
- phase: "keyed-decision",
1305
- decision,
1306
- totalKeyed,
1307
- oldKeyMapSize: oldKeyMap?.size ?? 0
1308
- };
1309
- } catch (e) {
1310
- void e;
1311
- }
837
+ if (typeof node === "number") {
838
+ return document.createTextNode(String(node));
1312
839
  }
1313
- let allSimpleText = false;
1314
- try {
1315
- if (keyedVnodes.length >= (Number(process.env.ASKR_BULK_TEXT_THRESHOLD) || 1024)) {
1316
- let missing = 0;
1317
- try {
1318
- const present = /* @__PURE__ */ new Set();
1319
- const parentChildren2 = Array.from(parent.children);
1320
- for (let i = 0; i < parentChildren2.length; i++) {
1321
- const attr = parentChildren2[i].getAttribute("data-key");
1322
- if (attr !== null) {
1323
- present.add(attr);
1324
- const n = Number(attr);
1325
- if (!Number.isNaN(n)) present.add(n);
1326
- }
1327
- }
1328
- for (let i = 0; i < keyedVnodes.length; i++) {
1329
- const k2 = keyedVnodes[i].key;
1330
- if (!present.has(k2)) missing++;
1331
- }
1332
- } catch {
1333
- missing = 0;
1334
- }
1335
- allSimpleText = keyedVnodes.length > 0 && keyedVnodes.every(({ vnode }) => {
1336
- if (!_isDOMElement(vnode)) return false;
1337
- const dv = vnode;
1338
- if (typeof dv.type !== "string") return false;
1339
- const ch = dv.children || dv.props?.children;
1340
- if (ch === void 0) return true;
1341
- if (Array.isArray(ch)) {
1342
- return ch.length === 1 && (typeof ch[0] === "string" || typeof ch[0] === "number");
1343
- }
1344
- return typeof ch === "string" || typeof ch === "number";
1345
- });
1346
- let hasPropsPresent = false;
1347
- for (let i = 0; i < keyedVnodes.length; i++) {
1348
- const vnode = keyedVnodes[i].vnode;
1349
- if (!_isDOMElement(vnode)) continue;
1350
- const props = vnode.props || {};
1351
- for (const k2 of Object.keys(props)) {
1352
- if (k2 === "children" || k2 === "key") continue;
1353
- if (k2.startsWith("on") && k2.length > 2) continue;
1354
- if (k2.startsWith("data-")) continue;
1355
- hasPropsPresent = true;
1356
- break;
1357
- }
1358
- if (hasPropsPresent) break;
1359
- }
1360
- const missingRatio = missing / Math.max(1, keyedVnodes.length);
1361
- if (missingRatio > 0.5 && allSimpleText && !hasPropsPresent) {
1362
- logger.warn(
1363
- "[Askr][FASTPATH] switching to positional bulk keyed fast-path due to missing keys ratio",
1364
- missingRatio
1365
- );
1366
- try {
1367
- if (isBulkCommitActive2()) markFastPathApplied(parent);
1368
- } catch (e) {
1369
- void e;
1370
- }
1371
- const stats = performBulkPositionalKeyedTextUpdate(parent, keyedVnodes);
1372
- if (process.env.NODE_ENV !== "production") {
1373
- try {
1374
- const gl = globalThis;
1375
- gl.__ASKR_LAST_FASTPATH_STATS = stats;
1376
- try {
1377
- _reconcilerRecordedParents.add(parent);
1378
- } catch (e) {
1379
- void e;
1380
- }
1381
- gl.__ASKR_LAST_FASTPATH_COMMIT_COUNT = 1;
1382
- const counters = gl.__ASKR_FASTPATH_COUNTERS || {};
1383
- counters.bulkKeyedPositionalHits = (counters.bulkKeyedPositionalHits || 0) + 1;
1384
- gl.__ASKR_FASTPATH_COUNTERS = counters;
1385
- } catch (e) {
1386
- void e;
1387
- }
1388
- }
1389
- return newKeyMap;
1390
- }
1391
- }
1392
- } catch (e) {
1393
- void e;
840
+ if (!node) {
841
+ return null;
1394
842
  }
1395
- if (!useFastPath && typeof globalThis !== "undefined") {
1396
- try {
1397
- let parentFastpathApplied = false;
1398
- try {
1399
- parentFastpathApplied = isFastPathApplied(parent);
1400
- } catch {
1401
- parentFastpathApplied = false;
1402
- }
1403
- if (!parentFastpathApplied) {
1404
- const gl = globalThis;
1405
- try {
1406
- if (_reconcilerRecordedParents.has(parent)) {
1407
- _reconcilerRecordedParents.delete(parent);
1408
- } else {
1409
- delete gl.__ASKR_LAST_FASTPATH_STATS;
1410
- delete gl.__ASKR_LAST_FASTPATH_REUSED;
1411
- }
1412
- } catch {
1413
- delete gl.__ASKR_LAST_FASTPATH_STATS;
1414
- delete gl.__ASKR_LAST_FASTPATH_REUSED;
1415
- }
1416
- }
1417
- } catch (e) {
1418
- void e;
843
+ if (Array.isArray(node)) {
844
+ const fragment = document.createDocumentFragment();
845
+ for (let i = 0; i < node.length; i++) {
846
+ const dom = createDOMNode(node[i]);
847
+ if (dom) fragment.appendChild(dom);
1419
848
  }
849
+ return fragment;
1420
850
  }
1421
- let _triedPositionalReuse = false;
1422
- try {
1423
- const bulkTextThreshold = Number(process.env.ASKR_BULK_TEXT_THRESHOLD) || 1024;
1424
- if (!useFastPath && keyedVnodes.length >= bulkTextThreshold && allSimpleText && !hasPropChanges) {
1425
- try {
1426
- if (isFastPathApplied(parent)) {
1427
- logger.warn(
1428
- "[Askr][FASTPATH] fast-path already applied on parent; skipping"
1429
- );
1430
- return newKeyMap;
1431
- }
1432
- } catch (e) {
1433
- void e;
1434
- }
1435
- let stable = true;
1436
- try {
1437
- const parentChildren2 = Array.from(parent.children);
1438
- if (parentChildren2.length === keyedVnodes.length) {
1439
- const allSimple = keyedVnodes.every(({ vnode }) => {
1440
- if (!_isDOMElement(vnode)) return false;
1441
- const dv = vnode;
1442
- if (typeof dv.type !== "string") return false;
1443
- const ch = dv.children || dv.props?.children;
1444
- if (ch === void 0) return true;
1445
- if (Array.isArray(ch)) {
1446
- return ch.length === 1 && (typeof ch[0] === "string" || typeof ch[0] === "number");
1447
- }
1448
- return typeof ch === "string" || typeof ch === "number";
1449
- });
1450
- if (allSimple || process.env.ASKR_FORCE_BULK_POSREUSE === "1") {
1451
- logger.warn(
1452
- "[Askr][FASTPATH] len-match heuristic triggered (positional bulk)"
1453
- );
1454
- if (process.env.NODE_ENV !== "production" || process.env.ASKR_FASTPATH_DEBUG === "1") {
1455
- try {
1456
- const gl = globalThis;
1457
- gl.__ASKR_BULK_DIAG = {
1458
- phase: "bulk-keyed-positional-trigger-lenmatch-early",
1459
- totalKeyed: keyedVnodes.length,
1460
- allSimple,
1461
- forced: process.env.ASKR_FORCE_BULK_POSREUSE === "1"
1462
- };
1463
- } catch (e) {
1464
- void e;
1465
- }
1466
- }
851
+ if (typeof node === "object" && node !== null && "type" in node) {
852
+ const type = node.type;
853
+ const props = node.props || {};
854
+ if (typeof type === "string") {
855
+ const el = document.createElement(type);
856
+ for (const key in props) {
857
+ const value = props[key];
858
+ if (key === "children" || key === "key") continue;
859
+ if (value === void 0 || value === null || value === false) continue;
860
+ if (key.startsWith("on") && key.length > 2) {
861
+ const eventName = key.slice(2).charAt(0).toLowerCase() + key.slice(3).toLowerCase();
862
+ const wrappedHandler = (event) => {
863
+ globalScheduler.setInHandler(true);
1467
864
  try {
1468
- if (isBulkCommitActive2()) markFastPathApplied(parent);
1469
- } catch (e) {
1470
- void e;
1471
- }
1472
- const stats = performBulkPositionalKeyedTextUpdate(
1473
- parent,
1474
- keyedVnodes
1475
- );
1476
- if (process.env.NODE_ENV !== "production" || process.env.ASKR_FASTPATH_DEBUG === "1") {
1477
- try {
1478
- const gl = globalThis;
1479
- gl.__ASKR_LAST_FASTPATH_STATS = stats;
1480
- gl.__ASKR_BULK_DIAG = {
1481
- phase: "bulk-keyed-positional-applied",
1482
- stats
1483
- };
1484
- const counters = gl.__ASKR_FASTPATH_COUNTERS || {};
1485
- counters.bulkKeyedPositionalHits = (counters.bulkKeyedPositionalHits || 0) + 1;
1486
- if (process.env.ASKR_FORCE_BULK_POSREUSE === "1")
1487
- counters.bulkKeyedPositionalForced = (counters.bulkKeyedPositionalForced || 0) + 1;
1488
- gl.__ASKR_FASTPATH_COUNTERS = counters;
1489
- } catch (e) {
1490
- void e;
865
+ value(event);
866
+ } catch (error) {
867
+ logger.error("[Askr] Event handler error:", error);
868
+ } finally {
869
+ globalScheduler.setInHandler(false);
870
+ const state = globalScheduler.getState();
871
+ if ((state.queueLength ?? 0) > 0 && !state.running) {
872
+ queueMicrotask(() => {
873
+ try {
874
+ if (!globalScheduler.isExecuting()) globalScheduler.flush();
875
+ } catch (err) {
876
+ queueMicrotask(() => {
877
+ throw err;
878
+ });
879
+ }
880
+ });
1491
881
  }
1492
882
  }
1493
- return newKeyMap;
883
+ };
884
+ const options = eventName === "wheel" || eventName === "scroll" || eventName.startsWith("touch") ? { passive: true } : void 0;
885
+ if (options !== void 0)
886
+ el.addEventListener(eventName, wrappedHandler, options);
887
+ else el.addEventListener(eventName, wrappedHandler);
888
+ if (!elementListeners.has(el)) {
889
+ elementListeners.set(el, /* @__PURE__ */ new Map());
1494
890
  }
1495
- }
1496
- if (parentChildren2.length !== keyedVnodes.length) stable = false;
1497
- else {
1498
- let keyMismatches = 0;
1499
- for (let i = 0; i < keyedVnodes.length; i++) {
1500
- const k2 = keyedVnodes[i].key;
1501
- const ch = parentChildren2[i];
1502
- if (!ch) {
1503
- stable = false;
1504
- break;
1505
- }
1506
- const attr = ch.getAttribute("data-key");
1507
- if (attr === null) {
1508
- stable = false;
1509
- break;
891
+ elementListeners.get(el).set(eventName, {
892
+ handler: wrappedHandler,
893
+ original: value,
894
+ options
895
+ });
896
+ } else if (key === "class" || key === "className") {
897
+ el.className = String(value);
898
+ } else if (key === "value" || key === "checked") {
899
+ const tag = type.toLowerCase();
900
+ if (key === "value") {
901
+ if (tag === "input" || tag === "textarea" || tag === "select") {
902
+ el.value = String(value);
903
+ el.setAttribute("value", String(value));
904
+ } else {
905
+ el.setAttribute("value", String(value));
1510
906
  }
1511
- if (String(k2) !== attr && String(Number(attr)) !== String(k2)) {
1512
- keyMismatches++;
907
+ } else {
908
+ if (tag === "input") {
909
+ el.checked = Boolean(value);
910
+ el.setAttribute("checked", String(Boolean(value)));
911
+ } else {
912
+ el.setAttribute("checked", String(Boolean(value)));
1513
913
  }
1514
914
  }
1515
- if (stable) {
1516
- if (keyMismatches === 0) {
1517
- logger.warn(
1518
- "[Askr][FASTPATH] applying bulk keyed text fast-path (stable keys)"
1519
- );
1520
- if (process.env.NODE_ENV !== "production" || process.env.ASKR_FASTPATH_DEBUG === "1") {
1521
- try {
1522
- const gl = globalThis;
1523
- gl.__ASKR_BULK_DIAG = {
1524
- phase: "bulk-keyed-stable-trigger",
1525
- totalKeyed: keyedVnodes.length,
1526
- hasPropChanges
1527
- };
1528
- } catch (e) {
1529
- void e;
1530
- }
1531
- }
1532
- try {
1533
- if (isBulkCommitActive2()) markFastPathApplied(parent);
1534
- } catch (e) {
1535
- void e;
1536
- }
1537
- const stats = performBulkKeyedTextReplace(
1538
- parent,
1539
- keyedVnodes,
1540
- oldKeyMap
1541
- );
1542
- if (process.env.NODE_ENV !== "production" || process.env.ASKR_FASTPATH_DEBUG === "1") {
1543
- try {
1544
- const gl = globalThis;
1545
- gl.__ASKR_LAST_FASTPATH_STATS = stats;
1546
- gl.__ASKR_BULK_DIAG = {
1547
- phase: "bulk-keyed-applied",
1548
- stats
1549
- };
1550
- const counters = gl.__ASKR_FASTPATH_COUNTERS || {};
1551
- counters.bulkKeyedTextHits = (counters.bulkKeyedTextHits || 0) + 1;
1552
- gl.__ASKR_FASTPATH_COUNTERS = counters;
1553
- } catch (e) {
1554
- void e;
915
+ } else {
916
+ el.setAttribute(key, String(value));
917
+ }
918
+ }
919
+ const vnodeKey = node.key ?? props?.key;
920
+ if (vnodeKey !== void 0) {
921
+ el.setAttribute("data-key", String(vnodeKey));
922
+ }
923
+ const children = props.children || node.children;
924
+ if (children) {
925
+ if (Array.isArray(children)) {
926
+ if (process.env.NODE_ENV !== "production") {
927
+ let hasElements = false;
928
+ let hasKeys = false;
929
+ for (let i = 0; i < children.length; i++) {
930
+ const item = children[i];
931
+ if (typeof item === "object" && item !== null && "type" in item) {
932
+ hasElements = true;
933
+ const itemProps = item.props || {};
934
+ if ("key" in itemProps) {
935
+ hasKeys = true;
936
+ break;
1555
937
  }
1556
938
  }
1557
- return newKeyMap;
1558
939
  }
1559
- if (parentChildren2.length === keyedVnodes.length) {
1560
- logger.warn(
1561
- "[Askr][FASTPATH] applying bulk keyed positional text fast-path (len-match)"
1562
- );
1563
- if (process.env.NODE_ENV !== "production" || process.env.ASKR_FASTPATH_DEBUG === "1") {
1564
- try {
1565
- const gl = globalThis;
1566
- gl.__ASKR_BULK_DIAG = {
1567
- phase: "bulk-keyed-positional-trigger-lenmatch",
1568
- totalKeyed: keyedVnodes.length,
1569
- keyMismatches
1570
- };
1571
- } catch (e) {
1572
- void e;
1573
- }
1574
- }
1575
- try {
1576
- if (isBulkCommitActive2()) markFastPathApplied(parent);
1577
- } catch (e) {
1578
- void e;
1579
- }
1580
- const stats = performBulkPositionalKeyedTextUpdate(
1581
- parent,
1582
- keyedVnodes
1583
- );
1584
- if (process.env.NODE_ENV !== "production" || process.env.ASKR_FASTPATH_DEBUG === "1") {
940
+ if (hasElements && !hasKeys) {
941
+ if (typeof console !== "undefined") {
1585
942
  try {
1586
- const gl = globalThis;
1587
- gl.__ASKR_LAST_FASTPATH_STATS = stats;
1588
- gl.__ASKR_BULK_DIAG = {
1589
- phase: "bulk-keyed-positional-applied",
1590
- stats
1591
- };
1592
- const counters = gl.__ASKR_FASTPATH_COUNTERS || {};
1593
- counters.bulkKeyedPositionalHits = (counters.bulkKeyedPositionalHits || 0) + 1;
1594
- gl.__ASKR_FASTPATH_COUNTERS = counters;
943
+ const inst = getCurrentInstance();
944
+ const name = inst?.fn?.name || "<anonymous>";
945
+ logger.warn(
946
+ `Missing keys on dynamic lists in ${name}. Each child in a list should have a unique "key" prop.`
947
+ );
1595
948
  } catch (e) {
949
+ logger.warn(
950
+ 'Missing keys on dynamic lists. Each child in a list should have a unique "key" prop.'
951
+ );
1596
952
  void e;
1597
953
  }
1598
954
  }
1599
- return newKeyMap;
1600
- }
1601
- const mismatchRatio = keyMismatches / keyedVnodes.length;
1602
- const POSITIONAL_THRESHOLD = 0.5;
1603
- if (mismatchRatio > POSITIONAL_THRESHOLD) {
1604
- logger.warn(
1605
- "[Askr][FASTPATH] applying bulk keyed positional text fast-path"
1606
- );
1607
- if (process.env.NODE_ENV !== "production" || process.env.ASKR_FASTPATH_DEBUG === "1") {
1608
- try {
1609
- globalThis.__ASKR_BULK_DIAG = {
1610
- phase: "bulk-keyed-positional-trigger",
1611
- totalKeyed: keyedVnodes.length,
1612
- keyMismatches
1613
- };
1614
- } catch (e) {
1615
- void e;
1616
- }
1617
- }
1618
- try {
1619
- if (isBulkCommitActive2()) markFastPathApplied(parent);
1620
- } catch (e) {
1621
- void e;
1622
- }
1623
- const stats = performBulkPositionalKeyedTextUpdate(
1624
- parent,
1625
- keyedVnodes
1626
- );
1627
- if (process.env.NODE_ENV !== "production" || process.env.ASKR_FASTPATH_DEBUG === "1") {
1628
- try {
1629
- const gl = globalThis;
1630
- gl.__ASKR_LAST_FASTPATH_STATS = stats;
1631
- gl.__ASKR_BULK_DIAG = {
1632
- phase: "bulk-keyed-positional-applied",
1633
- stats
1634
- };
1635
- const counters = gl.__ASKR_FASTPATH_COUNTERS || {};
1636
- counters.bulkKeyedPositionalHits = (counters.bulkKeyedPositionalHits || 0) + 1;
1637
- gl.__ASKR_FASTPATH_COUNTERS = counters;
1638
- } catch (e) {
1639
- void e;
1640
- }
1641
- }
1642
- return newKeyMap;
1643
955
  }
1644
956
  }
957
+ for (let i = 0; i < children.length; i++) {
958
+ const dom = createDOMNode(children[i]);
959
+ if (dom) el.appendChild(dom);
960
+ }
961
+ } else {
962
+ const dom = createDOMNode(children);
963
+ if (dom) el.appendChild(dom);
1645
964
  }
1646
- } catch {
1647
- stable = false;
1648
965
  }
966
+ return el;
1649
967
  }
1650
- } catch (e) {
1651
- void e;
1652
- }
1653
- if (!useFastPath) {
1654
- const totalKeyed2 = keyedVnodes.length;
1655
- const candidates = newChildren.filter(
1656
- (c) => _isDOMElement(c) && typeof c.type === "string"
1657
- );
1658
- const smallListPositionalReuseEligible = candidates.length > 0 && candidates.length <= 64 && parent.children.length === candidates.length && candidates.every((vnode) => {
1659
- const children = vnode.children || vnode.props?.children;
1660
- if (Array.isArray(children)) {
1661
- return children.length === 1 && (typeof children[0] === "string" || typeof children[0] === "number");
968
+ if (typeof type === "function") {
969
+ const frame = node[CONTEXT_FRAME_SYMBOL];
970
+ const snapshot = frame || getCurrentContextFrame();
971
+ const componentFn = type;
972
+ const isAsync = componentFn.constructor.name === "AsyncFunction";
973
+ if (isAsync) {
974
+ throw new Error(
975
+ "Async components are not supported. Use resource() for async work."
976
+ );
1662
977
  }
1663
- return children === void 0 || typeof children === "string" || typeof children === "number";
1664
- });
1665
- try {
1666
- if (smallListPositionalReuseEligible) {
1667
- let eligible = true;
1668
- for (let i = 0; i < totalKeyed2; i++) {
1669
- const vnode = keyedVnodes[i].vnode;
1670
- if (!_isDOMElement(vnode) || typeof vnode.type !== "string") {
1671
- eligible = false;
1672
- break;
1673
- }
1674
- const children = vnode.children || vnode.props?.children;
1675
- if (Array.isArray(children)) {
1676
- if (children.length !== 1) {
1677
- eligible = false;
1678
- break;
1679
- }
1680
- const c = children[0];
1681
- if (typeof c !== "string" && typeof c !== "number") {
1682
- eligible = false;
1683
- break;
1684
- }
1685
- } else if (children !== void 0 && typeof children !== "string" && typeof children !== "number") {
1686
- eligible = false;
1687
- break;
1688
- }
1689
- }
1690
- const anyKeyMatches = !!oldKeyMap && keyedVnodes.some((kv) => oldKeyMap.has(kv.key));
1691
- if (anyKeyMatches) {
1692
- eligible = false;
1693
- }
1694
- if (eligible || process.env.ASKR_FORCE_POSREUSE === "1") {
1695
- _triedPositionalReuse = true;
1696
- if (process.env.ASKR_FORCE_POSREUSE === "1") {
1697
- logger.warn(
1698
- "[Askr][POSREUSE][FORCED] forcing positional reuse path for testing"
1699
- );
1700
- } else {
1701
- logger.warn("[Askr][POSREUSE] positional reuse heuristic applied");
1702
- }
1703
- const existingChildren = parent.children;
1704
- for (let i = 0; i < totalKeyed2; i++) {
1705
- const { key, vnode } = keyedVnodes[i];
1706
- const current = existingChildren[i];
1707
- if (current && _isDOMElement(vnode)) {
1708
- const vnodeType = vnode.type;
1709
- if (current.tagName.toLowerCase() === vnodeType.toLowerCase()) {
1710
- updateElementFromVnode(current, vnode);
1711
- newKeyMap.set(key, current);
1712
- continue;
1713
- }
1714
- }
1715
- const newEl = createDOMNode(vnode);
1716
- if (newEl instanceof Element) {
1717
- if (current) {
1718
- cleanupInstanceIfPresent(current);
1719
- parent.replaceChild(newEl, current);
1720
- } else parent.appendChild(newEl);
1721
- newKeyMap.set(key, newEl);
1722
- }
1723
- }
1724
- for (const vnode of unkeyedVnodes) {
1725
- const newEl = createDOMNode(vnode);
1726
- if (newEl) parent.appendChild(newEl);
1727
- }
1728
- return newKeyMap;
1729
- }
978
+ const vnodeAny = node;
979
+ let childInstance = vnodeAny.__instance;
980
+ if (!childInstance) {
981
+ childInstance = createComponentInstance(
982
+ `comp-${Math.random().toString(36).slice(2, 7)}`,
983
+ componentFn,
984
+ props || {},
985
+ null
986
+ );
987
+ vnodeAny.__instance = childInstance;
1730
988
  }
1731
- } catch {
1732
- }
1733
- }
1734
- if (useFastPath) {
1735
- if (!isSchedulerExecuting()) {
1736
- logger.warn(
1737
- "[Askr][FASTPATH][DEV] Fast-path reconciliation invoked outside scheduler execution"
989
+ if (snapshot) {
990
+ childInstance.ownerFrame = snapshot;
991
+ }
992
+ const result = withContext(
993
+ snapshot,
994
+ () => renderComponentInline(childInstance)
1738
995
  );
1739
- }
1740
- let parentChildrenArr;
1741
- let localOldKeyMap;
1742
- if (totalKeyed <= 20) {
1743
- try {
1744
- const pc = parent.children;
1745
- parentChildrenArr = new Array(pc.length);
1746
- for (let i = 0; i < pc.length; i++)
1747
- parentChildrenArr[i] = pc[i];
1748
- } catch {
1749
- parentChildrenArr = void 0;
996
+ if (result instanceof Promise) {
997
+ throw new Error(
998
+ "Async components are not supported. Components must return synchronously."
999
+ );
1750
1000
  }
1751
- } else {
1752
- localOldKeyMap = /* @__PURE__ */ new Map();
1753
- try {
1754
- const parentChildren2 = Array.from(parent.children);
1755
- for (let i = 0; i < parentChildren2.length; i++) {
1756
- const ch = parentChildren2[i];
1757
- const k2 = ch.getAttribute("data-key");
1758
- if (k2 !== null) {
1759
- localOldKeyMap.set(k2, ch);
1760
- const n = Number(k2);
1761
- if (!Number.isNaN(n)) localOldKeyMap.set(n, ch);
1762
- }
1763
- }
1764
- } catch {
1765
- localOldKeyMap = void 0;
1001
+ const dom = withContext(snapshot, () => createDOMNode(result));
1002
+ if (dom instanceof Element) {
1003
+ mountInstanceInline(childInstance, dom);
1004
+ return dom;
1766
1005
  }
1006
+ const host = document.createElement("div");
1007
+ if (dom instanceof DocumentFragment) {
1008
+ host.appendChild(dom);
1009
+ } else if (dom) {
1010
+ host.appendChild(dom);
1011
+ }
1012
+ mountInstanceInline(childInstance, host);
1013
+ return host;
1767
1014
  }
1768
- logger.warn(
1769
- "[Askr][FASTPATH] oldKeyMap size:",
1770
- oldKeyMap?.size ?? 0,
1771
- "localOldKeyMap size:",
1772
- localOldKeyMap?.size
1773
- );
1774
- const tLookupStart = typeof performance !== "undefined" && performance.now ? performance.now() : Date.now();
1775
- const finalNodes = [];
1776
- let mapLookups = 0;
1777
- let createdNodes = 0;
1778
- let reusedCount = 0;
1779
- for (let i = 0; i < keyedVnodes.length; i++) {
1780
- const { key, vnode } = keyedVnodes[i];
1781
- mapLookups++;
1782
- let el;
1783
- if (totalKeyed <= 20 && parentChildrenArr) {
1784
- const ks = String(key);
1785
- for (let j = 0; j < parentChildrenArr.length; j++) {
1786
- const ch = parentChildrenArr[j];
1787
- const k2 = ch.getAttribute("data-key");
1788
- if (k2 !== null && (k2 === ks || Number(k2) === key)) {
1789
- el = ch;
1790
- break;
1015
+ if (typeof type === "symbol" && (type === Fragment || String(type) === "Symbol(Fragment)")) {
1016
+ const fragment = document.createDocumentFragment();
1017
+ const children = props.children || node.children;
1018
+ if (children) {
1019
+ if (Array.isArray(children)) {
1020
+ for (let i = 0; i < children.length; i++) {
1021
+ const dom = createDOMNode(children[i]);
1022
+ if (dom) fragment.appendChild(dom);
1791
1023
  }
1024
+ } else {
1025
+ const dom = createDOMNode(children);
1026
+ if (dom) fragment.appendChild(dom);
1792
1027
  }
1793
- if (!el) el = oldKeyMap?.get(key);
1794
- } else {
1795
- el = localOldKeyMap?.get(key) ?? oldKeyMap?.get(key);
1796
1028
  }
1797
- if (el) {
1798
- finalNodes.push(el);
1799
- reusedCount++;
1800
- } else {
1801
- const newEl = createDOMNode(vnode);
1802
- if (newEl) {
1803
- finalNodes.push(newEl);
1804
- createdNodes++;
1029
+ return fragment;
1030
+ }
1031
+ }
1032
+ return null;
1033
+ }
1034
+ function updateElementFromVnode(el, vnode, updateChildren = true) {
1035
+ if (!_isDOMElement(vnode)) {
1036
+ return;
1037
+ }
1038
+ const props = vnode.props || {};
1039
+ const vnodeKey = vnode.key ?? vnode.props?.key;
1040
+ if (vnodeKey !== void 0) {
1041
+ el.setAttribute("data-key", String(vnodeKey));
1042
+ }
1043
+ const existingListeners = elementListeners.get(el);
1044
+ const desiredEventNames = /* @__PURE__ */ new Set();
1045
+ for (const key in props) {
1046
+ const value = props[key];
1047
+ if (key === "children" || key === "key") continue;
1048
+ if (value === void 0 || value === null || value === false) {
1049
+ if (key === "class" || key === "className") {
1050
+ el.className = "";
1051
+ } else if (key.startsWith("on") && key.length > 2) {
1052
+ const eventName = key.slice(2).charAt(0).toLowerCase() + key.slice(3).toLowerCase();
1053
+ if (existingListeners && existingListeners.has(eventName)) {
1054
+ const entry = existingListeners.get(eventName);
1055
+ if (entry.options !== void 0)
1056
+ el.removeEventListener(eventName, entry.handler, entry.options);
1057
+ else el.removeEventListener(eventName, entry.handler);
1058
+ existingListeners.delete(eventName);
1805
1059
  }
1060
+ continue;
1061
+ } else {
1062
+ el.removeAttribute(key);
1806
1063
  }
1064
+ continue;
1807
1065
  }
1808
- for (const vnode of unkeyedVnodes) {
1809
- const newEl = createDOMNode(vnode);
1810
- if (newEl) {
1811
- finalNodes.push(newEl);
1812
- createdNodes++;
1066
+ if (key === "class" || key === "className") {
1067
+ el.className = String(value);
1068
+ } else if (key === "value" || key === "checked") {
1069
+ el[key] = value;
1070
+ } else if (key.startsWith("on") && key.length > 2) {
1071
+ const eventName = key.slice(2).charAt(0).toLowerCase() + key.slice(3).toLowerCase();
1072
+ desiredEventNames.add(eventName);
1073
+ const existing = existingListeners?.get(eventName);
1074
+ if (existing && existing.original === value) {
1075
+ continue;
1813
1076
  }
1814
- }
1815
- const t_lookup = typeof performance !== "undefined" && performance.now ? performance.now() - tLookupStart : 0;
1816
- if (process.env.ASKR_FASTPATH_GUARD === "1") {
1817
- const replaceChildrenCount = 0;
1818
- const otherMutationCount = 0;
1819
- const orig = {};
1820
- const elProto = Element.prototype;
1821
- const nodeProto = Node.prototype;
1822
- const removeFn = (() => {
1823
- if (typeof document === "undefined" || typeof document.createElement !== "function")
1824
- return void 0;
1077
+ if (existing) {
1078
+ el.removeEventListener(eventName, existing.handler);
1079
+ }
1080
+ const wrappedHandler = (event) => {
1081
+ globalScheduler.setInHandler(true);
1825
1082
  try {
1826
- const el = document.createElement("div");
1827
- return typeof el.remove === "function" ? el.remove : void 0;
1828
- } catch {
1829
- return void 0;
1830
- }
1831
- })();
1832
- if (elProto.replaceChildren)
1833
- orig.replaceChildren = elProto.replaceChildren;
1834
- if (nodeProto.appendChild) orig.appendChild = nodeProto.appendChild;
1835
- if (nodeProto.insertBefore) orig.insertBefore = nodeProto.insertBefore;
1836
- if (nodeProto.removeChild) orig.removeChild = nodeProto.removeChild;
1837
- if (nodeProto.replaceChild) orig.replaceChild = nodeProto.replaceChild;
1838
- if (removeFn) orig.remove = removeFn;
1839
- let violation = false;
1840
- try {
1841
- const fragment = document.createDocumentFragment();
1842
- for (let i = 0; i < finalNodes.length; i++)
1843
- fragment.appendChild(finalNodes[i]);
1844
- let commitCount = 0;
1845
- commitCount++;
1846
- parent.replaceChildren(fragment);
1847
- if (typeof globalThis !== "undefined") {
1848
- globalThis["__ASKR_LAST_FASTPATH_COMMIT_COUNT"] = commitCount;
1083
+ value(event);
1084
+ } catch (error) {
1085
+ logger.error("[Askr] Event handler error:", error);
1086
+ } finally {
1087
+ globalScheduler.setInHandler(false);
1849
1088
  }
1850
- } finally {
1851
- violation = otherMutationCount !== 0 || replaceChildrenCount < 1;
1852
- }
1853
- if (violation) {
1854
- logger.error(
1855
- "[Askr][DEV] Fast-path structural mutation invariant violated:",
1856
- {
1857
- replaceChildrenCount,
1858
- otherMutationCount
1859
- }
1860
- );
1861
- throw new Error(
1862
- "Fast-path must perform a single structural replacement (replaceChildren) and no other structural mutations"
1863
- );
1089
+ };
1090
+ const options = eventName === "wheel" || eventName === "scroll" || eventName.startsWith("touch") ? { passive: true } : void 0;
1091
+ if (options !== void 0)
1092
+ el.addEventListener(eventName, wrappedHandler, options);
1093
+ else el.addEventListener(eventName, wrappedHandler);
1094
+ if (!elementListeners.has(el)) {
1095
+ elementListeners.set(el, /* @__PURE__ */ new Map());
1864
1096
  }
1097
+ elementListeners.get(el).set(eventName, {
1098
+ handler: wrappedHandler,
1099
+ original: value,
1100
+ options
1101
+ });
1865
1102
  } else {
1866
- const tFragmentStart = Date.now();
1867
- const fragment = document.createDocumentFragment();
1868
- let fragmentAppendCount = 0;
1869
- for (let i = 0; i < finalNodes.length; i++) {
1870
- fragment.appendChild(finalNodes[i]);
1871
- fragmentAppendCount++;
1872
- }
1873
- const t_fragment = Date.now() - tFragmentStart;
1874
- const schedBefore = process.env.NODE_ENV !== "production" ? globalScheduler.getState() : null;
1875
- const wasExecuting = process.env.NODE_ENV !== "production" ? isSchedulerExecuting() : false;
1876
- const tCommitStart = Date.now();
1877
- if (process.env.NODE_ENV !== "production") {
1878
- }
1879
- let commitCount = 0;
1880
- commitCount++;
1881
- try {
1882
- const existing = Array.from(parent.childNodes);
1883
- for (const n of existing) cleanupInstanceIfPresent(n);
1884
- } catch (e) {
1885
- void e;
1886
- }
1887
- try {
1888
- const existing = Array.from(parent.childNodes);
1889
- for (const n of existing) cleanupInstanceIfPresent(n);
1890
- } catch (e) {
1891
- void e;
1892
- }
1893
- parent.replaceChildren(fragment);
1894
- if (typeof globalThis !== "undefined") {
1895
- globalThis["__ASKR_LAST_FASTPATH_COMMIT_COUNT"] = commitCount;
1896
- }
1897
- const t_commit = Date.now() - tCommitStart;
1898
- if (process.env.NODE_ENV !== "production") {
1899
- const schedAfter = globalScheduler.getState();
1900
- if (!wasExecuting) {
1901
- logger.warn(
1902
- "[Askr][FASTPATH][DEV] Fast-path commit invoked outside scheduler execution"
1903
- );
1904
- }
1905
- if (schedBefore && schedAfter) {
1906
- if (schedBefore.taskCount !== schedAfter.taskCount) {
1907
- logger.error(
1908
- "[Askr][FASTPATH][DEV] Scheduler tasks were enqueued during fast-path commit",
1909
- {
1910
- before: schedBefore,
1911
- after: schedAfter
1912
- }
1913
- );
1914
- throw new Error("Fast-path must not enqueue scheduler tasks");
1915
- }
1916
- }
1917
- const parentNodes = Array.from(parent.childNodes);
1918
- if (parentNodes.length !== finalNodes.length) {
1919
- logger.error("[Askr][FASTPATH][DEV] Parent child count mismatch", {
1920
- parentCount: parentNodes.length,
1921
- expected: finalNodes.length
1922
- });
1923
- throw new Error(
1924
- "Fast-path must perform a single structural replacement"
1925
- );
1926
- }
1927
- for (let i = 0; i < finalNodes.length; i++) {
1928
- if (parentNodes[i] !== finalNodes[i]) {
1929
- logger.error(
1930
- "[Askr][FASTPATH][DEV] Final DOM order mismatch at index",
1931
- i,
1932
- {
1933
- expected: finalNodes[i],
1934
- found: parentNodes[i]
1935
- }
1936
- );
1937
- throw new Error(
1938
- "Fast-path final DOM order does not match expected nodes"
1939
- );
1940
- }
1941
- }
1942
- }
1943
- const tBookkeepingStart = Date.now();
1944
- for (let i = 0; i < keyedVnodes.length; i++) {
1945
- const key = keyedVnodes[i].key;
1946
- const node = finalNodes[i];
1947
- if (node instanceof Element) newKeyMap.set(key, node);
1948
- }
1949
- const t_bookkeeping = Date.now() - tBookkeepingStart;
1950
- if (process.env.ASKR_FASTPATH_TRACE === "1" || process.env.NODE_ENV !== "production") {
1951
- const stats = {
1952
- n: totalKeyed,
1953
- moves: moveCount,
1954
- lisLen: 0,
1955
- t_lookup,
1956
- t_fragment,
1957
- t_commit,
1958
- t_bookkeeping,
1959
- fragmentAppendCount,
1960
- mapLookups,
1961
- createdNodes,
1962
- reusedCount
1963
- };
1964
- if (typeof globalThis !== "undefined") {
1965
- const _g = globalThis;
1966
- _g["__ASKR_LAST_FASTPATH_STATS"] = stats;
1967
- _g["__ASKR_LAST_FASTPATH_REUSED"] = reusedCount > 0;
1968
- const historyKey = "__ASKR_LAST_FASTPATH_HISTORY";
1969
- let hist = _g[historyKey];
1970
- if (!hist) {
1971
- hist = [];
1972
- _g[historyKey] = hist;
1973
- }
1974
- hist.push(stats);
1975
- }
1976
- logger.warn("[Askr][FASTPATH]", JSON.stringify(stats));
1977
- }
1103
+ el.setAttribute(key, String(value));
1978
1104
  }
1979
- return newKeyMap;
1980
1105
  }
1981
- const parentChildren = Array.from(parent.children);
1982
- const positions = new Array(keyedVnodes.length).fill(-1);
1983
- for (let i = 0; i < keyedVnodes.length; i++) {
1984
- const key = keyedVnodes[i].key;
1985
- const el = oldKeyMap?.get(key);
1986
- if (el && el.parentElement === parent) {
1987
- positions[i] = parentChildren.indexOf(el);
1988
- }
1989
- }
1990
- const tailsStart = typeof performance !== "undefined" && performance.now ? performance.now() : Date.now();
1991
- const keepSet = /* @__PURE__ */ new Set();
1992
- const tails = [];
1993
- const tailsIdx = [];
1994
- const prev = new Array(positions.length).fill(-1);
1995
- for (let i = 0; i < positions.length; i++) {
1996
- const pos = positions[i];
1997
- if (pos === -1) continue;
1998
- let lo = 0;
1999
- let hi = tails.length;
2000
- while (lo < hi) {
2001
- const mid = lo + hi >> 1;
2002
- if (tails[mid] < pos) lo = mid + 1;
2003
- else hi = mid;
2004
- }
2005
- if (lo === tails.length) {
2006
- tails.push(pos);
2007
- tailsIdx.push(i);
2008
- } else {
2009
- tails[lo] = pos;
2010
- tailsIdx[lo] = i;
1106
+ if (existingListeners) {
1107
+ for (const eventName of existingListeners.keys()) {
1108
+ const entry = existingListeners.get(eventName);
1109
+ if (!desiredEventNames.has(eventName)) {
1110
+ el.removeEventListener(eventName, entry.handler);
1111
+ existingListeners.delete(eventName);
1112
+ }
2011
1113
  }
2012
- prev[i] = lo > 0 ? tailsIdx[lo - 1] : -1;
1114
+ if (existingListeners.size === 0) elementListeners.delete(el);
2013
1115
  }
2014
- let k = tailsIdx.length ? tailsIdx[tailsIdx.length - 1] : -1;
2015
- while (k !== -1) {
2016
- keepSet.add(k);
2017
- k = prev[k];
1116
+ if (updateChildren) {
1117
+ const children = vnode.children || props.children;
1118
+ updateElementChildren(el, children);
2018
1119
  }
2019
- const tLIS = (typeof performance !== "undefined" && performance.now ? performance.now() : Date.now()) - tailsStart;
2020
- if (process.env.NODE_ENV !== "production" || process.env.ASKR_FASTPATH_DEBUG === "1") {
2021
- try {
2022
- const prev2 = globalThis.__ASKR_BULK_DIAG;
2023
- globalThis.__ASKR_BULK_DIAG = {
2024
- phase: "keyed-fallback-lis",
2025
- positionsFound: positions.filter((p) => p !== -1).length,
2026
- keepCount: keepSet.size,
2027
- tLIS,
2028
- previousDecision: prev2?.decision
2029
- };
2030
- } catch (e) {
2031
- void e;
2032
- }
1120
+ }
1121
+ function updateElementChildren(el, children) {
1122
+ if (!children) {
1123
+ el.textContent = "";
1124
+ return;
2033
1125
  }
2034
- let anchor = parent.firstChild;
2035
- for (let i = 0; i < keyedVnodes.length; i++) {
2036
- const { key, vnode } = keyedVnodes[i];
2037
- const el = oldKeyMap?.get(key);
2038
- if (el && el.parentElement === parent) {
2039
- if (keepSet.has(i)) {
2040
- if (anchor === el) {
2041
- anchor = el.nextSibling;
2042
- }
2043
- updateElementFromVnode(el, vnode);
2044
- newKeyMap.set(key, el);
2045
- } else {
2046
- parent.insertBefore(el, anchor);
2047
- updateElementFromVnode(el, vnode);
2048
- newKeyMap.set(key, el);
2049
- anchor = el.nextSibling;
2050
- }
1126
+ if (!Array.isArray(children) && (typeof children === "string" || typeof children === "number")) {
1127
+ if (el.childNodes.length === 1 && el.firstChild?.nodeType === 3) {
1128
+ el.firstChild.data = String(children);
2051
1129
  } else {
2052
- const newEl = createDOMNode(vnode);
2053
- if (newEl instanceof Element) {
2054
- parent.insertBefore(newEl, anchor);
2055
- newKeyMap.set(key, newEl);
2056
- anchor = newEl.nextSibling;
2057
- }
1130
+ el.textContent = String(children);
2058
1131
  }
1132
+ return;
2059
1133
  }
2060
- for (const vnode of unkeyedVnodes) {
2061
- const newEl = createDOMNode(vnode);
2062
- if (newEl) {
2063
- parent.appendChild(newEl);
2064
- }
1134
+ if (Array.isArray(children)) {
1135
+ updateUnkeyedChildren(el, children);
1136
+ return;
2065
1137
  }
2066
- return newKeyMap;
1138
+ el.textContent = "";
1139
+ const dom = createDOMNode(children);
1140
+ if (dom) el.appendChild(dom);
2067
1141
  }
2068
- function performBulkKeyedTextReplace(parent, keyedVnodes, oldKeyMap) {
2069
- const total = keyedVnodes.length;
2070
- const finalNodes = [];
2071
- let reused = 0;
2072
- let created = 0;
2073
- const t0 = typeof performance !== "undefined" && performance.now ? performance.now() : Date.now();
2074
- for (let i = 0; i < total; i++) {
2075
- const { key, vnode } = keyedVnodes[i];
2076
- const el = oldKeyMap?.get(key);
2077
- if (el && _isDOMElement(vnode) && typeof vnode.type === "string") {
2078
- const children = vnode.children || vnode.props?.children;
2079
- if (typeof children === "string" || typeof children === "number") {
2080
- if (el.childNodes.length === 1 && el.firstChild?.nodeType === 3) {
2081
- el.firstChild.data = String(children);
2082
- } else {
2083
- el.textContent = String(children);
2084
- }
2085
- } else if (Array.isArray(children) && children.length === 1 && (typeof children[0] === "string" || typeof children[0] === "number")) {
2086
- if (el.childNodes.length === 1 && el.firstChild?.nodeType === 3) {
2087
- el.firstChild.data = String(children[0]);
1142
+ function updateUnkeyedChildren(parent, newChildren) {
1143
+ const existing = Array.from(parent.children);
1144
+ if (existing.length === 0 && parent.childNodes.length > 0) {
1145
+ parent.textContent = "";
1146
+ }
1147
+ const max = Math.max(existing.length, newChildren.length);
1148
+ for (let i = 0; i < max; i++) {
1149
+ const current = existing[i];
1150
+ const next = newChildren[i];
1151
+ if (next === void 0 && current) {
1152
+ cleanupInstanceIfPresent(current);
1153
+ current.remove();
1154
+ continue;
1155
+ }
1156
+ if (!current && next !== void 0) {
1157
+ const dom = createDOMNode(next);
1158
+ if (dom) parent.appendChild(dom);
1159
+ continue;
1160
+ }
1161
+ if (!current || next === void 0) continue;
1162
+ if (typeof next === "string" || typeof next === "number") {
1163
+ current.textContent = String(next);
1164
+ } else if (_isDOMElement(next)) {
1165
+ if (typeof next.type === "string") {
1166
+ if (current.tagName.toLowerCase() === next.type.toLowerCase()) {
1167
+ updateElementFromVnode(current, next);
2088
1168
  } else {
2089
- el.textContent = String(children[0]);
1169
+ const dom = createDOMNode(next);
1170
+ if (dom) {
1171
+ if (current instanceof Element) removeAllListeners(current);
1172
+ cleanupInstanceIfPresent(current);
1173
+ parent.replaceChild(dom, current);
1174
+ }
2090
1175
  }
2091
1176
  } else {
2092
- updateElementFromVnode(el, vnode);
1177
+ const dom = createDOMNode(next);
1178
+ if (dom) {
1179
+ if (current instanceof Element) removeAllListeners(current);
1180
+ cleanupInstanceIfPresent(current);
1181
+ parent.replaceChild(dom, current);
1182
+ }
2093
1183
  }
2094
- finalNodes.push(el);
2095
- reused++;
2096
1184
  } else {
2097
- const dom = createDOMNode(vnode);
1185
+ const dom = createDOMNode(next);
2098
1186
  if (dom) {
2099
- finalNodes.push(dom);
2100
- created++;
2101
- }
2102
- }
2103
- }
2104
- try {
2105
- const toRemove = Array.from(parent.childNodes).filter(
2106
- (n) => !finalNodes.includes(n)
2107
- );
2108
- for (const n of toRemove) cleanupInstanceIfPresent(n);
2109
- } catch (e) {
2110
- void e;
2111
- }
2112
- const existingChildren = Array.from(parent.children);
2113
- const listenerSnapshots = new Array(existingChildren.length).fill(void 0);
2114
- try {
2115
- for (let i = 0; i < existingChildren.length; i++) {
2116
- const ch = existingChildren[i];
2117
- if (ch) {
2118
- const map = elementListeners.get(ch);
2119
- if (map && map.size > 0) {
2120
- const clone = /* @__PURE__ */ new Map();
2121
- for (const [k, v] of map) clone.set(k, v);
2122
- listenerSnapshots[i] = clone;
2123
- }
2124
- }
2125
- }
2126
- } catch (e) {
2127
- void e;
2128
- }
2129
- const fragment = document.createDocumentFragment();
2130
- for (let i = 0; i < finalNodes.length; i++)
2131
- fragment.appendChild(finalNodes[i]);
2132
- parent.replaceChildren(fragment);
2133
- try {
2134
- for (let i = 0; i < finalNodes.length; i++) {
2135
- const newNode = finalNodes[i];
2136
- const snapshot = listenerSnapshots[i];
2137
- if (snapshot && newNode instanceof Element) {
2138
- for (const [eventName, entry] of snapshot) {
2139
- const existing = elementListeners.get(newNode)?.get(eventName);
2140
- if (existing && existing.original === entry.original) continue;
2141
- newNode.addEventListener(eventName, entry.handler);
2142
- if (!elementListeners.has(newNode))
2143
- elementListeners.set(newNode, /* @__PURE__ */ new Map());
2144
- elementListeners.get(newNode).set(eventName, entry);
2145
- }
1187
+ if (current instanceof Element) removeAllListeners(current);
1188
+ cleanupInstanceIfPresent(current);
1189
+ parent.replaceChild(dom, current);
2146
1190
  }
2147
1191
  }
2148
- } catch (e) {
2149
- void e;
2150
- }
2151
- try {
2152
- keyedElements.delete(parent);
2153
- } catch (e) {
2154
- void e;
2155
1192
  }
2156
- const t = typeof performance !== "undefined" && performance.now ? performance.now() - t0 : 0;
2157
- const stats = {
2158
- n: total,
2159
- reused,
2160
- created,
2161
- t
2162
- };
2163
- return stats;
2164
1193
  }
2165
1194
  function performBulkPositionalKeyedTextUpdate(parent, keyedVnodes) {
2166
1195
  const total = keyedVnodes.length;
@@ -2174,6 +1203,18 @@ function performBulkPositionalKeyedTextUpdate(parent, keyedVnodes) {
2174
1203
  const vnodeType = vnode.type;
2175
1204
  if (ch.tagName.toLowerCase() === vnodeType.toLowerCase()) {
2176
1205
  const children = vnode.children || vnode.props?.children;
1206
+ try {
1207
+ if (process.env.ASKR_FASTPATH_DEBUG === "1" || process.env.ASKR_FASTPATH_DEBUG === "true") {
1208
+ logger.warn("[Askr][FASTPATH] positional idx", i, {
1209
+ chTag: ch.tagName.toLowerCase(),
1210
+ vnodeType,
1211
+ chChildNodes: ch.childNodes.length,
1212
+ childrenType: Array.isArray(children) ? "array" : typeof children
1213
+ });
1214
+ }
1215
+ } catch (e) {
1216
+ void e;
1217
+ }
2177
1218
  if (typeof children === "string" || typeof children === "number") {
2178
1219
  if (ch.childNodes.length === 1 && ch.firstChild?.nodeType === 3) {
2179
1220
  ch.firstChild.data = String(children);
@@ -2197,6 +1238,27 @@ function performBulkPositionalKeyedTextUpdate(parent, keyedVnodes) {
2197
1238
  }
2198
1239
  reused++;
2199
1240
  continue;
1241
+ } else {
1242
+ try {
1243
+ if (process.env.ASKR_FASTPATH_DEBUG === "1" || process.env.ASKR_FASTPATH_DEBUG === "true") {
1244
+ logger.warn("[Askr][FASTPATH] positional tag mismatch", i, {
1245
+ chTag: ch.tagName.toLowerCase(),
1246
+ vnodeType
1247
+ });
1248
+ }
1249
+ } catch (e) {
1250
+ void e;
1251
+ }
1252
+ }
1253
+ } else {
1254
+ try {
1255
+ if (process.env.ASKR_FASTPATH_DEBUG === "1" || process.env.ASKR_FASTPATH_DEBUG === "true") {
1256
+ logger.warn("[Askr][FASTPATH] positional missing or invalid", i, {
1257
+ ch: !!ch
1258
+ });
1259
+ }
1260
+ } catch (e) {
1261
+ void e;
2200
1262
  }
2201
1263
  }
2202
1264
  const dom = createDOMNode(vnode);
@@ -2220,12 +1282,99 @@ function performBulkPositionalKeyedTextUpdate(parent, keyedVnodes) {
2220
1282
  } catch (e) {
2221
1283
  void e;
2222
1284
  }
1285
+ const stats = { n: total, reused, updatedKeys, t };
1286
+ try {
1287
+ if (process.env.ASKR_FASTPATH_DEBUG === "1" || process.env.ASKR_FASTPATH_DEBUG === "true") {
1288
+ logger.warn("[Askr][FASTPATH] bulk positional stats", stats);
1289
+ }
1290
+ __ASKR_set("__LAST_FASTPATH_STATS", stats);
1291
+ __ASKR_set("__LAST_FASTPATH_COMMIT_COUNT", 1);
1292
+ __ASKR_incCounter("bulkKeyedPositionalHits");
1293
+ } catch (e) {
1294
+ void e;
1295
+ }
1296
+ return stats;
1297
+ }
1298
+ function performBulkTextReplace(parent, newChildren) {
1299
+ const t0 = typeof performance !== "undefined" && performance.now ? performance.now() : Date.now();
1300
+ const existing = Array.from(parent.childNodes);
1301
+ const finalNodes = [];
1302
+ let reused = 0;
1303
+ let created = 0;
1304
+ for (let i = 0; i < newChildren.length; i++) {
1305
+ const vnode = newChildren[i];
1306
+ const existingNode = existing[i];
1307
+ if (typeof vnode === "string" || typeof vnode === "number") {
1308
+ const text = String(vnode);
1309
+ if (existingNode && existingNode.nodeType === 3) {
1310
+ existingNode.data = text;
1311
+ finalNodes.push(existingNode);
1312
+ reused++;
1313
+ } else {
1314
+ finalNodes.push(document.createTextNode(text));
1315
+ created++;
1316
+ }
1317
+ continue;
1318
+ }
1319
+ if (typeof vnode === "object" && vnode !== null && "type" in vnode) {
1320
+ const vnodeObj = vnode;
1321
+ if (typeof vnodeObj.type === "string") {
1322
+ const tag = vnodeObj.type;
1323
+ if (existingNode && existingNode.nodeType === 1 && existingNode.tagName.toLowerCase() === tag.toLowerCase()) {
1324
+ updateElementFromVnode(existingNode, vnode);
1325
+ finalNodes.push(existingNode);
1326
+ reused++;
1327
+ continue;
1328
+ }
1329
+ }
1330
+ const dom = createDOMNode(vnode);
1331
+ if (dom) {
1332
+ finalNodes.push(dom);
1333
+ created++;
1334
+ continue;
1335
+ }
1336
+ }
1337
+ }
1338
+ const tBuild = (typeof performance !== "undefined" && performance.now ? performance.now() : Date.now()) - t0;
1339
+ try {
1340
+ const toRemove = Array.from(parent.childNodes).filter(
1341
+ (n) => !finalNodes.includes(n)
1342
+ );
1343
+ for (const n of toRemove) {
1344
+ if (n instanceof Element) removeAllListeners(n);
1345
+ cleanupInstanceIfPresent(n);
1346
+ }
1347
+ } catch (e) {
1348
+ void e;
1349
+ }
1350
+ const fragStart = Date.now();
1351
+ const fragment = document.createDocumentFragment();
1352
+ for (let i = 0; i < finalNodes.length; i++)
1353
+ fragment.appendChild(finalNodes[i]);
1354
+ try {
1355
+ __ASKR_incCounter("__DOM_REPLACE_COUNT");
1356
+ __ASKR_set("__LAST_DOM_REPLACE_STACK_DOM", new Error().stack);
1357
+ } catch (e) {
1358
+ void e;
1359
+ }
1360
+ parent.replaceChildren(fragment);
1361
+ const tCommit = Date.now() - fragStart;
1362
+ keyedElements.delete(parent);
2223
1363
  const stats = {
2224
- n: total,
1364
+ n: newChildren.length,
2225
1365
  reused,
2226
- updatedKeys,
2227
- t
1366
+ created,
1367
+ tBuild,
1368
+ tCommit
2228
1369
  };
1370
+ try {
1371
+ __ASKR_set("__LAST_BULK_TEXT_FASTPATH_STATS", stats);
1372
+ __ASKR_set("__LAST_FASTPATH_STATS", stats);
1373
+ __ASKR_set("__LAST_FASTPATH_COMMIT_COUNT", 1);
1374
+ __ASKR_incCounter("bulkTextFastpathHits");
1375
+ } catch (e) {
1376
+ void e;
1377
+ }
2229
1378
  return stats;
2230
1379
  }
2231
1380
  function isBulkTextFastPathEligible(parent, newChildren) {
@@ -2235,12 +1384,12 @@ function isBulkTextFastPathEligible(parent, newChildren) {
2235
1384
  if (total < threshold) {
2236
1385
  if (process.env.NODE_ENV !== "production" || process.env.ASKR_FASTPATH_DEBUG === "1") {
2237
1386
  try {
2238
- globalThis.__ASKR_BULK_DIAG = {
1387
+ __ASKR_set("__BULK_DIAG", {
2239
1388
  phase: "bulk-unkeyed-eligible",
2240
1389
  reason: "too-small",
2241
1390
  total,
2242
1391
  threshold
2243
- };
1392
+ });
2244
1393
  } catch (e) {
2245
1394
  void e;
2246
1395
  }
@@ -2259,11 +1408,11 @@ function isBulkTextFastPathEligible(parent, newChildren) {
2259
1408
  if (typeof dv.type === "function") {
2260
1409
  if (process.env.NODE_ENV !== "production" || process.env.ASKR_FASTPATH_DEBUG === "1") {
2261
1410
  try {
2262
- globalThis.__ASKR_BULK_DIAG = {
1411
+ __ASKR_set("__BULK_DIAG", {
2263
1412
  phase: "bulk-unkeyed-eligible",
2264
1413
  reason: "component-child",
2265
1414
  index: i
2266
- };
1415
+ });
2267
1416
  } catch (e) {
2268
1417
  void e;
2269
1418
  }
@@ -2292,438 +1441,806 @@ function isBulkTextFastPathEligible(parent, newChildren) {
2292
1441
  const eligible = fraction >= requiredFraction && parent.childNodes.length >= total;
2293
1442
  if (process.env.NODE_ENV !== "production" || process.env.ASKR_FASTPATH_DEBUG === "1") {
2294
1443
  try {
2295
- globalThis.__ASKR_BULK_DIAG = {
1444
+ __ASKR_set("__BULK_DIAG", {
2296
1445
  phase: "bulk-unkeyed-eligible",
2297
1446
  total,
2298
1447
  simple,
2299
1448
  fraction,
2300
1449
  requiredFraction,
2301
1450
  eligible
2302
- };
1451
+ });
2303
1452
  } catch (e) {
2304
1453
  void e;
2305
1454
  }
2306
1455
  }
2307
1456
  return eligible;
2308
1457
  }
2309
- function performBulkTextReplace(parent, newChildren) {
2310
- const t0 = typeof performance !== "undefined" && performance.now ? performance.now() : Date.now();
2311
- const existing = Array.from(parent.childNodes);
2312
- const finalNodes = [];
2313
- let reused = 0;
2314
- let created = 0;
2315
- for (let i = 0; i < newChildren.length; i++) {
2316
- const vnode = newChildren[i];
2317
- const existingNode = existing[i];
2318
- if (typeof vnode === "string" || typeof vnode === "number") {
2319
- const text = String(vnode);
2320
- if (existingNode && existingNode.nodeType === 3) {
2321
- existingNode.data = text;
2322
- finalNodes.push(existingNode);
2323
- reused++;
2324
- } else {
2325
- finalNodes.push(document.createTextNode(text));
2326
- created++;
2327
- }
2328
- continue;
1458
+ var IS_DOM_AVAILABLE;
1459
+ var init_dom = __esm({
1460
+ "src/renderer/dom.ts"() {
1461
+ "use strict";
1462
+ init_scheduler();
1463
+ init_logger();
1464
+ init_jsx_runtime();
1465
+ init_context();
1466
+ init_component();
1467
+ init_cleanup();
1468
+ init_diag();
1469
+ init_types();
1470
+ init_keyed();
1471
+ IS_DOM_AVAILABLE = typeof document !== "undefined";
1472
+ }
1473
+ });
1474
+
1475
+ // src/renderer/fastpath.ts
1476
+ function applyRendererFastPath(parent, keyedVnodes, oldKeyMap, unkeyedVnodes) {
1477
+ if (typeof document === "undefined") return null;
1478
+ const totalKeyed = keyedVnodes.length;
1479
+ if (totalKeyed === 0 && (!unkeyedVnodes || unkeyedVnodes.length === 0))
1480
+ return null;
1481
+ if (!isSchedulerExecuting()) {
1482
+ logger.warn(
1483
+ "[Askr][FASTPATH][DEV] Fast-path reconciliation invoked outside scheduler execution"
1484
+ );
1485
+ }
1486
+ let parentChildrenArr;
1487
+ let localOldKeyMap;
1488
+ if (totalKeyed <= 20) {
1489
+ try {
1490
+ const pc = parent.children;
1491
+ parentChildrenArr = new Array(pc.length);
1492
+ for (let i = 0; i < pc.length; i++)
1493
+ parentChildrenArr[i] = pc[i];
1494
+ } catch (e) {
1495
+ parentChildrenArr = void 0;
1496
+ void e;
2329
1497
  }
2330
- if (typeof vnode === "object" && vnode !== null && "type" in vnode) {
2331
- if (typeof vnode.type === "string") {
2332
- const vtype = vnode.type;
2333
- if (existingNode && existingNode.nodeType === 1 && existingNode.tagName.toLowerCase() === vtype.toLowerCase()) {
2334
- const el = existingNode;
2335
- const children = vnode.children || vnode.props?.children;
2336
- if (typeof children === "string" || typeof children === "number") {
2337
- if (el.childNodes.length === 1 && el.firstChild?.nodeType === 3) {
2338
- el.firstChild.data = String(children);
2339
- } else {
2340
- el.textContent = String(children);
2341
- }
2342
- } else if (Array.isArray(children) && children.length === 1 && (typeof children[0] === "string" || typeof children[0] === "number")) {
2343
- if (el.childNodes.length === 1 && el.firstChild?.nodeType === 3) {
2344
- el.firstChild.data = String(children[0]);
2345
- } else {
2346
- el.textContent = String(children[0]);
2347
- }
2348
- } else {
2349
- updateElementFromVnode(el, vnode);
2350
- }
2351
- finalNodes.push(el);
2352
- reused++;
2353
- continue;
2354
- }
2355
- const dom2 = createDOMNode(vnode);
2356
- if (dom2) {
2357
- finalNodes.push(dom2);
2358
- created++;
1498
+ } else {
1499
+ localOldKeyMap = /* @__PURE__ */ new Map();
1500
+ try {
1501
+ const parentChildren = Array.from(parent.children);
1502
+ for (let i = 0; i < parentChildren.length; i++) {
1503
+ const ch = parentChildren[i];
1504
+ const k = ch.getAttribute("data-key");
1505
+ if (k !== null) {
1506
+ localOldKeyMap.set(k, ch);
1507
+ const n = Number(k);
1508
+ if (!Number.isNaN(n)) localOldKeyMap.set(n, ch);
2359
1509
  }
2360
- continue;
2361
1510
  }
1511
+ } catch (e) {
1512
+ localOldKeyMap = void 0;
1513
+ void e;
2362
1514
  }
2363
- const dom = createDOMNode(vnode);
2364
- if (dom) {
2365
- finalNodes.push(dom);
2366
- created++;
2367
- }
2368
- }
2369
- const tBuild = typeof performance !== "undefined" && performance.now ? performance.now() - t0 : 0;
2370
- try {
2371
- const toRemove = Array.from(parent.childNodes).filter(
2372
- (n) => !finalNodes.includes(n)
2373
- );
2374
- for (const n of toRemove) cleanupInstanceIfPresent(n);
2375
- } catch (e) {
2376
- void e;
2377
1515
  }
2378
- const fragStart = Date.now();
2379
- const fragment = document.createDocumentFragment();
2380
- for (let i = 0; i < finalNodes.length; i++)
2381
- fragment.appendChild(finalNodes[i]);
2382
- parent.replaceChildren(fragment);
2383
- const tCommit = Date.now() - fragStart;
2384
- keyedElements.delete(parent);
2385
- const stats = {
2386
- n: newChildren.length,
2387
- reused,
2388
- created,
2389
- tBuild,
2390
- tCommit
2391
- };
2392
- return stats;
2393
- }
2394
- function updateElementFromVnode(el, vnode, updateChildren = true) {
2395
- if (!_isDOMElement(vnode)) {
2396
- return;
2397
- }
2398
- const props = vnode.props || {};
2399
- if (vnode.key !== void 0) {
2400
- el.setAttribute("data-key", String(vnode.key));
2401
- }
2402
- const existingListeners = elementListeners.get(el);
2403
- const desiredEventNames = /* @__PURE__ */ new Set();
2404
- for (const key in props) {
2405
- const value = props[key];
2406
- if (key === "children" || key === "key") continue;
2407
- if (value === void 0 || value === null || value === false) {
2408
- if (key === "class" || key === "className") {
2409
- el.className = "";
2410
- } else if (key.startsWith("on") && key.length > 2) {
2411
- const eventName = key.slice(2).charAt(0).toLowerCase() + key.slice(3).toLowerCase();
2412
- if (existingListeners && existingListeners.has(eventName)) {
2413
- const entry = existingListeners.get(eventName);
2414
- el.removeEventListener(eventName, entry.handler);
2415
- existingListeners.delete(eventName);
1516
+ const finalNodes = [];
1517
+ let mapLookups = 0;
1518
+ let createdNodes = 0;
1519
+ let reusedCount = 0;
1520
+ for (let i = 0; i < keyedVnodes.length; i++) {
1521
+ const { key, vnode } = keyedVnodes[i];
1522
+ mapLookups++;
1523
+ let el;
1524
+ if (totalKeyed <= 20 && parentChildrenArr) {
1525
+ const ks = String(key);
1526
+ for (let j = 0; j < parentChildrenArr.length; j++) {
1527
+ const ch = parentChildrenArr[j];
1528
+ const k = ch.getAttribute("data-key");
1529
+ if (k !== null && (k === ks || Number(k) === key)) {
1530
+ el = ch;
1531
+ break;
2416
1532
  }
2417
- continue;
2418
- } else {
2419
- el.removeAttribute(key);
2420
1533
  }
2421
- continue;
1534
+ if (!el) el = oldKeyMap?.get(key);
1535
+ } else {
1536
+ el = localOldKeyMap?.get(key) ?? oldKeyMap?.get(key);
2422
1537
  }
2423
- if (key === "class" || key === "className") {
2424
- el.className = String(value);
2425
- } else if (key === "value" || key === "checked") {
2426
- el[key] = value;
2427
- } else if (key.startsWith("on") && key.length > 2) {
2428
- const eventName = key.slice(2).charAt(0).toLowerCase() + key.slice(3).toLowerCase();
2429
- desiredEventNames.add(eventName);
2430
- const existing = existingListeners?.get(eventName);
2431
- if (existing && existing.original === value) {
2432
- continue;
2433
- }
2434
- if (existing) {
2435
- el.removeEventListener(eventName, existing.handler);
2436
- }
2437
- const wrappedHandler = (event) => {
2438
- globalScheduler.setInHandler(true);
2439
- try {
2440
- value(event);
2441
- } catch (error) {
2442
- logger.error("[Askr] Event handler error:", error);
2443
- } finally {
2444
- globalScheduler.setInHandler(false);
2445
- }
2446
- };
2447
- el.addEventListener(eventName, wrappedHandler);
2448
- if (!elementListeners.has(el)) {
2449
- elementListeners.set(el, /* @__PURE__ */ new Map());
2450
- }
2451
- elementListeners.get(el).set(eventName, {
2452
- handler: wrappedHandler,
2453
- original: value
2454
- });
1538
+ if (el) {
1539
+ finalNodes.push(el);
1540
+ reusedCount++;
2455
1541
  } else {
2456
- el.setAttribute(key, String(value));
1542
+ const newEl = createDOMNode(vnode);
1543
+ if (newEl) {
1544
+ finalNodes.push(newEl);
1545
+ createdNodes++;
1546
+ }
2457
1547
  }
2458
1548
  }
2459
- if (existingListeners) {
2460
- for (const eventName of existingListeners.keys()) {
2461
- const entry = existingListeners.get(eventName);
2462
- if (!desiredEventNames.has(eventName)) {
2463
- el.removeEventListener(eventName, entry.handler);
2464
- existingListeners.delete(eventName);
1549
+ if (unkeyedVnodes && unkeyedVnodes.length) {
1550
+ for (const vnode of unkeyedVnodes) {
1551
+ const newEl = createDOMNode(vnode);
1552
+ if (newEl) {
1553
+ finalNodes.push(newEl);
1554
+ createdNodes++;
2465
1555
  }
2466
1556
  }
2467
- if (existingListeners.size === 0) elementListeners.delete(el);
2468
1557
  }
2469
- if (updateChildren) {
2470
- const children = vnode.children || props.children;
2471
- updateElementChildren(el, children);
1558
+ try {
1559
+ const tFragmentStart = Date.now();
1560
+ const fragment = document.createDocumentFragment();
1561
+ let fragmentAppendCount = 0;
1562
+ for (let i = 0; i < finalNodes.length; i++) {
1563
+ fragment.appendChild(finalNodes[i]);
1564
+ fragmentAppendCount++;
1565
+ }
1566
+ try {
1567
+ const existing = Array.from(parent.childNodes);
1568
+ const toRemove = existing.filter((n) => !finalNodes.includes(n));
1569
+ for (const n of toRemove) {
1570
+ if (n instanceof Element) removeAllListeners(n);
1571
+ cleanupInstanceIfPresent(n);
1572
+ }
1573
+ } catch (e) {
1574
+ void e;
1575
+ }
1576
+ try {
1577
+ __ASKR_incCounter("__DOM_REPLACE_COUNT");
1578
+ __ASKR_set("__LAST_DOM_REPLACE_STACK_FASTPATH", new Error().stack);
1579
+ } catch (e) {
1580
+ void e;
1581
+ }
1582
+ parent.replaceChildren(fragment);
1583
+ try {
1584
+ __ASKR_set("__LAST_FASTPATH_COMMIT_COUNT", 1);
1585
+ } catch (e) {
1586
+ void e;
1587
+ }
1588
+ try {
1589
+ if (isBulkCommitActive2()) markFastPathApplied(parent);
1590
+ } catch (e) {
1591
+ void e;
1592
+ }
1593
+ const newKeyMap = /* @__PURE__ */ new Map();
1594
+ for (let i = 0; i < keyedVnodes.length; i++) {
1595
+ const key = keyedVnodes[i].key;
1596
+ const node = finalNodes[i];
1597
+ if (node instanceof Element) newKeyMap.set(key, node);
1598
+ }
1599
+ try {
1600
+ const stats = {
1601
+ n: totalKeyed,
1602
+ moves: 0,
1603
+ lisLen: 0,
1604
+ t_lookup: 0,
1605
+ t_fragment: Date.now() - tFragmentStart,
1606
+ t_commit: 0,
1607
+ t_bookkeeping: 0,
1608
+ fragmentAppendCount,
1609
+ mapLookups,
1610
+ createdNodes,
1611
+ reusedCount
1612
+ };
1613
+ if (typeof globalThis !== "undefined") {
1614
+ __ASKR_set("__LAST_FASTPATH_STATS", stats);
1615
+ __ASKR_set("__LAST_FASTPATH_REUSED", reusedCount > 0);
1616
+ __ASKR_incCounter("fastpathHistoryPush");
1617
+ }
1618
+ if (process.env.ASKR_FASTPATH_DEBUG === "1" || process.env.ASKR_FASTPATH_DEBUG === "true") {
1619
+ logger.warn(
1620
+ "[Askr][FASTPATH]",
1621
+ JSON.stringify({ n: totalKeyed, createdNodes, reusedCount })
1622
+ );
1623
+ }
1624
+ } catch (e) {
1625
+ void e;
1626
+ }
1627
+ try {
1628
+ _reconcilerRecordedParents.add(parent);
1629
+ } catch (e) {
1630
+ void e;
1631
+ }
1632
+ return newKeyMap;
1633
+ } catch (e) {
1634
+ void e;
1635
+ return null;
2472
1636
  }
2473
1637
  }
2474
- function updateElementChildren(el, children) {
2475
- if (!children) {
2476
- el.textContent = "";
2477
- return;
1638
+ var init_fastpath = __esm({
1639
+ "src/renderer/fastpath.ts"() {
1640
+ "use strict";
1641
+ init_dom();
1642
+ init_keyed();
1643
+ init_logger();
1644
+ init_cleanup();
1645
+ init_diag();
1646
+ init_scheduler();
1647
+ init_fastlane_shared();
2478
1648
  }
2479
- if (!Array.isArray(children) && (typeof children === "string" || typeof children === "number")) {
2480
- if (el.childNodes.length === 1 && el.firstChild?.nodeType === 3) {
2481
- el.firstChild.data = String(children);
1649
+ });
1650
+
1651
+ // src/renderer/reconcile.ts
1652
+ function reconcileKeyedChildren(parent, newChildren, oldKeyMap) {
1653
+ const newKeyMap = /* @__PURE__ */ new Map();
1654
+ const keyedVnodes = [];
1655
+ const unkeyedVnodes = [];
1656
+ for (let i = 0; i < newChildren.length; i++) {
1657
+ const child = newChildren[i];
1658
+ if (typeof child === "object" && child !== null && "type" in child) {
1659
+ const childObj = child;
1660
+ const rawKey = childObj.key ?? childObj.props?.key;
1661
+ if (rawKey !== void 0) {
1662
+ const key = typeof rawKey === "symbol" ? String(rawKey) : rawKey;
1663
+ keyedVnodes.push({ key, vnode: child });
1664
+ } else {
1665
+ unkeyedVnodes.push(child);
1666
+ }
2482
1667
  } else {
2483
- el.textContent = String(children);
1668
+ unkeyedVnodes.push(child);
2484
1669
  }
2485
- return;
2486
1670
  }
2487
- if (Array.isArray(children)) {
2488
- updateUnkeyedChildren(el, children);
2489
- return;
2490
- }
2491
- el.textContent = "";
2492
- const dom = createDOMNode(children);
2493
- if (dom) el.appendChild(dom);
2494
- }
2495
- function updateUnkeyedChildren(parent, newChildren) {
2496
- const existing = Array.from(parent.children);
2497
- if (existing.length === 0 && parent.childNodes.length > 0) {
2498
- parent.textContent = "";
1671
+ try {
1672
+ const decision = isKeyedReorderFastPathEligible(
1673
+ parent,
1674
+ newChildren,
1675
+ oldKeyMap
1676
+ );
1677
+ if (decision.useFastPath && keyedVnodes.length >= 128 || // If we're executing inside a runtime bulk commit (fastlane), prefer the
1678
+ // renderer fast-path to ensure the single-commit invariant is preserved.
1679
+ isBulkCommitActive2()) {
1680
+ try {
1681
+ const map = applyRendererFastPath(
1682
+ parent,
1683
+ keyedVnodes,
1684
+ oldKeyMap,
1685
+ unkeyedVnodes
1686
+ );
1687
+ if (map) {
1688
+ try {
1689
+ keyedElements.set(parent, map);
1690
+ } catch (e) {
1691
+ void e;
1692
+ }
1693
+ return map;
1694
+ }
1695
+ } catch (e) {
1696
+ void e;
1697
+ }
1698
+ }
1699
+ try {
1700
+ const total = keyedVnodes.length;
1701
+ if (total >= 10) {
1702
+ let matchCount = 0;
1703
+ try {
1704
+ for (let i = 0; i < total; i++) {
1705
+ const vnode = keyedVnodes[i].vnode;
1706
+ if (!vnode || typeof vnode !== "object" || typeof vnode.type !== "string")
1707
+ continue;
1708
+ const el = parent.children[i];
1709
+ if (!el) continue;
1710
+ if (el.tagName.toLowerCase() === String(vnode.type).toLowerCase())
1711
+ matchCount++;
1712
+ }
1713
+ } catch (e) {
1714
+ void e;
1715
+ }
1716
+ if (matchCount / total >= 0.9) {
1717
+ let hasPropChanges = false;
1718
+ try {
1719
+ for (let i = 0; i < total; i++) {
1720
+ const vnode = keyedVnodes[i].vnode;
1721
+ const el = parent.children[i];
1722
+ if (!el || !vnode || typeof vnode !== "object") continue;
1723
+ const props = vnode.props || {};
1724
+ for (const k of Object.keys(props)) {
1725
+ if (k === "children" || k === "key") continue;
1726
+ if (k.startsWith("on") && k.length > 2) continue;
1727
+ if (k.startsWith("data-")) continue;
1728
+ const v = props[k];
1729
+ try {
1730
+ if (k === "class" || k === "className") {
1731
+ if (el.className !== String(v)) {
1732
+ hasPropChanges = true;
1733
+ break;
1734
+ }
1735
+ } else if (k === "value" || k === "checked") {
1736
+ if (el[k] !== v) {
1737
+ hasPropChanges = true;
1738
+ break;
1739
+ }
1740
+ } else {
1741
+ const attr = el.getAttribute(k);
1742
+ if (v === void 0 || v === null || v === false) {
1743
+ if (attr !== null) {
1744
+ hasPropChanges = true;
1745
+ break;
1746
+ }
1747
+ } else if (String(v) !== attr) {
1748
+ hasPropChanges = true;
1749
+ break;
1750
+ }
1751
+ }
1752
+ } catch (e) {
1753
+ hasPropChanges = true;
1754
+ void e;
1755
+ break;
1756
+ }
1757
+ }
1758
+ if (hasPropChanges) break;
1759
+ }
1760
+ } catch (e) {
1761
+ void e;
1762
+ }
1763
+ if (hasPropChanges) {
1764
+ } else {
1765
+ try {
1766
+ const stats = performBulkPositionalKeyedTextUpdate(
1767
+ parent,
1768
+ keyedVnodes
1769
+ );
1770
+ if (process.env.NODE_ENV !== "production" || process.env.ASKR_FASTPATH_DEBUG === "1") {
1771
+ try {
1772
+ __ASKR_set("__LAST_FASTPATH_STATS", stats);
1773
+ __ASKR_set("__LAST_FASTPATH_COMMIT_COUNT", 1);
1774
+ __ASKR_incCounter("bulkKeyedPositionalHits");
1775
+ } catch (e) {
1776
+ void e;
1777
+ }
1778
+ }
1779
+ try {
1780
+ const map = /* @__PURE__ */ new Map();
1781
+ const children = Array.from(parent.children);
1782
+ for (let i = 0; i < children.length; i++) {
1783
+ const el = children[i];
1784
+ const k = el.getAttribute("data-key");
1785
+ if (k !== null) {
1786
+ map.set(k, el);
1787
+ const n = Number(k);
1788
+ if (!Number.isNaN(n)) map.set(n, el);
1789
+ }
1790
+ }
1791
+ keyedElements.set(parent, map);
1792
+ } catch (e) {
1793
+ void e;
1794
+ }
1795
+ return keyedElements.get(parent);
1796
+ } catch (e) {
1797
+ void e;
1798
+ }
1799
+ }
1800
+ }
1801
+ }
1802
+ } catch (e) {
1803
+ void e;
1804
+ }
1805
+ } catch (e) {
1806
+ void e;
2499
1807
  }
2500
- const max = Math.max(existing.length, newChildren.length);
2501
- for (let i = 0; i < max; i++) {
2502
- const current = existing[i];
2503
- const next = newChildren[i];
2504
- if (next === void 0 && current) {
2505
- cleanupInstanceIfPresent(current);
2506
- current.remove();
2507
- continue;
1808
+ const finalNodes = [];
1809
+ const usedOldEls = /* @__PURE__ */ new WeakSet();
1810
+ const resolveOldElOnce = (k) => {
1811
+ if (!oldKeyMap) return void 0;
1812
+ const direct = oldKeyMap.get(k);
1813
+ if (direct && !usedOldEls.has(direct)) {
1814
+ usedOldEls.add(direct);
1815
+ return direct;
1816
+ }
1817
+ const s = String(k);
1818
+ const byString = oldKeyMap.get(s);
1819
+ if (byString && !usedOldEls.has(byString)) {
1820
+ usedOldEls.add(byString);
1821
+ return byString;
1822
+ }
1823
+ const n = Number(String(k));
1824
+ if (!Number.isNaN(n)) {
1825
+ const byNum = oldKeyMap.get(n);
1826
+ if (byNum && !usedOldEls.has(byNum)) {
1827
+ usedOldEls.add(byNum);
1828
+ return byNum;
1829
+ }
2508
1830
  }
2509
- if (!current && next !== void 0) {
2510
- const dom = createDOMNode(next);
2511
- if (dom) parent.appendChild(dom);
2512
- continue;
1831
+ try {
1832
+ const children = Array.from(parent.children);
1833
+ for (const ch of children) {
1834
+ if (usedOldEls.has(ch)) continue;
1835
+ const attr = ch.getAttribute("data-key");
1836
+ if (attr === s) {
1837
+ usedOldEls.add(ch);
1838
+ return ch;
1839
+ }
1840
+ const numAttr = Number(attr);
1841
+ if (!Number.isNaN(numAttr) && numAttr === k) {
1842
+ usedOldEls.add(ch);
1843
+ return ch;
1844
+ }
1845
+ }
1846
+ } catch (e) {
1847
+ void e;
2513
1848
  }
2514
- if (!current || next === void 0) continue;
2515
- if (typeof next === "string" || typeof next === "number") {
2516
- current.textContent = String(next);
2517
- } else if (_isDOMElement(next)) {
2518
- if (typeof next.type === "string") {
2519
- if (current.tagName.toLowerCase() === next.type.toLowerCase()) {
2520
- updateElementFromVnode(current, next);
2521
- } else {
2522
- const dom = createDOMNode(next);
2523
- if (dom) {
2524
- cleanupInstanceIfPresent(current);
2525
- parent.replaceChild(dom, current);
2526
- }
1849
+ return void 0;
1850
+ };
1851
+ for (let i = 0; i < newChildren.length; i++) {
1852
+ const child = newChildren[i];
1853
+ if (typeof child === "object" && child !== null && "type" in child) {
1854
+ const childObj = child;
1855
+ const rawKey = childObj.key ?? childObj.props?.key;
1856
+ if (rawKey !== void 0) {
1857
+ const key = typeof rawKey === "symbol" ? String(rawKey) : rawKey;
1858
+ const el = resolveOldElOnce(key);
1859
+ if (el && el.parentElement === parent) {
1860
+ updateElementFromVnode(el, child);
1861
+ finalNodes.push(el);
1862
+ newKeyMap.set(key, el);
1863
+ continue;
2527
1864
  }
2528
- } else {
2529
- const dom = createDOMNode(next);
2530
- if (dom) {
2531
- cleanupInstanceIfPresent(current);
2532
- parent.replaceChild(dom, current);
1865
+ const dom2 = createDOMNode(child);
1866
+ if (dom2) {
1867
+ finalNodes.push(dom2);
1868
+ if (dom2 instanceof Element) newKeyMap.set(key, dom2);
2533
1869
  }
1870
+ continue;
2534
1871
  }
2535
- } else {
2536
- const dom = createDOMNode(next);
2537
- if (dom) {
2538
- cleanupInstanceIfPresent(current);
2539
- parent.replaceChild(dom, current);
1872
+ }
1873
+ try {
1874
+ const existing = parent.children[i];
1875
+ if (existing && (typeof child === "string" || typeof child === "number") && existing.nodeType === 1) {
1876
+ existing.textContent = String(child);
1877
+ finalNodes.push(existing);
1878
+ usedOldEls.add(existing);
1879
+ continue;
1880
+ }
1881
+ 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()) {
1882
+ updateElementFromVnode(existing, child);
1883
+ finalNodes.push(existing);
1884
+ usedOldEls.add(existing);
1885
+ continue;
1886
+ }
1887
+ try {
1888
+ const avail = Array.from(parent.children).find(
1889
+ (ch) => !usedOldEls.has(ch) && ch.getAttribute("data-key") === null
1890
+ );
1891
+ if (avail) {
1892
+ if (typeof child === "string" || typeof child === "number") {
1893
+ avail.textContent = String(child);
1894
+ } else if (typeof child === "object" && child !== null && "type" in child && typeof child.type === "string" && avail.tagName.toLowerCase() === String(child.type).toLowerCase()) {
1895
+ updateElementFromVnode(avail, child);
1896
+ } else {
1897
+ const dom2 = createDOMNode(child);
1898
+ if (dom2) {
1899
+ finalNodes.push(dom2);
1900
+ continue;
1901
+ }
1902
+ }
1903
+ usedOldEls.add(avail);
1904
+ finalNodes.push(avail);
1905
+ continue;
1906
+ }
1907
+ } catch (e) {
1908
+ void e;
2540
1909
  }
1910
+ } catch (e) {
1911
+ void e;
2541
1912
  }
1913
+ const dom = createDOMNode(child);
1914
+ if (dom) finalNodes.push(dom);
2542
1915
  }
2543
- }
2544
- function createDOMNode(node) {
2545
- if (typeof node === "string") {
2546
- return document.createTextNode(node);
1916
+ if (typeof document === "undefined") return newKeyMap;
1917
+ const fragment = document.createDocumentFragment();
1918
+ for (let i = 0; i < finalNodes.length; i++)
1919
+ fragment.appendChild(finalNodes[i]);
1920
+ try {
1921
+ const existing = Array.from(parent.childNodes);
1922
+ for (const n of existing) {
1923
+ if (n instanceof Element) removeAllListeners(n);
1924
+ cleanupInstanceIfPresent(n);
1925
+ }
1926
+ } catch (e) {
1927
+ void e;
2547
1928
  }
2548
- if (typeof node === "number") {
2549
- return document.createTextNode(String(node));
1929
+ try {
1930
+ __ASKR_incCounter("__DOM_REPLACE_COUNT");
1931
+ __ASKR_set("__LAST_DOM_REPLACE_STACK_RECONCILE", new Error().stack);
1932
+ } catch (e) {
1933
+ void e;
2550
1934
  }
2551
- if (!node) {
2552
- return null;
1935
+ parent.replaceChildren(fragment);
1936
+ keyedElements.delete(parent);
1937
+ return newKeyMap;
1938
+ }
1939
+ var init_reconcile = __esm({
1940
+ "src/renderer/reconcile.ts"() {
1941
+ "use strict";
1942
+ init_dom();
1943
+ init_keyed();
1944
+ init_cleanup();
1945
+ init_fastlane_shared();
1946
+ init_diag();
1947
+ init_fastpath();
2553
1948
  }
2554
- if (Array.isArray(node)) {
2555
- const fragment = document.createDocumentFragment();
2556
- for (let i = 0; i < node.length; i++) {
2557
- const dom = createDOMNode(node[i]);
2558
- if (dom) fragment.appendChild(dom);
1949
+ });
1950
+
1951
+ // src/renderer/evaluate.ts
1952
+ function evaluate(node, target, context) {
1953
+ if (!target) return;
1954
+ if (typeof document === "undefined") {
1955
+ if (process.env.NODE_ENV !== "production") {
1956
+ try {
1957
+ console.warn("[Askr] evaluate() called in non-DOM environment; no-op.");
1958
+ } catch (e) {
1959
+ void e;
1960
+ }
2559
1961
  }
2560
- return fragment;
1962
+ return;
2561
1963
  }
2562
- if (typeof node === "object" && node !== null && "type" in node) {
2563
- const type = node.type;
2564
- const props = node.props || {};
2565
- if (typeof type === "string") {
2566
- const el = document.createElement(type);
2567
- for (const key in props) {
2568
- const value = props[key];
2569
- if (key === "children" || key === "key") continue;
2570
- if (value === void 0 || value === null || value === false) continue;
2571
- if (key.startsWith("on") && key.length > 2) {
2572
- const eventName = key.slice(2).charAt(0).toLowerCase() + key.slice(3).toLowerCase();
2573
- const wrappedHandler = (event) => {
2574
- globalScheduler.setInHandler(true);
2575
- try {
2576
- value(event);
2577
- } catch (error) {
2578
- logger.error("[Askr] Event handler error:", error);
2579
- } finally {
2580
- globalScheduler.setInHandler(false);
2581
- const state = globalScheduler.getState();
2582
- if ((state.queueLength ?? 0) > 0 && !state.running) {
2583
- queueMicrotask(() => {
1964
+ if (context && domRanges.has(context)) {
1965
+ const range = domRanges.get(context);
1966
+ let current = range.start.nextSibling;
1967
+ while (current && current !== range.end) {
1968
+ const next = current.nextSibling;
1969
+ current.remove();
1970
+ current = next;
1971
+ }
1972
+ const dom = createDOMNode(node);
1973
+ if (dom) {
1974
+ target.insertBefore(dom, range.end);
1975
+ }
1976
+ } else if (context) {
1977
+ const start = document.createComment("component-start");
1978
+ const end = document.createComment("component-end");
1979
+ target.appendChild(start);
1980
+ target.appendChild(end);
1981
+ domRanges.set(context, { start, end });
1982
+ const dom = createDOMNode(node);
1983
+ if (dom) {
1984
+ target.insertBefore(dom, end);
1985
+ }
1986
+ } else {
1987
+ const vnode = node;
1988
+ const firstChild = target.children[0];
1989
+ if (firstChild && _isDOMElement(vnode) && typeof vnode.type === "string" && firstChild.tagName.toLowerCase() === vnode.type.toLowerCase()) {
1990
+ const vnodeChildren = vnode.children || vnode.props?.children;
1991
+ let isSimpleTextVNode = false;
1992
+ let textContent;
1993
+ if (!Array.isArray(vnodeChildren)) {
1994
+ if (typeof vnodeChildren === "string" || typeof vnodeChildren === "number") {
1995
+ isSimpleTextVNode = true;
1996
+ textContent = String(vnodeChildren);
1997
+ }
1998
+ } else if (vnodeChildren.length === 1) {
1999
+ const child = vnodeChildren[0];
2000
+ if (typeof child === "string" || typeof child === "number") {
2001
+ isSimpleTextVNode = true;
2002
+ textContent = String(child);
2003
+ }
2004
+ }
2005
+ if (isSimpleTextVNode && firstChild.childNodes.length === 1 && firstChild.firstChild?.nodeType === 3) {
2006
+ firstChild.firstChild.data = textContent;
2007
+ } else {
2008
+ if (vnodeChildren) {
2009
+ if (Array.isArray(vnodeChildren)) {
2010
+ const hasKeys = vnodeChildren.some(
2011
+ (child) => typeof child === "object" && child !== null && "key" in child
2012
+ );
2013
+ if (hasKeys) {
2014
+ let oldKeyMap = keyedElements.get(firstChild);
2015
+ if (!oldKeyMap) {
2016
+ oldKeyMap = /* @__PURE__ */ new Map();
2017
+ try {
2018
+ const children = Array.from(firstChild.children);
2019
+ for (let i = 0; i < children.length; i++) {
2020
+ const ch = children[i];
2021
+ const k = ch.getAttribute("data-key");
2022
+ if (k !== null) {
2023
+ oldKeyMap.set(k, ch);
2024
+ const n = Number(k);
2025
+ if (!Number.isNaN(n)) oldKeyMap.set(n, ch);
2026
+ }
2027
+ }
2028
+ if (oldKeyMap.size > 0)
2029
+ keyedElements.set(firstChild, oldKeyMap);
2030
+ } catch (e) {
2031
+ void e;
2032
+ }
2033
+ }
2034
+ try {
2035
+ if (process.env.ASKR_FORCE_BULK_POSREUSE === "1") {
2036
+ try {
2037
+ const keyedVnodes = [];
2038
+ for (let i = 0; i < vnodeChildren.length; i++) {
2039
+ const c = vnodeChildren[i];
2040
+ if (_isDOMElement(c) && c.key !== void 0) {
2041
+ keyedVnodes.push({
2042
+ key: c.key,
2043
+ vnode: c
2044
+ });
2045
+ }
2046
+ }
2047
+ if (keyedVnodes.length > 0 && keyedVnodes.length === vnodeChildren.length) {
2048
+ if (process.env.ASKR_FASTPATH_DEBUG === "1" || process.env.ASKR_FASTPATH_DEBUG === "true") {
2049
+ logger.warn(
2050
+ "[Askr][FASTPATH] forced positional bulk keyed reuse (evaluate-level)"
2051
+ );
2052
+ }
2053
+ const stats = performBulkPositionalKeyedTextUpdate(
2054
+ firstChild,
2055
+ keyedVnodes
2056
+ );
2057
+ if (process.env.NODE_ENV !== "production" || process.env.ASKR_FASTPATH_DEBUG === "1") {
2058
+ try {
2059
+ __ASKR_set("__LAST_FASTPATH_STATS", stats);
2060
+ __ASKR_set("__LAST_FASTPATH_COMMIT_COUNT", 1);
2061
+ __ASKR_incCounter("bulkKeyedPositionalForced");
2062
+ } catch (e) {
2063
+ void e;
2064
+ }
2065
+ }
2066
+ try {
2067
+ const map = /* @__PURE__ */ new Map();
2068
+ const children = Array.from(firstChild.children);
2069
+ for (let i = 0; i < children.length; i++) {
2070
+ const el = children[i];
2071
+ const k = el.getAttribute("data-key");
2072
+ if (k !== null) {
2073
+ map.set(k, el);
2074
+ const n = Number(k);
2075
+ if (!Number.isNaN(n)) map.set(n, el);
2076
+ }
2077
+ }
2078
+ keyedElements.set(firstChild, map);
2079
+ } catch (e) {
2080
+ void e;
2081
+ }
2082
+ } else {
2083
+ const newKeyMap = reconcileKeyedChildren(
2084
+ firstChild,
2085
+ vnodeChildren,
2086
+ oldKeyMap
2087
+ );
2088
+ keyedElements.set(firstChild, newKeyMap);
2089
+ }
2090
+ } catch (err) {
2091
+ if (process.env.ASKR_FASTPATH_DEBUG === "1" || process.env.ASKR_FASTPATH_DEBUG === "true") {
2092
+ logger.warn(
2093
+ "[Askr][FASTPATH] forced bulk path failed, falling back",
2094
+ err
2095
+ );
2096
+ }
2097
+ const newKeyMap = reconcileKeyedChildren(
2098
+ firstChild,
2099
+ vnodeChildren,
2100
+ oldKeyMap
2101
+ );
2102
+ keyedElements.set(firstChild, newKeyMap);
2103
+ }
2104
+ } else {
2105
+ const newKeyMap = reconcileKeyedChildren(
2106
+ firstChild,
2107
+ vnodeChildren,
2108
+ oldKeyMap
2109
+ );
2110
+ keyedElements.set(firstChild, newKeyMap);
2111
+ }
2112
+ } catch (e) {
2113
+ void e;
2114
+ const newKeyMap = reconcileKeyedChildren(
2115
+ firstChild,
2116
+ vnodeChildren,
2117
+ oldKeyMap
2118
+ );
2119
+ keyedElements.set(firstChild, newKeyMap);
2120
+ }
2121
+ } else {
2122
+ if (isBulkTextFastPathEligible(firstChild, vnodeChildren)) {
2123
+ const stats = performBulkTextReplace(firstChild, vnodeChildren);
2124
+ if (process.env.NODE_ENV !== "production") {
2584
2125
  try {
2585
- if (!globalScheduler.isExecuting()) globalScheduler.flush();
2586
- } catch (err) {
2587
- setTimeout(() => {
2588
- throw err;
2589
- });
2126
+ __ASKR_set("__LAST_BULK_TEXT_FASTPATH_STATS", stats);
2127
+ __ASKR_incCounter("bulkTextHits");
2128
+ } catch (e) {
2129
+ void e;
2590
2130
  }
2591
- });
2131
+ }
2132
+ } else {
2133
+ if (process.env.NODE_ENV !== "production") {
2134
+ try {
2135
+ __ASKR_incCounter("bulkTextMisses");
2136
+ } catch (e) {
2137
+ void e;
2138
+ }
2139
+ }
2140
+ updateUnkeyedChildren(firstChild, vnodeChildren);
2141
+ keyedElements.delete(firstChild);
2592
2142
  }
2593
2143
  }
2594
- };
2595
- el.addEventListener(eventName, wrappedHandler);
2596
- if (!elementListeners.has(el)) {
2597
- elementListeners.set(el, /* @__PURE__ */ new Map());
2144
+ } else {
2145
+ firstChild.textContent = "";
2146
+ const dom = createDOMNode(vnodeChildren);
2147
+ if (dom) firstChild.appendChild(dom);
2148
+ keyedElements.delete(firstChild);
2598
2149
  }
2599
- elementListeners.get(el).set(eventName, {
2600
- handler: wrappedHandler,
2601
- original: value
2602
- });
2603
- } else if (key === "class" || key === "className") {
2604
- el.className = String(value);
2605
- } else if (key === "value" || key === "checked") {
2606
- el[key] = value;
2607
- el.setAttribute(key, String(value));
2608
2150
  } else {
2609
- el.setAttribute(key, String(value));
2151
+ firstChild.textContent = "";
2152
+ keyedElements.delete(firstChild);
2610
2153
  }
2611
2154
  }
2612
- const vnodeKey = node.key;
2613
- if (vnodeKey !== void 0) {
2614
- el.setAttribute("data-key", String(vnodeKey));
2615
- }
2616
- const children = props.children || node.children;
2617
- if (children) {
2618
- if (Array.isArray(children)) {
2619
- if (process.env.NODE_ENV !== "production") {
2620
- let hasElements = false;
2621
- let hasKeys = false;
2622
- for (let i = 0; i < children.length; i++) {
2623
- const item = children[i];
2624
- if (typeof item === "object" && item !== null && "type" in item) {
2625
- hasElements = true;
2626
- const itemProps = item.props || {};
2627
- if ("key" in itemProps) {
2628
- hasKeys = true;
2629
- break;
2155
+ updateElementFromVnode(firstChild, vnode, false);
2156
+ } else {
2157
+ target.textContent = "";
2158
+ if (_isDOMElement(vnode) && typeof vnode.type === "string") {
2159
+ const children = vnode.children;
2160
+ if (Array.isArray(children) && children.some(
2161
+ (child) => typeof child === "object" && child !== null && "key" in child
2162
+ )) {
2163
+ const el = document.createElement(vnode.type);
2164
+ target.appendChild(el);
2165
+ const props = vnode.props || {};
2166
+ for (const [key, value] of Object.entries(props)) {
2167
+ if (key === "children" || key === "key") continue;
2168
+ if (value === void 0 || value === null || value === false)
2169
+ continue;
2170
+ if (key.startsWith("on") && key.length > 2) {
2171
+ const eventName = key.slice(2).charAt(0).toLowerCase() + key.slice(3).toLowerCase();
2172
+ const wrappedHandler = (event) => {
2173
+ globalScheduler.setInHandler(true);
2174
+ try {
2175
+ value(event);
2176
+ } catch (error) {
2177
+ logger.error("[Askr] Event handler error:", error);
2178
+ } finally {
2179
+ globalScheduler.setInHandler(false);
2630
2180
  }
2181
+ };
2182
+ const options = eventName === "wheel" || eventName === "scroll" || eventName.startsWith("touch") ? { passive: true } : void 0;
2183
+ if (options !== void 0)
2184
+ el.addEventListener(eventName, wrappedHandler, options);
2185
+ else el.addEventListener(eventName, wrappedHandler);
2186
+ if (!elementListeners.has(el)) {
2187
+ elementListeners.set(el, /* @__PURE__ */ new Map());
2631
2188
  }
2189
+ elementListeners.get(el).set(eventName, {
2190
+ handler: wrappedHandler,
2191
+ original: value,
2192
+ options
2193
+ });
2194
+ continue;
2632
2195
  }
2633
- if (hasElements && !hasKeys) {
2634
- if (typeof console !== "undefined") {
2635
- logger.warn(
2636
- 'Missing keys on dynamic lists. Each child in a list should have a unique "key" prop.'
2637
- );
2638
- }
2196
+ if (key === "class" || key === "className") {
2197
+ el.className = String(value);
2198
+ } else if (key === "value" || key === "checked") {
2199
+ el[key] = value;
2200
+ } else {
2201
+ el.setAttribute(key, String(value));
2639
2202
  }
2640
2203
  }
2641
- for (let i = 0; i < children.length; i++) {
2642
- const dom = createDOMNode(children[i]);
2643
- if (dom) el.appendChild(dom);
2644
- }
2645
- } else {
2646
- const dom = createDOMNode(children);
2647
- if (dom) el.appendChild(dom);
2204
+ const newKeyMap = reconcileKeyedChildren(el, children, void 0);
2205
+ keyedElements.set(el, newKeyMap);
2206
+ return;
2207
+ return;
2648
2208
  }
2649
2209
  }
2650
- return el;
2651
- }
2652
- if (typeof type === "function") {
2653
- const frame = node[CONTEXT_FRAME_SYMBOL];
2654
- const snapshot = frame || getCurrentContextFrame();
2655
- const componentFn = type;
2656
- const isAsync = componentFn.constructor.name === "AsyncFunction";
2657
- if (isAsync) {
2658
- throw new Error(
2659
- "Async components are not supported. Use resource() for async work."
2660
- );
2661
- }
2662
- const vnodeAny = node;
2663
- let childInstance = vnodeAny.__instance;
2664
- if (!childInstance) {
2665
- childInstance = createComponentInstance(
2666
- `comp-${Math.random().toString(36).slice(2, 7)}`,
2667
- componentFn,
2668
- props || {},
2669
- null
2670
- );
2671
- vnodeAny.__instance = childInstance;
2672
- }
2673
- if (snapshot) {
2674
- childInstance.ownerFrame = snapshot;
2675
- }
2676
- const result = withContext(
2677
- snapshot,
2678
- () => renderComponentInline(childInstance)
2679
- );
2680
- if (result instanceof Promise) {
2681
- throw new Error(
2682
- "Async components are not supported. Components must return synchronously."
2683
- );
2684
- }
2685
- const dom = withContext(snapshot, () => createDOMNode(result));
2686
- if (dom instanceof Element) {
2687
- mountInstanceInline(childInstance, dom);
2688
- } else {
2689
- const host = document.createElement("div");
2690
- mountInstanceInline(childInstance, host);
2691
- }
2692
- return dom;
2693
- }
2694
- if (typeof type === "symbol" && (type === Fragment || String(type) === "Symbol(Fragment)")) {
2695
- const fragment = document.createDocumentFragment();
2696
- const children = props.children || node.children;
2697
- if (children) {
2698
- if (Array.isArray(children)) {
2699
- for (let i = 0; i < children.length; i++) {
2700
- const dom = createDOMNode(children[i]);
2701
- if (dom) fragment.appendChild(dom);
2702
- }
2703
- } else {
2704
- const dom = createDOMNode(children);
2705
- if (dom) fragment.appendChild(dom);
2706
- }
2210
+ const dom = createDOMNode(vnode);
2211
+ if (dom) {
2212
+ target.appendChild(dom);
2707
2213
  }
2708
- return fragment;
2709
2214
  }
2710
2215
  }
2711
- return null;
2712
2216
  }
2713
- var domRanges, elementListeners, keyedElements, _reconcilerRecordedParents;
2714
- var init_dom = __esm({
2715
- "src/renderer/dom.ts"() {
2217
+ var domRanges;
2218
+ var init_evaluate = __esm({
2219
+ "src/renderer/evaluate.ts"() {
2716
2220
  "use strict";
2717
2221
  init_scheduler();
2718
- init_fastlane();
2719
2222
  init_logger();
2720
- init_jsx_runtime();
2721
- init_context();
2722
- init_component();
2223
+ init_cleanup();
2224
+ init_keyed();
2225
+ init_reconcile();
2226
+ init_types();
2227
+ init_dom();
2228
+ init_diag();
2723
2229
  domRanges = /* @__PURE__ */ new WeakMap();
2724
- elementListeners = /* @__PURE__ */ new WeakMap();
2725
- keyedElements = /* @__PURE__ */ new WeakMap();
2726
- _reconcilerRecordedParents = /* @__PURE__ */ new WeakSet();
2230
+ }
2231
+ });
2232
+
2233
+ // src/renderer/index.ts
2234
+ var init_renderer = __esm({
2235
+ "src/renderer/index.ts"() {
2236
+ "use strict";
2237
+ init_types();
2238
+ init_cleanup();
2239
+ init_keyed();
2240
+ init_dom();
2241
+ init_evaluate();
2242
+ init_evaluate();
2243
+ init_keyed();
2727
2244
  if (typeof globalThis !== "undefined") {
2728
2245
  const _g = globalThis;
2729
2246
  _g.__ASKR_RENDERER = {
@@ -2736,22 +2253,6 @@ var init_dom = __esm({
2736
2253
  });
2737
2254
 
2738
2255
  // src/runtime/component.ts
2739
- var component_exports = {};
2740
- __export(component_exports, {
2741
- cleanupComponent: () => cleanupComponent,
2742
- createComponentInstance: () => createComponentInstance,
2743
- executeComponent: () => executeComponent,
2744
- finalizeReadSubscriptions: () => finalizeReadSubscriptions,
2745
- getCurrentComponentInstance: () => getCurrentComponentInstance,
2746
- getCurrentInstance: () => getCurrentInstance,
2747
- getNextStateIndex: () => getNextStateIndex,
2748
- getSignal: () => getSignal,
2749
- mountComponent: () => mountComponent,
2750
- mountInstanceInline: () => mountInstanceInline,
2751
- registerMountOperation: () => registerMountOperation,
2752
- renderComponentInline: () => renderComponentInline,
2753
- setCurrentComponentInstance: () => setCurrentComponentInstance
2754
- });
2755
2256
  function createComponentInstance(id, fn, props, target) {
2756
2257
  const instance = {
2757
2258
  id,
@@ -2777,6 +2278,7 @@ function createComponentInstance(id, fn, props, target) {
2777
2278
  ownerFrame: null,
2778
2279
  // Will be set by renderer when vnode is marked
2779
2280
  ssr: false,
2281
+ cleanupStrict: false,
2780
2282
  isRoot: false,
2781
2283
  // Render-tracking (for precise state subscriptions)
2782
2284
  _currentRenderToken: void 0,
@@ -2871,12 +2373,41 @@ function runComponent(instance) {
2871
2373
  }
2872
2374
  globalScheduler.enqueue(() => {
2873
2375
  if (instance.target) {
2376
+ let oldChildren = [];
2874
2377
  try {
2875
2378
  const wasFirstMount = !instance.mounted;
2876
2379
  const oldInstance = currentInstance;
2877
2380
  currentInstance = instance;
2381
+ oldChildren = Array.from(instance.target.childNodes);
2878
2382
  try {
2879
2383
  evaluate(result, instance.target);
2384
+ } catch (e) {
2385
+ try {
2386
+ const newChildren = Array.from(instance.target.childNodes);
2387
+ for (const n of newChildren) {
2388
+ try {
2389
+ cleanupInstancesUnder(n);
2390
+ } catch (err) {
2391
+ logger.warn(
2392
+ "[Askr] error cleaning up failed commit children:",
2393
+ err
2394
+ );
2395
+ }
2396
+ }
2397
+ } catch (_err) {
2398
+ void _err;
2399
+ }
2400
+ try {
2401
+ __ASKR_incCounter("__DOM_REPLACE_COUNT");
2402
+ __ASKR_set(
2403
+ "__LAST_DOM_REPLACE_STACK_COMPONENT_RESTORE",
2404
+ new Error().stack
2405
+ );
2406
+ } catch (e2) {
2407
+ void e2;
2408
+ }
2409
+ instance.target.replaceChildren(...oldChildren);
2410
+ throw e;
2880
2411
  } finally {
2881
2412
  currentInstance = oldInstance;
2882
2413
  }
@@ -2886,7 +2417,35 @@ function runComponent(instance) {
2886
2417
  executeMountOperations(instance);
2887
2418
  }
2888
2419
  } catch (renderError) {
2889
- instance.target.innerHTML = domSnapshot;
2420
+ try {
2421
+ const currentChildren = Array.from(instance.target.childNodes);
2422
+ for (const n of currentChildren) {
2423
+ try {
2424
+ cleanupInstancesUnder(n);
2425
+ } catch (err) {
2426
+ logger.warn(
2427
+ "[Askr] error cleaning up partial children during rollback:",
2428
+ err
2429
+ );
2430
+ }
2431
+ }
2432
+ } catch (_err) {
2433
+ void _err;
2434
+ }
2435
+ try {
2436
+ try {
2437
+ __ASKR_incCounter("__DOM_REPLACE_COUNT");
2438
+ __ASKR_set(
2439
+ "__LAST_DOM_REPLACE_STACK_COMPONENT_ROLLBACK",
2440
+ new Error().stack
2441
+ );
2442
+ } catch (e) {
2443
+ void e;
2444
+ }
2445
+ instance.target.replaceChildren(...oldChildren);
2446
+ } catch {
2447
+ instance.target.innerHTML = domSnapshot;
2448
+ }
2890
2449
  throw renderError;
2891
2450
  }
2892
2451
  }
@@ -2935,21 +2494,24 @@ function executeComponentSync(instance) {
2935
2494
  executionFrame,
2936
2495
  () => instance.fn(instance.props, context)
2937
2496
  );
2938
- if (process.env.NODE_ENV !== "production") {
2939
- const renderTime = Date.now() - renderStartTime;
2940
- if (renderTime > 5) {
2941
- logger.warn(
2942
- `[askr] Slow render detected: ${renderTime}ms. Consider optimizing component performance.`
2943
- );
2944
- }
2497
+ const renderTime = Date.now() - renderStartTime;
2498
+ if (renderTime > 5) {
2499
+ logger.warn(
2500
+ `[askr] Slow render detected: ${renderTime}ms. Consider optimizing component performance.`
2501
+ );
2945
2502
  }
2946
2503
  if (!instance.firstRenderComplete) {
2947
2504
  instance.firstRenderComplete = true;
2948
2505
  }
2949
- if (process.env.NODE_ENV !== "production") {
2950
- for (let i = 0; i < instance.stateValues.length; i++) {
2951
- const state = instance.stateValues[i];
2952
- if (state && !state._hasBeenRead) {
2506
+ for (let i = 0; i < instance.stateValues.length; i++) {
2507
+ const state = instance.stateValues[i];
2508
+ if (state && !state._hasBeenRead) {
2509
+ try {
2510
+ const name = instance.fn?.name || "<anonymous>";
2511
+ logger.warn(
2512
+ `[askr] Unused state variable detected in ${name} at index ${i}. State should be read during render or removed.`
2513
+ );
2514
+ } catch {
2953
2515
  logger.warn(
2954
2516
  `[askr] Unused state variable detected. State should be read during render or removed.`
2955
2517
  );
@@ -2984,13 +2546,13 @@ function finalizeReadSubscriptions(instance) {
2984
2546
  if (token === void 0) return;
2985
2547
  for (const s of oldSet) {
2986
2548
  if (!newSet.has(s)) {
2987
- const readers = s?._readers;
2549
+ const readers = s._readers;
2988
2550
  if (readers) readers.delete(instance);
2989
2551
  }
2990
2552
  }
2991
2553
  instance.lastRenderToken = token;
2992
2554
  for (const s of newSet) {
2993
- let readers = s?._readers;
2555
+ let readers = s._readers;
2994
2556
  if (!readers) {
2995
2557
  readers = /* @__PURE__ */ new Map();
2996
2558
  s._readers = readers;
@@ -3008,13 +2570,30 @@ function mountComponent(instance) {
3008
2570
  executeComponent(instance);
3009
2571
  }
3010
2572
  function cleanupComponent(instance) {
2573
+ const cleanupErrors = [];
3011
2574
  for (const cleanup of instance.cleanupFns) {
3012
- cleanup();
2575
+ try {
2576
+ cleanup();
2577
+ } catch (err) {
2578
+ if (instance.cleanupStrict) {
2579
+ cleanupErrors.push(err);
2580
+ } else {
2581
+ if (process.env.NODE_ENV !== "production") {
2582
+ logger.warn("[Askr] cleanup function threw:", err);
2583
+ }
2584
+ }
2585
+ }
3013
2586
  }
3014
2587
  instance.cleanupFns = [];
2588
+ if (cleanupErrors.length > 0) {
2589
+ throw new AggregateError(
2590
+ cleanupErrors,
2591
+ `Cleanup failed for component ${instance.id}`
2592
+ );
2593
+ }
3015
2594
  if (instance._lastReadStates) {
3016
2595
  for (const s of instance._lastReadStates) {
3017
- const readers = s?._readers;
2596
+ const readers = s._readers;
3018
2597
  if (readers) readers.delete(instance);
3019
2598
  }
3020
2599
  instance._lastReadStates = /* @__PURE__ */ new Set();
@@ -3025,11 +2604,12 @@ var currentInstance, stateIndex, _globalRenderCounter;
3025
2604
  var init_component = __esm({
3026
2605
  "src/runtime/component.ts"() {
3027
2606
  "use strict";
3028
- init_dom();
3029
2607
  init_scheduler();
3030
2608
  init_context();
3031
2609
  init_logger();
3032
- init_fastlane();
2610
+ init_diag();
2611
+ init_fastlane_shared();
2612
+ init_renderer();
3033
2613
  currentInstance = null;
3034
2614
  stateIndex = 0;
3035
2615
  _globalRenderCounter = 0;
@@ -3253,7 +2833,7 @@ function route(path, handler, namespace) {
3253
2833
  }
3254
2834
  if (registrationLocked) {
3255
2835
  throw new Error(
3256
- "Route registration is locked after app startup. Register routes at module load time before calling createApp()."
2836
+ "Route registration is locked after app startup. Register routes at module load time before calling createIsland()."
3257
2837
  );
3258
2838
  }
3259
2839
  if (typeof handler !== "function") {
@@ -3407,18 +2987,19 @@ export {
3407
2987
  globalScheduler,
3408
2988
  scheduleEventHandler,
3409
2989
  init_scheduler,
3410
- isBulkCommitActive2 as isBulkCommitActive,
3411
- init_fastlane,
3412
2990
  withAsyncResourceContext,
3413
2991
  defineContext,
3414
2992
  readContext,
3415
2993
  getCurrentContextFrame,
3416
2994
  init_context,
2995
+ isBulkCommitActive2 as isBulkCommitActive,
2996
+ init_fastlane_shared,
3417
2997
  removeAllListeners,
3418
- init_dom,
2998
+ init_renderer,
3419
2999
  createComponentInstance,
3420
3000
  getCurrentComponentInstance,
3421
3001
  setCurrentComponentInstance,
3002
+ registerMountOperation,
3422
3003
  getCurrentInstance,
3423
3004
  getSignal,
3424
3005
  getNextStateIndex,
@@ -3439,4 +3020,4 @@ export {
3439
3020
  resolveRoute,
3440
3021
  route_exports
3441
3022
  };
3442
- //# sourceMappingURL=chunk-L7RL4LYV.js.map
3023
+ //# sourceMappingURL=chunk-MIPES65F.js.map