kerfjs 5.0.0-beta.9 → 5.0.0-beta.91

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 (83) hide show
  1. package/CHANGELOG.md +2592 -107
  2. package/README.md +96 -37
  3. package/ai/cursorrules +110 -61
  4. package/ai/manifest.json +15 -7
  5. package/ai/skill.md +117 -72
  6. package/dist/actions.d.ts +1 -1
  7. package/dist/actions.js +10 -6
  8. package/dist/actions.js.map +1 -1
  9. package/dist/array-signal.js +1 -1
  10. package/dist/async.js +22 -19
  11. package/dist/async.js.map +1 -1
  12. package/dist/attach.js +1 -66
  13. package/dist/attach.js.map +1 -1
  14. package/dist/{chunk-HW7KSM2Y.js → chunk-3C6F6NM4.js} +1 -1
  15. package/dist/{chunk-HW7KSM2Y.js.map → chunk-3C6F6NM4.js.map} +1 -1
  16. package/dist/{chunk-GY4XV2UV.js → chunk-4TX6YJDM.js} +10 -3
  17. package/dist/chunk-4TX6YJDM.js.map +1 -0
  18. package/dist/{chunk-QIP723L4.js → chunk-5WRGJZV6.js} +3 -2
  19. package/dist/chunk-5WRGJZV6.js.map +1 -0
  20. package/dist/{chunk-KZJXHFIB.js → chunk-BI4HGICV.js} +3 -3
  21. package/dist/chunk-BI4HGICV.js.map +1 -0
  22. package/dist/chunk-FM6N2B4N.js +70 -0
  23. package/dist/chunk-FM6N2B4N.js.map +1 -0
  24. package/dist/{chunk-LVH3GC6B.js → chunk-H7WTRM6F.js} +3 -3
  25. package/dist/chunk-H7WTRM6F.js.map +1 -0
  26. package/dist/{chunk-U6FK33SG.js → chunk-IHMDBBUJ.js} +2 -2
  27. package/dist/chunk-IHMDBBUJ.js.map +1 -0
  28. package/dist/chunk-JN7EJM3B.js +86 -0
  29. package/dist/chunk-JN7EJM3B.js.map +1 -0
  30. package/dist/{chunk-UZJ6I4T6.js → chunk-MK42GLPV.js} +1 -1
  31. package/dist/chunk-MK42GLPV.js.map +1 -0
  32. package/dist/{chunk-ZDCJZCNO.js → chunk-TFLNYFDO.js} +88 -48
  33. package/dist/chunk-TFLNYFDO.js.map +1 -0
  34. package/dist/{chunk-V46JKE44.js → chunk-WKIPLNVO.js} +1 -1
  35. package/dist/chunk-WKIPLNVO.js.map +1 -0
  36. package/dist/{chunk-SVATPF5R.js → chunk-XHHGPZRQ.js} +261 -165
  37. package/dist/chunk-XHHGPZRQ.js.map +1 -0
  38. package/dist/{delegate-CL9VTZFb.d.ts → delegate--CrfgYtC.d.ts} +0 -34
  39. package/dist/dev.d.ts +31 -13
  40. package/dist/dev.js +101 -35
  41. package/dist/dev.js.map +1 -1
  42. package/dist/html.js +5 -4
  43. package/dist/html.js.map +1 -1
  44. package/dist/index.d.ts +14 -9
  45. package/dist/index.js +29 -20
  46. package/dist/index.js.map +1 -1
  47. package/dist/jsx-runtime.d.ts +13 -5
  48. package/dist/jsx-runtime.js +1 -1
  49. package/dist/list.d.ts +7 -3
  50. package/dist/list.js +153 -46
  51. package/dist/list.js.map +1 -1
  52. package/dist/overlay.d.ts +128 -129
  53. package/dist/overlay.js +675 -366
  54. package/dist/overlay.js.map +1 -1
  55. package/dist/remount.js +2 -2
  56. package/dist/router.d.ts +13 -4
  57. package/dist/router.js +37 -10
  58. package/dist/router.js.map +1 -1
  59. package/dist/scope.d.ts +35 -8
  60. package/dist/scope.js +61 -30
  61. package/dist/scope.js.map +1 -1
  62. package/dist/testing.js +1 -1
  63. package/dist/timing.d.ts +3 -2
  64. package/dist/timing.js +1 -1
  65. package/dist/timing.js.map +1 -1
  66. package/llms.txt +13 -6
  67. package/package.json +33 -8
  68. package/setup/cli.mjs +90 -0
  69. package/setup/index.d.mts +37 -0
  70. package/setup/index.mjs +1347 -0
  71. package/setup/jsonc.mjs +201 -0
  72. package/setup/state.schema.json +56 -0
  73. package/dist/chunk-GY4XV2UV.js.map +0 -1
  74. package/dist/chunk-KPXIOG2C.js +0 -54
  75. package/dist/chunk-KPXIOG2C.js.map +0 -1
  76. package/dist/chunk-KZJXHFIB.js.map +0 -1
  77. package/dist/chunk-LVH3GC6B.js.map +0 -1
  78. package/dist/chunk-QIP723L4.js.map +0 -1
  79. package/dist/chunk-SVATPF5R.js.map +0 -1
  80. package/dist/chunk-U6FK33SG.js.map +0 -1
  81. package/dist/chunk-UZJ6I4T6.js.map +0 -1
  82. package/dist/chunk-V46JKE44.js.map +0 -1
  83. package/dist/chunk-ZDCJZCNO.js.map +0 -1
@@ -1,9 +1,9 @@
1
- import { itemVersion } from './chunk-QIP723L4.js';
2
- import { parseRowTemplate, rowContractError, parseSingleRow, collectTemplateChildren } from './chunk-V46JKE44.js';
3
- import { captureRowBindings, listSafeHtml, boundTextNodeOf, syncFormProp, newBindingContext, disposeRowBindings, wireBindings, isSafeHtml, _setBindingContext, TEXT_MARKER_PREFIX, ROW_TEXT_PREFIX, wireRowBindings, carryOrRewireRowBindings, granularListSafeHtml } from './chunk-ZDCJZCNO.js';
4
- import { effect } from './chunk-U6FK33SG.js';
5
- import { LIST_MARKER_PREFIX, flattenWithoutListItems, collectLists, flatten } from './chunk-GY4XV2UV.js';
6
- import { devHooks } from './chunk-HW7KSM2Y.js';
1
+ import { itemVersion } from './chunk-5WRGJZV6.js';
2
+ import { parseRowTemplate, rowContractError, parseSingleRow, collectTemplateChildren } from './chunk-WKIPLNVO.js';
3
+ import { captureRowBindings, listSafeHtml, boundTextNodeOf, syncFormProp, newBindingContext, wireBindings, disposeRowBindings, isSafeHtml, _setBindingContext, TEXT_MARKER_PREFIX, ROW_TEXT_PREFIX, wireRowBindings, carryOrRewireRowBindings, granularListSafeHtml } from './chunk-TFLNYFDO.js';
4
+ import { effect } from './chunk-IHMDBBUJ.js';
5
+ import { LIST_MARKER_PREFIX, flattenWithoutListItems, collectLists, isOverlayHost, flatten } from './chunk-4TX6YJDM.js';
6
+ import { devHooks } from './chunk-3C6F6NM4.js';
7
7
 
8
8
  // src/list-render-state.ts
9
9
  function deriveListRenderState(bindingCount) {
@@ -18,7 +18,8 @@ function decideListPath(state, patches, snapshotLength, previousBindingCount) {
18
18
  for (const p of patches) {
19
19
  if (p.type === "insert") netDelta += 1;
20
20
  else if (p.type === "remove") netDelta -= 1;
21
- else if (p.type === "replace") return { path: "snapshot", reason: "replace" };
21
+ else if (p.type === "replace")
22
+ return { path: "snapshot", reason: "replace" };
22
23
  }
23
24
  const count = previousBindingCount ?? 0;
24
25
  if (count + netDelta !== snapshotLength) {
@@ -110,11 +111,18 @@ function _hasGranularIndexShift(previousBindingCount, patches) {
110
111
  const rendered = [];
111
112
  for (let i = 0; i < previousBindingCount; i++) rendered.push(i);
112
113
  for (const patch of patches) {
113
- if (patch.type === "insert") rendered.splice(patch.index, 0, patch.index);
114
- else if (patch.type === "remove") rendered.splice(patch.index, 1);
115
- else if (patch.type === "move") {
116
- const [moved] = rendered.splice(patch.from, 1);
117
- rendered.splice(patch.to, 0, moved);
114
+ switch (patch.type) {
115
+ case "insert":
116
+ rendered.splice(patch.index, 0, patch.index);
117
+ break;
118
+ case "remove":
119
+ rendered.splice(patch.index, 1);
120
+ break;
121
+ case "move": {
122
+ const [moved] = rendered.splice(patch.from, 1);
123
+ rendered.splice(patch.to, 0, moved);
124
+ break;
125
+ }
118
126
  }
119
127
  }
120
128
  return rendered.some((renderedAt, index) => renderedAt !== index);
@@ -153,10 +161,12 @@ function eachGranular(sig, render, cacheKey, listKey) {
153
161
  return eachSnapshotById(snapshot, render, cacheKey, id, sig);
154
162
  }
155
163
  }
156
- const renderRow = (item, index) => captureRowBindings(() => inRowScope(() => {
157
- const out = render(item, index);
158
- return isSafeHtml(out) ? out.toString() : out;
159
- }));
164
+ const renderRow = (item, index) => captureRowBindings(
165
+ () => inRowScope(() => {
166
+ const out = render(item, index);
167
+ return isSafeHtml(out) ? out.toString() : out;
168
+ })
169
+ );
160
170
  const internalPatches = new Array(patches.length);
161
171
  const cache = ctx.caches.get(id);
162
172
  try {
@@ -223,13 +233,16 @@ function eachSnapshotById(items, render, cacheKey, id, source) {
223
233
  devHooks.staleIndex?.(id);
224
234
  }
225
235
  } else {
226
- const captured = captureRowBindings(() => inRowScope(() => {
227
- const out = render(item, i);
228
- return isSafeHtml(out) ? out.toString() : out;
229
- }));
236
+ const captured = captureRowBindings(
237
+ () => inRowScope(() => {
238
+ const out = render(item, i);
239
+ return isSafeHtml(out) ? out.toString() : out;
240
+ })
241
+ );
230
242
  html = captured.html;
231
243
  bindings = captured.bindings;
232
- if (cache !== null) cache.set(item, { cacheKey: k, html, bindings, version, index: i });
244
+ if (cache !== null)
245
+ cache.set(item, { cacheKey: k, html, bindings, version, index: i });
233
246
  }
234
247
  segItems[i] = { ref: item, cacheKey: k, html, bindings };
235
248
  }
@@ -281,16 +294,19 @@ function restoreFocus(snap) {
281
294
  if (domSelection === null) return;
282
295
  const [anchorNode, anchorOffset, focusNode, focusOffset] = domSelection;
283
296
  try {
284
- document.getSelection()?.setBaseAndExtent(
285
- anchorNode,
286
- anchorOffset,
287
- focusNode,
288
- focusOffset
289
- );
297
+ document.getSelection()?.setBaseAndExtent(anchorNode, anchorOffset, focusNode, focusOffset);
290
298
  } catch {
291
299
  }
292
300
  }
293
301
 
302
+ // src/utils/isUserAgentOwnedAttr.ts
303
+ function isUserAgentOwnedAttr(tagNameUpper, name) {
304
+ if (tagNameUpper.includes("-")) {
305
+ return name === "open" || name === "role" || name.startsWith("aria-");
306
+ }
307
+ return name === "open" && (tagNameUpper === "DETAILS" || tagNameUpper === "DIALOG");
308
+ }
309
+
294
310
  // src/utils/moveNode.ts
295
311
  function moveNode(parent, node, ref) {
296
312
  const move = parent.moveBefore;
@@ -343,7 +359,11 @@ function protectionTag(node) {
343
359
  const { dataset } = node;
344
360
  return (dataset.morphSkip !== void 0 ? "s" : "") + (dataset.morphSkipChildren !== void 0 ? "c" : "") + (dataset.morphPreserve !== void 0 ? "p" : "");
345
361
  }
346
- var MARKER_PREFIXES = [LIST_MARKER_PREFIX, TEXT_MARKER_PREFIX, ROW_TEXT_PREFIX];
362
+ var MARKER_PREFIXES = [
363
+ LIST_MARKER_PREFIX,
364
+ TEXT_MARKER_PREFIX,
365
+ ROW_TEXT_PREFIX
366
+ ];
347
367
  function isMarker(node) {
348
368
  if (node.nodeType !== COMMENT_NODE) return false;
349
369
  const { data } = node;
@@ -421,7 +441,8 @@ function morphChildren(fromParent, toParent, ownedItems) {
421
441
  if (matched === null && fromChild !== null && toChild.nodeType === COMMENT_NODE && toChild.data.startsWith(LIST_MARKER_PREFIX)) {
422
442
  const wantData = toChild.data;
423
443
  for (let scan = fromChild.nextSibling; scan !== null; scan = scan.nextSibling) {
424
- if (scan.nodeType !== COMMENT_NODE || scan.data !== wantData) continue;
444
+ if (scan.nodeType !== COMMENT_NODE || scan.data !== wantData)
445
+ continue;
425
446
  const regionEnd = afterListRegion(scan, ownedItems);
426
447
  const run = [];
427
448
  for (let r = scan; r !== null && r !== regionEnd; r = r.nextSibling) {
@@ -487,9 +508,6 @@ function morphElement(fromEl, toEl, ownedItems) {
487
508
  fromEl.value = toEl.textContent;
488
509
  }
489
510
  }
490
- function isUserAgentOwnedAttr(tagName, name) {
491
- return name === "open" && (tagName === "DETAILS" || tagName === "DIALOG");
492
- }
493
511
  function morphAttributes(fromEl, toEl) {
494
512
  const toAttrs = toEl.attributes;
495
513
  for (let i = 0; i < toAttrs.length; i++) {
@@ -534,7 +552,10 @@ function preserveTextEntryState(fromEl, toEl) {
534
552
  const toInput = toEl;
535
553
  toInput.value = fromInput.value;
536
554
  try {
537
- toInput.setSelectionRange(fromInput.selectionStart, fromInput.selectionEnd);
555
+ toInput.setSelectionRange(
556
+ fromInput.selectionStart,
557
+ fromInput.selectionEnd
558
+ );
538
559
  } catch {
539
560
  }
540
561
  }
@@ -567,7 +588,8 @@ function tryAttributeOnlyFastPath(liveNode, oldHtml, newHtml) {
567
588
  if (oldGt === -1 || newGt === -1) return false;
568
589
  if (oldHtml.length - oldGt !== newHtml.length - newGt) return false;
569
590
  if (oldHtml.slice(oldGt) !== newHtml.slice(newGt)) return false;
570
- if (containsDataMorphSkip(oldHtml) || containsDataMorphSkip(newHtml)) return false;
591
+ if (containsDataMorphSkip(oldHtml) || containsDataMorphSkip(newHtml))
592
+ return false;
571
593
  const oldTag = parseOpeningTag(oldHtml, oldGt);
572
594
  const newTag = parseOpeningTag(newHtml, newGt);
573
595
  if (oldTag === null || newTag === null) return false;
@@ -588,14 +610,15 @@ function tryAttributeOnlyFastPath(liveNode, oldHtml, newHtml) {
588
610
  }
589
611
  for (const name of oldTag.attrs.keys()) {
590
612
  if (newTag.attrs.has(name)) continue;
591
- if (isUserAgentOwnedAttr2(liveTagUpper, name)) continue;
613
+ if (isUserAgentOwnedAttr(liveTagUpper, name)) continue;
592
614
  liveNode.removeAttribute(name);
593
615
  syncFormProp(liveNode, name, "", false);
594
616
  }
595
617
  return true;
596
618
  }
597
619
  function tryTextContentFastPath(liveNode, oldHtml, newHtml) {
598
- if (containsDataMorphSkip(oldHtml) || containsDataMorphSkip(newHtml)) return false;
620
+ if (containsDataMorphSkip(oldHtml) || containsDataMorphSkip(newHtml))
621
+ return false;
599
622
  let p = 0;
600
623
  const minLen = Math.min(oldHtml.length, newHtml.length);
601
624
  while (p < minLen && oldHtml.charCodeAt(p) === newHtml.charCodeAt(p)) p++;
@@ -610,7 +633,8 @@ function tryTextContentFastPath(liveNode, oldHtml, newHtml) {
610
633
  if (!isPureTextWindow(newHtml, p, newWinEnd)) return false;
611
634
  if (p === 0) return false;
612
635
  const boundaryCc = oldHtml.charCodeAt(p - 1);
613
- if (boundaryCc === LT || boundaryCc === DQUOTE || boundaryCc === SQUOTE || boundaryCc === EQ || boundaryCc === AMP) return false;
636
+ if (boundaryCc === LT || boundaryCc === DQUOTE || boundaryCc === SQUOTE || boundaryCc === EQ || boundaryCc === AMP)
637
+ return false;
614
638
  const textStart = lastIndexOfChar(oldHtml, GT, p - 1);
615
639
  if (textStart === -1) return false;
616
640
  const textEnd = oldHtml.indexOf("<", p);
@@ -637,7 +661,8 @@ function containsDataMorphSkip(html) {
637
661
  function isPureTextWindow(html, start, end) {
638
662
  for (let i = start; i < end; i++) {
639
663
  const cc = html.charCodeAt(i);
640
- if (cc === LT || cc === GT || cc === DQUOTE || cc === SQUOTE || cc === AMP || cc === EQ) return false;
664
+ if (cc === LT || cc === GT || cc === DQUOTE || cc === SQUOTE || cc === AMP || cc === EQ)
665
+ return false;
641
666
  }
642
667
  return true;
643
668
  }
@@ -730,9 +755,6 @@ function unescapeAttrValue(s) {
730
755
  if (s.indexOf("&") === -1) return s;
731
756
  return s.replace(/&quot;/g, '"').replace(/&#39;/g, "'").replace(/&lt;/g, "<").replace(/&gt;/g, ">").replace(/&amp;/g, "&");
732
757
  }
733
- function isUserAgentOwnedAttr2(tagNameUpper, name) {
734
- return name === "open" && (tagNameUpper === "DETAILS" || tagNameUpper === "DIALOG");
735
- }
736
758
 
737
759
  // src/list-reconcile-granular.ts
738
760
  function reconcileGranular(binding, patches) {
@@ -769,7 +791,14 @@ function reconcileGranular(binding, patches) {
769
791
  if (runLen === 1) {
770
792
  applySingleInsert(liveParent, items, patch, endAnchor(binding));
771
793
  } else {
772
- applyBulkInsert(liveParent, items, patches, i, runEnd, endAnchor(binding));
794
+ applyBulkInsert(
795
+ liveParent,
796
+ items,
797
+ patches,
798
+ i,
799
+ runEnd,
800
+ endAnchor(binding)
801
+ );
773
802
  }
774
803
  i = runEnd;
775
804
  continue;
@@ -881,7 +910,10 @@ function applyBulkUpdate(liveParent, items, patches, start, end) {
881
910
  morphChanges.push({ patchIdx: k, html: p.html });
882
911
  }
883
912
  if (morphChanges.length === 0) return;
884
- const { content, count } = parseRowTemplate(morphChanges.map((c) => c.html).join(""), liveParent);
913
+ const { content, count } = parseRowTemplate(
914
+ morphChanges.map((c) => c.html).join(""),
915
+ liveParent
916
+ );
885
917
  if (count !== morphChanges.length) {
886
918
  throw findOffendingChange(patches, morphChanges, liveParent);
887
919
  }
@@ -924,18 +956,30 @@ function applyBulkInsert(liveParent, items, patches, start, end, tailAnchor) {
924
956
  function findOffendingInsert(patches, start, htmls, liveParent) {
925
957
  for (let i = 0; i < htmls.length; i++) {
926
958
  if (parseRowTemplate(htmls[i], liveParent).count !== 1) {
927
- return rowContractError(patches[start + i].index, htmls[i], liveParent);
959
+ return rowContractError(
960
+ patches[start + i].index,
961
+ htmls[i],
962
+ liveParent
963
+ );
928
964
  }
929
965
  }
930
- return new Error("each(): bulk-insert mismatch with no per-row offender (kerf bug).");
966
+ return new Error(
967
+ "each(): bulk-insert mismatch with no per-row offender (kerf bug)."
968
+ );
931
969
  }
932
970
  function findOffendingChange(patches, changes, liveParent) {
933
971
  for (const c of changes) {
934
972
  if (parseRowTemplate(c.html, liveParent).count !== 1) {
935
- return rowContractError(patches[c.patchIdx].index, c.html, liveParent);
973
+ return rowContractError(
974
+ patches[c.patchIdx].index,
975
+ c.html,
976
+ liveParent
977
+ );
936
978
  }
937
979
  }
938
- return new Error("each(): bulk-update mismatch with no per-row offender (kerf bug).");
980
+ return new Error(
981
+ "each(): bulk-update mismatch with no per-row offender (kerf bug)."
982
+ );
939
983
  }
940
984
 
941
985
  // src/list-reconcile-inplace.ts
@@ -960,7 +1004,12 @@ function tryInPlaceContentUpdate(binding, listSeg) {
960
1004
  }
961
1005
  function updateRowInPlace(liveParent, old, ni, index) {
962
1006
  if (old.html === ni.html || tryAttributeOnlyFastPath(old.node, old.html, ni.html) || tryTextContentFastPath(old.node, old.html, ni.html)) {
963
- const kept = carryOrRewireRowBindings(old.node, old.bindings, old.bindingDisposers, ni.bindings);
1007
+ const kept = carryOrRewireRowBindings(
1008
+ old.node,
1009
+ old.bindings,
1010
+ old.bindingDisposers,
1011
+ ni.bindings
1012
+ );
964
1013
  return {
965
1014
  ref: ni.ref,
966
1015
  cacheKey: ni.cacheKey,
@@ -973,7 +1022,12 @@ function updateRowInPlace(liveParent, old, ni, index) {
973
1022
  const newNode = parseSingleRow(ni.html, index, liveParent);
974
1023
  if (old.node.tagName === newNode.tagName) {
975
1024
  _morphElement(old.node, newNode);
976
- const kept = carryOrRewireRowBindings(old.node, old.bindings, old.bindingDisposers, ni.bindings);
1025
+ const kept = carryOrRewireRowBindings(
1026
+ old.node,
1027
+ old.bindings,
1028
+ old.bindingDisposers,
1029
+ ni.bindings
1030
+ );
977
1031
  return {
978
1032
  ref: ni.ref,
979
1033
  cacheKey: ni.cacheKey,
@@ -985,7 +1039,12 @@ function updateRowInPlace(liveParent, old, ni, index) {
985
1039
  }
986
1040
  disposeRowBindings(old.bindingDisposers);
987
1041
  liveParent.replaceChild(newNode, old.node);
988
- const fresh = carryOrRewireRowBindings(newNode, void 0, void 0, ni.bindings);
1042
+ const fresh = carryOrRewireRowBindings(
1043
+ newNode,
1044
+ void 0,
1045
+ void 0,
1046
+ ni.bindings
1047
+ );
989
1048
  return {
990
1049
  ref: ni.ref,
991
1050
  cacheKey: ni.cacheKey,
@@ -1070,15 +1129,22 @@ function buildFreshNodes(newRecord, freshIndices, freshHtmls, liveParent) {
1070
1129
  function findOffendingRow(newRecord, freshIndices, freshHtmls, liveParent) {
1071
1130
  for (let i = 0; i < freshHtmls.length; i++) {
1072
1131
  if (parseRowTemplate(freshHtmls[i], liveParent).count !== 1) {
1073
- return rowContractError(freshIndices[i], newRecord[freshIndices[i]].html, liveParent);
1132
+ return rowContractError(
1133
+ freshIndices[i],
1134
+ newRecord[freshIndices[i]].html,
1135
+ liveParent
1136
+ );
1074
1137
  }
1075
1138
  }
1076
- return new Error("each(): bulk-parse mismatch with no per-row offender (kerf bug).");
1139
+ return new Error(
1140
+ "each(): bulk-parse mismatch with no per-row offender (kerf bug)."
1141
+ );
1077
1142
  }
1078
1143
  function removeOldNodes(liveParent, removedItems) {
1079
1144
  for (const item of removedItems) {
1080
1145
  disposeRowBindings(item.bindingDisposers);
1081
- if (item.node.parentElement === liveParent) liveParent.removeChild(item.node);
1146
+ if (item.node.parentElement === liveParent)
1147
+ liveParent.removeChild(item.node);
1082
1148
  }
1083
1149
  }
1084
1150
  function applyMoves(liveParent, newRecord, prevIdx, stable, tailAnchor) {
@@ -1130,6 +1196,111 @@ function reconcileList(binding, listSeg) {
1130
1196
  reconcileSnapshot(binding, listSeg);
1131
1197
  }
1132
1198
 
1199
+ // src/mount-list-bindings.ts
1200
+ function anyRebuiltListIsGranular(segment, rebuilt) {
1201
+ if (!rebuilt.size) return false;
1202
+ const lists = collectLists(segment);
1203
+ for (const id of rebuilt) {
1204
+ if (lists.get(id)?.patches) return true;
1205
+ }
1206
+ return false;
1207
+ }
1208
+ function bindListsFromMarkers(rootEl, segment, bindings, inlinedItems, rebuiltLists) {
1209
+ const lists = collectLists(segment);
1210
+ const found = [];
1211
+ collectComments(rootEl, found);
1212
+ for (const marker of found) {
1213
+ if (!marker.data.startsWith(LIST_MARKER_PREFIX)) continue;
1214
+ const id = marker.data.slice(LIST_MARKER_PREFIX.length);
1215
+ const existing = bindings.get(id);
1216
+ if (existing) {
1217
+ if (existing.marker === marker && rootEl.contains(existing.marker))
1218
+ continue;
1219
+ for (const item of existing.items) {
1220
+ disposeRowBindings(item.bindingDisposers);
1221
+ if (rootEl.contains(item.node)) {
1222
+ item.node.parentElement?.removeChild(item.node);
1223
+ }
1224
+ }
1225
+ bindings.delete(id);
1226
+ rebuiltLists?.add(id);
1227
+ devHooks.listRebind?.(id, marker.parentElement);
1228
+ }
1229
+ const listSeg = lists.get(id);
1230
+ const liveParent = marker.parentElement;
1231
+ const items = [];
1232
+ if (inlinedItems) {
1233
+ let next = marker.nextElementSibling;
1234
+ for (let i = 0; i < listSeg.items.length && next !== null; i++) {
1235
+ validateInlinedRowMatch(listSeg.items[i].html, i, next, liveParent);
1236
+ items.push({
1237
+ ref: listSeg.items[i].ref,
1238
+ cacheKey: listSeg.items[i].cacheKey,
1239
+ html: listSeg.items[i].html,
1240
+ node: next,
1241
+ bindings: listSeg.items[i].bindings
1242
+ });
1243
+ next = next.nextElementSibling;
1244
+ }
1245
+ }
1246
+ const binding = { liveParent, items, marker };
1247
+ if (items.length > 0) {
1248
+ devHooks.missingRowKey?.(items[0].node, items[0].html, binding);
1249
+ }
1250
+ devHooks.eachInMorphSkip?.(id, liveParent, rootEl);
1251
+ bindings.set(id, binding);
1252
+ for (const bound of items) {
1253
+ if (bound.bindings?.length) {
1254
+ bound.bindingDisposers = wireRowBindings(bound.node, bound.bindings);
1255
+ }
1256
+ }
1257
+ }
1258
+ }
1259
+ function validateInlinedRowMatch(expectedHtml, index, boundEl, liveParent) {
1260
+ if (boundEl.outerHTML === expectedHtml) return;
1261
+ const { content, count } = parseRowTemplate(expectedHtml, liveParent);
1262
+ if (count !== 1) throw rowContractError(index, expectedHtml, liveParent);
1263
+ const expectedTag = content.firstElementChild.tagName;
1264
+ if (boundEl.tagName !== expectedTag)
1265
+ throw rowStructureError(index, boundEl.tagName, expectedTag);
1266
+ }
1267
+ function rowStructureError(index, gotTag, wantTag) {
1268
+ const got = gotTag.toLowerCase();
1269
+ const want = wantTag.toLowerCase();
1270
+ return new Error(
1271
+ `each(): row ${index} renders <${want}>, but the HTML parser wrapped the rows in <${got}> \u2014 so kerf cannot bind one row per element. This happens when an each() of <${want}> sits directly inside a table: the parser inserts <${got}> around the whole run. Put the each() inside an explicit <${got}> (e.g. <table><${got}>{each(...)}</${got}></table>) so the rows are the direct children kerf binds.`
1272
+ );
1273
+ }
1274
+ function collectOwnedItems(bindings) {
1275
+ const owned = /* @__PURE__ */ new Set();
1276
+ for (const b of bindings.values()) {
1277
+ for (const item of b.items) owned.add(item.node);
1278
+ }
1279
+ return owned;
1280
+ }
1281
+ function cleanupOrphanBindings(segment, bindings, renderCtx) {
1282
+ const liveIds = collectLists(segment);
1283
+ for (const [id, binding] of bindings) {
1284
+ if (liveIds.has(id)) continue;
1285
+ for (const item of binding.items) {
1286
+ disposeRowBindings(item.bindingDisposers);
1287
+ item.node.remove();
1288
+ }
1289
+ binding.marker.remove();
1290
+ bindings.delete(id);
1291
+ renderCtx.bindingCounts.delete(id);
1292
+ renderCtx.bindingSources.delete(id);
1293
+ renderCtx.caches.delete(id);
1294
+ }
1295
+ }
1296
+ function collectComments(node, out) {
1297
+ for (let c = node.firstChild; c; c = c.nextSibling) {
1298
+ if (c.nodeType === Node.COMMENT_NODE) out.push(c);
1299
+ else if (c.nodeType === Node.ELEMENT_NODE && !isOverlayHost(c))
1300
+ collectComments(c, out);
1301
+ }
1302
+ }
1303
+
1133
1304
  // src/mount.ts
1134
1305
  var MOUNTED_MARKER = /* @__PURE__ */ Symbol.for("kerfjs.mounted");
1135
1306
  var NESTED_MOUNT_MSG = "mount: rootEl is already inside (or contains) a mounted tree. kerf supports one mount per tree \u2014 compose with plain functions that return JSX instead of nesting mounts.";
@@ -1155,15 +1326,17 @@ function assertNotInsideMountedTree(rootEl) {
1155
1326
  );
1156
1327
  }
1157
1328
  let ancestor = rootEl.parentElement;
1158
- while (ancestor) {
1329
+ while (ancestor && !isOverlayHost(ancestor)) {
1159
1330
  if (isMounted(ancestor)) throw new Error(NESTED_MOUNT_MSG);
1160
1331
  ancestor = ancestor.parentElement;
1161
1332
  }
1162
1333
  const stack = [];
1163
- for (let i = 0; i < rootEl.children.length; i++) stack.push(rootEl.children[i]);
1334
+ for (let i = 0; i < rootEl.children.length; i++)
1335
+ stack.push(rootEl.children[i]);
1164
1336
  while (stack.length > 0) {
1165
1337
  const cur = stack.pop();
1166
1338
  if (isMounted(cur)) throw new Error(NESTED_MOUNT_MSG);
1339
+ if (isOverlayHost(cur)) continue;
1167
1340
  for (let i = 0; i < cur.children.length; i++) stack.push(cur.children[i]);
1168
1341
  }
1169
1342
  }
@@ -1241,7 +1414,8 @@ function mount(rootEl, render) {
1241
1414
  valueOnlyWarnCtx
1242
1415
  );
1243
1416
  if (anyRebuiltListIsGranular(segment, renderCtx.rebuiltLists)) {
1244
- for (const id of renderCtx.rebuiltLists) renderCtx.bindingCounts.delete(id);
1417
+ for (const id of renderCtx.rebuiltLists)
1418
+ renderCtx.bindingCounts.delete(id);
1245
1419
  result = runRenderPass();
1246
1420
  segment = resultToSegment(result);
1247
1421
  nextStaticHtml = runSubsequentRender(
@@ -1255,7 +1429,8 @@ function mount(rootEl, render) {
1255
1429
  }
1256
1430
  if (nextStaticHtml !== prevStaticHtml) {
1257
1431
  bindingDisposers = wireBindings(rootEl, bindingCtx, bindingDisposers);
1258
- if (devHooks.staleBindingEnabled?.()) prevWiredBindings = bindingCtx.list;
1432
+ if (devHooks.staleBindingEnabled?.())
1433
+ prevWiredBindings = bindingCtx.list;
1259
1434
  } else {
1260
1435
  devHooks.staleBinding?.(prevWiredBindings, bindingCtx.list);
1261
1436
  }
@@ -1283,11 +1458,7 @@ function mount(rootEl, render) {
1283
1458
  renderCtx.previousCallCount = renderCtx.counter;
1284
1459
  devHooks.listInvariants?.(rootEl, bindings, expectedCounts || void 0);
1285
1460
  }
1286
- const disposeEffect = effect(() => {
1287
- reconcileAndCommitListPhase(renderStaticPhase());
1288
- });
1289
- return () => {
1290
- disposeEffect();
1461
+ const releaseResources = () => {
1291
1462
  for (const d of bindingDisposers) d();
1292
1463
  bindingDisposers = [];
1293
1464
  for (const b of bindings.values()) {
@@ -1296,6 +1467,21 @@ function mount(rootEl, render) {
1296
1467
  listenerWarnObserver?.disconnect();
1297
1468
  setMounted(rootEl, false);
1298
1469
  };
1470
+ const priorChildren = rootEl.firstChild ? Array.from(rootEl.childNodes) : [];
1471
+ let disposeEffect;
1472
+ try {
1473
+ disposeEffect = effect(() => {
1474
+ reconcileAndCommitListPhase(renderStaticPhase());
1475
+ });
1476
+ } catch (err) {
1477
+ releaseResources();
1478
+ rootEl.replaceChildren(...priorChildren);
1479
+ throw err;
1480
+ }
1481
+ return () => {
1482
+ disposeEffect();
1483
+ releaseResources();
1484
+ };
1299
1485
  }
1300
1486
  function runFirstRender(rootEl, segment, bindings) {
1301
1487
  rootEl.innerHTML = flatten(segment, true);
@@ -1307,12 +1493,22 @@ function runSubsequentRender(rootEl, segment, bindings, renderCtx, prevStaticHtm
1307
1493
  if (currentStaticHtml === prevStaticHtml) {
1308
1494
  return prevStaticHtml;
1309
1495
  }
1310
- devHooks.valueOnlyRerender?.(prevStaticHtml, currentStaticHtml, valueOnlyWarnCtx);
1496
+ devHooks.valueOnlyRerender?.(
1497
+ prevStaticHtml,
1498
+ currentStaticHtml,
1499
+ valueOnlyWarnCtx
1500
+ );
1311
1501
  cleanupOrphanBindings(segment, bindings, renderCtx);
1312
1502
  const template = rootEl.cloneNode(false);
1313
1503
  template.innerHTML = currentStaticHtml;
1314
1504
  morph(rootEl, template, collectOwnedItems(bindings));
1315
- bindListsFromMarkers(rootEl, segment, bindings, false, renderCtx.rebuiltLists);
1505
+ bindListsFromMarkers(
1506
+ rootEl,
1507
+ segment,
1508
+ bindings,
1509
+ false,
1510
+ renderCtx.rebuiltLists
1511
+ );
1316
1512
  return currentStaticHtml;
1317
1513
  }
1318
1514
  function coerceRenderResult(result) {
@@ -1322,107 +1518,7 @@ function coerceRenderResult(result) {
1322
1518
  function resultToSegment(result) {
1323
1519
  return isSafeHtml(result) ? result.__segment ?? { kind: "static", html: result.__html } : { kind: "static", html: coerceRenderResult(result) };
1324
1520
  }
1325
- function anyRebuiltListIsGranular(segment, rebuilt) {
1326
- if (!rebuilt.size) return false;
1327
- const lists = collectLists(segment);
1328
- for (const id of rebuilt) {
1329
- if (lists.get(id)?.patches) return true;
1330
- }
1331
- return false;
1332
- }
1333
- function bindListsFromMarkers(rootEl, segment, bindings, inlinedItems, rebuiltLists) {
1334
- const lists = collectLists(segment);
1335
- const found = [];
1336
- collectComments(rootEl, found);
1337
- for (const marker of found) {
1338
- if (!marker.data.startsWith(LIST_MARKER_PREFIX)) continue;
1339
- const id = marker.data.slice(LIST_MARKER_PREFIX.length);
1340
- const existing = bindings.get(id);
1341
- if (existing) {
1342
- if (existing.marker === marker && rootEl.contains(existing.marker)) continue;
1343
- for (const item of existing.items) {
1344
- disposeRowBindings(item.bindingDisposers);
1345
- if (rootEl.contains(item.node)) {
1346
- item.node.parentElement?.removeChild(item.node);
1347
- }
1348
- }
1349
- bindings.delete(id);
1350
- rebuiltLists?.add(id);
1351
- devHooks.listRebind?.(id, marker.parentElement);
1352
- }
1353
- const listSeg = lists.get(id);
1354
- const liveParent = marker.parentElement;
1355
- const items = [];
1356
- if (inlinedItems) {
1357
- let next = marker.nextElementSibling;
1358
- for (let i = 0; i < listSeg.items.length && next !== null; i++) {
1359
- validateInlinedRowMatch(listSeg.items[i].html, i, next, liveParent);
1360
- const rowBindings = listSeg.items[i].bindings;
1361
- const bound = {
1362
- ref: listSeg.items[i].ref,
1363
- cacheKey: listSeg.items[i].cacheKey,
1364
- html: listSeg.items[i].html,
1365
- node: next,
1366
- bindings: rowBindings
1367
- };
1368
- if (rowBindings?.length) {
1369
- bound.bindingDisposers = wireRowBindings(next, rowBindings);
1370
- }
1371
- items.push(bound);
1372
- next = next.nextElementSibling;
1373
- }
1374
- }
1375
- const binding = { liveParent, items, marker };
1376
- if (items.length > 0) {
1377
- devHooks.missingRowKey?.(items[0].node, items[0].html, binding);
1378
- }
1379
- devHooks.eachInMorphSkip?.(id, liveParent, rootEl);
1380
- bindings.set(id, binding);
1381
- }
1382
- }
1383
- function validateInlinedRowMatch(expectedHtml, index, boundEl, liveParent) {
1384
- if (boundEl.outerHTML === expectedHtml) return;
1385
- const { content, count } = parseRowTemplate(expectedHtml, liveParent);
1386
- if (count !== 1) throw rowContractError(index, expectedHtml, liveParent);
1387
- const expectedTag = content.firstElementChild.tagName;
1388
- if (boundEl.tagName !== expectedTag) throw rowStructureError(index, boundEl.tagName, expectedTag);
1389
- }
1390
- function rowStructureError(index, gotTag, wantTag) {
1391
- const got = gotTag.toLowerCase();
1392
- const want = wantTag.toLowerCase();
1393
- return new Error(
1394
- `each(): row ${index} renders <${want}>, but the HTML parser wrapped the rows in <${got}> \u2014 so kerf cannot bind one row per element. This happens when an each() of <${want}> sits directly inside a table: the parser inserts <${got}> around the whole run. Put the each() inside an explicit <${got}> (e.g. <table><${got}>{each(...)}</${got}></table>) so the rows are the direct children kerf binds.`
1395
- );
1396
- }
1397
- function collectOwnedItems(bindings) {
1398
- const owned = /* @__PURE__ */ new Set();
1399
- for (const b of bindings.values()) {
1400
- for (const item of b.items) owned.add(item.node);
1401
- }
1402
- return owned;
1403
- }
1404
- function cleanupOrphanBindings(segment, bindings, renderCtx) {
1405
- const liveIds = collectLists(segment);
1406
- for (const [id, binding] of bindings) {
1407
- if (liveIds.has(id)) continue;
1408
- for (const item of binding.items) {
1409
- disposeRowBindings(item.bindingDisposers);
1410
- item.node.remove();
1411
- }
1412
- binding.marker.remove();
1413
- bindings.delete(id);
1414
- renderCtx.bindingCounts.delete(id);
1415
- renderCtx.bindingSources.delete(id);
1416
- renderCtx.caches.delete(id);
1417
- }
1418
- }
1419
- function collectComments(node, out) {
1420
- for (let c = node.firstChild; c; c = c.nextSibling) {
1421
- if (c.nodeType === Node.COMMENT_NODE) out.push(c);
1422
- else if (c.nodeType === Node.ELEMENT_NODE) collectComments(c, out);
1423
- }
1424
- }
1425
1521
 
1426
1522
  export { captureFocus, each, morph, mount, moveNode, restoreFocus };
1427
1523
 
1428
- //# sourceMappingURL=chunk-SVATPF5R.js.map
1524
+ //# sourceMappingURL=chunk-XHHGPZRQ.js.map