@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.
package/dist/dev/index.js CHANGED
@@ -257,17 +257,73 @@ var OperationProperties = class {
257
257
 
258
258
  // src/ExternallyAssigned.ts
259
259
  var AUTO_ID = /* @__PURE__ */ Symbol("autoId");
260
+ var ID_PROPS = /* @__PURE__ */ Symbol("idProps");
260
261
  function AutoId(_target, context) {
261
262
  context.addInitializer(function() {
262
- Object.defineProperty(Object.getPrototypeOf(this), AUTO_ID, {
263
- value: String(context.name),
264
- configurable: true
265
- });
263
+ const proto = Object.getPrototypeOf(this);
264
+ if (!Object.prototype.hasOwnProperty.call(proto, AUTO_ID)) {
265
+ Object.defineProperty(proto, AUTO_ID, {
266
+ value: String(context.name),
267
+ configurable: true
268
+ });
269
+ }
270
+ addIdentityProp(proto, String(context.name));
271
+ });
272
+ }
273
+ function Id(_target, context) {
274
+ context.addInitializer(function() {
275
+ const proto = Object.getPrototypeOf(this);
276
+ addIdentityProp(proto, String(context.name));
266
277
  });
267
278
  }
279
+ function addIdentityProp(proto, propertyName) {
280
+ if (!Object.prototype.hasOwnProperty.call(proto, ID_PROPS)) {
281
+ Object.defineProperty(proto, ID_PROPS, {
282
+ value: [],
283
+ configurable: true,
284
+ writable: true
285
+ });
286
+ }
287
+ const list = proto[ID_PROPS];
288
+ if (!list.includes(propertyName)) list.push(propertyName);
289
+ }
268
290
  function getAutoIdProperty(proto) {
269
291
  return AUTO_ID in proto ? proto[AUTO_ID] : void 0;
270
292
  }
293
+ function getIdentityProperties(proto) {
294
+ const chain = [];
295
+ let current = proto;
296
+ while (current) {
297
+ chain.push(current);
298
+ current = Object.getPrototypeOf(current);
299
+ }
300
+ const merged = [];
301
+ for (let i = chain.length - 1; i >= 0; i--) {
302
+ const proto2 = chain[i];
303
+ if (!Object.prototype.hasOwnProperty.call(proto2, ID_PROPS)) continue;
304
+ const list = proto2[ID_PROPS];
305
+ for (const name of list) {
306
+ if (!merged.includes(name)) merged.push(name);
307
+ }
308
+ }
309
+ return merged;
310
+ }
311
+ function getIdentity(obj) {
312
+ const props = getIdentityProperties(Object.getPrototypeOf(obj));
313
+ if (props.length === 0) return void 0;
314
+ if (props.length === 1) {
315
+ return obj[props[0]];
316
+ }
317
+ const result = {};
318
+ for (const p of props) result[p] = obj[p];
319
+ return result;
320
+ }
321
+ function getIdentityObject(obj) {
322
+ const props = getIdentityProperties(Object.getPrototypeOf(obj));
323
+ const result = {};
324
+ for (const p of props) result[p] = obj[p];
325
+ return result;
326
+ }
271
327
 
272
328
  // src/TrackedObjectStateMachine.ts
273
329
  function applyStateTransition(obj, event, direction, context) {
@@ -1332,15 +1388,18 @@ var TrackedCollectionChanged = class {
1332
1388
  // src/EventRegistry.ts
1333
1389
  var EVENT_METADATA = /* @__PURE__ */ Symbol("eventMetadata");
1334
1390
  var instanceState = /* @__PURE__ */ new WeakMap();
1391
+ var instanceHistory = /* @__PURE__ */ new WeakMap();
1392
+ var instanceHistoryTimes = /* @__PURE__ */ new WeakMap();
1335
1393
  var subscribedInstances = /* @__PURE__ */ new WeakSet();
1336
1394
  var itemOriginCollection = /* @__PURE__ */ new WeakMap();
1395
+ var DEFAULT_GROUP = "";
1337
1396
  function hasOwnEventMetadata(proto) {
1338
1397
  return Object.prototype.hasOwnProperty.call(proto, EVENT_METADATA);
1339
1398
  }
1340
1399
  function ownEventMetadata(proto) {
1341
1400
  return hasOwnEventMetadata(proto) ? proto[EVENT_METADATA] : void 0;
1342
1401
  }
1343
- function registerEventProperty(proto, property, eventType) {
1402
+ function registerEventProperty(proto, property, options) {
1344
1403
  if (!hasOwnEventMetadata(proto)) {
1345
1404
  Object.defineProperty(proto, EVENT_METADATA, {
1346
1405
  value: /* @__PURE__ */ new Map(),
@@ -1349,7 +1408,7 @@ function registerEventProperty(proto, property, eventType) {
1349
1408
  }
1350
1409
  const map = proto[EVENT_METADATA];
1351
1410
  if (!map.has(property)) {
1352
- map.set(property, eventType);
1411
+ map.set(property, options);
1353
1412
  }
1354
1413
  }
1355
1414
  function getEventMetadata(proto) {
@@ -1363,31 +1422,97 @@ function getEventMetadata(proto) {
1363
1422
  for (let i = chain.length - 1; i >= 0; i--) {
1364
1423
  const own = ownEventMetadata(chain[i]);
1365
1424
  if (!own) continue;
1366
- own.forEach((eventType, property) => {
1367
- if (!merged.has(property)) merged.set(property, eventType);
1425
+ own.forEach((options, property) => {
1426
+ if (!merged.has(property)) merged.set(property, options);
1368
1427
  });
1369
1428
  }
1370
1429
  return merged;
1371
1430
  }
1431
+ var contextStacks = /* @__PURE__ */ new WeakMap();
1432
+ function pushContext(tracker, ctx) {
1433
+ let stack = contextStacks.get(tracker);
1434
+ if (!stack) {
1435
+ stack = [];
1436
+ contextStacks.set(tracker, stack);
1437
+ }
1438
+ stack.push(ctx);
1439
+ }
1440
+ function popContext(tracker) {
1441
+ const stack = contextStacks.get(tracker);
1442
+ if (stack && stack.length > 0) stack.pop();
1443
+ }
1444
+ function peekContext(tracker) {
1445
+ const stack = contextStacks.get(tracker);
1446
+ return stack && stack.length > 0 ? stack[stack.length - 1] : void 0;
1447
+ }
1372
1448
  function ensureEventStateSubscription(target) {
1373
1449
  if (subscribedInstances.has(target)) return;
1374
1450
  subscribedInstances.add(target);
1375
1451
  target.changed.subscribe((evt) => {
1376
1452
  const meta = getEventMetadata(Object.getPrototypeOf(target));
1377
- if (!meta.has(evt.property)) return;
1378
- const state = getOrCreateEventState(target);
1379
- let entry = state.get(evt.property);
1380
- if (!entry) {
1381
- entry = { originalValue: evt.oldValue, currentValue: evt.newValue };
1382
- state.set(evt.property, entry);
1453
+ const propMeta = meta.get(evt.property);
1454
+ if (!propMeta) return;
1455
+ if (propMeta.history) {
1456
+ appendHistoryEntry(
1457
+ target,
1458
+ evt.property,
1459
+ evt.newValue,
1460
+ evt.oldValue,
1461
+ propMeta.history,
1462
+ propMeta.coalesceWithin
1463
+ );
1383
1464
  } else {
1384
- entry.currentValue = evt.newValue;
1385
- }
1386
- if (entry.currentValue === entry.originalValue) {
1387
- state.delete(evt.property);
1465
+ updateFinalValue(target, evt.property, evt.newValue, evt.oldValue);
1388
1466
  }
1389
1467
  });
1390
1468
  }
1469
+ function updateFinalValue(target, property, newValue, oldValue) {
1470
+ const state = getOrCreateEventState(target);
1471
+ let entry = state.get(property);
1472
+ if (!entry) {
1473
+ entry = { originalValue: oldValue, currentValue: newValue };
1474
+ state.set(property, entry);
1475
+ } else {
1476
+ entry.currentValue = newValue;
1477
+ }
1478
+ if (entry.currentValue === entry.originalValue) {
1479
+ state.delete(property);
1480
+ }
1481
+ }
1482
+ function appendHistoryEntry(target, property, newValue, oldValue, historyConfig, coalesceWithin) {
1483
+ const chains = getOrCreateHistoryState(target);
1484
+ let chain = chains.get(property);
1485
+ if (!chain) {
1486
+ chain = [];
1487
+ chains.set(property, chain);
1488
+ }
1489
+ const times = getOrCreateHistoryTimes(target);
1490
+ const now = Date.now();
1491
+ const lastTime = times.get(property);
1492
+ const shouldReplace = coalesceWithin !== void 0 && lastTime !== void 0 && now - lastTime < coalesceWithin && chain.length > 0;
1493
+ let entry;
1494
+ if (typeof historyConfig === "object" && historyConfig.entryFactory) {
1495
+ const ctx = peekContext(target.tracker);
1496
+ const change = { time: /* @__PURE__ */ new Date() };
1497
+ entry = historyConfig.entryFactory(target, newValue, oldValue, change, ctx);
1498
+ } else {
1499
+ entry = { property, value: newValue };
1500
+ }
1501
+ if (shouldReplace) {
1502
+ chain[chain.length - 1] = entry;
1503
+ } else {
1504
+ chain.push(entry);
1505
+ }
1506
+ times.set(property, now);
1507
+ }
1508
+ function getOrCreateHistoryTimes(target) {
1509
+ let times = instanceHistoryTimes.get(target);
1510
+ if (!times) {
1511
+ times = /* @__PURE__ */ new Map();
1512
+ instanceHistoryTimes.set(target, times);
1513
+ }
1514
+ return times;
1515
+ }
1391
1516
  function getOrCreateEventState(target) {
1392
1517
  let state = instanceState.get(target);
1393
1518
  if (!state) {
@@ -1396,11 +1521,24 @@ function getOrCreateEventState(target) {
1396
1521
  }
1397
1522
  return state;
1398
1523
  }
1524
+ function getOrCreateHistoryState(target) {
1525
+ let state = instanceHistory.get(target);
1526
+ if (!state) {
1527
+ state = /* @__PURE__ */ new Map();
1528
+ instanceHistory.set(target, state);
1529
+ }
1530
+ return state;
1531
+ }
1399
1532
  function getEventState(target) {
1400
1533
  return instanceState.get(target) ?? /* @__PURE__ */ new Map();
1401
1534
  }
1535
+ function getHistoryState(target) {
1536
+ return instanceHistory.get(target) ?? /* @__PURE__ */ new Map();
1537
+ }
1402
1538
  function clearEventState(target) {
1403
1539
  instanceState.delete(target);
1540
+ instanceHistory.delete(target);
1541
+ instanceHistoryTimes.delete(target);
1404
1542
  }
1405
1543
  function recordItemOrigin(item, collection) {
1406
1544
  itemOriginCollection.set(item, collection);
@@ -1409,42 +1547,312 @@ function getItemOrigin(item) {
1409
1547
  return itemOriginCollection.get(item);
1410
1548
  }
1411
1549
 
1550
+ // src/EventTrackedCollection.ts
1551
+ var EventTrackedCollection = class extends TrackedCollection {
1552
+ constructor(tracker, items, validator, options) {
1553
+ super(tracker, items, validator);
1554
+ this._historyOps = [];
1555
+ this._baselineItems = /* @__PURE__ */ new Set();
1556
+ this._eventOptions = options;
1557
+ for (const item of this.collection) {
1558
+ this._baselineItems.add(item);
1559
+ if (item instanceof TrackedObject) {
1560
+ recordItemOrigin(item, this);
1561
+ this._validateItemHasIdentity(item);
1562
+ }
1563
+ }
1564
+ this.changed.subscribe((evt) => {
1565
+ for (const item of evt.added) {
1566
+ if (item instanceof TrackedObject) {
1567
+ recordItemOrigin(item, this);
1568
+ this._validateItemHasIdentity(item);
1569
+ }
1570
+ }
1571
+ if (this._isAggregateMode() && !this.tracker._isReplaying) {
1572
+ for (const item of evt.added) {
1573
+ if (item instanceof TrackedObject) {
1574
+ this._recordAddOp(item);
1575
+ }
1576
+ }
1577
+ for (const item of evt.removed) {
1578
+ if (item instanceof TrackedObject) {
1579
+ this._recordRemoveOp(item);
1580
+ }
1581
+ }
1582
+ }
1583
+ });
1584
+ if (this._isHistoryMode()) {
1585
+ this._attachItemChangeWatchers();
1586
+ }
1587
+ }
1588
+ /** @internal */
1589
+ get _lifecycleOptions() {
1590
+ if (!this._eventOptions) return void 0;
1591
+ if (this._isAggregateMode()) return void 0;
1592
+ return {
1593
+ itemAdded: this._eventOptions.itemAdded,
1594
+ itemRemoved: this._eventOptions.itemRemoved
1595
+ };
1596
+ }
1597
+ /** @internal */
1598
+ get _aggregateOptions() {
1599
+ return this._isAggregateMode() ? this._eventOptions : void 0;
1600
+ }
1601
+ /** @internal */
1602
+ get _historyOpsList() {
1603
+ return this._historyOps;
1604
+ }
1605
+ /** @internal */
1606
+ _clearHistoryOps() {
1607
+ this._historyOps.length = 0;
1608
+ this._baselineItems.clear();
1609
+ for (const item of this.collection) this._baselineItems.add(item);
1610
+ }
1611
+ /** @internal */
1612
+ _isInBaseline(item) {
1613
+ return this._baselineItems.has(item);
1614
+ }
1615
+ /** @internal */
1616
+ _baselineListSnapshot() {
1617
+ return [...this._baselineItems];
1618
+ }
1619
+ /** @internal */
1620
+ _rebaseline() {
1621
+ this._baselineItems.clear();
1622
+ for (const item of this.collection) this._baselineItems.add(item);
1623
+ }
1624
+ _isAggregateMode() {
1625
+ if (!this._eventOptions) return false;
1626
+ return this._eventOptions.eventType !== void 0 || this._eventOptions.history !== void 0;
1627
+ }
1628
+ _isHistoryMode() {
1629
+ return !!this._eventOptions?.history;
1630
+ }
1631
+ _validateItemHasIdentity(item) {
1632
+ const props = getIdentityProperties(Object.getPrototypeOf(item));
1633
+ if (props.length === 0) {
1634
+ throw new Error(
1635
+ `EventTrackedCollection item type ${item.constructor.name} must declare at least one @Id or @AutoId property when used with eventType or history mode`
1636
+ );
1637
+ }
1638
+ }
1639
+ _attachItemChangeWatchers() {
1640
+ const watchItem = (item) => {
1641
+ item.changed.subscribe((_evt) => {
1642
+ if (this.tracker._isReplaying) return;
1643
+ if (!this.collection.includes(item)) return;
1644
+ this._recordChangeOp(item);
1645
+ });
1646
+ };
1647
+ for (const item of this.collection) {
1648
+ if (item instanceof TrackedObject) watchItem(item);
1649
+ }
1650
+ this.changed.subscribe((evt) => {
1651
+ for (const item of evt.added) {
1652
+ if (item instanceof TrackedObject) watchItem(item);
1653
+ }
1654
+ });
1655
+ }
1656
+ _recordAddOp(item) {
1657
+ const opts = this._eventOptions;
1658
+ if (typeof opts?.history === "object" && opts.history.entryFactory) {
1659
+ const ctx = peekContext(this.tracker);
1660
+ const change = { time: /* @__PURE__ */ new Date() };
1661
+ this._historyOps.push({
1662
+ op: "add",
1663
+ raw: opts.history.entryFactory(item, change, ctx)
1664
+ });
1665
+ } else if (opts?.history === true) {
1666
+ this._historyOps.push({ op: "add", item, snapshot: snapshotItemAtRecordTime(item) });
1667
+ }
1668
+ }
1669
+ _recordRemoveOp(item) {
1670
+ const opts = this._eventOptions;
1671
+ const identity = getIdentity(item);
1672
+ if (typeof opts?.history === "object" && opts.history.entryFactory) {
1673
+ const ctx = peekContext(this.tracker);
1674
+ const change = { time: /* @__PURE__ */ new Date() };
1675
+ this._historyOps.push({
1676
+ op: "remove",
1677
+ raw: opts.history.entryFactory(item, change, ctx)
1678
+ });
1679
+ } else if (opts?.history === true) {
1680
+ this._historyOps.push({ op: "remove", identity });
1681
+ }
1682
+ }
1683
+ _recordChangeOpDiff(item) {
1684
+ const proto = Object.getPrototypeOf(item);
1685
+ const idProps = getIdentityProperties(proto);
1686
+ const meta = getEventMetadata(proto);
1687
+ const rec = item;
1688
+ const diff = {};
1689
+ for (const [propName] of meta) {
1690
+ if (idProps.includes(propName)) continue;
1691
+ const v = rec[propName];
1692
+ diff[propName] = v === void 0 ? null : v;
1693
+ }
1694
+ return diff;
1695
+ }
1696
+ _recordChangeOp(item) {
1697
+ const opts = this._eventOptions;
1698
+ if (typeof opts?.history === "object" && opts.history.entryFactory) {
1699
+ const ctx = peekContext(this.tracker);
1700
+ const change = { time: /* @__PURE__ */ new Date() };
1701
+ this._historyOps.push({
1702
+ op: "change",
1703
+ raw: opts.history.entryFactory(item, change, ctx)
1704
+ });
1705
+ } else if (opts?.history === true) {
1706
+ const identity = getIdentity(item);
1707
+ const diff = this._recordChangeOpDiff(item);
1708
+ this._historyOps.push({ op: "change", identity, item, diff });
1709
+ }
1710
+ }
1711
+ };
1712
+ function snapshotItemAtRecordTime(item) {
1713
+ const proto = Object.getPrototypeOf(item);
1714
+ const meta = getEventMetadata(proto);
1715
+ const idProps = getIdentityProperties(proto);
1716
+ const autoIdProp = getAutoIdProperty(proto);
1717
+ const rec = item;
1718
+ const snap = {};
1719
+ for (const idProp of idProps) {
1720
+ if (idProp === autoIdProp) snap[idProp] = null;
1721
+ else snap[idProp] = rec[idProp];
1722
+ }
1723
+ for (const [propName] of meta) {
1724
+ if (idProps.includes(propName)) continue;
1725
+ const v = rec[propName];
1726
+ snap[propName] = v === void 0 ? null : v;
1727
+ }
1728
+ return snap;
1729
+ }
1730
+
1412
1731
  // src/EventTracker.ts
1413
1732
  var EventTracker = class extends Tracker {
1733
+ withContext(ctx, action) {
1734
+ pushContext(this, ctx);
1735
+ try {
1736
+ return action();
1737
+ } finally {
1738
+ popContext(this);
1739
+ }
1740
+ }
1414
1741
  onCommit(keys) {
1415
1742
  super.onCommit(keys);
1416
1743
  for (const obj of this.trackedObjects) {
1417
1744
  clearEventState(obj);
1418
1745
  }
1746
+ for (const col of this.trackedCollections) {
1747
+ if (col instanceof EventTrackedCollection) {
1748
+ col._clearHistoryOps();
1749
+ }
1750
+ }
1419
1751
  }
1420
1752
  generateEvents() {
1421
1753
  const events = [];
1422
1754
  for (const obj of this.trackedObjects) {
1423
- const meta = getEventMetadata(Object.getPrototypeOf(obj));
1424
1755
  const origin = getItemOrigin(obj);
1425
- const lifecycle = origin?._lifecycleOptions;
1756
+ if (!origin) continue;
1757
+ const legacy = origin._lifecycleOptions;
1758
+ if (!legacy) continue;
1759
+ const meta = getEventMetadata(Object.getPrototypeOf(obj));
1426
1760
  switch (obj.state) {
1427
1761
  case "Insert" /* Insert */:
1428
- if (lifecycle?.itemAdded) {
1429
- events.push(buildItemAddedEvent(obj, meta, lifecycle.itemAdded));
1762
+ if (legacy.itemAdded) {
1763
+ events.push(buildItemAddedEvent(obj, meta, legacy.itemAdded));
1430
1764
  }
1431
1765
  break;
1432
1766
  case "Deleted" /* Deleted */:
1433
- if (lifecycle?.itemRemoved) {
1434
- events.push(buildItemRemovedEvent(obj, lifecycle.itemRemoved));
1767
+ if (legacy.itemRemoved) {
1768
+ events.push(buildItemRemovedEvent(obj, legacy.itemRemoved));
1435
1769
  }
1436
1770
  break;
1437
1771
  case "Changed" /* Changed */:
1438
1772
  pushFieldClusterEvents(obj, meta, events);
1439
1773
  break;
1440
- case "Unchanged" /* Unchanged */:
1441
- default:
1442
- break;
1443
1774
  }
1444
1775
  }
1776
+ const ownedCollections = /* @__PURE__ */ new Map();
1777
+ for (const col of this.trackedCollections) {
1778
+ if (!(col instanceof EventTrackedCollection)) continue;
1779
+ const agg = col._aggregateOptions;
1780
+ if (!agg?.owner) continue;
1781
+ const owner = agg.owner.object;
1782
+ let list = ownedCollections.get(owner);
1783
+ if (!list) {
1784
+ list = [];
1785
+ ownedCollections.set(owner, list);
1786
+ }
1787
+ list.push(col);
1788
+ }
1789
+ for (const obj of this.trackedObjects) {
1790
+ const origin = getItemOrigin(obj);
1791
+ if (origin) continue;
1792
+ const groups = /* @__PURE__ */ new Map();
1793
+ const meta = getEventMetadata(Object.getPrototypeOf(obj));
1794
+ const stateMap = getEventState(obj);
1795
+ const historyMap = getHistoryState(obj);
1796
+ for (const [propName, propMeta] of meta) {
1797
+ const group = propMeta.eventType ?? DEFAULT_GROUP;
1798
+ if (propMeta.history) {
1799
+ const chain = historyMap.get(propName);
1800
+ if (chain && chain.length > 0) {
1801
+ addToGroup(groups, group, propName, [...chain]);
1802
+ }
1803
+ } else {
1804
+ const entry = stateMap.get(propName);
1805
+ if (entry) {
1806
+ addToGroup(groups, group, propName, normalizeValue(entry.currentValue));
1807
+ }
1808
+ }
1809
+ }
1810
+ const owned = ownedCollections.get(obj) ?? [];
1811
+ for (const col of owned) {
1812
+ const slot = computeCollectionSlot(col, this);
1813
+ if (slot === void 0) continue;
1814
+ const agg = col._aggregateOptions;
1815
+ const group = agg.eventType ?? DEFAULT_GROUP;
1816
+ const propertyName = agg.owner.property;
1817
+ addToGroup(groups, group, propertyName, slot);
1818
+ }
1819
+ if (groups.size === 0) continue;
1820
+ const identity = getIdentity(obj);
1821
+ for (const [group, payload] of groups) {
1822
+ const event = {
1823
+ eventType: group,
1824
+ payload,
1825
+ trackingId: obj.trackingId
1826
+ };
1827
+ if (identity !== void 0) {
1828
+ event.targetId = identity;
1829
+ }
1830
+ events.push(event);
1831
+ }
1832
+ }
1833
+ for (const col of this.trackedCollections) {
1834
+ if (!(col instanceof EventTrackedCollection)) continue;
1835
+ const agg = col._aggregateOptions;
1836
+ if (!agg || agg.owner) continue;
1837
+ const slot = computeCollectionSlot(col, this);
1838
+ if (slot === void 0) continue;
1839
+ const eventType = agg.eventType ?? DEFAULT_GROUP;
1840
+ events.push({
1841
+ eventType,
1842
+ payload: slot
1843
+ });
1844
+ }
1445
1845
  return events;
1446
1846
  }
1447
1847
  };
1848
+ function addToGroup(groups, group, key, value) {
1849
+ let payload = groups.get(group);
1850
+ if (!payload) {
1851
+ payload = {};
1852
+ groups.set(group, payload);
1853
+ }
1854
+ payload[key] = value;
1855
+ }
1448
1856
  function buildItemAddedEvent(obj, meta, eventType) {
1449
1857
  const payload = {};
1450
1858
  for (const [propertyName] of meta) {
@@ -1473,12 +1881,13 @@ function pushFieldClusterEvents(obj, meta, events) {
1473
1881
  const state = getEventState(obj);
1474
1882
  if (state.size === 0) return;
1475
1883
  const grouped = /* @__PURE__ */ new Map();
1476
- for (const [propertyName, eventType] of meta) {
1884
+ for (const [propertyName, propMeta] of meta) {
1477
1885
  if (!state.has(propertyName)) continue;
1478
- let bucket = grouped.get(eventType);
1886
+ if (!propMeta.eventType) continue;
1887
+ let bucket = grouped.get(propMeta.eventType);
1479
1888
  if (!bucket) {
1480
1889
  bucket = [];
1481
- grouped.set(eventType, bucket);
1890
+ grouped.set(propMeta.eventType, bucket);
1482
1891
  }
1483
1892
  bucket.push(propertyName);
1484
1893
  }
@@ -1500,57 +1909,175 @@ function pushFieldClusterEvents(obj, meta, events) {
1500
1909
  events.push(event);
1501
1910
  }
1502
1911
  }
1912
+ function computeCollectionSlot(col, tracker) {
1913
+ const agg = col._aggregateOptions;
1914
+ const isHistory = agg.history !== void 0 && agg.history !== false;
1915
+ if (isHistory) return computeHistoryOps(col, agg);
1916
+ return computeBucketedSlot(col, tracker);
1917
+ }
1918
+ function computeHistoryOps(col, agg) {
1919
+ const opsList = col._historyOpsList;
1920
+ const hasFactory = typeof agg.history === "object" && agg.history.entryFactory;
1921
+ const ops = [];
1922
+ for (const op of opsList) {
1923
+ if (hasFactory) {
1924
+ ops.push(op.raw);
1925
+ continue;
1926
+ }
1927
+ if (op.op === "add") {
1928
+ ops.push({ op: "add", item: op.snapshot ?? (op.item ? snapshotItemForAdded(op.item) : {}) });
1929
+ } else if (op.op === "remove") {
1930
+ const idObj = op.identity && typeof op.identity === "object" ? op.identity : identityToSingleKeyObject(op.identity, col);
1931
+ ops.push({ op: "remove", ...idObj });
1932
+ } else if (op.op === "change" && op.item) {
1933
+ const idObj = getIdentityObject(op.item);
1934
+ const diff = op.diff ?? computeItemDiff(op.item);
1935
+ ops.push({ op: "change", ...idObj, ...diff });
1936
+ }
1937
+ }
1938
+ if (ops.length === 0) return void 0;
1939
+ return { ops };
1940
+ }
1941
+ function identityToSingleKeyObject(identity, col) {
1942
+ const baseline = col._baselineListSnapshot();
1943
+ const sample = baseline.find((i) => i instanceof TrackedObject);
1944
+ const currentSample = col.collection.find(
1945
+ (i) => i instanceof TrackedObject
1946
+ );
1947
+ const proto = sample ? Object.getPrototypeOf(sample) : currentSample ? Object.getPrototypeOf(currentSample) : void 0;
1948
+ if (!proto) return {};
1949
+ const props = getIdentityProperties(proto);
1950
+ if (props.length === 1) return { [props[0]]: identity };
1951
+ return identity && typeof identity === "object" ? identity : {};
1952
+ }
1953
+ function computeBucketedSlot(col, tracker) {
1954
+ const added = [];
1955
+ const changed = [];
1956
+ const removed = [];
1957
+ const isPrimitive = !hasTrackedObjectItems(col);
1958
+ for (const item of col.collection) {
1959
+ if (item instanceof TrackedObject) {
1960
+ if (item.state === "Insert" /* Insert */) {
1961
+ added.push(snapshotItemForAdded(item));
1962
+ } else if (item.state === "Changed" /* Changed */) {
1963
+ const entry = buildChangedEntry(item);
1964
+ if (entry) changed.push(entry);
1965
+ }
1966
+ }
1967
+ }
1968
+ if (isPrimitive) {
1969
+ for (const item of col.collection) {
1970
+ if (!col._isInBaseline(item)) added.push(item);
1971
+ }
1972
+ for (const baselineItem of col._baselineListSnapshot()) {
1973
+ if (!col.collection.includes(baselineItem)) removed.push(baselineItem);
1974
+ }
1975
+ } else {
1976
+ for (const obj of tracker.trackedObjects) {
1977
+ if (obj.state !== "Deleted" /* Deleted */) continue;
1978
+ if (getItemOrigin(obj) !== col) continue;
1979
+ const identity = getIdentity(obj);
1980
+ removed.push(identity);
1981
+ }
1982
+ }
1983
+ if (added.length === 0 && changed.length === 0 && removed.length === 0) {
1984
+ return void 0;
1985
+ }
1986
+ const slot = { added, removed };
1987
+ if (!isPrimitive) slot.changed = changed;
1988
+ return slot;
1989
+ }
1990
+ function hasTrackedObjectItems(col) {
1991
+ for (const item of col.collection) {
1992
+ if (item instanceof TrackedObject) return true;
1993
+ }
1994
+ for (const item of col._baselineListSnapshot()) {
1995
+ if (item instanceof TrackedObject) return true;
1996
+ }
1997
+ return false;
1998
+ }
1999
+ function snapshotItemForAdded(item) {
2000
+ const proto = Object.getPrototypeOf(item);
2001
+ const meta = getEventMetadata(proto);
2002
+ const idProps = getIdentityProperties(proto);
2003
+ const autoIdProp = getAutoIdProperty(proto);
2004
+ const snapshot = {};
2005
+ const rec = item;
2006
+ for (const idProp of idProps) {
2007
+ if (idProp === autoIdProp) {
2008
+ snapshot[idProp] = null;
2009
+ } else {
2010
+ snapshot[idProp] = rec[idProp];
2011
+ }
2012
+ }
2013
+ for (const [propName] of meta) {
2014
+ if (idProps.includes(propName)) continue;
2015
+ snapshot[propName] = normalizeValue(rec[propName]);
2016
+ }
2017
+ return snapshot;
2018
+ }
2019
+ function buildChangedEntry(item) {
2020
+ const state = getEventState(item);
2021
+ const history = getHistoryState(item);
2022
+ if (state.size === 0 && history.size === 0) return void 0;
2023
+ const proto = Object.getPrototypeOf(item);
2024
+ const idProps = getIdentityProperties(proto);
2025
+ const rec = item;
2026
+ const entry = {};
2027
+ for (const idProp of idProps) {
2028
+ entry[idProp] = rec[idProp];
2029
+ }
2030
+ for (const [propName, e] of state) {
2031
+ if (idProps.includes(propName)) continue;
2032
+ entry[propName] = normalizeValue(e.currentValue);
2033
+ }
2034
+ for (const [propName, chain] of history) {
2035
+ if (idProps.includes(propName)) continue;
2036
+ entry[propName] = [...chain];
2037
+ }
2038
+ return entry;
2039
+ }
2040
+ function computeItemDiff(item) {
2041
+ const state = getEventState(item);
2042
+ const proto = Object.getPrototypeOf(item);
2043
+ const idProps = getIdentityProperties(proto);
2044
+ const diff = {};
2045
+ for (const [propName, e] of state) {
2046
+ if (idProps.includes(propName)) continue;
2047
+ diff[propName] = normalizeValue(e.currentValue);
2048
+ }
2049
+ return diff;
2050
+ }
1503
2051
  function normalizeValue(value) {
1504
2052
  return value === void 0 ? null : value;
1505
2053
  }
1506
2054
 
1507
2055
  // src/EventTracked.ts
1508
- function EventTracked(eventType, validator, onChange, options) {
1509
- const trackedDecorator = Tracked(validator, onChange, options);
2056
+ function EventTracked(validator, onChange, options) {
2057
+ const trackedDecorator = Tracked(validator, onChange, {
2058
+ coalesceWithin: options?.coalesceWithin
2059
+ });
1510
2060
  function decorator(target, context) {
1511
2061
  const result = trackedDecorator(target, context);
1512
2062
  const propertyName = String(context.name);
1513
2063
  context.addInitializer(function() {
1514
- registerEventProperty(
1515
- Object.getPrototypeOf(this),
1516
- propertyName,
1517
- eventType
1518
- );
2064
+ registerEventProperty(Object.getPrototypeOf(this), propertyName, {
2065
+ eventType: options?.eventType,
2066
+ history: options?.history,
2067
+ coalesceWithin: options?.coalesceWithin
2068
+ });
1519
2069
  ensureEventStateSubscription(this);
1520
2070
  });
1521
2071
  return result;
1522
2072
  }
1523
2073
  return decorator;
1524
2074
  }
1525
-
1526
- // src/EventTrackedCollection.ts
1527
- var EventTrackedCollection = class extends TrackedCollection {
1528
- constructor(tracker, items, validator, eventOptions) {
1529
- super(tracker, items, validator);
1530
- this._eventOptions = eventOptions;
1531
- for (const item of this.collection) {
1532
- if (item instanceof TrackedObject) {
1533
- recordItemOrigin(item, this);
1534
- }
1535
- }
1536
- this.changed.subscribe((evt) => {
1537
- for (const item of evt.added) {
1538
- if (item instanceof TrackedObject) {
1539
- recordItemOrigin(item, this);
1540
- }
1541
- }
1542
- });
1543
- }
1544
- /** @internal */
1545
- get _lifecycleOptions() {
1546
- return this._eventOptions;
1547
- }
1548
- };
1549
2075
  export {
1550
2076
  AutoId,
1551
2077
  EventTracked,
1552
2078
  EventTrackedCollection,
1553
2079
  EventTracker,
2080
+ Id,
1554
2081
  State,
1555
2082
  Tracked,
1556
2083
  TrackedCollection,
@@ -1558,6 +2085,9 @@ export {
1558
2085
  TrackedObject,
1559
2086
  Tracker,
1560
2087
  TrackerSession,
1561
- TypedEvent
2088
+ TypedEvent,
2089
+ getIdentity,
2090
+ getIdentityObject,
2091
+ getIdentityProperties
1562
2092
  };
1563
2093
  //# sourceMappingURL=index.js.map