@torpor/view 1.1.1 → 1.1.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.
package/dist/index.cjs CHANGED
@@ -992,6 +992,22 @@ function getElementFragment(document, array, index, html, ns) {
992
992
  return array[index].cloneNode(true);
993
993
  }
994
994
  //#endregion
995
+ //#region src/render/isServerComponent.ts
996
+ /**
997
+ * Whether a component function was compiled for SSR
998
+ *
999
+ * Server components are emitted as `async function`s taking
1000
+ * `(props, context, slots)` and returning a `{ body, head }` pair, while
1001
+ * client components are plain functions taking
1002
+ * `(parent, anchor, props, context, slots)`. Checking the function's
1003
+ * constructor distinguishes the two without calling it (which would start
1004
+ * the render). The name lives on the intrinsic `AsyncFunction` object, so
1005
+ * it survives minification.
1006
+ */
1007
+ function isServerComponent(component) {
1008
+ return component.constructor?.name === "AsyncFunction";
1009
+ }
1010
+ //#endregion
995
1011
  //#region src/render/hydrate.ts
996
1012
  /**
997
1013
  * Hydrates a component into an existing DOM tree
@@ -1000,6 +1016,7 @@ function getElementFragment(document, array, index, html, ns) {
1000
1016
  * @param props An object containing component props
1001
1017
  */
1002
1018
  function hydrate(parent, component, props, slots) {
1019
+ if (isServerComponent(component)) throw new Error("The component was compiled for SSR (it renders to an HTML string) and cannot be hydrated in the browser. Import the client build instead, e.g. with the '?client' import query");
1003
1020
  context.hydrationNode = parent.firstChild;
1004
1021
  context.rootRegion = newRegion(require_devContext.devContext.enabled ? "Root" : void 0);
1005
1022
  context.previousRegion = context.rootRegion;
@@ -1018,6 +1035,7 @@ function hydrate(parent, component, props, slots) {
1018
1035
  * @param props An object containing component props
1019
1036
  */
1020
1037
  function mount(parent, component, props, slots) {
1038
+ if (isServerComponent(component)) throw new Error("The component was compiled for SSR (it renders to an HTML string) and cannot be mounted in the browser. Import the client build instead, e.g. with the '?client' import query");
1021
1039
  if (parent.childElementCount > 0) throw new Error("The parent node must have no child elements");
1022
1040
  while (parent.firstChild !== null) parent.firstChild.remove();
1023
1041
  if (context.rootRegion === null) {
@@ -1139,8 +1157,14 @@ function nodeAnchor(node) {
1139
1157
  if (isCommentNode(node) && node.data === "[") {
1140
1158
  const startParent = node.parentNode;
1141
1159
  let currentNode = nodeNext(node);
1142
- const firstInside = currentNode;
1143
1160
  node.remove();
1161
+ while (currentNode !== null && isCommentNode(currentNode) && currentNode.data === "^") {
1162
+ const next = currentNode.nextSibling;
1163
+ currentNode.remove();
1164
+ currentNode = next;
1165
+ context.hydrationNode = currentNode;
1166
+ }
1167
+ const firstInside = currentNode;
1144
1168
  let level = 1;
1145
1169
  while (currentNode !== null) {
1146
1170
  if (isCommentNode(currentNode)) {
@@ -1151,9 +1175,10 @@ function nodeAnchor(node) {
1151
1175
  const endNode = currentNode.nextSibling;
1152
1176
  currentNode.remove();
1153
1177
  if (context.hydrationNode === currentNode) context.hydrationNode = endNode;
1178
+ const firstContent = firstInside === currentNode ? null : firstInside;
1154
1179
  const region = context.activeRegion;
1155
- if (region.startNode === null && firstInside !== null) region.startNode = firstInside;
1156
- setFirstInsideNode(endNode, firstInside);
1180
+ if (region.startNode === null) region.startNode = firstContent ?? endNode;
1181
+ setFirstInsideNode(endNode, firstContent);
1157
1182
  return endNode;
1158
1183
  }
1159
1184
  }
@@ -1433,6 +1458,52 @@ function runControlBranch(region, oldIndex, index) {
1433
1458
  return true;
1434
1459
  }
1435
1460
  //#endregion
1461
+ //#region src/render/checkListKeys.ts
1462
+ const warnedByRegion = /* @__PURE__ */ new WeakMap();
1463
+ /**
1464
+ * DEV-ONLY: warns when a keyed `@for` list contains duplicate `@key` values.
1465
+ *
1466
+ * `runListItems` matches old and new rows by key (first match wins), so
1467
+ * duplicate keys make updates and removals hit the wrong rows, desyncing the
1468
+ * DOM from the data. Called from `runList` only when `devContext.enabled`, so
1469
+ * it never runs in a production build.
1470
+ *
1471
+ * `undefined`/`null` keys are the "unkeyed" case (rows are matched
1472
+ * positionally) and are ignored.
1473
+ *
1474
+ * @param region The `@for` list's region, used to dedupe warnings per list.
1475
+ * @param specs The new row specs, in order.
1476
+ */
1477
+ function checkListKeys(region, specs) {
1478
+ let seen;
1479
+ for (let i = 0; i < specs.length; i++) {
1480
+ const key = specs[i].key;
1481
+ if (key === void 0 || key === null) continue;
1482
+ if (seen === void 0) seen = /* @__PURE__ */ new Set();
1483
+ if (!seen.has(key)) {
1484
+ seen.add(key);
1485
+ continue;
1486
+ }
1487
+ let warned = warnedByRegion.get(region);
1488
+ if (warned === void 0) {
1489
+ warned = /* @__PURE__ */ new Set();
1490
+ warnedByRegion.set(region, warned);
1491
+ }
1492
+ if (!warned.has(key)) {
1493
+ warned.add(key);
1494
+ console.warn(`[torpor] Duplicate @key value ${formatKey(key)} in a keyed @for list. Keys must be unique, or updates and removals will affect the wrong rows.`);
1495
+ }
1496
+ }
1497
+ }
1498
+ function formatKey(key) {
1499
+ try {
1500
+ const json = JSON.stringify(key);
1501
+ return json === void 0 ? String(key) : json;
1502
+ } catch {
1503
+ return String(key);
1504
+ }
1505
+ }
1506
+ //#endregion
1436
1507
  //#region src/watch/deepWrap.ts
1437
1508
  /**
1438
1509
  * Wraps a value with `$watch` if it is a plain object (or Date/Map/Set) that
@@ -2250,6 +2321,7 @@ function runList(region, parent, anchor, buildItems, create, update, noWatch) {
2250
2321
  const oldRegion = pushRegion(region, first);
2251
2322
  first = false;
2252
2323
  const newSpecs = buildItems();
2324
+ if (require_devContext.devContext.enabled) checkListKeys(region, newSpecs);
2253
2325
  context.activeTarget = null;
2254
2326
  listItems = runListItems(region, parent, anchor, listItems, newSpecs, create, update, noWatch);
2255
2327
  popRegion(oldRegion);
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.cts","names":[],"sources":["../src/types/Animation.ts","../src/render/addAnimation.ts","../src/render/addEvent.ts","../src/render/addElement.ts","../src/render/addFragment.ts","../src/render/applyProps.ts","../src/render/clearLayoutSlot.ts","../src/render/fillLayoutSlot.ts","../src/render/formatText.ts","../src/render/firstInsideNode.ts","../src/render/getFragment.ts","../src/render/getElementFragment.ts","../src/render/hydrate.ts","../src/render/mount.ts","../src/render/unmount.ts","../src/types/ListItemSpec.ts","../src/types/ListItem.ts","../src/render/newListItem.ts","../src/render/newRegion.ts","../src/render/nodeAnchor.ts","../src/render/nodeChild.ts","../src/render/nodeNext.ts","../src/render/nodeRootElement.ts","../src/render/nodeRoot.ts","../src/render/nodeSkip.ts","../src/render/popRegion.ts","../src/render/pushRegion.ts","../src/render/rerunRegionEffects.ts","../src/render/restoreHydration.ts","../src/render/runControl.ts","../src/render/runControlBranch.ts","../src/render/runList.ts","../src/render/runAwait.ts","../src/render/runTry.ts","../src/render/saveHydration.ts","../src/render/headElement.ts","../src/render/setAttribute.ts","../src/render/setDynamicElement.ts","../src/types/Bindable.ts","../src/watch/$batch.ts","../src/watch/$async.ts","../src/watch/$bind.ts","../src/watch/$cache.ts","../src/watch/$onmount.ts","../src/watch/$peek.ts","../src/watch/$pending.ts","../src/watch/$refresh.ts","../src/watch/$run.ts","../src/watch/$stream.ts","../src/watch/$unwrap.ts","../src/types/WatchOptions.ts","../src/watch/$watch.ts"],"mappings":"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"}
1
+ {"version":3,"file":"index.d.cts","names":[],"sources":["../src/types/Animation.ts","../src/render/addAnimation.ts","../src/render/addEvent.ts","../src/render/addElement.ts","../src/render/addFragment.ts","../src/render/applyProps.ts","../src/render/clearLayoutSlot.ts","../src/render/fillLayoutSlot.ts","../src/render/formatText.ts","../src/render/firstInsideNode.ts","../src/render/getFragment.ts","../src/render/getElementFragment.ts","../src/render/hydrate.ts","../src/render/mount.ts","../src/render/unmount.ts","../src/types/ListItemSpec.ts","../src/types/ListItem.ts","../src/render/newListItem.ts","../src/render/newRegion.ts","../src/render/nodeAnchor.ts","../src/render/nodeChild.ts","../src/render/nodeNext.ts","../src/render/nodeRootElement.ts","../src/render/nodeRoot.ts","../src/render/nodeSkip.ts","../src/render/popRegion.ts","../src/render/pushRegion.ts","../src/render/rerunRegionEffects.ts","../src/render/restoreHydration.ts","../src/render/runControl.ts","../src/render/runControlBranch.ts","../src/render/runList.ts","../src/render/runAwait.ts","../src/render/runTry.ts","../src/render/saveHydration.ts","../src/render/headElement.ts","../src/render/setAttribute.ts","../src/render/setDynamicElement.ts","../src/types/Bindable.ts","../src/watch/$batch.ts","../src/watch/$async.ts","../src/watch/$bind.ts","../src/watch/$cache.ts","../src/watch/$onmount.ts","../src/watch/$peek.ts","../src/watch/$pending.ts","../src/watch/$refresh.ts","../src/watch/$run.ts","../src/watch/$stream.ts","../src/watch/$unwrap.ts","../src/types/WatchOptions.ts","../src/watch/$watch.ts"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.mts","names":[],"sources":["../src/types/Animation.ts","../src/render/addAnimation.ts","../src/render/addEvent.ts","../src/render/addElement.ts","../src/render/addFragment.ts","../src/render/applyProps.ts","../src/render/clearLayoutSlot.ts","../src/render/fillLayoutSlot.ts","../src/render/formatText.ts","../src/render/firstInsideNode.ts","../src/render/getFragment.ts","../src/render/getElementFragment.ts","../src/render/hydrate.ts","../src/render/mount.ts","../src/render/unmount.ts","../src/types/ListItemSpec.ts","../src/types/ListItem.ts","../src/render/newListItem.ts","../src/render/newRegion.ts","../src/render/nodeAnchor.ts","../src/render/nodeChild.ts","../src/render/nodeNext.ts","../src/render/nodeRootElement.ts","../src/render/nodeRoot.ts","../src/render/nodeSkip.ts","../src/render/popRegion.ts","../src/render/pushRegion.ts","../src/render/rerunRegionEffects.ts","../src/render/restoreHydration.ts","../src/render/runControl.ts","../src/render/runControlBranch.ts","../src/render/runList.ts","../src/render/runAwait.ts","../src/render/runTry.ts","../src/render/saveHydration.ts","../src/render/headElement.ts","../src/render/setAttribute.ts","../src/render/setDynamicElement.ts","../src/types/Bindable.ts","../src/watch/$batch.ts","../src/watch/$async.ts","../src/watch/$bind.ts","../src/watch/$cache.ts","../src/watch/$onmount.ts","../src/watch/$peek.ts","../src/watch/$pending.ts","../src/watch/$refresh.ts","../src/watch/$run.ts","../src/watch/$stream.ts","../src/watch/$unwrap.ts","../src/types/WatchOptions.ts","../src/watch/$watch.ts"],"mappings":"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"}
1
+ {"version":3,"file":"index.d.mts","names":[],"sources":["../src/types/Animation.ts","../src/render/addAnimation.ts","../src/render/addEvent.ts","../src/render/addElement.ts","../src/render/addFragment.ts","../src/render/applyProps.ts","../src/render/clearLayoutSlot.ts","../src/render/fillLayoutSlot.ts","../src/render/formatText.ts","../src/render/firstInsideNode.ts","../src/render/getFragment.ts","../src/render/getElementFragment.ts","../src/render/hydrate.ts","../src/render/mount.ts","../src/render/unmount.ts","../src/types/ListItemSpec.ts","../src/types/ListItem.ts","../src/render/newListItem.ts","../src/render/newRegion.ts","../src/render/nodeAnchor.ts","../src/render/nodeChild.ts","../src/render/nodeNext.ts","../src/render/nodeRootElement.ts","../src/render/nodeRoot.ts","../src/render/nodeSkip.ts","../src/render/popRegion.ts","../src/render/pushRegion.ts","../src/render/rerunRegionEffects.ts","../src/render/restoreHydration.ts","../src/render/runControl.ts","../src/render/runControlBranch.ts","../src/render/runList.ts","../src/render/runAwait.ts","../src/render/runTry.ts","../src/render/saveHydration.ts","../src/render/headElement.ts","../src/render/setAttribute.ts","../src/render/setDynamicElement.ts","../src/types/Bindable.ts","../src/watch/$batch.ts","../src/watch/$async.ts","../src/watch/$bind.ts","../src/watch/$cache.ts","../src/watch/$onmount.ts","../src/watch/$peek.ts","../src/watch/$pending.ts","../src/watch/$refresh.ts","../src/watch/$run.ts","../src/watch/$stream.ts","../src/watch/$unwrap.ts","../src/types/WatchOptions.ts","../src/watch/$watch.ts"],"mappings":";;;;;UAAyB;EACxB,WAAW,aAAa;EACxB,mBAAmB;;;;iBCCI,aAAa,IAAI,aAAa,QAAQ,WAAW,OAAO;;;iBCDxD,SACvB,IAAI,SACJ,cACA,YAAA,MAAkB,SAAS;;;;;;;;;;;;;;;;;;;;;;iBCiBJ,WAAW,MAAM,SAAS,QAAQ,YAAY,QAAQ;;;iBCnBtD,YACvB,UAAU,kBACV,QAAQ,YACR,QAAQ,aACR,UAAU,WACV,YAAY;;;iBCLW,WACvB,IAAI,SACJ,OAAO,iCACP;;;;;;;;;;iBCKuB,gBAAgB,QAAQ;;;;;;;;;;;;;;;;;;iBCYxB,eACvB,WAAW,WACX,MAAM,YACN,QAAQ,YACR,QAAQ,aACR,SAAS,OAAO,mBAChB,WAAW,OAAO,oBAChB;;;iBC5BqB,WAAW;;;;;;;;iBCiBX,gBAAgB,QAAQ,YAAY;;;iBCnBpC,YACvB,UAAU,UACV,OAAO,oBACP,eACA,cACA,eACE;;;;;;;;;;;;;;;;;;;;;;;;;;;iBCkBqB,mBACvB,UAAU,UACV,OAAO,WACP,eACA,cACA,eACE;;;;;;;;;iBCfqB,QACvB,QAAQ,YACR,WAAW,WACX,QAAQ,qBACR,QAAQ,eAAe;;;;;;;;;;iBCJA,MACvB,QAAQ,YACR,WAAW,WACX,QAAQ,qBACR,QAAQ,eAAe;;;;;;;;;;;;;;;iBCHA,QAAQ,QAAQ;;;;;;;;;;;;;;;;;;;UCAf;EACxB;EACA;;;;UCfwB,iBAAiB,QAAQ;;;iBCD1B,YACvB,MAAM,OAAO,mBACb,WACA,gBACE;;;iBCJqB,UAAU,gBAAgB;;;;;;;;;;;;;;;;;iBCkB1B,WAAW,MAAM,YAAY;;;;;;;;;;iBCX7B,UAAU,QAAQ,OAAO;;;;;;;;;;;iBCGzB,SAAS,MAAM,WAAW,iBAAe;;;;;;;;;;;;;;;;;iBCKzC,gBAAgB,MAAM,UAAU;;;;;;;;;;;;;;;;;;;;;;;iBCShC,SAAS,QAAQ,MAAM,iBAAe;;;;;;;;;;;iBCftC,SAAS,MAAM,WAAW,gBAAgB;;;iBCP1C,UAAU,WAAW;;;iBCArB,WAAW,QAAQ,QAAQ,qBAAmB;;;;;;;;;;;;;;;;;;;;;;;;;;iBCyB9C,mBAAmB,QAAQ,QAAQ;;;;;;;;;;;;;;;;;iBCbnC,iBAAiB,MAAM;;;;;;;iBCLvB,WACvB,QAAQ,QACR,QAAQ,aACR,SAAS,QAAQ,sBACjB;;;iBCZuB,iBAAiB,QAAQ,QAAQ,kBAAkB;;;;;;;;;;;;;;;iBCoBnD,QACvB,QAAQ,QACR,QAAQ,YACR,QAAQ,aACR,kBAAkB,gBAClB,SAAS,MAAM,UAAU,QAAQ,sBACjC,SAAS,SAAS,UAAU,SAAS,uBACrC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBCuBuB,SACvB,QAAQ,QACR,QAAQ,aACR,gBAAgB,QAAQ,sBACxB,cAAc,QAAQ,8BACtB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBCAuB,OACvB,QAAQ,QACR,QAAQ,aACR,YAAY,QAAQ,sBACpB,eAAe,QAAQ,aAAa,6BACpC;;;;;;;;;iBCvDuB,iBAAiB;;;;;;;;;iBCFjB,YAAY,iBAAiB,mBAAmB;;;iBCNhD,aAAa,IAAI,SAAS,cAAc;;;iBCKxC,kBAAkB,IAAI,aAAa,cAAc;;;;;;;;;;;;;;;;;;;KCWpE,SAAS,KAAK;;;;;;;;;iBCNK,OAAO,GAAG,UAAU,IAAI;;;;;;;;;;;;;;;;;;;;;;;;;;iBCsBxB,OAAO,GAAG,UAAU,QAAQ,IAAI,WAAW,eAAe;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBCG1D,MACvB,OAAO,OAAO,mBACd,OAAO,OAAO,kCACX;;;;;;;;iBC5BoB,OAAO,GAAG,UAAU,IAAI;;;;;;;;;;;;;;;;;;;;;;;;iBCcxB,SAAS,UAAU;;;;;;;;;;;iBCdnB,MAAM,GAAG,UAAU,IAAI;;;;;;;;;;;;;;;;;;;;;;;;;iBCcvB,SAAS;;;;;;UCZhB;;;;;;;;EAQhB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBA4CuB,SAAS,eAAe,UAAU;;;;;;;;;;iBChDlC,KACvB,UAAU,gBACV,eACA,UAAU,KAAK,0CACb;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBCqBqB,QAAQ,GAC/B,QAAQ,aAAa,IACrB,UAAU,OAAO,YACjB;EAAY;;;;;;;;;iBCrCW,QAAQ,UAAU,OAAO,mBAAmB,QAAQ,IAAI;;;;;;UCJvD;;;;EAIxB;;;;;;;;;;;;;iBCoBuB,OAAO,kBAAkB,QAAQ,GAAG,UAAU,eAAe"}
package/dist/index.mjs CHANGED
@@ -991,6 +991,22 @@ function getElementFragment(document, array, index, html, ns) {
991
991
  return array[index].cloneNode(true);
992
992
  }
993
993
  //#endregion
994
+ //#region src/render/isServerComponent.ts
995
+ /**
996
+ * Whether a component function was compiled for SSR
997
+ *
998
+ * Server components are emitted as `async function`s taking
999
+ * `(props, context, slots)` and returning a `{ body, head }` pair, while
1000
+ * client components are plain functions taking
1001
+ * `(parent, anchor, props, context, slots)`. Checking the function's
1002
+ * constructor distinguishes the two without calling it (which would start
1003
+ * the render). The name lives on the intrinsic `AsyncFunction` object, so
1004
+ * it survives minification.
1005
+ */
1006
+ function isServerComponent(component) {
1007
+ return component.constructor?.name === "AsyncFunction";
1008
+ }
1009
+ //#endregion
994
1010
  //#region src/render/hydrate.ts
995
1011
  /**
996
1012
  * Hydrates a component into an existing DOM tree
@@ -999,6 +1015,7 @@ function getElementFragment(document, array, index, html, ns) {
999
1015
  * @param props An object containing component props
1000
1016
  */
1001
1017
  function hydrate(parent, component, props, slots) {
1018
+ if (isServerComponent(component)) throw new Error("The component was compiled for SSR (it renders to an HTML string) and cannot be hydrated in the browser. Import the client build instead, e.g. with the '?client' import query");
1002
1019
  context.hydrationNode = parent.firstChild;
1003
1020
  context.rootRegion = newRegion(devContext.enabled ? "Root" : void 0);
1004
1021
  context.previousRegion = context.rootRegion;
@@ -1017,6 +1034,7 @@ function hydrate(parent, component, props, slots) {
1017
1034
  * @param props An object containing component props
1018
1035
  */
1019
1036
  function mount(parent, component, props, slots) {
1037
+ if (isServerComponent(component)) throw new Error("The component was compiled for SSR (it renders to an HTML string) and cannot be mounted in the browser. Import the client build instead, e.g. with the '?client' import query");
1020
1038
  if (parent.childElementCount > 0) throw new Error("The parent node must have no child elements");
1021
1039
  while (parent.firstChild !== null) parent.firstChild.remove();
1022
1040
  if (context.rootRegion === null) {
@@ -1138,8 +1156,14 @@ function nodeAnchor(node) {
1138
1156
  if (isCommentNode(node) && node.data === "[") {
1139
1157
  const startParent = node.parentNode;
1140
1158
  let currentNode = nodeNext(node);
1141
- const firstInside = currentNode;
1142
1159
  node.remove();
1160
+ while (currentNode !== null && isCommentNode(currentNode) && currentNode.data === "^") {
1161
+ const next = currentNode.nextSibling;
1162
+ currentNode.remove();
1163
+ currentNode = next;
1164
+ context.hydrationNode = currentNode;
1165
+ }
1166
+ const firstInside = currentNode;
1143
1167
  let level = 1;
1144
1168
  while (currentNode !== null) {
1145
1169
  if (isCommentNode(currentNode)) {
@@ -1150,9 +1174,10 @@ function nodeAnchor(node) {
1150
1174
  const endNode = currentNode.nextSibling;
1151
1175
  currentNode.remove();
1152
1176
  if (context.hydrationNode === currentNode) context.hydrationNode = endNode;
1177
+ const firstContent = firstInside === currentNode ? null : firstInside;
1153
1178
  const region = context.activeRegion;
1154
- if (region.startNode === null && firstInside !== null) region.startNode = firstInside;
1155
- setFirstInsideNode(endNode, firstInside);
1179
+ if (region.startNode === null) region.startNode = firstContent ?? endNode;
1180
+ setFirstInsideNode(endNode, firstContent);
1156
1181
  return endNode;
1157
1182
  }
1158
1183
  }
@@ -1432,6 +1457,52 @@ function runControlBranch(region, oldIndex, index) {
1432
1457
  return true;
1433
1458
  }
1434
1459
  //#endregion
1460
+ //#region src/render/checkListKeys.ts
1461
+ const warnedByRegion = /* @__PURE__ */ new WeakMap();
1462
+ /**
1463
+ * DEV-ONLY: warns when a keyed `@for` list contains duplicate `@key` values.
1464
+ *
1465
+ * `runListItems` matches old and new rows by key (first match wins), so
1466
+ * duplicate keys make updates and removals hit the wrong rows, desyncing the
1467
+ * DOM from the data. Called from `runList` only when `devContext.enabled`, so
1468
+ * it never runs in a production build.
1469
+ *
1470
+ * `undefined`/`null` keys are the "unkeyed" case (rows are matched
1471
+ * positionally) and are ignored.
1472
+ *
1473
+ * @param region The `@for` list's region, used to dedupe warnings per list.
1474
+ * @param specs The new row specs, in order.
1475
+ */
1476
+ function checkListKeys(region, specs) {
1477
+ let seen;
1478
+ for (let i = 0; i < specs.length; i++) {
1479
+ const key = specs[i].key;
1480
+ if (key === void 0 || key === null) continue;
1481
+ if (seen === void 0) seen = /* @__PURE__ */ new Set();
1482
+ if (!seen.has(key)) {
1483
+ seen.add(key);
1484
+ continue;
1485
+ }
1486
+ let warned = warnedByRegion.get(region);
1487
+ if (warned === void 0) {
1488
+ warned = /* @__PURE__ */ new Set();
1489
+ warnedByRegion.set(region, warned);
1490
+ }
1491
+ if (!warned.has(key)) {
1492
+ warned.add(key);
1493
+ console.warn(`[torpor] Duplicate @key value ${formatKey(key)} in a keyed @for list. Keys must be unique, or updates and removals will affect the wrong rows.`);
1494
+ }
1495
+ }
1496
+ }
1497
+ function formatKey(key) {
1498
+ try {
1499
+ const json = JSON.stringify(key);
1500
+ return json === void 0 ? String(key) : json;
1501
+ } catch {
1502
+ return String(key);
1503
+ }
1504
+ }
1505
+ //#endregion
1435
1506
  //#region src/watch/deepWrap.ts
1436
1507
  /**
1437
1508
  * Wraps a value with `$watch` if it is a plain object (or Date/Map/Set) that
@@ -2249,6 +2320,7 @@ function runList(region, parent, anchor, buildItems, create, update, noWatch) {
2249
2320
  const oldRegion = pushRegion(region, first);
2250
2321
  first = false;
2251
2322
  const newSpecs = buildItems();
2323
+ if (devContext.enabled) checkListKeys(region, newSpecs);
2252
2324
  context.activeTarget = null;
2253
2325
  listItems = runListItems(region, parent, anchor, listItems, newSpecs, create, update, noWatch);
2254
2326
  popRegion(oldRegion);