veles 1.1.2 → 1.1.4

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
@@ -1,5 +1,5 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- const require__utils = require("./_utils-DbrPydEE.cjs");
2
+ const require__utils = require("./_utils-CXVRQTuz.cjs");
3
3
  //#region src/attach-component.ts
4
4
  /**
5
5
  * Attach Veles component tree to a regular HTML node.
@@ -66,69 +66,42 @@ function updateUseValueSelector({ value, selectorTrackingElement, newTrackingSel
66
66
  newVelesElementNode.parentVelesElement = parentVelesElementRendered;
67
67
  if ("executedVelesNode" in newVelesElementNode && newVelesElementNode.phantom) {
68
68
  const insertAllPhantomChildren = (adjacentNode) => {
69
+ const adjacentSourceNode = require__utils.getExecutedVelesNodeSourceNode(adjacentNode);
69
70
  newVelesElementNode.childComponents.forEach((childComponentofPhantom) => {
70
- if ("executedVelesNode" in childComponentofPhantom) {
71
- adjacentNode.html.before(childComponentofPhantom.html);
72
- childComponentofPhantom.parentVelesElement = adjacentNode.parentVelesElement;
73
- } else {
74
- const velesElementNode = require__utils.getExecutedComponentVelesNode(childComponentofPhantom);
75
- if (!velesElementNode) console.error("can't find HTML tree in a component chain");
76
- else {
77
- adjacentNode.html.before(velesElementNode.html);
78
- velesElementNode.parentVelesElement = adjacentNode.parentVelesElement;
79
- }
80
- }
71
+ const childNode = "executedVelesComponent" in childComponentofPhantom ? require__utils.getExecutedComponentVelesNode(childComponentofPhantom) : childComponentofPhantom;
72
+ adjacentSourceNode.before(require__utils.getExecutedVelesNodeSourceNode(childNode));
73
+ childNode.parentVelesElement = adjacentNode.parentVelesElement;
81
74
  });
82
75
  };
83
76
  if ("executedVelesNode" in oldVelesElementNode && oldVelesElementNode.phantom) {
84
77
  let isInserted = false;
85
78
  oldVelesElementNode.childComponents.forEach((childComponentofPhantom) => {
86
- if ("executedVelesNode" in childComponentofPhantom) {
87
- if (!isInserted) {
88
- insertAllPhantomChildren(childComponentofPhantom);
89
- isInserted = true;
90
- }
91
- childComponentofPhantom.html.remove();
92
- } else {
93
- const velesElementNode = require__utils.getExecutedComponentVelesNode(childComponentofPhantom);
94
- if (!velesElementNode) console.error("can't find HTML tree in a component chain");
95
- else {
96
- if (!isInserted) {
97
- insertAllPhantomChildren(velesElementNode);
98
- isInserted = true;
99
- }
100
- velesElementNode.html.remove();
101
- }
79
+ const childNode = "executedVelesComponent" in childComponentofPhantom ? require__utils.getExecutedComponentVelesNode(childComponentofPhantom) : childComponentofPhantom;
80
+ if (!isInserted) {
81
+ insertAllPhantomChildren(childNode);
82
+ isInserted = true;
102
83
  }
84
+ require__utils.getExecutedVelesNodeSourceNode(childNode).remove();
103
85
  });
104
86
  } else {
105
87
  insertAllPhantomChildren(oldVelesElementNode);
106
- oldVelesElementNode.html.remove();
88
+ require__utils.getExecutedVelesNodeSourceNode(oldVelesElementNode).remove();
107
89
  }
108
90
  } else if ("executedVelesNode" in oldVelesElementNode && oldVelesElementNode.phantom) {
109
91
  let isInserted = false;
110
92
  oldVelesElementNode.childComponents.forEach((childComponentofPhantom) => {
111
- if ("executedVelesNode" in childComponentofPhantom) {
112
- if (!isInserted) {
113
- childComponentofPhantom.html.before(newVelesElementNode.html);
114
- isInserted = true;
115
- }
116
- childComponentofPhantom.html.remove();
117
- } else {
118
- const velesElementNode = require__utils.getExecutedComponentVelesNode(childComponentofPhantom);
119
- if (!velesElementNode) console.error("can't find HTML tree in a component chain");
120
- else {
121
- if (!isInserted) {
122
- velesElementNode.html.before(newVelesElementNode.html);
123
- isInserted = true;
124
- }
125
- velesElementNode.html.remove();
126
- }
93
+ const childSourceNode = require__utils.getExecutedVelesNodeSourceNode("executedVelesComponent" in childComponentofPhantom ? require__utils.getExecutedComponentVelesNode(childComponentofPhantom) : childComponentofPhantom);
94
+ if (!isInserted) {
95
+ childSourceNode.before(require__utils.getExecutedVelesNodeSourceNode(newVelesElementNode));
96
+ isInserted = true;
127
97
  }
98
+ childSourceNode.remove();
128
99
  });
129
100
  } else try {
130
- if (parentVelesElementRendered.portal) parentVelesElementRendered.portal.replaceChild(newVelesElementNode.html, oldVelesElementNode.html);
131
- else parentVelesElementRendered.html.replaceChild(newVelesElementNode.html, oldVelesElementNode.html);
101
+ const newSourceNode = require__utils.getExecutedVelesNodeSourceNode(newVelesElementNode);
102
+ const oldSourceNode = require__utils.getExecutedVelesNodeSourceNode(oldVelesElementNode);
103
+ if (parentVelesElementRendered.portal) parentVelesElementRendered.portal.replaceChild(newSourceNode, oldSourceNode);
104
+ else parentVelesElementRendered.html.replaceChild(newSourceNode, oldSourceNode);
132
105
  } catch (e) {
133
106
  console.error("failed to update in renderSelected", e);
134
107
  }
@@ -205,7 +178,7 @@ function updateUseAttributeValue({ element, value }) {
205
178
  //#endregion
206
179
  //#region src/create-state/update-render-each-value.ts
207
180
  function updateUseValueIteratorValue({ value, trackingIterator, createState }) {
208
- const { cb, key, renderedElements, elementsByKey, wrapperComponent, selector, savedContext } = trackingIterator;
181
+ const { anchor, cb, key, renderedElements, elementsByKey, wrapperComponent, selector, savedContext } = trackingIterator;
209
182
  if (!wrapperComponent) {
210
183
  console.error("there is no wrapper component for the iterator");
211
184
  return;
@@ -219,19 +192,19 @@ function updateUseValueIteratorValue({ value, trackingIterator, createState }) {
219
192
  const elements = selector ? selector(value) : value;
220
193
  if (Array.isArray(elements)) {
221
194
  const newRenderedElements = [];
222
- const newElementsByKey = {};
223
- const renderedExistingElements = {};
195
+ const newElementsByKey = /* @__PURE__ */ new Map();
196
+ const renderedExistingElements = /* @__PURE__ */ new Set();
224
197
  elements.forEach((element, index) => {
225
- let calculatedKey = "";
198
+ let calculatedKey = null;
226
199
  if (typeof key === "string" && typeof element === "object" && element !== null && key in element) calculatedKey = element[key];
227
200
  else if (typeof key === "function") calculatedKey = key({
228
201
  element,
229
202
  index
230
203
  });
231
- if (!calculatedKey) return;
232
- const existingElement = elementsByKey[calculatedKey];
204
+ if (calculatedKey == null) return;
205
+ const existingElement = elementsByKey.get(calculatedKey);
233
206
  if (existingElement) {
234
- renderedExistingElements[calculatedKey] = true;
207
+ renderedExistingElements.add(calculatedKey);
235
208
  if (existingElement.elementState.get() !== element) existingElement.elementState.set(element);
236
209
  if (existingElement.indexState.get() !== index) existingElement.indexState.set(index);
237
210
  newRenderedElements.push([
@@ -239,12 +212,12 @@ function updateUseValueIteratorValue({ value, trackingIterator, createState }) {
239
212
  calculatedKey,
240
213
  existingElement.elementState
241
214
  ]);
242
- newElementsByKey[calculatedKey] = {
215
+ newElementsByKey.set(calculatedKey, {
243
216
  elementState: existingElement.elementState,
244
217
  indexState: existingElement.indexState,
245
218
  indexValue: index,
246
219
  node: existingElement.node
247
- };
220
+ });
248
221
  } else {
249
222
  const elementState = createState(element);
250
223
  const indexState = createState(index);
@@ -260,12 +233,12 @@ function updateUseValueIteratorValue({ value, trackingIterator, createState }) {
260
233
  calculatedKey,
261
234
  elementState
262
235
  ]);
263
- newElementsByKey[calculatedKey] = {
236
+ newElementsByKey.set(calculatedKey, {
264
237
  elementState,
265
238
  indexState,
266
239
  indexValue: index,
267
240
  node
268
- };
241
+ });
269
242
  }
270
243
  });
271
244
  const newChildRenderedComponents = [];
@@ -279,61 +252,65 @@ function updateUseValueIteratorValue({ value, trackingIterator, createState }) {
279
252
  newChildComponents.push(newRenderedElement[0]);
280
253
  if (positioningOffset[index]) offset = offset + positioningOffset[index];
281
254
  const [newNode, calculatedKey, _newState] = newRenderedElement;
282
- const existingElement = elementsByKey[calculatedKey];
255
+ const existingElement = elementsByKey.get(calculatedKey);
283
256
  if (existingElement) {
284
- const existingElementNode = require__utils.getExecutedComponentVelesNode(require__utils.getMountedNodeExecutedVersion(existingElement.node, "Existing iterator node is expected to be mounted"));
257
+ const existingElementSourceNode = require__utils.getExecutedVelesNodeSourceNode(require__utils.getExecutedComponentVelesNode(require__utils.getMountedNodeExecutedVersion(existingElement.node, "Existing iterator node is expected to be mounted")));
285
258
  if (existingElement.indexValue + offset === index) {
286
- currentElement = existingElementNode.html;
259
+ currentElement = existingElementSourceNode;
287
260
  return;
288
261
  }
289
262
  if (existingElement.indexValue + offset > index) {
290
263
  if (currentElement) {
291
- currentElement.after(existingElementNode.html);
264
+ currentElement.after(existingElementSourceNode);
292
265
  positioningOffset[existingElement.indexValue + 1] = -1;
293
266
  } else {
294
267
  const firstRenderedElement = renderedElements[0]?.[0];
295
- if (firstRenderedElement?.executedVersion) require__utils.getExecutedComponentVelesNode(firstRenderedElement.executedVersion).html.before(existingElementNode.html);
268
+ if (firstRenderedElement?.executedVersion) require__utils.getExecutedVelesNodeSourceNode(require__utils.getExecutedComponentVelesNode(firstRenderedElement.executedVersion)).before(existingElementSourceNode);
296
269
  }
297
- currentElement = existingElementNode.html;
270
+ currentElement = existingElementSourceNode;
298
271
  offset = offset + 1;
299
272
  } else {
300
273
  if (currentElement) {
301
- currentElement.after(existingElementNode.html);
274
+ currentElement.after(existingElementSourceNode);
302
275
  positioningOffset[existingElement.indexValue + 1] = 1;
303
276
  } else {
304
277
  const firstRenderedElement = renderedElements[0]?.[0];
305
- if (firstRenderedElement?.executedVersion) require__utils.getExecutedComponentVelesNode(firstRenderedElement.executedVersion).html.before(existingElementNode.html);
278
+ if (firstRenderedElement?.executedVersion) require__utils.getExecutedVelesNodeSourceNode(require__utils.getExecutedComponentVelesNode(firstRenderedElement.executedVersion)).before(existingElementSourceNode);
306
279
  }
307
- currentElement = existingElementNode.html;
280
+ currentElement = existingElementSourceNode;
308
281
  offset = offset - 1;
309
282
  }
310
283
  } else {
311
284
  const newNodeExecutedVersion = require__utils.getMountedNodeExecutedVersion(newNode, "New iterator node is expected to be mounted");
312
285
  const newNodeVelesElement = require__utils.getExecutedComponentVelesNode(newNodeExecutedVersion);
286
+ const newNodeSourceNode = require__utils.getExecutedVelesNodeSourceNode(newNodeVelesElement);
313
287
  newNodeVelesElement.parentVelesElement = parentVelesElement;
314
- if (currentElement) currentElement.after(newNodeVelesElement.html);
288
+ if (currentElement) currentElement.after(newNodeSourceNode);
315
289
  else {
316
290
  const firstRenderedElement = renderedElements[0]?.[0];
317
- if (firstRenderedElement?.executedVersion) require__utils.getExecutedComponentVelesNode(firstRenderedElement.executedVersion).html.before(newNodeVelesElement.html);
318
- else parentVelesElement.html.prepend(newNodeVelesElement.html);
291
+ if (firstRenderedElement?.executedVersion) require__utils.getExecutedVelesNodeSourceNode(require__utils.getExecutedComponentVelesNode(firstRenderedElement.executedVersion)).before(newNodeSourceNode);
292
+ else anchor.html.before(newNodeSourceNode);
319
293
  }
320
294
  offset = offset + 1;
321
- currentElement = newNodeVelesElement.html;
295
+ currentElement = newNodeSourceNode;
322
296
  newElementsCount = newElementsCount + 1;
323
297
  require__utils.callMountHandlers(newNodeExecutedVersion);
324
298
  }
325
299
  });
326
300
  if (renderedElements.length === newRenderedElements.length + newElementsCount) {} else renderedElements.forEach(([oldNode, calculatedKey]) => {
327
- if (renderedExistingElements[calculatedKey] === true) return;
301
+ if (renderedExistingElements.has(calculatedKey)) return;
328
302
  else {
329
303
  const oldNodeExecutedVersion = require__utils.getMountedNodeExecutedVersion(oldNode, "Removed iterator node is expected to be mounted");
330
- require__utils.getExecutedComponentVelesNode(oldNodeExecutedVersion).html.remove();
304
+ require__utils.getExecutedVelesNodeSourceNode(require__utils.getExecutedComponentVelesNode(oldNodeExecutedVersion)).remove();
331
305
  require__utils.callUnmountHandlers(oldNodeExecutedVersion);
332
306
  if ("executedVelesNode" in wrapperVelesElementNode) wrapperVelesElementNode.childComponents = wrapperVelesElementNode.childComponents.filter((childComponent) => childComponent !== oldNodeExecutedVersion);
333
307
  else throw new Error("Wrapper iterator element is a string");
334
308
  if ("velesNode" in wrapperComponent) wrapperComponent.childComponents = wrapperComponent.childComponents.filter((childComponent) => childComponent !== oldNode);
335
309
  }
336
310
  });
311
+ if (!anchor.executedVersion) throw new Error("Iterator anchor is expected to be mounted");
312
+ newChildRenderedComponents.push(anchor.executedVersion);
313
+ newChildComponents.push(anchor);
337
314
  if ("executedVelesNode" in wrapperVelesElementNode) wrapperVelesElementNode.childComponents = newChildRenderedComponents;
338
315
  if ("velesNode" in wrapperComponent) wrapperComponent.childComponents = newChildComponents;
339
316
  trackingIterator.renderedElements = newRenderedElements;
@@ -370,6 +347,113 @@ function triggerUpdates({ value, createState, trackers, get }) {
370
347
  });
371
348
  }
372
349
  //#endregion
350
+ //#region src/create-state/min-heap.ts
351
+ /**
352
+ * A stable min-heap. Values with the same priority are returned in
353
+ * insertion order.
354
+ */
355
+ var MinHeap = class {
356
+ constructor(compareValues) {
357
+ this._entries = [];
358
+ this._insertionOrder = 0;
359
+ this._compareValues = compareValues;
360
+ }
361
+ get size() {
362
+ return this._entries.length;
363
+ }
364
+ push(value) {
365
+ const entry = {
366
+ value,
367
+ insertionOrder: this._insertionOrder++
368
+ };
369
+ let index = this._entries.length;
370
+ this._entries.push(entry);
371
+ while (index > 0) {
372
+ const parentIndex = Math.floor((index - 1) / 2);
373
+ const parentEntry = this._entries[parentIndex];
374
+ if (this.compareEntries(parentEntry, entry) <= 0) break;
375
+ this._entries[index] = parentEntry;
376
+ index = parentIndex;
377
+ }
378
+ this._entries[index] = entry;
379
+ }
380
+ pop() {
381
+ const firstEntry = this._entries[0];
382
+ const lastEntry = this._entries.pop();
383
+ if (!firstEntry || !lastEntry) return void 0;
384
+ if (this._entries.length === 0) return firstEntry.value;
385
+ let index = 0;
386
+ while (true) {
387
+ const leftChildIndex = index * 2 + 1;
388
+ const rightChildIndex = leftChildIndex + 1;
389
+ let smallestIndex = index;
390
+ if (leftChildIndex < this._entries.length && this.compareEntries(this._entries[leftChildIndex], lastEntry) < 0) smallestIndex = leftChildIndex;
391
+ if (rightChildIndex < this._entries.length && this.compareEntries(this._entries[rightChildIndex], smallestIndex === index ? lastEntry : this._entries[leftChildIndex]) < 0) smallestIndex = rightChildIndex;
392
+ if (smallestIndex === index) break;
393
+ this._entries[index] = this._entries[smallestIndex];
394
+ index = smallestIndex;
395
+ }
396
+ this._entries[index] = lastEntry;
397
+ return firstEntry.value;
398
+ }
399
+ compareEntries(entry1, entry2) {
400
+ const valueComparison = this._compareValues(entry1.value, entry2.value);
401
+ return valueComparison === 0 ? entry1.insertionOrder - entry2.insertionOrder : valueComparison;
402
+ }
403
+ };
404
+ //#endregion
405
+ //#region src/create-state/state-scheduler.ts
406
+ /**
407
+ * Coordinates notifications produced by one synchronous state transaction.
408
+ *
409
+ * A subscriber can synchronously update another state. That nested update adds
410
+ * its notifications to this same scheduler, allowing an older notification for
411
+ * the same state to be replaced before it is delivered.
412
+ */
413
+ var StateScheduler = class {
414
+ constructor() {
415
+ this._notifications = new MinHeap((notification1, notification2) => notification1.rank - notification2.rank);
416
+ this._notificationsByKey = /* @__PURE__ */ new Map();
417
+ }
418
+ scheduleNotification(notification) {
419
+ const existingNotification = this._notificationsByKey.get(notification.key);
420
+ if (existingNotification) {
421
+ existingNotification.value = notification.value;
422
+ return;
423
+ }
424
+ this._notificationsByKey.set(notification.key, notification);
425
+ this._notifications.push(notification);
426
+ }
427
+ deliverNotifications() {
428
+ while (this._notifications.size > 0) {
429
+ const notification = this._notifications.pop();
430
+ if (this._notificationsByKey.get(notification.key) !== notification) continue;
431
+ this._notificationsByKey.delete(notification.key);
432
+ notification.notify(notification.value, notification.previousValue);
433
+ }
434
+ }
435
+ };
436
+ let activeScheduler;
437
+ /**
438
+ * Top-level state updates own the scheduler and synchronously drain it. Updates
439
+ * made by subscribers reuse the active scheduler, so they cannot deliver a
440
+ * nested notification batch ahead of notifications already waiting to run.
441
+ */
442
+ function runWithStateScheduler(runUpdate) {
443
+ if (activeScheduler) {
444
+ runUpdate(activeScheduler);
445
+ return;
446
+ }
447
+ const scheduler = new StateScheduler();
448
+ activeScheduler = scheduler;
449
+ try {
450
+ runUpdate(scheduler);
451
+ scheduler.deliverNotifications();
452
+ } finally {
453
+ activeScheduler = void 0;
454
+ }
455
+ }
456
+ //#endregion
373
457
  //#region src/create-state/state-core.ts
374
458
  const emptyValue = Symbol("veles-state-core-empty");
375
459
  const defaultEquality = (value1, value2) => value1 === value2;
@@ -380,8 +464,10 @@ var StateCore = class StateCore {
380
464
  this._dirty = false;
381
465
  this._children = /* @__PURE__ */ new Set();
382
466
  this._value = initialValue;
383
- this._parents = new Set(options.parents);
467
+ const parents = options.parents || [];
468
+ this._parents = new Set(parents);
384
469
  this._compute = options.compute;
470
+ this._rank = parents.reduce((rank, parent) => Math.max(rank, parent._rank + 1), 0);
385
471
  this._dirty = Boolean(options.dirty);
386
472
  this._equality = options.equality || defaultEquality;
387
473
  }
@@ -400,14 +486,16 @@ var StateCore = class StateCore {
400
486
  }
401
487
  set(newValue) {
402
488
  if (this._equality(this._value, newValue)) return;
403
- const prevValue = this._value;
404
- this._prevValue = prevValue;
405
- this._value = newValue;
406
- this.notifySubscribers(newValue, prevValue);
407
- this._children.forEach((child) => {
408
- child._dirty = true;
489
+ runWithStateScheduler((scheduler) => {
490
+ const prevValue = this._value;
491
+ this._prevValue = prevValue;
492
+ this._value = newValue;
493
+ this.scheduleNotification(scheduler, newValue, prevValue);
494
+ this._children.forEach((child) => {
495
+ child._dirty = true;
496
+ });
497
+ this.flush(scheduler);
409
498
  });
410
- this.flush();
411
499
  }
412
500
  update(fn) {
413
501
  const newValue = fn(this._value);
@@ -494,37 +582,62 @@ var StateCore = class StateCore {
494
582
  this._dirty = false;
495
583
  return { changed };
496
584
  }
497
- flush() {
498
- const queue = Array.from(this._children);
499
- const queued = new Set(queue);
500
- while (queue.length > 0) {
501
- const child = queue.shift();
585
+ flush(scheduler) {
586
+ /**
587
+ * We utilize a min-heap to ensure that we don't trigger notifications
588
+ * before all the previous necessary work is done. Otherwise it can
589
+ * happen too early, and we'd need to recursively check that every parent
590
+ * is not dirty and schedule it again.
591
+ *
592
+ * Here we achieve this by assinging a rank to each core state, which is
593
+ * determined by the highest rank of any of the parent + 1.
594
+ *
595
+ * After that, we process them at the same rank, ensuring that "short" branches
596
+ * are not executed before all their parents are done with their recomputation.
597
+ */
598
+ const queue = new MinHeap((node1, node2) => node1._rank - node2._rank);
599
+ const queued = /* @__PURE__ */ new Set();
600
+ const enqueue = (node) => {
601
+ if (queued.has(node)) return;
602
+ queue.push(node);
603
+ queued.add(node);
604
+ };
605
+ this._children.forEach(enqueue);
606
+ while (queue.size > 0) {
607
+ const child = queue.pop();
502
608
  queued.delete(child);
503
609
  if (!child._dirty) continue;
504
- if (child.hasDirtyParents()) {
505
- queue.push(child);
506
- queued.add(child);
610
+ let shouldRescheduleChild = false;
611
+ child._parents.forEach((parentNode) => {
612
+ if (parentNode._dirty) {
613
+ enqueue(parentNode);
614
+ shouldRescheduleChild = true;
615
+ }
616
+ });
617
+ if (shouldRescheduleChild) {
618
+ enqueue(child);
507
619
  continue;
508
620
  }
509
621
  const { changed } = child.recompute();
510
622
  const value = child._value;
511
623
  if (!changed) continue;
512
- child.notifySubscribers(value, child._prevValue);
624
+ child.scheduleNotification(scheduler, value, child._prevValue);
513
625
  child._children.forEach((grandchild) => {
514
626
  grandchild._dirty = true;
515
- if (!queued.has(grandchild)) {
516
- queue.push(grandchild);
517
- queued.add(grandchild);
518
- }
627
+ enqueue(grandchild);
519
628
  });
520
629
  }
521
630
  }
522
- hasDirtyParents() {
523
- let hasDirtyParent = false;
524
- this._parents.forEach((parent) => {
525
- if (parent._dirty) hasDirtyParent = true;
631
+ scheduleNotification(scheduler, value, previousValue) {
632
+ scheduler.scheduleNotification({
633
+ key: this,
634
+ rank: this._rank,
635
+ value,
636
+ previousValue,
637
+ notify: (notificationValue, notificationPreviousValue) => {
638
+ this.notifySubscribers(notificationValue, notificationPreviousValue);
639
+ }
526
640
  });
527
- return hasDirtyParent;
528
641
  }
529
642
  notifySubscribers(value, prevValue) {
530
643
  if (value === emptyValue) return;
@@ -645,7 +758,10 @@ function createStateFromCore(core, subscribeCallback) {
645
758
  trackingParams.savedContext = currentContext;
646
759
  const wrapperComponent = require__utils.createElement((_props, componentAPI) => {
647
760
  const children = [];
648
- const elementsByKey = {};
761
+ const anchor = require__utils.createTextElement("");
762
+ anchor.needExecutedVersion = true;
763
+ trackingParams.anchor = anchor;
764
+ const elementsByKey = /* @__PURE__ */ new Map();
649
765
  const stateValue = core.get();
650
766
  const elements = options.selector ? options.selector(stateValue) : stateValue;
651
767
  if (!Array.isArray(elements)) {
@@ -653,7 +769,7 @@ function createStateFromCore(core, subscribeCallback) {
653
769
  return null;
654
770
  }
655
771
  elements.forEach((element, index) => {
656
- let calculatedKey = "";
772
+ let calculatedKey = null;
657
773
  if (typeof options.key === "string" && typeof element === "object" && element !== null && options.key in element) calculatedKey = element[options.key];
658
774
  else if (typeof options.key === "function") calculatedKey = options.key({
659
775
  element,
@@ -661,18 +777,18 @@ function createStateFromCore(core, subscribeCallback) {
661
777
  });
662
778
  const elementState = createState(element);
663
779
  const indexState = createState(index);
664
- if (!calculatedKey) return;
780
+ if (calculatedKey == null) return;
665
781
  let node = cb({
666
782
  elementState,
667
783
  indexState
668
784
  });
669
785
  node.needExecutedVersion = true;
670
- elementsByKey[calculatedKey] = {
786
+ elementsByKey.set(calculatedKey, {
671
787
  node,
672
788
  indexState,
673
789
  indexValue: index,
674
790
  elementState
675
- };
791
+ });
676
792
  children.push([
677
793
  node,
678
794
  calculatedKey,
@@ -689,7 +805,7 @@ function createStateFromCore(core, subscribeCallback) {
689
805
  });
690
806
  return require__utils.createElement("div", {
691
807
  phantom: true,
692
- children: children.map((child) => child[0])
808
+ children: [...children.map((child) => child[0]), anchor]
693
809
  });
694
810
  });
695
811
  wrapperComponent.needExecutedVersion = true;
package/dist/index.d.cts CHANGED
@@ -1,4 +1,4 @@
1
- import { a as VelesComponentObject, i as VelesChildren, n as createElement, o as VelesElement, r as AttributeHelper, s as VelesStringElement, t as Fragment } from "./fragment-C16zCm8L.cjs";
1
+ import { a as VelesComponentObject, i as VelesChildren, n as createElement, o as VelesElement, r as AttributeHelper, s as VelesStringElement, t as Fragment } from "./fragment-COsp_SDE.cjs";
2
2
 
3
3
  //#region src/attach-component.d.ts
4
4
  /**
@@ -24,6 +24,7 @@ declare function onUnmount(cb: Function): void;
24
24
  type StateEquality<ValueType> = (value1: ValueType, value2: ValueType) => boolean;
25
25
  type StateLike<ValueType> = State<ValueType>;
26
26
  type ArrayElement<T> = T extends ReadonlyArray<infer Element> ? Element : never;
27
+ type IteratorKey = string | number;
27
28
  type State<ValueType> = {
28
29
  track(cb: (value: ValueType) => void | Function, options?: {
29
30
  callOnMount?: boolean;
@@ -45,7 +46,7 @@ type State<ValueType> = {
45
46
  key: string | ((options: {
46
47
  element: Element;
47
48
  index: number;
48
- }) => string);
49
+ }) => IteratorKey);
49
50
  selector?: undefined;
50
51
  } : never, cb: (props: {
51
52
  elementState: State<Element>;
@@ -55,7 +56,7 @@ type State<ValueType> = {
55
56
  key: string | ((options: {
56
57
  element: Element;
57
58
  index: number;
58
- }) => string);
59
+ }) => IteratorKey);
59
60
  selector: (value: ValueType) => SelectorValueType;
60
61
  }, cb: (props: {
61
62
  elementState: State<Element>;
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { a as VelesComponentObject, i as VelesChildren, n as createElement, o as VelesElement, r as AttributeHelper, s as VelesStringElement, t as Fragment } from "./fragment-yC0-T2O-.js";
1
+ import { a as VelesComponentObject, i as VelesChildren, n as createElement, o as VelesElement, r as AttributeHelper, s as VelesStringElement, t as Fragment } from "./fragment-CPcYB5D5.js";
2
2
 
3
3
  //#region src/attach-component.d.ts
4
4
  /**
@@ -24,6 +24,7 @@ declare function onUnmount(cb: Function): void;
24
24
  type StateEquality<ValueType> = (value1: ValueType, value2: ValueType) => boolean;
25
25
  type StateLike<ValueType> = State<ValueType>;
26
26
  type ArrayElement<T> = T extends ReadonlyArray<infer Element> ? Element : never;
27
+ type IteratorKey = string | number;
27
28
  type State<ValueType> = {
28
29
  track(cb: (value: ValueType) => void | Function, options?: {
29
30
  callOnMount?: boolean;
@@ -45,7 +46,7 @@ type State<ValueType> = {
45
46
  key: string | ((options: {
46
47
  element: Element;
47
48
  index: number;
48
- }) => string);
49
+ }) => IteratorKey);
49
50
  selector?: undefined;
50
51
  } : never, cb: (props: {
51
52
  elementState: State<Element>;
@@ -55,7 +56,7 @@ type State<ValueType> = {
55
56
  key: string | ((options: {
56
57
  element: Element;
57
58
  index: number;
58
- }) => string);
59
+ }) => IteratorKey);
59
60
  selector: (value: ValueType) => SelectorValueType;
60
61
  }, cb: (props: {
61
62
  elementState: State<Element>;