@askrjs/askr 0.2.3 → 0.3.0

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 (117) hide show
  1. package/CHANGELOG.md +11 -0
  2. package/dist/access-CEA2pOry.js +1046 -0
  3. package/dist/actions/index.d.ts +16 -2
  4. package/dist/actions/index.js +5 -5
  5. package/dist/{activity-ZxJXI9hF.js → activity-oy2YFTFF.js} +22 -16
  6. package/dist/benchmark.js +8 -9
  7. package/dist/boot/index.d.ts +21 -9
  8. package/dist/boot/index.js +1 -1
  9. package/dist/{boot-D4GaLA0d.js → boot-BMBMWUGI.js} +680 -440
  10. package/dist/capabilities-Bbf0mlp9.js +42 -0
  11. package/dist/components/index.d.ts +11 -8
  12. package/dist/components/index.js +1 -1
  13. package/dist/{compose-ref-Bfaf4kUC.js → compose-ref-v0qGlHoN.js} +7 -1
  14. package/dist/control/index.d.ts +20 -2
  15. package/dist/control/index.js +1 -1
  16. package/dist/{control-57x3wa4J.js → control-BR_mrX__.js} +4 -4
  17. package/dist/core.d.ts +163 -0
  18. package/dist/csp-nonce-BfFWcd0w.js +173 -0
  19. package/dist/data/index.d.ts +74 -2
  20. package/dist/data/index.js +3 -3
  21. package/dist/{data-B7XCDeC8.js → data-2edW0jhF.js} +133 -138
  22. package/dist/{data-runtime-7t_Y0iv3.js → data-runtime-CxoY98lX.js} +22 -15
  23. package/dist/{deferred-BELyvMsC.js → deferred-CbgNhf67.js} +5 -5
  24. package/dist/dom-renderer.d.ts +77 -0
  25. package/dist/domains/actions.d.ts +70 -0
  26. package/dist/domains/component.d.ts +119 -0
  27. package/dist/domains/context.d.ts +73 -0
  28. package/dist/domains/control.d.ts +328 -0
  29. package/dist/domains/data.d.ts +461 -0
  30. package/dist/domains/diagnostics.d.ts +21 -0
  31. package/dist/domains/lifecycle.d.ts +112 -0
  32. package/dist/domains/refs.d.ts +18 -0
  33. package/dist/domains/renderer.d.ts +127 -0
  34. package/dist/domains/routing.d.ts +501 -0
  35. package/dist/domains/scheduler.d.ts +79 -0
  36. package/dist/domains/server.d.ts +112 -0
  37. package/dist/domains/state.d.ts +129 -0
  38. package/dist/domains/telemetry.d.ts +29 -0
  39. package/dist/eager-control.d.ts +8 -0
  40. package/dist/{effect-4LwBwV58.js → effect-C3Zsc1k6.js} +14 -41
  41. package/dist/{types-DkVJhVs9.d.ts → elements.d.ts} +84 -42
  42. package/dist/{for-internal-DeoSg_AV.js → for-state-BDykeOJL.js} +623 -511
  43. package/dist/foundations/icon/index.d.ts +67 -16
  44. package/dist/foundations/icon/index.js +1 -1
  45. package/dist/foundations/index.d.ts +26 -3
  46. package/dist/foundations/index.js +1 -1
  47. package/dist/foundations/interactions/index.d.ts +73 -34
  48. package/dist/foundations/interactions/index.js +1 -1
  49. package/dist/foundations/state/index.d.ts +12 -5
  50. package/dist/foundations/state/index.js +1 -1
  51. package/dist/foundations/structures/index.d.ts +44 -3
  52. package/dist/foundations/structures/index.js +1 -1
  53. package/dist/foundations/utilities/index.d.ts +34 -3
  54. package/dist/foundations/utilities/index.js +1 -1
  55. package/dist/fx/index.d.ts +72 -25
  56. package/dist/fx/index.js +8 -8
  57. package/dist/index.d.ts +121 -4
  58. package/dist/index.js +316 -47
  59. package/dist/{component-internal-Dl02WQKS.js → instance-VAFYVJOa.js} +495 -283
  60. package/dist/{interactions-D102JxBR.js → interactions-CTeACdT_.js} +1 -1
  61. package/dist/jsx-dev-runtime.d.ts +51 -11
  62. package/dist/jsx-globals.d.ts +2 -0
  63. package/dist/jsx-runtime-CPJXAOBF.js +43 -0
  64. package/dist/jsx-runtime.d.ts +17 -3
  65. package/dist/jsx-runtime.js +2 -42
  66. package/dist/jsx.d.ts +108 -0
  67. package/dist/{manifest-D6rfj_jC.js → manifest-D1P3UNWV.js} +4 -9
  68. package/dist/{navigate-C46GtScG.js → navigate-prC5V8XV.js} +204 -476
  69. package/dist/{navigate-wel1e6cU.d.ts → navigation.d.ts} +10 -8
  70. package/dist/{readable-CEcteIDE.js → notify-DxxuDPeY.js} +96 -34
  71. package/dist/{query-registry-Bua3cDyb.js → query-registry-LrYpQsDJ.js} +1 -1
  72. package/dist/{compose-ref-DOJHoW5X.d.ts → refs.d.ts} +11 -7
  73. package/dist/{ssr-BaC5qHEw.js → render-resolved-BC0W7_5b.js} +165 -558
  74. package/dist/{renderer-BhXofszH.js → renderer-CT0HeY58.js} +6866 -5715
  75. package/dist/{resolution-t8J0yNaU.js → resolution-3DskUkjG.js} +189 -180
  76. package/dist/{resource-operation-B8q-QRUx.js → resource-18-sFRl9.js} +98 -58
  77. package/dist/resources/index.d.ts +64 -46
  78. package/dist/resources/index.js +215 -9
  79. package/dist/route-activity.d.ts +21 -0
  80. package/dist/{route-matching-Bo6UqG-5.js → route-matching-CUzZ31gT.js} +68 -82
  81. package/dist/router/index.d.ts +378 -79
  82. package/dist/router/index.js +9 -9
  83. package/dist/router-internal-7nuNEHfG.js +36 -0
  84. package/dist/scope-Cq_iYbTR.js +403 -0
  85. package/dist/{selector-C7rIpOnN.js → selector-BSPVkjKP.js} +41 -41
  86. package/dist/{shared-CGrx2AOL.js → shared-kU8yvryp.js} +3 -6
  87. package/dist/{snapshot-source-BB42xgwb.js → snapshot-source-An4iKajG.js} +1 -1
  88. package/dist/ssg/index.d.ts +51 -15
  89. package/dist/ssg/index.js +269 -209
  90. package/dist/ssr/index.d.ts +121 -68
  91. package/dist/ssr/index.js +2 -1
  92. package/dist/ssr-CiNm6Vzl.js +393 -0
  93. package/dist/{ssr-DSHd_Q9K.js → ssr-EPjwJH5R.js} +5 -1
  94. package/dist/{state-BzhOW2TX.js → state-BXJfudq1.js} +10 -15
  95. package/dist/{structures-BwfeO4Iz.js → structures-CjEzfj4h.js} +37 -57
  96. package/dist/{index-r5THqzPN.d.ts → structures.d.ts} +57 -36
  97. package/dist/testing/index.d.ts +125 -24
  98. package/dist/testing/index.js +26 -62
  99. package/dist/timer-C7uqnJVt.js +85 -0
  100. package/dist/{index-B1gu0_m0.d.ts → utilities.d.ts} +24 -13
  101. package/dist/{verify-hydration-_Q6avFp0.js → verify-hydration-ikaxotf7.js} +8 -5
  102. package/package.json +13 -15
  103. package/dist/access-Rr7CMa06.js +0 -507
  104. package/dist/activity-CAJ5eho-.d.ts +0 -14
  105. package/dist/benchmark.d.ts +0 -26
  106. package/dist/cleanup-DsrbV-rr.js +0 -535
  107. package/dist/component-cleanup-DjpwO9WX.js +0 -143
  108. package/dist/component-scope-4I3yEMkb.js +0 -195
  109. package/dist/control-DdFw-Caz.d.ts +0 -8
  110. package/dist/csp-nonce-C9OuML0U.js +0 -72
  111. package/dist/css-BuPaTOfe.js +0 -70
  112. package/dist/fastlane-Buz1Rvlr.js +0 -166
  113. package/dist/index-DSqXWc77.d.ts +0 -1402
  114. package/dist/jsx-runtime-BTzDdBJ_.d.ts +0 -2
  115. package/dist/jsx-runtime-Cdd6Gkb0.d.ts +0 -38
  116. package/dist/lifecycle-batch-Be-CLDjV.js +0 -253
  117. package/dist/lifecycle-operations-C5m03NQc.js +0 -195
@@ -1,15 +1,14 @@
1
+ import { a as getRuntimeEvaluation, c as getRuntimeScopes, i as getRuntimeCleanup, v as logger, w as isDevelopmentEnvironment } from "./access-CEA2pOry.js";
2
+ import { C as rebaseVNodeTreeWithContextFrame, I as DIRECT_RANGE_OWNER, O as registerOwnedChildScope, T as cleanupComponent, _ as haveEquivalentContextFrames, j as finalizeInlineReadSubscriptions, n as createComponentInstance, s as renderScopedComponent } from "./instance-VAFYVJOa.js";
3
+ import { A as releaseOwnedChild, C as disposeOwnership, D as ownChild, O as ownCleanup, S as detachOwnedChildren, b as attachOwnership, f as getCurrentComponentInstance, i as claimHookIndex, k as registerScopedOwnership, m as getCurrentStateIndex, o as clearRenderTracking, y as OwnershipRecord } from "./scope-Cq_iYbTR.js";
4
+ import { t as adoptComponentParent } from "./capabilities-Bbf0mlp9.js";
5
+ import { i as commitTransaction, l as registerCommitParticipant, n as applyTransaction, o as discardTransaction, p as suspendTransaction, r as beginCommitTransaction, s as getCurrentCommitTransaction, t as notifyReadableSource, u as registerCommitRollback, v as recordReadableRead, y as shouldCoalesceFineGrainedItemReads } from "./notify-DxxuDPeY.js";
1
6
  import { n as _isDOMElement } from "./vnode-D24p6IW2.js";
2
- import { o as getRuntimeRenderer, x as isDevelopmentEnvironment, y as logger } from "./access-Rr7CMa06.js";
3
- import { h as getCurrentStateIndex, i as claimHookIndex, o as clearRenderTracking, p as getCurrentInstance } from "./component-scope-4I3yEMkb.js";
4
- import { _ as haveEquivalentContextFrames, b as rebaseVNodeTreeWithContextFrame, n as createComponentInstance, s as renderScopedComponent } from "./component-internal-Dl02WQKS.js";
5
- import { d as shouldCoalesceFineGrainedItemReads, l as notifyReadableReaders, o as markReactivePropsDirtySource, s as markReadableDerivedSubscribersDirty, u as recordReadableRead } from "./readable-CEcteIDE.js";
6
- import { i as unregisterOwnedChildScope, r as registerOwnedChildScope, t as cleanupComponent } from "./component-cleanup-DjpwO9WX.js";
7
- import { o as finalizeInlineReadSubscriptions, u as registerLifecycleTransaction } from "./lifecycle-batch-Be-CLDjV.js";
8
- //#region src/runtime/child-scope.ts
7
+ //#region src/runtime/ownership/child-scope.ts
9
8
  const EMPTY_CHILD_SCOPE_PROPS = {};
10
9
  const childScopesByInstance = /* @__PURE__ */ new WeakMap();
11
10
  function executeChildScopeComponent() {
12
- const instance = getCurrentInstance();
11
+ const instance = getCurrentComponentInstance();
13
12
  const scope = instance ? childScopesByInstance.get(instance) : void 0;
14
13
  if (!scope?._renderFn) return null;
15
14
  return scope._renderFn();
@@ -19,12 +18,35 @@ function ensureChildScopeFlushTask(scope) {
19
18
  if (instance._pendingFlushTask) return;
20
19
  instance._pendingFlushTask = () => {
21
20
  instance.hasPendingUpdate = false;
22
- if (instance.notifyUpdate === null || scope._disposed) return;
23
- renderScope(scope);
24
- scope._onDirty?.();
21
+ if (instance.notifyUpdate === null || instance.owner.disposed) return;
22
+ if (scope._preparedTransaction) discardTransaction(scope._preparedTransaction);
23
+ const transaction = beginCommitTransaction();
24
+ scope._preparedTransaction = transaction;
25
+ const snapshot = captureChildScopeTransactionSnapshot(scope);
26
+ registerCommitRollback(() => {
27
+ if (!instance.owner.disposed) restoreChildScopeTransactionSnapshot(scope, snapshot);
28
+ });
29
+ try {
30
+ renderScope(scope);
31
+ suspendTransaction(transaction);
32
+ if (scope._onDirty) scope._onDirty();
33
+ else {
34
+ scope._preparedTransaction = void 0;
35
+ commitTransaction(transaction);
36
+ }
37
+ } catch (error) {
38
+ discardTransaction(transaction);
39
+ scope._preparedTransaction = void 0;
40
+ throw error;
41
+ } finally {
42
+ suspendTransaction(transaction);
43
+ }
25
44
  };
26
45
  }
27
46
  var ChildScopeImpl = class {
47
+ get [DIRECT_RANGE_OWNER]() {
48
+ return true;
49
+ }
28
50
  constructor(parent, key, onDirty, ownership) {
29
51
  this.previousVnode = void 0;
30
52
  this.vnode = void 0;
@@ -34,17 +56,18 @@ var ChildScopeImpl = class {
34
56
  this.hydrationPending = true;
35
57
  this.blueprintOwner = void 0;
36
58
  this._renderFn = void 0;
37
- this._disposed = false;
38
59
  this._renderedOwnerFrame = null;
39
60
  this.key = key;
40
- this._parent = parent;
61
+ this._parentOwnership = parent?.owner ?? null;
41
62
  this._onDirty = onDirty;
42
63
  this._ownership = ownership;
43
64
  this._startStateIndex = getCurrentStateIndex();
44
65
  this.componentInstance = createComponentInstance("child-scope", executeChildScopeComponent, EMPTY_CHILD_SCOPE_PROPS, null);
45
66
  childScopesByInstance.set(this.componentInstance, this);
67
+ this.componentInstance.owner.finalizer = this;
68
+ registerScopedOwnership(this, this.componentInstance.owner);
46
69
  if (parent) {
47
- this.componentInstance.parentInstance = parent;
70
+ adoptComponentParent(this.componentInstance, parent);
48
71
  this.componentInstance.ownerFrame = parent.ownerFrame;
49
72
  this.componentInstance.portalScope = parent.portalScope;
50
73
  if (ownership) ownership.add(this);
@@ -59,29 +82,38 @@ var ChildScopeImpl = class {
59
82
  return renderScope(this);
60
83
  }
61
84
  dispose() {
62
- if (this._disposed) return;
63
- this._disposed = true;
64
- if (this._parent) if (this._ownership) this._ownership.delete(this);
65
- else unregisterOwnedChildScope(this._parent, this);
66
- childScopesByInstance.delete(this.componentInstance);
67
85
  cleanupComponent(this.componentInstance);
68
- this._renderFn = void 0;
69
- this.previousVnode = void 0;
70
- this.vnode = void 0;
71
- this.dom = void 0;
72
- this.range = void 0;
73
- this.needsDomUpdate = false;
74
- this.hydrationPending = false;
75
- this.blueprintOwner = void 0;
76
- this.componentInstance.hasPendingUpdate = false;
86
+ }
87
+ release() {
88
+ try {
89
+ if (this._preparedTransaction) discardTransaction(this._preparedTransaction);
90
+ this._preparedTransaction = void 0;
91
+ if (this._ownership) this._ownership.delete(this);
92
+ else if (this._parentOwnership) releaseOwnedChild(this._parentOwnership, this);
93
+ } finally {
94
+ childScopesByInstance.delete(this.componentInstance);
95
+ this._renderFn = void 0;
96
+ this.previousVnode = void 0;
97
+ this.vnode = void 0;
98
+ getRuntimeScopes().clearChildScopeHost(this);
99
+ this.needsDomUpdate = false;
100
+ this.hydrationPending = false;
101
+ this.blueprintOwner = void 0;
102
+ this.componentInstance.hasPendingUpdate = false;
103
+ this._parentOwnership = null;
104
+ this._onDirty = void 0;
105
+ this._ownership = void 0;
106
+ this._renderedOwnerFrame = null;
107
+ }
77
108
  }
78
109
  };
79
110
  function renderScope(scope) {
80
- if (scope._disposed) {
111
+ if (scope.componentInstance.owner.disposed) {
81
112
  if (isDevelopmentEnvironment()) throw new Error(`[askr] Attempted to render disposed child scope ${String(scope.key)}.`);
82
113
  return scope.vnode;
83
114
  }
84
115
  if (!scope._renderFn) return scope.vnode;
116
+ joinChildScopePreparation(scope);
85
117
  const { componentInstance } = scope;
86
118
  const previousVNode = scope.vnode;
87
119
  try {
@@ -103,15 +135,23 @@ function renderScope(scope) {
103
135
  function rerenderChildScope(scope) {
104
136
  return renderScope(scope);
105
137
  }
138
+ /** Join prepared reads and scope restoration to the transaction applying its output. */
139
+ function joinChildScopePreparation(scope) {
140
+ const mutable = scope;
141
+ const transaction = mutable._preparedTransaction;
142
+ const current = getCurrentCommitTransaction();
143
+ if (!transaction || !current || transaction === current) return;
144
+ mutable._preparedTransaction = void 0;
145
+ applyTransaction(transaction, () => {});
146
+ commitTransaction(transaction);
147
+ }
106
148
  /** @internal */
107
149
  function captureChildScopeTransactionSnapshot(scope) {
108
150
  const mutableScope = scope;
109
151
  return {
110
152
  previousVnode: scope.previousVnode,
111
153
  vnode: scope.vnode,
112
- dom: scope.dom,
113
- range: scope.range,
114
- domTextData: scope.dom?.nodeType === 3 ? scope.dom.data : void 0,
154
+ host: getRuntimeScopes().captureChildScopeHost(scope),
115
155
  needsDomUpdate: scope.needsDomUpdate,
116
156
  hydrationPending: scope.hydrationPending,
117
157
  renderFn: mutableScope._renderFn,
@@ -123,9 +163,7 @@ function restoreChildScopeTransactionSnapshot(scope, snapshot) {
123
163
  const mutableScope = scope;
124
164
  scope.previousVnode = snapshot.previousVnode;
125
165
  scope.vnode = snapshot.vnode;
126
- scope.dom = snapshot.dom;
127
- scope.range = snapshot.range;
128
- if (snapshot.domTextData !== void 0 && snapshot.dom?.nodeType === 3) snapshot.dom.data = snapshot.domTextData;
166
+ snapshot.host?.restore(scope);
129
167
  scope.needsDomUpdate = snapshot.needsDomUpdate;
130
168
  scope.hydrationPending = snapshot.hydrationPending;
131
169
  mutableScope._renderFn = snapshot.renderFn;
@@ -138,7 +176,7 @@ function createChildScope(parent, key, onDirty, ownership) {
138
176
  return new ChildScopeImpl(parent, key, onDirty, ownership);
139
177
  }
140
178
  //#endregion
141
- //#region src/runtime/control.ts
179
+ //#region src/runtime/control/branches.ts
142
180
  function setScopeContextFrame(scope, frame) {
143
181
  if (scope) scope.componentInstance.ownerFrame = frame;
144
182
  }
@@ -192,7 +230,13 @@ function beginControlTransaction(state) {
192
230
  registered: false
193
231
  };
194
232
  state._transaction = transaction;
195
- transaction.registered = registerLifecycleTransaction(transaction, () => commitControlTransaction(state, transaction), () => rollbackControlTransaction(state, transaction));
233
+ transaction.registered = registerCommitParticipant({
234
+ key: transaction,
235
+ collision: "keep-first",
236
+ publish: () => publishControlTransaction(state, transaction),
237
+ settle: () => commitControlTransaction(state, transaction),
238
+ rollback: () => rollbackControlTransaction(state, transaction)
239
+ });
196
240
  return transaction;
197
241
  }
198
242
  function stageBranchRemoval(state, scope) {
@@ -200,31 +244,37 @@ function stageBranchRemoval(state, scope) {
200
244
  const transaction = state._transaction;
201
245
  if (transaction && !transaction.removedScopes.includes(scope)) transaction.removedScopes.push(scope);
202
246
  else if (!transaction) disposeChildScope(scope);
203
- const removedRange = getRuntimeRenderer().resolveChildScopeRange?.(scope) ?? scope.range;
247
+ const removedRange = getRuntimeEvaluation().resolveChildScopeRange?.(scope) ?? scope.range;
204
248
  const removedDom = removedRange?.single ? removedRange.start : scope.dom;
205
249
  if (removedDom) state.lastRemovedNodes.push(removedDom);
206
250
  if (removedRange) state.lastRemovedRanges.push(removedRange);
207
251
  }
208
- function commitControlTransaction(state, transaction) {
252
+ function publishControlTransaction(state, transaction) {
209
253
  if (state._transaction !== transaction) return;
254
+ if (transaction.shouldClearDomUpdateState) {
255
+ for (const scope of getBranchScopes(state)) scope.needsDomUpdate = false;
256
+ state.lastRemovedNodes = [];
257
+ state.lastRemovedRanges = [];
258
+ }
259
+ state._transaction = null;
260
+ transaction.published = true;
261
+ }
262
+ function commitControlTransaction(state, transaction) {
263
+ if (!transaction.published) publishControlTransaction(state, transaction);
264
+ if (!transaction.published) return;
265
+ transaction.published = false;
210
266
  const errors = [];
211
267
  for (const scope of transaction.removedScopes) try {
212
268
  disposeChildScope(scope);
213
269
  } catch (error) {
214
270
  errors.push(error);
215
271
  }
216
- if (transaction.shouldClearDomUpdateState) {
217
- if ("truthyScope" in state && state.truthyScope) state.truthyScope.needsDomUpdate = false;
218
- if ("fallbackScope" in state && state.fallbackScope) state.fallbackScope.needsDomUpdate = false;
219
- if (state.activeScope) state.activeScope.needsDomUpdate = false;
220
- state.lastRemovedNodes = [];
221
- state.lastRemovedRanges = [];
222
- }
223
- state._transaction = null;
224
- if (errors.length > 0) throw new AggregateError(errors, "Control branch cleanup failed");
272
+ transaction.removedScopes.length = 0;
273
+ if (errors.length) throw new AggregateError(errors, "Control branch cleanup failed");
225
274
  }
226
275
  function rollbackControlTransaction(state, transaction) {
227
- if (state._transaction !== transaction) return;
276
+ if (state._transaction !== transaction && !transaction.published) return;
277
+ transaction.published = false;
228
278
  const committedScopes = new Set(transaction.snapshot.scopeSnapshots.keys());
229
279
  const currentScopes = getBranchScopes(state);
230
280
  const errors = [];
@@ -271,7 +321,7 @@ function createShowState(value, renderTruthy, renderFallback) {
271
321
  fallbackScope: null,
272
322
  lastRemovedNodes: [],
273
323
  lastRemovedRanges: [],
274
- parentInstance: getCurrentInstance(),
324
+ parentInstance: getCurrentComponentInstance(),
275
325
  _enqueueBoundaryCommit: null,
276
326
  _hasPendingBoundaryCommit: false,
277
327
  _transaction: null,
@@ -347,7 +397,7 @@ function createCaseState(matches, fallback) {
347
397
  lastRemovedNodes: [],
348
398
  lastRemovedRanges: [],
349
399
  matches,
350
- parentInstance: getCurrentInstance(),
400
+ parentInstance: getCurrentComponentInstance(),
351
401
  _enqueueBoundaryCommit: null,
352
402
  _hasPendingBoundaryCommit: false,
353
403
  _transaction: null
@@ -393,8 +443,9 @@ function clearCaseDomUpdateState(state) {
393
443
  state.lastRemovedNodes = [];
394
444
  state.lastRemovedRanges = [];
395
445
  }
446
+ const recordBenchEvent = (_event) => {};
396
447
  //#endregion
397
- //#region src/runtime/for-signals.ts
448
+ //#region src/runtime/control/for-signals.ts
398
449
  function peekForSignal() {
399
450
  return this._value;
400
451
  }
@@ -410,9 +461,10 @@ function setForIndexValue(newValue, notifyReaders = true) {
410
461
  if (notifyReaders) notifyForSignalReaders(this);
411
462
  }
412
463
  function notifyForSignalReaders(source, skipInstance, skipOwnedBy) {
413
- markReadableDerivedSubscribersDirty(source);
414
- markReactivePropsDirtySource(source);
415
- notifyReadableReaders(source, skipInstance, skipOwnedBy);
464
+ notifyReadableSource(source, {
465
+ skipInstance,
466
+ skipOwnedBy
467
+ });
416
468
  }
417
469
  function createForIndexSignal(initialIndex) {
418
470
  function readIndexSignal() {
@@ -551,14 +603,16 @@ const REACTIVE_FOR_ITEM_PROXY_HANDLER = {
551
603
  if (properties === null) metadata.coalescedProperties = prop;
552
604
  else if (Array.isArray(properties)) {
553
605
  if (!properties.includes(prop)) properties.push(prop);
554
- } else if (properties !== prop && metadata.coalescedProperty2 !== prop) if (metadata.coalescedProperty2 === null) metadata.coalescedProperty2 = prop;
555
- else {
556
- metadata.coalescedProperties = [
557
- properties,
558
- metadata.coalescedProperty2,
559
- prop
560
- ];
561
- metadata.coalescedProperty2 = null;
606
+ } else if (properties !== prop && metadata.coalescedProperty2 !== prop) {
607
+ if (metadata.coalescedProperty2 === null) metadata.coalescedProperty2 = prop;
608
+ else {
609
+ metadata.coalescedProperties = [
610
+ properties,
611
+ metadata.coalescedProperty2,
612
+ prop
613
+ ];
614
+ metadata.coalescedProperty2 = null;
615
+ }
562
616
  }
563
617
  let itemSignal = metadata.itemSignal;
564
618
  if (!itemSignal) {
@@ -622,29 +676,15 @@ const REACTIVE_FOR_ITEM_PROXY_HANDLER = {
622
676
  });
623
677
  }
624
678
  };
625
- const recordBenchEvent = (_event) => {};
626
679
  //#endregion
627
- //#region src/runtime/for-scopes.ts
628
- function resolveScopeBoundary(scope) {
629
- const range = getRuntimeRenderer().resolveChildScopeRange?.(scope) ?? scope.range;
630
- return {
631
- dom: range?.single ? range.start : scope.dom,
632
- range
633
- };
634
- }
680
+ //#region src/runtime/control/for-scopes.ts
635
681
  function recordRemovedBoundary(forState, dom, range) {
636
- if (range && !range.single) forState.lastRemovedRanges.push(range);
637
- else if (dom) forState.lastRemovedNodes.push(dom);
682
+ getRuntimeScopes().recordRemovedScopeBoundary(dom, range, forState.lastRemovedNodes, forState.lastRemovedRanges);
638
683
  }
639
- function captureRemovedScopeNodes(forState, dom, range) {
684
+ function prepareRemovedScope(forState, scope) {
640
685
  const transaction = forState._transaction;
641
- if (!transaction) return;
642
- const nodes = transaction.removedScopeNodes ??= [];
643
- if (range && !range.single) {
644
- nodes.push(range.start);
645
- for (let node = range.start.nextSibling; node && node !== range.end; node = node.nextSibling) nodes.push(node);
646
- nodes.push(range.end);
647
- } else if (dom) nodes.push(dom);
686
+ if (!transaction) return getRuntimeScopes().resolveScopeBoundary(scope);
687
+ return getRuntimeScopes().prepareScopeRemoval(scope, forState.lastRemovedNodes, forState.lastRemovedRanges, transaction.removedScopeNodes ??= []);
648
688
  }
649
689
  function enqueueForScopeUpdate(parent) {
650
690
  parent?._enqueueRun?.();
@@ -761,12 +801,10 @@ function renderItemScope(forState, scope, item, indexSignal, key) {
761
801
  return vnode;
762
802
  }
763
803
  function disposeItemInstance(forState, itemInstance, domCleanup) {
764
- const { dom: removedDom, range: removedRange } = resolveScopeBoundary(itemInstance.scope);
804
+ const { dom: removedDom, range: removedRange } = prepareRemovedScope(forState, itemInstance.scope);
765
805
  const transaction = forState._transaction;
766
806
  if (transaction) {
767
807
  (transaction.removedScopes ??= []).push(itemInstance.scope);
768
- captureRemovedScopeNodes(forState, removedDom, removedRange);
769
- recordRemovedBoundary(forState, removedDom, removedRange);
770
808
  return;
771
809
  }
772
810
  try {
@@ -775,9 +813,7 @@ function disposeItemInstance(forState, itemInstance, domCleanup) {
775
813
  if (isDevelopmentEnvironment()) console.error("[For] Cleanup error:", err);
776
814
  }
777
815
  if (!removedDom && !removedRange) return;
778
- if (removedDom instanceof Element) {
779
- if (domCleanup === "teardown") getRuntimeRenderer().teardownNodeSubtree(removedDom);
780
- }
816
+ if (domCleanup === "teardown") getRuntimeScopes().teardownScopeHost(removedDom, void 0);
781
817
  recordRemovedBoundary(forState, removedDom, removedRange);
782
818
  }
783
819
  function createItemInstance(key, item, index, forState) {
@@ -836,15 +872,17 @@ function updateItemInstance(forState, itemInstance, item) {
836
872
  if (itemSignal && scopeReadsSource(scope, itemSignal)) scopeReadsChangedSignal = true;
837
873
  let coalescedPropertyChanged = false;
838
874
  const coalescedProperties = reactiveItemState.coalescedProperties;
839
- if (coalescedProperties !== null) if (Array.isArray(coalescedProperties)) {
840
- for (const property of coalescedProperties) if (!Object.is(readForItemProperty(previousItem, property), readForItemProperty(item, property))) {
841
- coalescedPropertyChanged = true;
842
- break;
875
+ if (coalescedProperties !== null) {
876
+ if (Array.isArray(coalescedProperties)) {
877
+ for (const property of coalescedProperties) if (!Object.is(readForItemProperty(previousItem, property), readForItemProperty(item, property))) {
878
+ coalescedPropertyChanged = true;
879
+ break;
880
+ }
881
+ } else {
882
+ coalescedPropertyChanged = !Object.is(readForItemProperty(previousItem, coalescedProperties), readForItemProperty(item, coalescedProperties));
883
+ const secondProperty = reactiveItemState.coalescedProperty2;
884
+ if (secondProperty !== null && !coalescedPropertyChanged) coalescedPropertyChanged = !Object.is(readForItemProperty(previousItem, secondProperty), readForItemProperty(item, secondProperty));
843
885
  }
844
- } else {
845
- coalescedPropertyChanged = !Object.is(readForItemProperty(previousItem, coalescedProperties), readForItemProperty(item, coalescedProperties));
846
- const secondProperty = reactiveItemState.coalescedProperty2;
847
- if (secondProperty !== null && !coalescedPropertyChanged) coalescedPropertyChanged = !Object.is(readForItemProperty(previousItem, secondProperty), readForItemProperty(item, secondProperty));
848
886
  }
849
887
  const wholeItemChanged = itemSignal?._hasBeenRead === true && (reactiveItemState.wholeItemRead || coalescedPropertyChanged);
850
888
  const itemShapeChanged = !wholeItemChanged && changedPropertySignals.length === 0 && !haveSameOwnKeys(previousItem, item);
@@ -860,26 +898,24 @@ function updateItemInstance(forState, itemInstance, item) {
860
898
  removeForParentReaders(forState.parentInstance, itemSignal);
861
899
  itemSignal.set(item, notifyReaders);
862
900
  }
863
- if (scopeReadsChangedSignal) if (itemSignal !== null && scopeDirectlyReadsSource(scope, itemSignal) || changedPropertySignals.some(([propertySignal]) => scopeDirectlyReadsSource(scope, propertySignal))) rerenderItemInstance(forState, itemInstance, itemInstance.reactiveItem);
864
- else scope.markDirty();
901
+ if (scopeReadsChangedSignal) {
902
+ if (itemSignal !== null && scopeDirectlyReadsSource(scope, itemSignal) || changedPropertySignals.some(([propertySignal]) => scopeDirectlyReadsSource(scope, propertySignal))) rerenderItemInstance(forState, itemInstance, itemInstance.reactiveItem);
903
+ else scope.markDirty();
904
+ }
865
905
  return visibleChange;
866
906
  }
867
907
  const FOR_FALLBACK_SCOPE_KEY = "__for-fallback__";
868
908
  function disposeFallbackScope(forState, domCleanup) {
869
909
  const fallbackScope = forState.fallbackScope;
870
910
  if (!fallbackScope) return;
871
- const { dom: removedDom, range: removedRange } = resolveScopeBoundary(fallbackScope);
911
+ const { dom: removedDom, range: removedRange } = prepareRemovedScope(forState, fallbackScope);
872
912
  const transaction = forState._transaction;
873
- if (transaction) {
874
- (transaction.removedScopes ??= []).push(fallbackScope);
875
- captureRemovedScopeNodes(forState, removedDom, removedRange);
876
- } else disposeChildScope(fallbackScope);
913
+ if (transaction) (transaction.removedScopes ??= []).push(fallbackScope);
914
+ else disposeChildScope(fallbackScope);
877
915
  forState.fallbackScope = null;
878
916
  if (!removedDom && !removedRange) return;
879
- if (removedDom instanceof Element) {
880
- if (domCleanup === "teardown") getRuntimeRenderer().teardownNodeSubtree(removedDom);
881
- }
882
- recordRemovedBoundary(forState, removedDom, removedRange);
917
+ if (domCleanup === "teardown") getRuntimeScopes().teardownScopeHost(removedDom, void 0);
918
+ if (!transaction) recordRemovedBoundary(forState, removedDom, removedRange);
883
919
  }
884
920
  function renderFallbackScope(forState) {
885
921
  if (forState.fallback == null || forState.fallback === false) {
@@ -905,12 +941,7 @@ function disposeAllItems(forState, domCleanup) {
905
941
  transaction.removeAllItems = true;
906
942
  for (let index = 0; index < forState.orderedItems.length; index++) {
907
943
  const scope = forState.orderedItems[index]?.scope;
908
- const { dom: removedDom, range } = scope ? resolveScopeBoundary(scope) : {
909
- dom: void 0,
910
- range: void 0
911
- };
912
- if (scope) captureRemovedScopeNodes(forState, removedDom, range);
913
- recordRemovedBoundary(forState, removedDom, range);
944
+ if (scope) prepareRemovedScope(forState, scope);
914
945
  }
915
946
  forState.items = /* @__PURE__ */ new Map();
916
947
  forState.orderedKeys = [];
@@ -927,345 +958,118 @@ function disposeAllItems(forState, domCleanup) {
927
958
  forState.orderedKeys = orderedKeys;
928
959
  }
929
960
  //#endregion
930
- //#region src/runtime/for-reconcile.ts
931
- function hasUnmountedComponentHost(node) {
932
- if (!node) return false;
933
- const host = node;
934
- if (host.__ASKR_INSTANCE?.mounted === false) return true;
935
- return host.__ASKR_INSTANCES?.some((instance) => !instance.mounted) ?? false;
936
- }
937
- function failForValidation(message) {
938
- throw new Error(message);
939
- }
940
- function validateForKeys(forState, newArray) {
941
- if (!isDevelopmentEnvironment()) return;
942
- const seen = /* @__PURE__ */ new Set();
943
- const keyKinds = /* @__PURE__ */ new Map();
944
- for (let i = 0; i < newArray.length; i++) {
945
- const key = forState.byFn(newArray[i], i);
946
- if (key === null || key === void 0) failForValidation("[askr] Invalid For key detected. Keys should be stable, non-null, and unique within a For list.");
947
- if (seen.has(key)) failForValidation(`[askr] Duplicate For key detected: ${String(key)}. Keys should be stable, non-null, and unique within a For list.`);
948
- seen.add(key);
949
- const keyKind = typeof key;
950
- const previousKeyKind = forState.devKeyKinds?.get(key);
951
- if (previousKeyKind && previousKeyKind !== keyKind) failForValidation(`[askr] For key type changed for ${String(key)}. Keys must remain consistently typed across renders.`);
952
- keyKinds.set(key, keyKind);
953
- }
954
- forState.devKeyKinds = keyKinds;
961
+ //#region src/runtime/control/for-paths/pending.ts
962
+ /**
963
+ * Publish the strategy a path chose, together with the three pending fields
964
+ * every path owns.
965
+ *
966
+ * Every parameter is required, so a path cannot leave one of these behind by
967
+ * omission — which is how they were previously written, one scattered
968
+ * assignment at a time. The three remaining `pending*` fields belong to exactly
969
+ * one path each (`pendingInsertedIndex`, `pendingAppendStart`,
970
+ * `pendingRemovedKey`) and are assigned by that path directly; the others leave
971
+ * them as reconciliation found them, since each commit clears them through
972
+ * `clearForDomUpdateState`.
973
+ */
974
+ function setForCommitPending(forState, strategy, pendingDirtyIndices, pendingSwapIndices, pendingMoveOnly) {
975
+ forState.lastCommitStrategy = strategy;
976
+ forState.pendingDirtyIndices = pendingDirtyIndices;
977
+ forState.pendingSwapIndices = pendingSwapIndices;
978
+ forState.pendingMoveOnly = pendingMoveOnly;
955
979
  }
956
- function reconcileForItems(forState, newArray) {
957
- forState.currentItems = newArray;
958
- validateForKeys(forState, newArray);
959
- const { items, orderedKeys, byFn } = forState;
960
- const oldLen = orderedKeys.length;
961
- const newLen = newArray.length;
962
- forState.lastRemovedNodes = [];
963
- forState.lastRemovedRanges = [];
964
- forState.pendingRemovedKey = null;
965
- if (newLen === 0) {
966
- if (oldLen > 0) disposeAllItems(forState, forState.fallback ? "teardown" : "none");
967
- forState.lastCommitStrategy = "TRUNCATE";
968
- forState.pendingDirtyIndices = null;
969
- forState.pendingSwapIndices = null;
970
- forState.pendingMoveOnly = false;
971
- return renderFallbackScope(forState);
972
- }
973
- if (forState.fallbackScope) disposeFallbackScope(forState, "none");
974
- if (newLen === oldLen + 1) {
975
- let oldIndex = 0;
976
- let insertedIndex = -1;
977
- let insertedKey = null;
978
- for (let newIndex = 0; newIndex < newLen; newIndex++) {
979
- const item = newArray[newIndex];
980
- const key = byFn(item, newIndex);
981
- const expectedKey = orderedKeys[oldIndex];
982
- if (oldIndex < oldLen && key === expectedKey) {
983
- const existing = items.get(key);
984
- if (!existing || existing.item !== item || existing.scope.needsDomUpdate || oldIndex !== newIndex && existing.indexSignal._hasBeenRead) {
985
- insertedIndex = -2;
986
- break;
987
- }
988
- oldIndex++;
989
- } else if (insertedIndex === -1 && !items.has(key)) {
990
- insertedIndex = newIndex;
991
- insertedKey = key;
992
- } else {
993
- insertedIndex = -2;
994
- break;
995
- }
996
- }
997
- if (insertedIndex >= 0 && oldIndex === oldLen && insertedKey !== null) {
998
- const insertedItem = createItemInstance(insertedKey, newArray[insertedIndex], insertedIndex, forState);
999
- const resultVNodes = forState.orderedVNodes.slice();
1000
- const resultItems = forState.orderedItems.slice();
1001
- const resultKeys = orderedKeys.slice();
1002
- resultVNodes.splice(insertedIndex, 0, insertedItem.scope.vnode);
1003
- resultItems.splice(insertedIndex, 0, insertedItem);
1004
- resultKeys.splice(insertedIndex, 0, insertedKey);
1005
- items.set(insertedKey, insertedItem);
1006
- forState.lastCommitStrategy = "INSERT_ONE";
1007
- forState.orderedKeys = resultKeys;
1008
- forState.orderedItems = resultItems;
1009
- forState.orderedVNodes = resultVNodes;
1010
- forState.pendingDirtyIndices = null;
1011
- forState.pendingSwapIndices = null;
1012
- forState.pendingMoveOnly = false;
1013
- forState.pendingInsertedIndex = insertedIndex;
1014
- for (let index = insertedIndex; index < resultItems.length; index += 1) syncForItemIndex(forState, resultItems[index], index);
1015
- return resultVNodes;
980
+ //#endregion
981
+ //#region src/runtime/control/for-paths/append.ts
982
+ /**
983
+ * Rows added at the tail, every existing key still at its own index.
984
+ *
985
+ * When the retained prefix is also clean the commit can skip it entirely, which
986
+ * `pendingAppendStart` reports; otherwise the prefix is updated in place and the
987
+ * commit starts from zero.
988
+ *
989
+ * Guarded by `oldLen < newLen`.
990
+ */
991
+ function tryAppendPath(forState, newArray, items, orderedKeys, byFn, oldLen, newLen) {
992
+ let canUseAppendPath = true;
993
+ let canSkipCommittedPrefix = true;
994
+ for (let i = 0; i < oldLen; i++) {
995
+ const key = byFn(newArray[i], i);
996
+ if (key !== orderedKeys[i]) {
997
+ canUseAppendPath = false;
998
+ break;
1016
999
  }
1000
+ const existing = forState.orderedItems[i] ?? items.get(key);
1001
+ if (!existing || existing.item !== newArray[i] || existing.scope.needsDomUpdate || existing.scope.hydrationPending) canSkipCommittedPrefix = false;
1017
1002
  }
1018
- if (oldLen < newLen) {
1019
- let canUseAppendPath = true;
1020
- let canSkipCommittedPrefix = true;
1021
- for (let i = 0; i < oldLen; i++) {
1022
- const key = byFn(newArray[i], i);
1023
- if (key !== orderedKeys[i]) {
1024
- canUseAppendPath = false;
1025
- break;
1026
- }
1027
- const existing = forState.orderedItems[i] ?? items.get(key);
1028
- if (!existing || existing.item !== newArray[i] || existing.scope.needsDomUpdate || existing.scope.hydrationPending) canSkipCommittedPrefix = false;
1029
- }
1030
- if (canUseAppendPath) {
1031
- forState.lastCommitStrategy = "APPEND";
1032
- const resultVNodes = forState.orderedVNodes;
1033
- const resultItems = forState.orderedItems;
1034
- resultVNodes.length = newLen;
1035
- resultItems.length = newLen;
1036
- if (!canSkipCommittedPrefix) for (let i = 0; i < oldLen; i++) {
1037
- const item = newArray[i];
1038
- const key = orderedKeys[i];
1039
- const existing = resultItems[i] ?? items.get(key);
1040
- updateItemInstance(forState, existing, item);
1041
- resultItems[i] = existing;
1042
- resultVNodes[i] = existing.scope.vnode;
1043
- }
1044
- for (let i = oldLen; i < newLen; i++) {
1045
- const item = newArray[i];
1046
- const key = byFn(item, i);
1047
- const itemInstance = createItemInstance(key, item, i, forState);
1048
- items.set(key, itemInstance);
1049
- resultItems[i] = itemInstance;
1050
- resultVNodes[i] = itemInstance.scope.vnode;
1051
- orderedKeys[i] = key;
1052
- }
1053
- forState.orderedVNodes = resultVNodes;
1054
- forState.pendingDirtyIndices = null;
1055
- forState.pendingSwapIndices = null;
1056
- forState.pendingMoveOnly = false;
1057
- forState.pendingAppendStart = canSkipCommittedPrefix ? oldLen : 0;
1058
- return resultVNodes;
1059
- }
1003
+ if (!canUseAppendPath) return null;
1004
+ const resultVNodes = forState.orderedVNodes;
1005
+ const resultItems = forState.orderedItems;
1006
+ resultVNodes.length = newLen;
1007
+ resultItems.length = newLen;
1008
+ if (!canSkipCommittedPrefix) for (let i = 0; i < oldLen; i++) {
1009
+ const item = newArray[i];
1010
+ const key = orderedKeys[i];
1011
+ const existing = resultItems[i] ?? items.get(key);
1012
+ updateItemInstance(forState, existing, item);
1013
+ resultItems[i] = existing;
1014
+ resultVNodes[i] = existing.scope.vnode;
1060
1015
  }
1061
- if (newLen < oldLen) {
1062
- if (oldLen === newLen + 1) {
1063
- let removedIndex = -1;
1064
- for (let i = 0; i < newLen; i++) if (byFn(newArray[i], i) !== orderedKeys[i]) {
1065
- removedIndex = i;
1066
- break;
1067
- }
1068
- if (removedIndex !== -1) {
1069
- let canUseRemoveOnePath = true;
1070
- for (let i = removedIndex; i < newLen; i++) if (byFn(newArray[i], i) !== orderedKeys[i + 1]) {
1071
- canUseRemoveOnePath = false;
1072
- break;
1073
- }
1074
- if (canUseRemoveOnePath) {
1075
- forState.lastCommitStrategy = "REMOVE_ONE";
1076
- const resultVNodes = forState.orderedVNodes;
1077
- const resultItems = forState.orderedItems;
1078
- const removedKey = orderedKeys[removedIndex];
1079
- const removedItem = resultItems[removedIndex] ?? items.get(removedKey);
1080
- orderedKeys.copyWithin(removedIndex, removedIndex + 1);
1081
- resultItems.copyWithin(removedIndex, removedIndex + 1);
1082
- resultVNodes.copyWithin(removedIndex, removedIndex + 1);
1083
- orderedKeys.length = newLen;
1084
- resultItems.length = newLen;
1085
- resultVNodes.length = newLen;
1086
- const dirtyIndices = [];
1087
- for (let i = 0; i < newLen; i++) {
1088
- const item = newArray[i];
1089
- const existing = resultItems[i];
1090
- const scopeNeedsDomUpdate = existing.scope.needsDomUpdate;
1091
- const itemChanged = existing.item !== item;
1092
- const indexSignal = existing.indexSignal;
1093
- const indexChanged = i >= removedIndex && indexSignal.peek() !== i;
1094
- if (itemChanged) updateItemInstance(forState, existing, item);
1095
- const indexVisibleChange = indexChanged ? syncForItemIndex(forState, existing, i) : false;
1096
- if (itemChanged || indexVisibleChange || scopeNeedsDomUpdate) dirtyIndices.push(i);
1097
- resultVNodes[i] = existing.scope.vnode;
1098
- }
1099
- if (removedItem) {
1100
- disposeItemInstance(forState, removedItem, "teardown");
1101
- items.delete(removedKey);
1102
- }
1103
- forState.pendingRemovedKey = removedKey;
1104
- forState.pendingDirtyIndices = dirtyIndices;
1105
- forState.pendingSwapIndices = null;
1106
- forState.pendingMoveOnly = false;
1107
- return resultVNodes;
1108
- }
1109
- }
1110
- }
1111
- let canUseTruncatePath = true;
1112
- for (let i = 0; i < newLen; i++) if (byFn(newArray[i], i) !== orderedKeys[i]) {
1113
- canUseTruncatePath = false;
1114
- break;
1115
- }
1116
- if (canUseTruncatePath) {
1117
- forState.lastCommitStrategy = "TRUNCATE";
1118
- const resultVNodes = forState.orderedVNodes;
1119
- const resultItems = forState.orderedItems;
1120
- resultVNodes.length = newLen;
1121
- resultItems.length = newLen;
1122
- const isFullClear = newLen === 0;
1123
- const dirtyIndices = [];
1124
- for (let i = 0; i < newLen; i++) {
1125
- const item = newArray[i];
1126
- const key = orderedKeys[i];
1127
- const existing = items.get(key);
1128
- const itemChanged = existing.item !== item;
1129
- const scopeNeedsDomUpdate = existing.scope.needsDomUpdate || hasUnmountedComponentHost(existing.scope.dom);
1130
- if (itemChanged) updateItemInstance(forState, existing, item);
1131
- if (itemChanged || scopeNeedsDomUpdate) dirtyIndices.push(i);
1132
- resultItems[i] = existing;
1133
- resultVNodes[i] = existing.scope.vnode;
1134
- }
1135
- for (let i = newLen; i < oldLen; i++) {
1136
- const key = orderedKeys[i];
1137
- const itemInstance = items.get(key);
1138
- if (itemInstance) {
1139
- disposeItemInstance(forState, itemInstance, isFullClear ? "full-clear" : "teardown");
1140
- items.delete(key);
1141
- }
1142
- }
1143
- orderedKeys.length = newLen;
1144
- forState.orderedKeys = orderedKeys;
1145
- forState.orderedItems.length = newLen;
1146
- forState.orderedVNodes = resultVNodes;
1147
- forState.pendingDirtyIndices = dirtyIndices;
1148
- forState.pendingSwapIndices = null;
1149
- forState.pendingMoveOnly = false;
1150
- return resultVNodes;
1151
- }
1016
+ for (let i = oldLen; i < newLen; i++) {
1017
+ const item = newArray[i];
1018
+ const key = byFn(item, i);
1019
+ const itemInstance = createItemInstance(key, item, i, forState);
1020
+ items.set(key, itemInstance);
1021
+ resultItems[i] = itemInstance;
1022
+ resultVNodes[i] = itemInstance.scope.vnode;
1023
+ orderedKeys[i] = key;
1152
1024
  }
1153
- if (oldLen === newLen) {
1154
- let canUseNoReorderPath = true;
1155
- for (let i = 0; i < oldLen; i++) if (byFn(newArray[i], i) !== orderedKeys[i]) {
1156
- canUseNoReorderPath = false;
1157
- break;
1158
- }
1159
- if (canUseNoReorderPath) {
1160
- forState.lastCommitStrategy = "NO_REORDER";
1161
- const resultVNodes = forState.orderedVNodes;
1162
- const resultItems = forState.orderedItems;
1163
- resultVNodes.length = oldLen;
1164
- resultItems.length = oldLen;
1165
- const dirtyIndices = [];
1166
- for (let i = 0; i < oldLen; i++) {
1167
- const item = newArray[i];
1168
- const key = orderedKeys[i];
1169
- const existing = items.get(key);
1170
- if (existing.item !== item) updateItemInstance(forState, existing, item);
1171
- if (existing.scope.needsDomUpdate) dirtyIndices.push(i);
1172
- resultItems[i] = existing;
1173
- resultVNodes[i] = existing.scope.vnode;
1174
- }
1175
- forState.pendingDirtyIndices = dirtyIndices;
1176
- forState.pendingSwapIndices = null;
1177
- forState.pendingMoveOnly = false;
1178
- return resultVNodes;
1179
- }
1180
- let firstMismatch = -1;
1181
- let secondMismatch = -1;
1182
- let firstMismatchKey = null;
1183
- let secondMismatchKey = null;
1184
- let mismatchCount = 0;
1185
- let canUseSwapPath = true;
1186
- for (let i = 0; i < oldLen; i++) {
1187
- const item = newArray[i];
1188
- const key = byFn(newArray[i], i);
1189
- if (key === orderedKeys[i]) {
1190
- const existing = items.get(key);
1191
- if (existing && (existing.item !== item || existing.scope.needsDomUpdate)) {
1192
- canUseSwapPath = false;
1193
- break;
1194
- }
1195
- continue;
1196
- }
1197
- mismatchCount++;
1198
- if (firstMismatch === -1) {
1199
- firstMismatch = i;
1200
- firstMismatchKey = key;
1201
- continue;
1202
- }
1203
- if (secondMismatch === -1) {
1204
- secondMismatch = i;
1205
- secondMismatchKey = key;
1206
- continue;
1207
- }
1208
- mismatchCount = 3;
1209
- break;
1210
- }
1211
- if (canUseSwapPath && mismatchCount === 2 && firstMismatch !== -1 && secondMismatch !== -1 && firstMismatchKey === orderedKeys[secondMismatch] && secondMismatchKey === orderedKeys[firstMismatch]) {
1212
- forState.lastCommitStrategy = "SWAP";
1213
- const nextOrderedKeys = orderedKeys.slice();
1214
- nextOrderedKeys[firstMismatch] = firstMismatchKey;
1215
- nextOrderedKeys[secondMismatch] = secondMismatchKey;
1216
- const resultVNodes = forState.orderedVNodes;
1217
- const firstExisting = items.get(firstMismatchKey);
1218
- const secondExisting = items.get(secondMismatchKey);
1219
- const firstItem = newArray[firstMismatch];
1220
- const secondItem = newArray[secondMismatch];
1221
- const nextOrderedItems = forState.orderedItems.slice();
1222
- nextOrderedItems[firstMismatch] = firstExisting;
1223
- nextOrderedItems[secondMismatch] = secondExisting;
1224
- if (firstExisting.item !== firstItem) updateItemInstance(forState, firstExisting, firstItem);
1225
- if (secondExisting.item !== secondItem) updateItemInstance(forState, secondExisting, secondItem);
1226
- syncForItemIndex(forState, firstExisting, firstMismatch);
1227
- syncForItemIndex(forState, secondExisting, secondMismatch);
1228
- resultVNodes[firstMismatch] = firstExisting.scope.vnode;
1229
- resultVNodes[secondMismatch] = secondExisting.scope.vnode;
1230
- forState.orderedKeys = nextOrderedKeys;
1231
- forState.orderedItems = nextOrderedItems;
1232
- forState.pendingDirtyIndices = null;
1233
- forState.pendingSwapIndices = [firstMismatch, secondMismatch];
1234
- forState.pendingMoveOnly = true;
1235
- return resultVNodes;
1236
- }
1237
- let canUseMoveOnlyPath = true;
1238
- const moveOnlyKeys = [];
1239
- const moveOnlyItems = [];
1240
- const moveOnlyVNodes = [];
1241
- for (let i = 0; i < oldLen; i++) {
1242
- const item = newArray[i];
1243
- const key = byFn(item, i);
1244
- const existing = items.get(key);
1245
- if (!existing || existing.item !== item || existing.indexSignal._hasBeenRead && existing.indexSignal.peek() !== i) {
1246
- canUseMoveOnlyPath = false;
1247
- break;
1248
- }
1249
- moveOnlyKeys[i] = key;
1250
- moveOnlyItems[i] = existing;
1251
- moveOnlyVNodes[i] = existing.scope.vnode;
1252
- }
1253
- if (canUseMoveOnlyPath) {
1254
- forState.lastCommitStrategy = "FULL_KEYED";
1255
- for (let i = 0; i < moveOnlyItems.length; i++) {
1256
- syncForItemIndex(forState, moveOnlyItems[i], i);
1257
- moveOnlyVNodes[i] = moveOnlyItems[i].scope.vnode;
1258
- }
1259
- forState.orderedKeys = moveOnlyKeys;
1260
- forState.orderedItems = moveOnlyItems;
1261
- forState.orderedVNodes = moveOnlyVNodes;
1262
- forState.pendingDirtyIndices = null;
1263
- forState.pendingSwapIndices = null;
1264
- forState.pendingMoveOnly = true;
1265
- return moveOnlyVNodes;
1266
- }
1025
+ forState.orderedVNodes = resultVNodes;
1026
+ setForCommitPending(forState, "APPEND", null, null, false);
1027
+ forState.pendingAppendStart = canSkipCommittedPrefix ? oldLen : 0;
1028
+ return resultVNodes;
1029
+ }
1030
+ //#endregion
1031
+ //#region src/runtime/control/for-paths/full-keyed.ts
1032
+ /**
1033
+ * A pure permutation: every new key resolves to an existing, unchanged item
1034
+ * whose index signal is either unread or already correct.
1035
+ *
1036
+ * Commits as FULL_KEYED, but flagged `pendingMoveOnly` so the commit can move
1037
+ * the existing nodes instead of rebuilding them.
1038
+ *
1039
+ * Guarded by `oldLen === newLen`, and runs only after NO_REORDER and SWAP
1040
+ * decline.
1041
+ */
1042
+ function tryMoveOnlyKeyedPath(forState, newArray, items, byFn, oldLen) {
1043
+ const moveOnlyKeys = [];
1044
+ const moveOnlyItems = [];
1045
+ const moveOnlyVNodes = [];
1046
+ for (let i = 0; i < oldLen; i++) {
1047
+ const item = newArray[i];
1048
+ const key = byFn(item, i);
1049
+ const existing = items.get(key);
1050
+ if (!existing || existing.item !== item || existing.indexSignal._hasBeenRead && existing.indexSignal.peek() !== i) return null;
1051
+ moveOnlyKeys[i] = key;
1052
+ moveOnlyItems[i] = existing;
1053
+ moveOnlyVNodes[i] = existing.scope.vnode;
1267
1054
  }
1268
- forState.lastCommitStrategy = "FULL_KEYED";
1055
+ for (let i = 0; i < moveOnlyItems.length; i++) {
1056
+ syncForItemIndex(forState, moveOnlyItems[i], i);
1057
+ moveOnlyVNodes[i] = moveOnlyItems[i].scope.vnode;
1058
+ }
1059
+ forState.orderedKeys = moveOnlyKeys;
1060
+ forState.orderedItems = moveOnlyItems;
1061
+ forState.orderedVNodes = moveOnlyVNodes;
1062
+ setForCommitPending(forState, "FULL_KEYED", null, null, true);
1063
+ return moveOnlyVNodes;
1064
+ }
1065
+ /**
1066
+ * The general keyed reconciliation every other path declines to handle.
1067
+ *
1068
+ * One pass over the new array creates, reuses or reindexes each row while
1069
+ * tracking which old keys survive; whatever is left over is disposed. Always
1070
+ * succeeds, so it takes no guard and returns no `null`.
1071
+ */
1072
+ function fullKeyedPath(forState, newArray, items, orderedKeys, byFn) {
1269
1073
  const toRemove = new Set(orderedKeys);
1270
1074
  const newOrderedKeys = [];
1271
1075
  const newOrderedItems = [];
@@ -1305,22 +1109,327 @@ function reconcileForItems(forState, newArray) {
1305
1109
  forState.orderedKeys = newOrderedKeys;
1306
1110
  forState.orderedItems = newOrderedItems;
1307
1111
  forState.orderedVNodes = resultVNodes;
1308
- forState.pendingDirtyIndices = null;
1309
- forState.pendingSwapIndices = null;
1310
- forState.pendingMoveOnly = moveOnly;
1112
+ setForCommitPending(forState, "FULL_KEYED", null, null, moveOnly);
1311
1113
  return resultVNodes;
1312
1114
  }
1313
1115
  //#endregion
1314
- //#region src/runtime/for-internal.ts
1116
+ //#region src/runtime/control/for-paths/insert-one.ts
1315
1117
  /**
1316
- * For primitive runtime
1118
+ * One row inserted, every existing DOM node retained.
1119
+ *
1120
+ * Requires that no shifted row has read its index signal and that no retained
1121
+ * row is already dirty: this path only syncs the inserted index, so a pending
1122
+ * update anywhere else would be silently dropped. Every other shape falls
1123
+ * through to the general keyed path.
1124
+ *
1125
+ * Guarded by `newLen === oldLen + 1`.
1126
+ */
1127
+ function tryInsertOnePath(forState, newArray, items, orderedKeys, byFn, oldLen, newLen) {
1128
+ let oldIndex = 0;
1129
+ let insertedIndex = -1;
1130
+ let insertedKey = null;
1131
+ for (let newIndex = 0; newIndex < newLen; newIndex++) {
1132
+ const item = newArray[newIndex];
1133
+ const key = byFn(item, newIndex);
1134
+ const expectedKey = orderedKeys[oldIndex];
1135
+ if (oldIndex < oldLen && key === expectedKey) {
1136
+ const existing = items.get(key);
1137
+ if (!existing || existing.item !== item || existing.scope.needsDomUpdate || oldIndex !== newIndex && existing.indexSignal._hasBeenRead) {
1138
+ insertedIndex = -2;
1139
+ break;
1140
+ }
1141
+ oldIndex++;
1142
+ } else if (insertedIndex === -1 && !items.has(key)) {
1143
+ insertedIndex = newIndex;
1144
+ insertedKey = key;
1145
+ } else {
1146
+ insertedIndex = -2;
1147
+ break;
1148
+ }
1149
+ }
1150
+ if (!(insertedIndex >= 0 && oldIndex === oldLen && insertedKey !== null)) return null;
1151
+ const insertedItem = createItemInstance(insertedKey, newArray[insertedIndex], insertedIndex, forState);
1152
+ const resultVNodes = forState.orderedVNodes.slice();
1153
+ const resultItems = forState.orderedItems.slice();
1154
+ const resultKeys = orderedKeys.slice();
1155
+ resultVNodes.splice(insertedIndex, 0, insertedItem.scope.vnode);
1156
+ resultItems.splice(insertedIndex, 0, insertedItem);
1157
+ resultKeys.splice(insertedIndex, 0, insertedKey);
1158
+ items.set(insertedKey, insertedItem);
1159
+ forState.orderedKeys = resultKeys;
1160
+ forState.orderedItems = resultItems;
1161
+ forState.orderedVNodes = resultVNodes;
1162
+ for (let index = insertedIndex; index < resultItems.length; index += 1) syncForItemIndex(forState, resultItems[index], index);
1163
+ setForCommitPending(forState, "INSERT_ONE", null, null, false);
1164
+ forState.pendingInsertedIndex = insertedIndex;
1165
+ return resultVNodes;
1166
+ }
1167
+ //#endregion
1168
+ //#region src/runtime/control/for-paths/no-reorder.ts
1169
+ /**
1170
+ * Same length, same key at every index: an in-place update with no DOM moves.
1171
+ *
1172
+ * Dirtiness is read from the scope after `updateItemInstance`, so a row whose
1173
+ * item is unchanged but whose subtree was invalidated still commits.
1174
+ *
1175
+ * Guarded by `oldLen === newLen`.
1176
+ */
1177
+ function tryNoReorderPath(forState, newArray, items, orderedKeys, byFn, oldLen) {
1178
+ for (let i = 0; i < oldLen; i++) if (byFn(newArray[i], i) !== orderedKeys[i]) return null;
1179
+ const resultVNodes = forState.orderedVNodes;
1180
+ const resultItems = forState.orderedItems;
1181
+ resultVNodes.length = oldLen;
1182
+ resultItems.length = oldLen;
1183
+ const dirtyIndices = [];
1184
+ for (let i = 0; i < oldLen; i++) {
1185
+ const item = newArray[i];
1186
+ const key = orderedKeys[i];
1187
+ const existing = items.get(key);
1188
+ if (existing.item !== item) updateItemInstance(forState, existing, item);
1189
+ if (existing.scope.needsDomUpdate) dirtyIndices.push(i);
1190
+ resultItems[i] = existing;
1191
+ resultVNodes[i] = existing.scope.vnode;
1192
+ }
1193
+ setForCommitPending(forState, "NO_REORDER", dirtyIndices, null, false);
1194
+ return resultVNodes;
1195
+ }
1196
+ //#endregion
1197
+ //#region src/runtime/control/for-paths/remove-one.ts
1198
+ /**
1199
+ * Exactly one row removed, the suffix shifted up by one.
1200
+ *
1201
+ * The shifted suffix keeps its instances; only rows whose item or index
1202
+ * actually changed land in the dirty list. `pendingRemovedKey` tells the commit
1203
+ * which keyed-map entry to drop, and the commit detaches that node before
1204
+ * applying the plan so dirty rows anchor on post-removal indices.
1317
1205
  *
1318
- * Manages For state storage, hook binding, source evaluation,
1319
- * and DOM update state cleanup.
1206
+ * Guarded by `oldLen === newLen + 1`.
1320
1207
  */
1208
+ function tryRemoveOnePath(forState, newArray, items, orderedKeys, byFn, newLen) {
1209
+ let removedIndex = -1;
1210
+ for (let i = 0; i < newLen; i++) if (byFn(newArray[i], i) !== orderedKeys[i]) {
1211
+ removedIndex = i;
1212
+ break;
1213
+ }
1214
+ if (removedIndex === -1) return null;
1215
+ for (let i = removedIndex; i < newLen; i++) if (byFn(newArray[i], i) !== orderedKeys[i + 1]) return null;
1216
+ const resultVNodes = forState.orderedVNodes;
1217
+ const resultItems = forState.orderedItems;
1218
+ const removedKey = orderedKeys[removedIndex];
1219
+ const removedItem = resultItems[removedIndex] ?? items.get(removedKey);
1220
+ orderedKeys.copyWithin(removedIndex, removedIndex + 1);
1221
+ resultItems.copyWithin(removedIndex, removedIndex + 1);
1222
+ resultVNodes.copyWithin(removedIndex, removedIndex + 1);
1223
+ orderedKeys.length = newLen;
1224
+ resultItems.length = newLen;
1225
+ resultVNodes.length = newLen;
1226
+ const dirtyIndices = [];
1227
+ for (let i = 0; i < newLen; i++) {
1228
+ const item = newArray[i];
1229
+ const existing = resultItems[i];
1230
+ const scopeNeedsDomUpdate = existing.scope.needsDomUpdate;
1231
+ const itemChanged = existing.item !== item;
1232
+ const indexSignal = existing.indexSignal;
1233
+ const indexChanged = i >= removedIndex && indexSignal.peek() !== i;
1234
+ if (itemChanged) updateItemInstance(forState, existing, item);
1235
+ const indexVisibleChange = indexChanged ? syncForItemIndex(forState, existing, i) : false;
1236
+ if (itemChanged || indexVisibleChange || scopeNeedsDomUpdate) dirtyIndices.push(i);
1237
+ resultVNodes[i] = existing.scope.vnode;
1238
+ }
1239
+ if (removedItem) {
1240
+ disposeItemInstance(forState, removedItem, "teardown");
1241
+ items.delete(removedKey);
1242
+ }
1243
+ setForCommitPending(forState, "REMOVE_ONE", dirtyIndices, null, false);
1244
+ forState.pendingRemovedKey = removedKey;
1245
+ return resultVNodes;
1246
+ }
1247
+ //#endregion
1248
+ //#region src/runtime/control/for-paths/swap.ts
1249
+ /**
1250
+ * Exactly two rows exchanged, everything else untouched.
1251
+ *
1252
+ * Bails when any key-matched row already carries a pending DOM update: SWAP
1253
+ * only ever syncs the two swapped indices, so such a row would silently lose
1254
+ * its update. This mirrors the INSERT_ONE guard.
1255
+ *
1256
+ * Guarded by `oldLen === newLen`, and runs only after NO_REORDER declines.
1257
+ */
1258
+ function trySwapPath(forState, newArray, items, orderedKeys, byFn, oldLen) {
1259
+ let firstMismatch = -1;
1260
+ let secondMismatch = -1;
1261
+ let firstMismatchKey = null;
1262
+ let secondMismatchKey = null;
1263
+ let mismatchCount = 0;
1264
+ let canUseSwapPath = true;
1265
+ for (let i = 0; i < oldLen; i++) {
1266
+ const item = newArray[i];
1267
+ const key = byFn(newArray[i], i);
1268
+ if (key === orderedKeys[i]) {
1269
+ const existing = items.get(key);
1270
+ if (existing && (existing.item !== item || existing.scope.needsDomUpdate)) {
1271
+ canUseSwapPath = false;
1272
+ break;
1273
+ }
1274
+ continue;
1275
+ }
1276
+ mismatchCount++;
1277
+ if (firstMismatch === -1) {
1278
+ firstMismatch = i;
1279
+ firstMismatchKey = key;
1280
+ continue;
1281
+ }
1282
+ if (secondMismatch === -1) {
1283
+ secondMismatch = i;
1284
+ secondMismatchKey = key;
1285
+ continue;
1286
+ }
1287
+ mismatchCount = 3;
1288
+ break;
1289
+ }
1290
+ if (!(canUseSwapPath && mismatchCount === 2 && firstMismatch !== -1 && secondMismatch !== -1 && firstMismatchKey === orderedKeys[secondMismatch] && secondMismatchKey === orderedKeys[firstMismatch])) return null;
1291
+ const nextOrderedKeys = orderedKeys.slice();
1292
+ nextOrderedKeys[firstMismatch] = firstMismatchKey;
1293
+ nextOrderedKeys[secondMismatch] = secondMismatchKey;
1294
+ const resultVNodes = forState.orderedVNodes;
1295
+ const firstExisting = items.get(firstMismatchKey);
1296
+ const secondExisting = items.get(secondMismatchKey);
1297
+ const firstItem = newArray[firstMismatch];
1298
+ const secondItem = newArray[secondMismatch];
1299
+ const nextOrderedItems = forState.orderedItems.slice();
1300
+ nextOrderedItems[firstMismatch] = firstExisting;
1301
+ nextOrderedItems[secondMismatch] = secondExisting;
1302
+ if (firstExisting.item !== firstItem) updateItemInstance(forState, firstExisting, firstItem);
1303
+ if (secondExisting.item !== secondItem) updateItemInstance(forState, secondExisting, secondItem);
1304
+ syncForItemIndex(forState, firstExisting, firstMismatch);
1305
+ syncForItemIndex(forState, secondExisting, secondMismatch);
1306
+ resultVNodes[firstMismatch] = firstExisting.scope.vnode;
1307
+ resultVNodes[secondMismatch] = secondExisting.scope.vnode;
1308
+ forState.orderedKeys = nextOrderedKeys;
1309
+ forState.orderedItems = nextOrderedItems;
1310
+ setForCommitPending(forState, "SWAP", null, [firstMismatch, secondMismatch], true);
1311
+ return resultVNodes;
1312
+ }
1313
+ //#endregion
1314
+ //#region src/runtime/control/for-paths/truncate.ts
1315
+ /**
1316
+ * The tail dropped, the surviving prefix keeping its keys and positions.
1317
+ *
1318
+ * Survivors are updated in place; the removed tail is disposed here, so the
1319
+ * commit only has to patch the dirty survivors and detach the orphaned nodes.
1320
+ * A survivor also counts as dirty when its component host was unmounted out
1321
+ * from under it.
1322
+ *
1323
+ * Guarded by `newLen < oldLen`.
1324
+ */
1325
+ function tryTruncatePath(forState, newArray, items, orderedKeys, byFn, oldLen, newLen) {
1326
+ for (let i = 0; i < newLen; i++) if (byFn(newArray[i], i) !== orderedKeys[i]) return null;
1327
+ const resultVNodes = forState.orderedVNodes;
1328
+ const resultItems = forState.orderedItems;
1329
+ resultVNodes.length = newLen;
1330
+ resultItems.length = newLen;
1331
+ const isFullClear = newLen === 0;
1332
+ const dirtyIndices = [];
1333
+ for (let i = 0; i < newLen; i++) {
1334
+ const item = newArray[i];
1335
+ const key = orderedKeys[i];
1336
+ const existing = items.get(key);
1337
+ const itemChanged = existing.item !== item;
1338
+ const scopeNeedsDomUpdate = existing.scope.needsDomUpdate || getRuntimeScopes().hasUnmountedComponentHost(existing.scope.dom);
1339
+ if (itemChanged) updateItemInstance(forState, existing, item);
1340
+ if (itemChanged || scopeNeedsDomUpdate) dirtyIndices.push(i);
1341
+ resultItems[i] = existing;
1342
+ resultVNodes[i] = existing.scope.vnode;
1343
+ }
1344
+ for (let i = newLen; i < oldLen; i++) {
1345
+ const key = orderedKeys[i];
1346
+ const itemInstance = items.get(key);
1347
+ if (itemInstance) {
1348
+ disposeItemInstance(forState, itemInstance, isFullClear ? "full-clear" : "teardown");
1349
+ items.delete(key);
1350
+ }
1351
+ }
1352
+ orderedKeys.length = newLen;
1353
+ forState.orderedKeys = orderedKeys;
1354
+ forState.orderedItems.length = newLen;
1355
+ forState.orderedVNodes = resultVNodes;
1356
+ setForCommitPending(forState, "TRUNCATE", dirtyIndices, null, false);
1357
+ return resultVNodes;
1358
+ }
1359
+ //#endregion
1360
+ //#region src/runtime/control/for-reconcile.ts
1361
+ function failForValidation(message) {
1362
+ throw new Error(message);
1363
+ }
1364
+ function validateForKeys(forState, newArray) {
1365
+ if (!isDevelopmentEnvironment()) return;
1366
+ const seen = /* @__PURE__ */ new Set();
1367
+ const keyKinds = /* @__PURE__ */ new Map();
1368
+ for (let i = 0; i < newArray.length; i++) {
1369
+ const key = forState.byFn(newArray[i], i);
1370
+ if (key === null || key === void 0) failForValidation("[askr] Invalid For key detected. Keys should be stable, non-null, and unique within a For list.");
1371
+ if (seen.has(key)) failForValidation(`[askr] Duplicate For key detected: ${String(key)}. Keys should be stable, non-null, and unique within a For list.`);
1372
+ seen.add(key);
1373
+ const keyKind = typeof key;
1374
+ const previousKeyKind = forState.devKeyKinds?.get(key);
1375
+ if (previousKeyKind && previousKeyKind !== keyKind) failForValidation(`[askr] For key type changed for ${String(key)}. Keys must remain consistently typed across renders.`);
1376
+ keyKinds.set(key, keyKind);
1377
+ }
1378
+ forState.devKeyKinds = keyKinds;
1379
+ }
1380
+ function reconcileForItems(forState, newArray) {
1381
+ forState.currentItems = newArray;
1382
+ validateForKeys(forState, newArray);
1383
+ const { items, orderedKeys, byFn } = forState;
1384
+ const oldLen = orderedKeys.length;
1385
+ const newLen = newArray.length;
1386
+ forState.lastRemovedNodes = [];
1387
+ forState.lastRemovedRanges = [];
1388
+ forState.pendingRemovedKey = null;
1389
+ if (newLen === 0) {
1390
+ if (oldLen > 0) disposeAllItems(forState, forState.fallback ? "teardown" : "none");
1391
+ forState.lastCommitStrategy = "TRUNCATE";
1392
+ forState.pendingDirtyIndices = null;
1393
+ forState.pendingSwapIndices = null;
1394
+ forState.pendingMoveOnly = false;
1395
+ return renderFallbackScope(forState);
1396
+ }
1397
+ if (forState.fallbackScope) disposeFallbackScope(forState, "none");
1398
+ /** Close out the pass. Each path has already published its own decision. */
1399
+ const finish = (vnodes) => {
1400
+ return vnodes;
1401
+ };
1402
+ if (newLen === oldLen + 1) {
1403
+ const inserted = tryInsertOnePath(forState, newArray, items, orderedKeys, byFn, oldLen, newLen);
1404
+ if (inserted) return finish(inserted);
1405
+ }
1406
+ if (oldLen < newLen) {
1407
+ const appended = tryAppendPath(forState, newArray, items, orderedKeys, byFn, oldLen, newLen);
1408
+ if (appended) return finish(appended);
1409
+ }
1410
+ if (newLen < oldLen) {
1411
+ if (oldLen === newLen + 1) {
1412
+ const removed = tryRemoveOnePath(forState, newArray, items, orderedKeys, byFn, newLen);
1413
+ if (removed) return finish(removed);
1414
+ }
1415
+ const truncated = tryTruncatePath(forState, newArray, items, orderedKeys, byFn, oldLen, newLen);
1416
+ if (truncated) return finish(truncated);
1417
+ }
1418
+ if (oldLen === newLen) {
1419
+ const unmoved = tryNoReorderPath(forState, newArray, items, orderedKeys, byFn, oldLen);
1420
+ if (unmoved) return finish(unmoved);
1421
+ const swapped = trySwapPath(forState, newArray, items, orderedKeys, byFn, oldLen);
1422
+ if (swapped) return finish(swapped);
1423
+ const moved = tryMoveOnlyKeyedPath(forState, newArray, items, byFn, oldLen);
1424
+ if (moved) return finish(moved);
1425
+ }
1426
+ return finish(fullKeyedPath(forState, newArray, items, orderedKeys, byFn));
1427
+ }
1428
+ //#endregion
1429
+ //#region src/runtime/control/for-state.ts
1321
1430
  const forStates = /* @__PURE__ */ new WeakMap();
1322
1431
  function getForStore(instance) {
1323
- const generation = instance._ownershipGeneration;
1432
+ const generation = instance.owner.identity;
1324
1433
  let store = forStates.get(generation);
1325
1434
  if (!store) {
1326
1435
  store = /* @__PURE__ */ new Map();
@@ -1329,36 +1438,25 @@ function getForStore(instance) {
1329
1438
  return store;
1330
1439
  }
1331
1440
  function createForState(eachSource, byFn, renderFn, fallback) {
1332
- const parentInstance = getCurrentInstance();
1333
- const ownedScopes = /* @__PURE__ */ new Set();
1334
- let bulkDisposing = false;
1441
+ const parentInstance = getCurrentComponentInstance();
1442
+ const scopeOwner = new OwnershipRecord();
1335
1443
  const scopeOwnership = {
1336
- add: (scope) => ownedScopes.add(scope),
1337
- delete: (scope) => bulkDisposing ? false : ownedScopes.delete(scope),
1444
+ add: (scope) => ownChild(scopeOwner, scope),
1445
+ delete: (scope) => releaseOwnedChild(scopeOwner, scope),
1338
1446
  bulkDispose(run) {
1339
- bulkDisposing = true;
1340
- try {
1341
- run();
1342
- } finally {
1343
- bulkDisposing = false;
1344
- ownedScopes.clear();
1345
- }
1447
+ detachOwnedChildren(scopeOwner);
1448
+ run();
1346
1449
  }
1347
1450
  };
1348
- if (parentInstance) registerOwnedChildScope(parentInstance, {
1349
- key: "for-boundary",
1350
- dispose() {
1351
- scopeOwnership.bulkDispose(() => {
1352
- const errors = [];
1353
- for (const scope of ownedScopes) try {
1354
- scope.dispose();
1355
- } catch (error) {
1356
- errors.push(error);
1357
- }
1358
- if (errors.length > 0) throw new AggregateError(errors, "For scope cleanup failed");
1359
- });
1360
- }
1361
- });
1451
+ if (parentInstance) {
1452
+ registerScopedOwnership({
1453
+ key: "for-boundary",
1454
+ dispose() {
1455
+ disposeOwnership(scopeOwner);
1456
+ }
1457
+ }, scopeOwner);
1458
+ attachOwnership(scopeOwner, parentInstance.owner);
1459
+ }
1362
1460
  return {
1363
1461
  kind: "for",
1364
1462
  _contextFrame: null,
@@ -1395,10 +1493,10 @@ function createForState(eachSource, byFn, renderFn, fallback) {
1395
1493
  };
1396
1494
  }
1397
1495
  function useForState(eachSource, byFn, renderFn, fallback) {
1398
- const instance = getCurrentInstance();
1496
+ const instance = getCurrentComponentInstance();
1399
1497
  if (!instance) throw new Error("For can only be created during component render execution.");
1400
1498
  const hookIndex = claimHookIndex(instance, "For");
1401
- const generation = instance._ownershipGeneration;
1499
+ const generation = instance.owner.identity;
1402
1500
  const store = getForStore(instance);
1403
1501
  const existing = store.get(hookIndex);
1404
1502
  if (existing) {
@@ -1410,11 +1508,14 @@ function useForState(eachSource, byFn, renderFn, fallback) {
1410
1508
  }
1411
1509
  const created = createForState(eachSource, byFn, renderFn, fallback);
1412
1510
  store.set(hookIndex, created);
1413
- (instance.cleanupFns ??= []).push(() => {
1414
- created._sourceEffect?.cleanup();
1415
- created._sourceEffect = null;
1416
- store.delete(hookIndex);
1417
- if (store.size === 0 && forStates.get(generation) === store) forStates.delete(generation);
1511
+ ownCleanup(instance.owner, () => {
1512
+ try {
1513
+ created._sourceEffect?.cleanup();
1514
+ } finally {
1515
+ created._sourceEffect = null;
1516
+ store.delete(hookIndex);
1517
+ if (store.size === 0 && forStates.get(generation) === store) forStates.delete(generation);
1518
+ }
1418
1519
  });
1419
1520
  return created;
1420
1521
  }
@@ -1476,7 +1577,13 @@ function beginForStateTransaction(forState, collectionMode = "auto") {
1476
1577
  }
1477
1578
  function registerForStateTransaction(forState, transaction = forState._transaction) {
1478
1579
  if (!transaction || forState._transaction !== transaction) return false;
1479
- return registerLifecycleTransaction(transaction, () => commitForStateTransaction(forState, transaction), () => rollbackForStateTransaction(forState, transaction));
1580
+ return registerCommitParticipant({
1581
+ key: transaction,
1582
+ collision: "keep-first",
1583
+ publish: () => publishForStateTransaction(forState, transaction),
1584
+ settle: () => commitForStateTransaction(forState, transaction),
1585
+ rollback: () => rollbackForStateTransaction(forState, transaction)
1586
+ });
1480
1587
  }
1481
1588
  function finalizeForStateRemovals(forState, transaction) {
1482
1589
  const removedScopes = transaction.removedScopes;
@@ -1491,20 +1598,7 @@ function finalizeForStateRemovals(forState, transaction) {
1491
1598
  cleanupErrors.push(error);
1492
1599
  }
1493
1600
  if (transaction.removedScopeNodes) return;
1494
- if (typeof Element !== "undefined" && removedDom instanceof Element) try {
1495
- getRuntimeRenderer().teardownNodeSubtree(removedDom);
1496
- } catch (error) {
1497
- cleanupErrors.push(error);
1498
- }
1499
- if (removedRange && !removedRange.single) for (let node = removedRange.start.nextSibling; node && node !== removedRange.end;) {
1500
- const next = node.nextSibling;
1501
- try {
1502
- getRuntimeRenderer().teardownNodeSubtree(node);
1503
- } catch (error) {
1504
- cleanupErrors.push(error);
1505
- }
1506
- node = next;
1507
- }
1601
+ getRuntimeScopes().teardownScopeHost(removedDom, removedRange, (error) => cleanupErrors.push(error));
1508
1602
  };
1509
1603
  if (transaction.removeAllItems) forState._scopeOwnership.bulkDispose(() => {
1510
1604
  for (let index = 0; index < transaction.orderedItems.length; index++) {
@@ -1515,7 +1609,7 @@ function finalizeForStateRemovals(forState, transaction) {
1515
1609
  for (const scope of removedScopes ?? []) finalizeScope(scope);
1516
1610
  const removedScopeNodes = transaction.removedScopeNodes;
1517
1611
  if (removedScopeNodes) for (const node of removedScopeNodes) try {
1518
- getRuntimeRenderer().teardownNodeSubtree(node);
1612
+ getRuntimeCleanup().teardownNodeSubtree(node);
1519
1613
  } catch (error) {
1520
1614
  cleanupErrors.push(error);
1521
1615
  }
@@ -1539,22 +1633,40 @@ function finalizeForDomUpdateFlags(forState) {
1539
1633
  }
1540
1634
  if (forState.fallbackScope) forState.fallbackScope.needsDomUpdate = false;
1541
1635
  }
1636
+ function publishForStateTransaction(forState, transaction) {
1637
+ if (forState._transaction !== transaction) return;
1638
+ if (transaction.shouldClearDomUpdateState) finalizeForDomUpdateFlags(forState);
1639
+ forState._committedItems = forState.currentItems;
1640
+ forState._transaction = null;
1641
+ transaction.published = true;
1642
+ }
1542
1643
  function commitForStateTransaction(forState, transaction = forState._transaction) {
1543
- if (!transaction || forState._transaction !== transaction) return;
1644
+ if (!transaction) return;
1645
+ if (!transaction.published) publishForStateTransaction(forState, transaction);
1646
+ if (!transaction.published) return;
1647
+ transaction.published = false;
1648
+ const errors = [];
1544
1649
  try {
1545
- finalizeForStateRemovals(forState, transaction);
1546
- finalizeForSignalEffects(transaction);
1547
- if (transaction.shouldClearDomUpdateState) finalizeForDomUpdateFlags(forState);
1650
+ try {
1651
+ finalizeForStateRemovals(forState, transaction);
1652
+ } catch (error) {
1653
+ errors.push(error);
1654
+ }
1655
+ try {
1656
+ finalizeForSignalEffects(transaction);
1657
+ } catch (error) {
1658
+ errors.push(error);
1659
+ }
1548
1660
  } finally {
1549
1661
  transaction.itemSnapshots?.clear();
1550
1662
  transaction.unreadIndexSnapshots?.clear();
1551
1663
  transaction.fallbackScopeSnapshot = null;
1552
- forState._committedItems = forState.currentItems;
1553
- forState._transaction = null;
1554
1664
  }
1665
+ if (errors.length) throw new AggregateError(errors, "For settlement failed");
1555
1666
  }
1556
1667
  function rollbackForStateTransaction(forState, transaction = forState._transaction) {
1557
- if (!transaction || forState._transaction !== transaction) return;
1668
+ if (!transaction || forState._transaction !== transaction && !transaction.published) return;
1669
+ transaction.published = false;
1558
1670
  const rollbackCleanupErrors = [];
1559
1671
  const scopesToDispose = /* @__PURE__ */ new Set();
1560
1672
  const committedScopes = transaction.collectionSnapshotMode === "reset-empty" ? null : new Set(transaction.orderedItems.map((item) => item.scope));
@@ -1638,4 +1750,4 @@ function clearForDomUpdateState(forState) {
1638
1750
  forState._hasResolvedItemDom = forState.orderedKeys.length > 0;
1639
1751
  }
1640
1752
  //#endregion
1641
- export { setControlBoundaryContextFrame as _, registerForStateTransaction as a, rerenderChildScope as b, captureForFallbackTransactionSnapshot as c, clearCaseDomUpdateState as d, clearShowDomUpdateState as f, evaluateShowState as g, evaluateCaseState as h, evaluateForState as i, captureForItemTransactionSnapshot as l, createShowState as m, clearForDomUpdateState as n, rollbackForStateTransaction as o, createCaseState as p, commitForStateTransaction as r, useForState as s, beginForStateTransaction as t, recordBenchEvent as u, createChildScope as v, disposeChildScope as y };
1753
+ export { setControlBoundaryContextFrame as _, registerForStateTransaction as a, joinChildScopePreparation as b, captureForFallbackTransactionSnapshot as c, clearCaseDomUpdateState as d, clearShowDomUpdateState as f, evaluateShowState as g, evaluateCaseState as h, evaluateForState as i, captureForItemTransactionSnapshot as l, createShowState as m, clearForDomUpdateState as n, rollbackForStateTransaction as o, createCaseState as p, commitForStateTransaction as r, useForState as s, beginForStateTransaction as t, recordBenchEvent as u, createChildScope as v, rerenderChildScope as x, disposeChildScope as y };