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