@katn30/trakr 3.0.0 → 4.0.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.
@@ -24,6 +24,7 @@ __export(index_exports, {
24
24
  EventTracked: () => EventTracked,
25
25
  EventTrackedCollection: () => EventTrackedCollection,
26
26
  EventTracker: () => EventTracker,
27
+ Id: () => Id,
27
28
  State: () => State,
28
29
  Tracked: () => Tracked,
29
30
  TrackedCollection: () => TrackedCollection,
@@ -31,7 +32,10 @@ __export(index_exports, {
31
32
  TrackedObject: () => TrackedObject,
32
33
  Tracker: () => Tracker,
33
34
  TrackerSession: () => TrackerSession,
34
- TypedEvent: () => TypedEvent
35
+ TypedEvent: () => TypedEvent,
36
+ getIdentity: () => getIdentity,
37
+ getIdentityObject: () => getIdentityObject,
38
+ getIdentityProperties: () => getIdentityProperties
35
39
  });
36
40
  module.exports = __toCommonJS(index_exports);
37
41
 
@@ -294,17 +298,73 @@ var OperationProperties = class {
294
298
 
295
299
  // src/ExternallyAssigned.ts
296
300
  var AUTO_ID = /* @__PURE__ */ Symbol("autoId");
301
+ var ID_PROPS = /* @__PURE__ */ Symbol("idProps");
297
302
  function AutoId(_target, context) {
298
303
  context.addInitializer(function() {
299
- Object.defineProperty(Object.getPrototypeOf(this), AUTO_ID, {
300
- value: String(context.name),
301
- configurable: true
302
- });
304
+ const proto = Object.getPrototypeOf(this);
305
+ if (!Object.prototype.hasOwnProperty.call(proto, AUTO_ID)) {
306
+ Object.defineProperty(proto, AUTO_ID, {
307
+ value: String(context.name),
308
+ configurable: true
309
+ });
310
+ }
311
+ addIdentityProp(proto, String(context.name));
312
+ });
313
+ }
314
+ function Id(_target, context) {
315
+ context.addInitializer(function() {
316
+ const proto = Object.getPrototypeOf(this);
317
+ addIdentityProp(proto, String(context.name));
303
318
  });
304
319
  }
320
+ function addIdentityProp(proto, propertyName) {
321
+ if (!Object.prototype.hasOwnProperty.call(proto, ID_PROPS)) {
322
+ Object.defineProperty(proto, ID_PROPS, {
323
+ value: [],
324
+ configurable: true,
325
+ writable: true
326
+ });
327
+ }
328
+ const list = proto[ID_PROPS];
329
+ if (!list.includes(propertyName)) list.push(propertyName);
330
+ }
305
331
  function getAutoIdProperty(proto) {
306
332
  return AUTO_ID in proto ? proto[AUTO_ID] : void 0;
307
333
  }
334
+ function getIdentityProperties(proto) {
335
+ const chain = [];
336
+ let current = proto;
337
+ while (current) {
338
+ chain.push(current);
339
+ current = Object.getPrototypeOf(current);
340
+ }
341
+ const merged = [];
342
+ for (let i = chain.length - 1; i >= 0; i--) {
343
+ const proto2 = chain[i];
344
+ if (!Object.prototype.hasOwnProperty.call(proto2, ID_PROPS)) continue;
345
+ const list = proto2[ID_PROPS];
346
+ for (const name of list) {
347
+ if (!merged.includes(name)) merged.push(name);
348
+ }
349
+ }
350
+ return merged;
351
+ }
352
+ function getIdentity(obj) {
353
+ const props = getIdentityProperties(Object.getPrototypeOf(obj));
354
+ if (props.length === 0) return void 0;
355
+ if (props.length === 1) {
356
+ return obj[props[0]];
357
+ }
358
+ const result = {};
359
+ for (const p of props) result[p] = obj[p];
360
+ return result;
361
+ }
362
+ function getIdentityObject(obj) {
363
+ const props = getIdentityProperties(Object.getPrototypeOf(obj));
364
+ const result = {};
365
+ for (const p of props) result[p] = obj[p];
366
+ return result;
367
+ }
308
368
 
309
369
  // src/TrackedObjectStateMachine.ts
310
370
  function applyStateTransition(obj, event, direction, context) {
@@ -1369,15 +1429,18 @@ var TrackedCollectionChanged = class {
1369
1429
  // src/EventRegistry.ts
1370
1430
  var EVENT_METADATA = /* @__PURE__ */ Symbol("eventMetadata");
1371
1431
  var instanceState = /* @__PURE__ */ new WeakMap();
1432
+ var instanceHistory = /* @__PURE__ */ new WeakMap();
1433
+ var instanceHistoryTimes = /* @__PURE__ */ new WeakMap();
1372
1434
  var subscribedInstances = /* @__PURE__ */ new WeakSet();
1373
1435
  var itemOriginCollection = /* @__PURE__ */ new WeakMap();
1436
+ var DEFAULT_GROUP = "";
1374
1437
  function hasOwnEventMetadata(proto) {
1375
1438
  return Object.prototype.hasOwnProperty.call(proto, EVENT_METADATA);
1376
1439
  }
1377
1440
  function ownEventMetadata(proto) {
1378
1441
  return hasOwnEventMetadata(proto) ? proto[EVENT_METADATA] : void 0;
1379
1442
  }
1380
- function registerEventProperty(proto, property, eventType) {
1443
+ function registerEventProperty(proto, property, options) {
1381
1444
  if (!hasOwnEventMetadata(proto)) {
1382
1445
  Object.defineProperty(proto, EVENT_METADATA, {
1383
1446
  value: /* @__PURE__ */ new Map(),
@@ -1386,7 +1449,7 @@ function registerEventProperty(proto, property, eventType) {
1386
1449
  }
1387
1450
  const map = proto[EVENT_METADATA];
1388
1451
  if (!map.has(property)) {
1389
- map.set(property, eventType);
1452
+ map.set(property, options);
1390
1453
  }
1391
1454
  }
1392
1455
  function getEventMetadata(proto) {
@@ -1400,31 +1463,97 @@ function getEventMetadata(proto) {
1400
1463
  for (let i = chain.length - 1; i >= 0; i--) {
1401
1464
  const own = ownEventMetadata(chain[i]);
1402
1465
  if (!own) continue;
1403
- own.forEach((eventType, property) => {
1404
- if (!merged.has(property)) merged.set(property, eventType);
1466
+ own.forEach((options, property) => {
1467
+ if (!merged.has(property)) merged.set(property, options);
1405
1468
  });
1406
1469
  }
1407
1470
  return merged;
1408
1471
  }
1472
+ var contextStacks = /* @__PURE__ */ new WeakMap();
1473
+ function pushContext(tracker, ctx) {
1474
+ let stack = contextStacks.get(tracker);
1475
+ if (!stack) {
1476
+ stack = [];
1477
+ contextStacks.set(tracker, stack);
1478
+ }
1479
+ stack.push(ctx);
1480
+ }
1481
+ function popContext(tracker) {
1482
+ const stack = contextStacks.get(tracker);
1483
+ if (stack && stack.length > 0) stack.pop();
1484
+ }
1485
+ function peekContext(tracker) {
1486
+ const stack = contextStacks.get(tracker);
1487
+ return stack && stack.length > 0 ? stack[stack.length - 1] : void 0;
1488
+ }
1409
1489
  function ensureEventStateSubscription(target) {
1410
1490
  if (subscribedInstances.has(target)) return;
1411
1491
  subscribedInstances.add(target);
1412
1492
  target.changed.subscribe((evt) => {
1413
1493
  const meta = getEventMetadata(Object.getPrototypeOf(target));
1414
- if (!meta.has(evt.property)) return;
1415
- const state = getOrCreateEventState(target);
1416
- let entry = state.get(evt.property);
1417
- if (!entry) {
1418
- entry = { originalValue: evt.oldValue, currentValue: evt.newValue };
1419
- state.set(evt.property, entry);
1494
+ const propMeta = meta.get(evt.property);
1495
+ if (!propMeta) return;
1496
+ if (propMeta.history) {
1497
+ appendHistoryEntry(
1498
+ target,
1499
+ evt.property,
1500
+ evt.newValue,
1501
+ evt.oldValue,
1502
+ propMeta.history,
1503
+ propMeta.coalesceWithin
1504
+ );
1420
1505
  } else {
1421
- entry.currentValue = evt.newValue;
1422
- }
1423
- if (entry.currentValue === entry.originalValue) {
1424
- state.delete(evt.property);
1506
+ updateFinalValue(target, evt.property, evt.newValue, evt.oldValue);
1425
1507
  }
1426
1508
  });
1427
1509
  }
1510
+ function updateFinalValue(target, property, newValue, oldValue) {
1511
+ const state = getOrCreateEventState(target);
1512
+ let entry = state.get(property);
1513
+ if (!entry) {
1514
+ entry = { originalValue: oldValue, currentValue: newValue };
1515
+ state.set(property, entry);
1516
+ } else {
1517
+ entry.currentValue = newValue;
1518
+ }
1519
+ if (entry.currentValue === entry.originalValue) {
1520
+ state.delete(property);
1521
+ }
1522
+ }
1523
+ function appendHistoryEntry(target, property, newValue, oldValue, historyConfig, coalesceWithin) {
1524
+ const chains = getOrCreateHistoryState(target);
1525
+ let chain = chains.get(property);
1526
+ if (!chain) {
1527
+ chain = [];
1528
+ chains.set(property, chain);
1529
+ }
1530
+ const times = getOrCreateHistoryTimes(target);
1531
+ const now = Date.now();
1532
+ const lastTime = times.get(property);
1533
+ const shouldReplace = coalesceWithin !== void 0 && lastTime !== void 0 && now - lastTime < coalesceWithin && chain.length > 0;
1534
+ let entry;
1535
+ if (typeof historyConfig === "object" && historyConfig.entryFactory) {
1536
+ const ctx = peekContext(target.tracker);
1537
+ const change = { time: /* @__PURE__ */ new Date() };
1538
+ entry = historyConfig.entryFactory(target, newValue, oldValue, change, ctx);
1539
+ } else {
1540
+ entry = { property, value: newValue };
1541
+ }
1542
+ if (shouldReplace) {
1543
+ chain[chain.length - 1] = entry;
1544
+ } else {
1545
+ chain.push(entry);
1546
+ }
1547
+ times.set(property, now);
1548
+ }
1549
+ function getOrCreateHistoryTimes(target) {
1550
+ let times = instanceHistoryTimes.get(target);
1551
+ if (!times) {
1552
+ times = /* @__PURE__ */ new Map();
1553
+ instanceHistoryTimes.set(target, times);
1554
+ }
1555
+ return times;
1556
+ }
1428
1557
  function getOrCreateEventState(target) {
1429
1558
  let state = instanceState.get(target);
1430
1559
  if (!state) {
@@ -1433,11 +1562,24 @@ function getOrCreateEventState(target) {
1433
1562
  }
1434
1563
  return state;
1435
1564
  }
1565
+ function getOrCreateHistoryState(target) {
1566
+ let state = instanceHistory.get(target);
1567
+ if (!state) {
1568
+ state = /* @__PURE__ */ new Map();
1569
+ instanceHistory.set(target, state);
1570
+ }
1571
+ return state;
1572
+ }
1436
1573
  function getEventState(target) {
1437
1574
  return instanceState.get(target) ?? /* @__PURE__ */ new Map();
1438
1575
  }
1576
+ function getHistoryState(target) {
1577
+ return instanceHistory.get(target) ?? /* @__PURE__ */ new Map();
1578
+ }
1439
1579
  function clearEventState(target) {
1440
1580
  instanceState.delete(target);
1581
+ instanceHistory.delete(target);
1582
+ instanceHistoryTimes.delete(target);
1441
1583
  }
1442
1584
  function recordItemOrigin(item, collection) {
1443
1585
  itemOriginCollection.set(item, collection);
@@ -1446,42 +1588,312 @@ function getItemOrigin(item) {
1446
1588
  return itemOriginCollection.get(item);
1447
1589
  }
1448
1590
 
1591
+ // src/EventTrackedCollection.ts
1592
+ var EventTrackedCollection = class extends TrackedCollection {
1593
+ constructor(tracker, items, validator, options) {
1594
+ super(tracker, items, validator);
1595
+ this._historyOps = [];
1596
+ this._baselineItems = /* @__PURE__ */ new Set();
1597
+ this._eventOptions = options;
1598
+ for (const item of this.collection) {
1599
+ this._baselineItems.add(item);
1600
+ if (item instanceof TrackedObject) {
1601
+ recordItemOrigin(item, this);
1602
+ this._validateItemHasIdentity(item);
1603
+ }
1604
+ }
1605
+ this.changed.subscribe((evt) => {
1606
+ for (const item of evt.added) {
1607
+ if (item instanceof TrackedObject) {
1608
+ recordItemOrigin(item, this);
1609
+ this._validateItemHasIdentity(item);
1610
+ }
1611
+ }
1612
+ if (this._isAggregateMode() && !this.tracker._isReplaying) {
1613
+ for (const item of evt.added) {
1614
+ if (item instanceof TrackedObject) {
1615
+ this._recordAddOp(item);
1616
+ }
1617
+ }
1618
+ for (const item of evt.removed) {
1619
+ if (item instanceof TrackedObject) {
1620
+ this._recordRemoveOp(item);
1621
+ }
1622
+ }
1623
+ }
1624
+ });
1625
+ if (this._isHistoryMode()) {
1626
+ this._attachItemChangeWatchers();
1627
+ }
1628
+ }
1629
+ /** @internal */
1630
+ get _lifecycleOptions() {
1631
+ if (!this._eventOptions) return void 0;
1632
+ if (this._isAggregateMode()) return void 0;
1633
+ return {
1634
+ itemAdded: this._eventOptions.itemAdded,
1635
+ itemRemoved: this._eventOptions.itemRemoved
1636
+ };
1637
+ }
1638
+ /** @internal */
1639
+ get _aggregateOptions() {
1640
+ return this._isAggregateMode() ? this._eventOptions : void 0;
1641
+ }
1642
+ /** @internal */
1643
+ get _historyOpsList() {
1644
+ return this._historyOps;
1645
+ }
1646
+ /** @internal */
1647
+ _clearHistoryOps() {
1648
+ this._historyOps.length = 0;
1649
+ this._baselineItems.clear();
1650
+ for (const item of this.collection) this._baselineItems.add(item);
1651
+ }
1652
+ /** @internal */
1653
+ _isInBaseline(item) {
1654
+ return this._baselineItems.has(item);
1655
+ }
1656
+ /** @internal */
1657
+ _baselineListSnapshot() {
1658
+ return [...this._baselineItems];
1659
+ }
1660
+ /** @internal */
1661
+ _rebaseline() {
1662
+ this._baselineItems.clear();
1663
+ for (const item of this.collection) this._baselineItems.add(item);
1664
+ }
1665
+ _isAggregateMode() {
1666
+ if (!this._eventOptions) return false;
1667
+ return this._eventOptions.eventType !== void 0 || this._eventOptions.history !== void 0;
1668
+ }
1669
+ _isHistoryMode() {
1670
+ return !!this._eventOptions?.history;
1671
+ }
1672
+ _validateItemHasIdentity(item) {
1673
+ const props = getIdentityProperties(Object.getPrototypeOf(item));
1674
+ if (props.length === 0) {
1675
+ throw new Error(
1676
+ `EventTrackedCollection item type ${item.constructor.name} must declare at least one @Id or @AutoId property when used with eventType or history mode`
1677
+ );
1678
+ }
1679
+ }
1680
+ _attachItemChangeWatchers() {
1681
+ const watchItem = (item) => {
1682
+ item.changed.subscribe((_evt) => {
1683
+ if (this.tracker._isReplaying) return;
1684
+ if (!this.collection.includes(item)) return;
1685
+ this._recordChangeOp(item);
1686
+ });
1687
+ };
1688
+ for (const item of this.collection) {
1689
+ if (item instanceof TrackedObject) watchItem(item);
1690
+ }
1691
+ this.changed.subscribe((evt) => {
1692
+ for (const item of evt.added) {
1693
+ if (item instanceof TrackedObject) watchItem(item);
1694
+ }
1695
+ });
1696
+ }
1697
+ _recordAddOp(item) {
1698
+ const opts = this._eventOptions;
1699
+ if (typeof opts?.history === "object" && opts.history.entryFactory) {
1700
+ const ctx = peekContext(this.tracker);
1701
+ const change = { time: /* @__PURE__ */ new Date() };
1702
+ this._historyOps.push({
1703
+ op: "add",
1704
+ raw: opts.history.entryFactory(item, change, ctx)
1705
+ });
1706
+ } else if (opts?.history === true) {
1707
+ this._historyOps.push({ op: "add", item, snapshot: snapshotItemAtRecordTime(item) });
1708
+ }
1709
+ }
1710
+ _recordRemoveOp(item) {
1711
+ const opts = this._eventOptions;
1712
+ const identity = getIdentity(item);
1713
+ if (typeof opts?.history === "object" && opts.history.entryFactory) {
1714
+ const ctx = peekContext(this.tracker);
1715
+ const change = { time: /* @__PURE__ */ new Date() };
1716
+ this._historyOps.push({
1717
+ op: "remove",
1718
+ raw: opts.history.entryFactory(item, change, ctx)
1719
+ });
1720
+ } else if (opts?.history === true) {
1721
+ this._historyOps.push({ op: "remove", identity });
1722
+ }
1723
+ }
1724
+ _recordChangeOpDiff(item) {
1725
+ const proto = Object.getPrototypeOf(item);
1726
+ const idProps = getIdentityProperties(proto);
1727
+ const meta = getEventMetadata(proto);
1728
+ const rec = item;
1729
+ const diff = {};
1730
+ for (const [propName] of meta) {
1731
+ if (idProps.includes(propName)) continue;
1732
+ const v = rec[propName];
1733
+ diff[propName] = v === void 0 ? null : v;
1734
+ }
1735
+ return diff;
1736
+ }
1737
+ _recordChangeOp(item) {
1738
+ const opts = this._eventOptions;
1739
+ if (typeof opts?.history === "object" && opts.history.entryFactory) {
1740
+ const ctx = peekContext(this.tracker);
1741
+ const change = { time: /* @__PURE__ */ new Date() };
1742
+ this._historyOps.push({
1743
+ op: "change",
1744
+ raw: opts.history.entryFactory(item, change, ctx)
1745
+ });
1746
+ } else if (opts?.history === true) {
1747
+ const identity = getIdentity(item);
1748
+ const diff = this._recordChangeOpDiff(item);
1749
+ this._historyOps.push({ op: "change", identity, item, diff });
1750
+ }
1751
+ }
1752
+ };
1753
+ function snapshotItemAtRecordTime(item) {
1754
+ const proto = Object.getPrototypeOf(item);
1755
+ const meta = getEventMetadata(proto);
1756
+ const idProps = getIdentityProperties(proto);
1757
+ const autoIdProp = getAutoIdProperty(proto);
1758
+ const rec = item;
1759
+ const snap = {};
1760
+ for (const idProp of idProps) {
1761
+ if (idProp === autoIdProp) snap[idProp] = null;
1762
+ else snap[idProp] = rec[idProp];
1763
+ }
1764
+ for (const [propName] of meta) {
1765
+ if (idProps.includes(propName)) continue;
1766
+ const v = rec[propName];
1767
+ snap[propName] = v === void 0 ? null : v;
1768
+ }
1769
+ return snap;
1770
+ }
1771
+
1449
1772
  // src/EventTracker.ts
1450
1773
  var EventTracker = class extends Tracker {
1774
+ withContext(ctx, action) {
1775
+ pushContext(this, ctx);
1776
+ try {
1777
+ return action();
1778
+ } finally {
1779
+ popContext(this);
1780
+ }
1781
+ }
1451
1782
  onCommit(keys) {
1452
1783
  super.onCommit(keys);
1453
1784
  for (const obj of this.trackedObjects) {
1454
1785
  clearEventState(obj);
1455
1786
  }
1787
+ for (const col of this.trackedCollections) {
1788
+ if (col instanceof EventTrackedCollection) {
1789
+ col._clearHistoryOps();
1790
+ }
1791
+ }
1456
1792
  }
1457
1793
  generateEvents() {
1458
1794
  const events = [];
1459
1795
  for (const obj of this.trackedObjects) {
1460
- const meta = getEventMetadata(Object.getPrototypeOf(obj));
1461
1796
  const origin = getItemOrigin(obj);
1462
- const lifecycle = origin?._lifecycleOptions;
1797
+ if (!origin) continue;
1798
+ const legacy = origin._lifecycleOptions;
1799
+ if (!legacy) continue;
1800
+ const meta = getEventMetadata(Object.getPrototypeOf(obj));
1463
1801
  switch (obj.state) {
1464
1802
  case "Insert" /* Insert */:
1465
- if (lifecycle?.itemAdded) {
1466
- events.push(buildItemAddedEvent(obj, meta, lifecycle.itemAdded));
1803
+ if (legacy.itemAdded) {
1804
+ events.push(buildItemAddedEvent(obj, meta, legacy.itemAdded));
1467
1805
  }
1468
1806
  break;
1469
1807
  case "Deleted" /* Deleted */:
1470
- if (lifecycle?.itemRemoved) {
1471
- events.push(buildItemRemovedEvent(obj, lifecycle.itemRemoved));
1808
+ if (legacy.itemRemoved) {
1809
+ events.push(buildItemRemovedEvent(obj, legacy.itemRemoved));
1472
1810
  }
1473
1811
  break;
1474
1812
  case "Changed" /* Changed */:
1475
1813
  pushFieldClusterEvents(obj, meta, events);
1476
1814
  break;
1477
- case "Unchanged" /* Unchanged */:
1478
- default:
1479
- break;
1480
1815
  }
1481
1816
  }
1817
+ const ownedCollections = /* @__PURE__ */ new Map();
1818
+ for (const col of this.trackedCollections) {
1819
+ if (!(col instanceof EventTrackedCollection)) continue;
1820
+ const agg = col._aggregateOptions;
1821
+ if (!agg?.owner) continue;
1822
+ const owner = agg.owner.object;
1823
+ let list = ownedCollections.get(owner);
1824
+ if (!list) {
1825
+ list = [];
1826
+ ownedCollections.set(owner, list);
1827
+ }
1828
+ list.push(col);
1829
+ }
1830
+ for (const obj of this.trackedObjects) {
1831
+ const origin = getItemOrigin(obj);
1832
+ if (origin) continue;
1833
+ const groups = /* @__PURE__ */ new Map();
1834
+ const meta = getEventMetadata(Object.getPrototypeOf(obj));
1835
+ const stateMap = getEventState(obj);
1836
+ const historyMap = getHistoryState(obj);
1837
+ for (const [propName, propMeta] of meta) {
1838
+ const group = propMeta.eventType ?? DEFAULT_GROUP;
1839
+ if (propMeta.history) {
1840
+ const chain = historyMap.get(propName);
1841
+ if (chain && chain.length > 0) {
1842
+ addToGroup(groups, group, propName, [...chain]);
1843
+ }
1844
+ } else {
1845
+ const entry = stateMap.get(propName);
1846
+ if (entry) {
1847
+ addToGroup(groups, group, propName, normalizeValue(entry.currentValue));
1848
+ }
1849
+ }
1850
+ }
1851
+ const owned = ownedCollections.get(obj) ?? [];
1852
+ for (const col of owned) {
1853
+ const slot = computeCollectionSlot(col, this);
1854
+ if (slot === void 0) continue;
1855
+ const agg = col._aggregateOptions;
1856
+ const group = agg.eventType ?? DEFAULT_GROUP;
1857
+ const propertyName = agg.owner.property;
1858
+ addToGroup(groups, group, propertyName, slot);
1859
+ }
1860
+ if (groups.size === 0) continue;
1861
+ const identity = getIdentity(obj);
1862
+ for (const [group, payload] of groups) {
1863
+ const event = {
1864
+ eventType: group,
1865
+ payload,
1866
+ trackingId: obj.trackingId
1867
+ };
1868
+ if (identity !== void 0) {
1869
+ event.targetId = identity;
1870
+ }
1871
+ events.push(event);
1872
+ }
1873
+ }
1874
+ for (const col of this.trackedCollections) {
1875
+ if (!(col instanceof EventTrackedCollection)) continue;
1876
+ const agg = col._aggregateOptions;
1877
+ if (!agg || agg.owner) continue;
1878
+ const slot = computeCollectionSlot(col, this);
1879
+ if (slot === void 0) continue;
1880
+ const eventType = agg.eventType ?? DEFAULT_GROUP;
1881
+ events.push({
1882
+ eventType,
1883
+ payload: slot
1884
+ });
1885
+ }
1482
1886
  return events;
1483
1887
  }
1484
1888
  };
1889
+ function addToGroup(groups, group, key, value) {
1890
+ let payload = groups.get(group);
1891
+ if (!payload) {
1892
+ payload = {};
1893
+ groups.set(group, payload);
1894
+ }
1895
+ payload[key] = value;
1896
+ }
1485
1897
  function buildItemAddedEvent(obj, meta, eventType) {
1486
1898
  const payload = {};
1487
1899
  for (const [propertyName] of meta) {
@@ -1510,12 +1922,13 @@ function pushFieldClusterEvents(obj, meta, events) {
1510
1922
  const state = getEventState(obj);
1511
1923
  if (state.size === 0) return;
1512
1924
  const grouped = /* @__PURE__ */ new Map();
1513
- for (const [propertyName, eventType] of meta) {
1925
+ for (const [propertyName, propMeta] of meta) {
1514
1926
  if (!state.has(propertyName)) continue;
1515
- let bucket = grouped.get(eventType);
1927
+ if (!propMeta.eventType) continue;
1928
+ let bucket = grouped.get(propMeta.eventType);
1516
1929
  if (!bucket) {
1517
1930
  bucket = [];
1518
- grouped.set(eventType, bucket);
1931
+ grouped.set(propMeta.eventType, bucket);
1519
1932
  }
1520
1933
  bucket.push(propertyName);
1521
1934
  }
@@ -1537,58 +1950,176 @@ function pushFieldClusterEvents(obj, meta, events) {
1537
1950
  events.push(event);
1538
1951
  }
1539
1952
  }
1953
+ function computeCollectionSlot(col, tracker) {
1954
+ const agg = col._aggregateOptions;
1955
+ const isHistory = agg.history !== void 0 && agg.history !== false;
1956
+ if (isHistory) return computeHistoryOps(col, agg);
1957
+ return computeBucketedSlot(col, tracker);
1958
+ }
1959
+ function computeHistoryOps(col, agg) {
1960
+ const opsList = col._historyOpsList;
1961
+ const hasFactory = typeof agg.history === "object" && agg.history.entryFactory;
1962
+ const ops = [];
1963
+ for (const op of opsList) {
1964
+ if (hasFactory) {
1965
+ ops.push(op.raw);
1966
+ continue;
1967
+ }
1968
+ if (op.op === "add") {
1969
+ ops.push({ op: "add", item: op.snapshot ?? (op.item ? snapshotItemForAdded(op.item) : {}) });
1970
+ } else if (op.op === "remove") {
1971
+ const idObj = op.identity && typeof op.identity === "object" ? op.identity : identityToSingleKeyObject(op.identity, col);
1972
+ ops.push({ op: "remove", ...idObj });
1973
+ } else if (op.op === "change" && op.item) {
1974
+ const idObj = getIdentityObject(op.item);
1975
+ const diff = op.diff ?? computeItemDiff(op.item);
1976
+ ops.push({ op: "change", ...idObj, ...diff });
1977
+ }
1978
+ }
1979
+ if (ops.length === 0) return void 0;
1980
+ return { ops };
1981
+ }
1982
+ function identityToSingleKeyObject(identity, col) {
1983
+ const baseline = col._baselineListSnapshot();
1984
+ const sample = baseline.find((i) => i instanceof TrackedObject);
1985
+ const currentSample = col.collection.find(
1986
+ (i) => i instanceof TrackedObject
1987
+ );
1988
+ const proto = sample ? Object.getPrototypeOf(sample) : currentSample ? Object.getPrototypeOf(currentSample) : void 0;
1989
+ if (!proto) return {};
1990
+ const props = getIdentityProperties(proto);
1991
+ if (props.length === 1) return { [props[0]]: identity };
1992
+ return identity && typeof identity === "object" ? identity : {};
1993
+ }
1994
+ function computeBucketedSlot(col, tracker) {
1995
+ const added = [];
1996
+ const changed = [];
1997
+ const removed = [];
1998
+ const isPrimitive = !hasTrackedObjectItems(col);
1999
+ for (const item of col.collection) {
2000
+ if (item instanceof TrackedObject) {
2001
+ if (item.state === "Insert" /* Insert */) {
2002
+ added.push(snapshotItemForAdded(item));
2003
+ } else if (item.state === "Changed" /* Changed */) {
2004
+ const entry = buildChangedEntry(item);
2005
+ if (entry) changed.push(entry);
2006
+ }
2007
+ }
2008
+ }
2009
+ if (isPrimitive) {
2010
+ for (const item of col.collection) {
2011
+ if (!col._isInBaseline(item)) added.push(item);
2012
+ }
2013
+ for (const baselineItem of col._baselineListSnapshot()) {
2014
+ if (!col.collection.includes(baselineItem)) removed.push(baselineItem);
2015
+ }
2016
+ } else {
2017
+ for (const obj of tracker.trackedObjects) {
2018
+ if (obj.state !== "Deleted" /* Deleted */) continue;
2019
+ if (getItemOrigin(obj) !== col) continue;
2020
+ const identity = getIdentity(obj);
2021
+ removed.push(identity);
2022
+ }
2023
+ }
2024
+ if (added.length === 0 && changed.length === 0 && removed.length === 0) {
2025
+ return void 0;
2026
+ }
2027
+ const slot = { added, removed };
2028
+ if (!isPrimitive) slot.changed = changed;
2029
+ return slot;
2030
+ }
2031
+ function hasTrackedObjectItems(col) {
2032
+ for (const item of col.collection) {
2033
+ if (item instanceof TrackedObject) return true;
2034
+ }
2035
+ for (const item of col._baselineListSnapshot()) {
2036
+ if (item instanceof TrackedObject) return true;
2037
+ }
2038
+ return false;
2039
+ }
2040
+ function snapshotItemForAdded(item) {
2041
+ const proto = Object.getPrototypeOf(item);
2042
+ const meta = getEventMetadata(proto);
2043
+ const idProps = getIdentityProperties(proto);
2044
+ const autoIdProp = getAutoIdProperty(proto);
2045
+ const snapshot = {};
2046
+ const rec = item;
2047
+ for (const idProp of idProps) {
2048
+ if (idProp === autoIdProp) {
2049
+ snapshot[idProp] = null;
2050
+ } else {
2051
+ snapshot[idProp] = rec[idProp];
2052
+ }
2053
+ }
2054
+ for (const [propName] of meta) {
2055
+ if (idProps.includes(propName)) continue;
2056
+ snapshot[propName] = normalizeValue(rec[propName]);
2057
+ }
2058
+ return snapshot;
2059
+ }
2060
+ function buildChangedEntry(item) {
2061
+ const state = getEventState(item);
2062
+ const history = getHistoryState(item);
2063
+ if (state.size === 0 && history.size === 0) return void 0;
2064
+ const proto = Object.getPrototypeOf(item);
2065
+ const idProps = getIdentityProperties(proto);
2066
+ const rec = item;
2067
+ const entry = {};
2068
+ for (const idProp of idProps) {
2069
+ entry[idProp] = rec[idProp];
2070
+ }
2071
+ for (const [propName, e] of state) {
2072
+ if (idProps.includes(propName)) continue;
2073
+ entry[propName] = normalizeValue(e.currentValue);
2074
+ }
2075
+ for (const [propName, chain] of history) {
2076
+ if (idProps.includes(propName)) continue;
2077
+ entry[propName] = [...chain];
2078
+ }
2079
+ return entry;
2080
+ }
2081
+ function computeItemDiff(item) {
2082
+ const state = getEventState(item);
2083
+ const proto = Object.getPrototypeOf(item);
2084
+ const idProps = getIdentityProperties(proto);
2085
+ const diff = {};
2086
+ for (const [propName, e] of state) {
2087
+ if (idProps.includes(propName)) continue;
2088
+ diff[propName] = normalizeValue(e.currentValue);
2089
+ }
2090
+ return diff;
2091
+ }
1540
2092
  function normalizeValue(value) {
1541
2093
  return value === void 0 ? null : value;
1542
2094
  }
1543
2095
 
1544
2096
  // src/EventTracked.ts
1545
- function EventTracked(eventType, validator, onChange, options) {
1546
- const trackedDecorator = Tracked(validator, onChange, options);
2097
+ function EventTracked(validator, onChange, options) {
2098
+ const trackedDecorator = Tracked(validator, onChange, {
2099
+ coalesceWithin: options?.coalesceWithin
2100
+ });
1547
2101
  function decorator(target, context) {
1548
2102
  const result = trackedDecorator(target, context);
1549
2103
  const propertyName = String(context.name);
1550
2104
  context.addInitializer(function() {
1551
- registerEventProperty(
1552
- Object.getPrototypeOf(this),
1553
- propertyName,
1554
- eventType
1555
- );
2105
+ registerEventProperty(Object.getPrototypeOf(this), propertyName, {
2106
+ eventType: options?.eventType,
2107
+ history: options?.history,
2108
+ coalesceWithin: options?.coalesceWithin
2109
+ });
1556
2110
  ensureEventStateSubscription(this);
1557
2111
  });
1558
2112
  return result;
1559
2113
  }
1560
2114
  return decorator;
1561
2115
  }
1562
-
1563
- // src/EventTrackedCollection.ts
1564
- var EventTrackedCollection = class extends TrackedCollection {
1565
- constructor(tracker, items, validator, eventOptions) {
1566
- super(tracker, items, validator);
1567
- this._eventOptions = eventOptions;
1568
- for (const item of this.collection) {
1569
- if (item instanceof TrackedObject) {
1570
- recordItemOrigin(item, this);
1571
- }
1572
- }
1573
- this.changed.subscribe((evt) => {
1574
- for (const item of evt.added) {
1575
- if (item instanceof TrackedObject) {
1576
- recordItemOrigin(item, this);
1577
- }
1578
- }
1579
- });
1580
- }
1581
- /** @internal */
1582
- get _lifecycleOptions() {
1583
- return this._eventOptions;
1584
- }
1585
- };
1586
2116
  // Annotate the CommonJS export names for ESM import in node:
1587
2117
  0 && (module.exports = {
1588
2118
  AutoId,
1589
2119
  EventTracked,
1590
2120
  EventTrackedCollection,
1591
2121
  EventTracker,
2122
+ Id,
1592
2123
  State,
1593
2124
  Tracked,
1594
2125
  TrackedCollection,
@@ -1596,6 +2127,9 @@ var EventTrackedCollection = class extends TrackedCollection {
1596
2127
  TrackedObject,
1597
2128
  Tracker,
1598
2129
  TrackerSession,
1599
- TypedEvent
2130
+ TypedEvent,
2131
+ getIdentity,
2132
+ getIdentityObject,
2133
+ getIdentityProperties
1600
2134
  });
1601
2135
  //# sourceMappingURL=index.cjs.map