@katn30/trakr 1.1.1 → 2.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/README.md CHANGED
@@ -263,51 +263,73 @@ tracker.undo(); // reverts all four at once → status = ''
263
263
 
264
264
  Properties without `coalesceWithin` — and all `Date`, `boolean`, and `object` properties — are never coalesced; every write produces its own undo step.
265
265
 
266
- ### Manual composing
266
+ ### Sessions
267
267
 
268
- When you need to group any set of writes across multiple properties, objects, or collections — into a single undo step, use the manual composing API:
268
+ `startSession()` returns a `TrackerSession` that groups all writes made during the session into a single undo step. Call `session.end()` to commit or `session.rollback()` to revert:
269
269
 
270
270
  ```typescript
271
- tracker.startComposing();
271
+ const session = tracker.startSession();
272
272
 
273
273
  model.firstName = 'Alice';
274
274
  model.lastName = 'Smith';
275
275
  model.email = 'alice@example.com';
276
276
 
277
- tracker.endComposing(); // all three writes become one undo step
277
+ session.end(); // all three writes become one undo step
278
278
 
279
279
  tracker.undo(); // reverts firstName, lastName, and email together
280
280
  ```
281
281
 
282
- Call `rollbackComposing()` instead of `endComposing()` to revert all changes made since `startComposing()`:
283
-
284
282
  ```typescript
285
- tracker.startComposing();
283
+ const session = tracker.startSession();
286
284
 
287
285
  model.firstName = 'Alice';
288
286
  model.lastName = 'Smith';
289
287
 
290
- tracker.rollbackComposing(); // all writes since startComposing are reverted
288
+ session.rollback(); // all writes since startSession are reverted
291
289
  ```
292
290
 
293
- A second call to `startComposing()` while a session is already active is a no-op — nesting is not supported.
291
+ A second call to `startSession()` while a session is already active is a no-op — nesting is not supported.
294
292
 
295
- **Typical use case edit modal**
293
+ **Edit modal with save button**
296
294
 
297
- Open a modal that edits a slice of the model. If the user confirms, the entire set of edits lands in the undo history as one step. If the user cancels, all edits are rolled back invisibly.
295
+ The canonical use case is a modal that edits a slice of the model. Pass a **property scope** a list of `[object, propertyNames]` tuples — and the session exposes `isDirty` and `isValid` bounded to those properties, so a save button can be driven correctly regardless of the state of the rest of the application.
298
296
 
299
297
  ```typescript
298
+ import { PropertyScope } from 'trakr';
299
+
300
300
  function openEditModal(model: PersonModel) {
301
- tracker.startComposing();
301
+ const session = tracker.startSession([
302
+ [model, ['firstName', 'lastName', 'email']],
303
+ ]);
302
304
 
303
305
  showModal({
304
306
  model,
305
- onConfirm: () => tracker.endComposing(),
306
- onCancel: () => tracker.rollbackComposing(),
307
+ onConfirm: () => session.end(),
308
+ onCancel: () => session.rollback(),
309
+ canSave: () => session.isDirty === true && session.isValid === true,
307
310
  });
308
311
  }
309
312
  ```
310
313
 
314
+ **`isDirty`** is `false` when the session starts (even if other objects are already dirty elsewhere), and becomes `true` the moment the user writes to any property listed in the scope.
315
+
316
+ **`isValid`** checks `validationMessages` for every declared property. If any has a validation error — including one that existed *before* the session started — `isValid` is `false`, keeping the save button disabled until the user resolves it.
317
+
318
+ Both return `undefined` when `startSession()` is called without a scope argument.
319
+
320
+ **Multiple objects in scope**
321
+
322
+ Pass one tuple per object:
323
+
324
+ ```typescript
325
+ const session = tracker.startSession([
326
+ [person, ['firstName', 'email']],
327
+ [address, ['street', 'city']],
328
+ ]);
329
+ ```
330
+
331
+ Properties not listed — and all other tracked objects — are ignored by `isDirty` and `isValid`. The scope has no effect on what gets committed or rolled back: `session.end()` always merges everything written since `startSession()` into one undo step, and `session.rollback()` always reverts it all.
332
+
311
333
  ### Dependency tracking
312
334
 
313
335
  Validators can read other properties of the same model — for example, a `scheduleDays` field might be required only when `isEnabled` is `true`. trakr automatically tracks which properties each validator reads, and re-runs only the affected validators when those properties change.
@@ -588,12 +610,13 @@ tracker.onCommit(keys); // same, plus write real server IDs to @AutoId fie
588
610
  2. Transitions every tracked object's `state` to `Unchanged` and resets `dirtyCounter`.
589
611
  3. Appends the state change into the existing last undo operation — so undo atomically reverts both the user's edits and the committed state together (no spurious extra undo steps).
590
612
 
591
- **Manual composing**
613
+ **Sessions**
592
614
 
593
615
  ```typescript
594
- tracker.startComposing(); // begin grouping subsequent changes
595
- tracker.endComposing(); // commit all changes become one undo step
596
- tracker.rollbackComposing(); // revert — all changes since startComposing are rolled back
616
+ const session = tracker.startSession(); // begin a session
617
+ const session = tracker.startSession([…]); // same, with a property scope
618
+ session.end(); // commit — all changes become one undo step
619
+ session.rollback(); // revert — all changes since startSession
597
620
  ```
598
621
 
599
622
  **Object construction**
@@ -27,6 +27,7 @@ __export(index_exports, {
27
27
  TrackedCollectionChanged: () => TrackedCollectionChanged,
28
28
  TrackedObject: () => TrackedObject,
29
29
  Tracker: () => Tracker,
30
+ TrackerSession: () => TrackerSession,
30
31
  TypedEvent: () => TypedEvent
31
32
  });
32
33
  module.exports = __toCommonJS(index_exports);
@@ -249,6 +250,242 @@ function validateSingleProperty(tracked, property) {
249
250
  return error;
250
251
  }
251
252
 
253
+ // src/OperationProperties.ts
254
+ var OperationProperties = class {
255
+ constructor(trackedObject, property, type, validator, coalesceWithin) {
256
+ this.trackedObject = trackedObject;
257
+ this.property = property;
258
+ this.type = type;
259
+ this.validator = validator;
260
+ this.coalesceWithin = coalesceWithin;
261
+ }
262
+ };
263
+
264
+ // src/ExternallyAssigned.ts
265
+ var AUTO_ID = /* @__PURE__ */ Symbol("autoId");
266
+ function AutoId(_target, context) {
267
+ context.addInitializer(function() {
268
+ Object.defineProperty(Object.getPrototypeOf(this), AUTO_ID, {
269
+ value: String(context.name),
270
+ configurable: true
271
+ });
272
+ });
273
+ }
274
+ function getAutoIdProperty(proto) {
275
+ return AUTO_ID in proto ? proto[AUTO_ID] : void 0;
276
+ }
277
+
278
+ // src/TrackedObjectStateMachine.ts
279
+ function applyStateTransition(obj, event, direction, context) {
280
+ if (event === "added") {
281
+ applyAdded(obj, direction);
282
+ } else if (event === "removed") {
283
+ applyRemoved(obj, direction, context);
284
+ } else {
285
+ applyCommitted(obj, direction, context);
286
+ }
287
+ }
288
+ function applyAdded(obj, direction) {
289
+ if (direction === "undo") {
290
+ obj._setState("Unchanged" /* Unchanged */);
291
+ } else {
292
+ obj._setState("Insert" /* Insert */);
293
+ }
294
+ }
295
+ function applyRemoved(obj, direction, context) {
296
+ if (direction === "undo") {
297
+ const prev = context?.prevState ?? "Unchanged" /* Unchanged */;
298
+ obj._setState(prev);
299
+ if (prev === "Insert" /* Insert */) {
300
+ if (context?.prevDirtyCounter !== void 0) {
301
+ obj._setDirtyCounter(context.prevDirtyCounter);
302
+ }
303
+ }
304
+ } else {
305
+ if (obj.state === "Insert" /* Insert */) {
306
+ obj._setState("Unchanged" /* Unchanged */);
307
+ obj._setDirtyCounter(0);
308
+ } else {
309
+ obj._setState("Deleted" /* Deleted */);
310
+ }
311
+ }
312
+ }
313
+ function applyCommitted(obj, direction, context) {
314
+ if (direction === "undo") {
315
+ const prev = context?.prevState ?? "Unchanged" /* Unchanged */;
316
+ if (prev === "Insert" /* Insert */) {
317
+ obj._setState("Deleted" /* Deleted */);
318
+ } else if (prev === "Deleted" /* Deleted */) {
319
+ obj._setState("Insert" /* Insert */);
320
+ } else if (prev === "Changed" /* Changed */) {
321
+ obj._setState("Changed" /* Changed */);
322
+ }
323
+ } else {
324
+ if ((context?.prevState === "Insert" /* Insert */ || context?.prevState === "Changed" /* Changed */) && context.autoIdProp && context.realId !== void 0) {
325
+ obj[context.autoIdProp] = context.realId;
326
+ }
327
+ obj._setState("Unchanged" /* Unchanged */);
328
+ obj._setDirtyCounter(0);
329
+ }
330
+ }
331
+ function buildCommittedContext(obj, autoIdProp, keys) {
332
+ const prevState = obj.state;
333
+ let realId;
334
+ if ((prevState === "Insert" /* Insert */ || prevState === "Changed" /* Changed */) && keys) {
335
+ realId = keys.find((k) => k.trackingId === obj.trackingId)?.value;
336
+ }
337
+ return { prevState, autoIdProp, realId };
338
+ }
339
+
340
+ // src/TrackedObject.ts
341
+ var TrackedObject = class {
342
+ constructor(tracker) {
343
+ this.tracker = tracker;
344
+ this._dirtyCounter = 0;
345
+ this._isValid = true;
346
+ this._state = "Unchanged" /* Unchanged */;
347
+ this.changed = new TypedEvent();
348
+ this.trackedChanged = new TypedEvent();
349
+ if (!tracker._isConstructing) {
350
+ throw new Error(`${this.constructor.name} must be created inside tracker.construct()`);
351
+ }
352
+ this.trackingId = tracker._nextTrackingId();
353
+ this.validationMessages = /* @__PURE__ */ new Map();
354
+ tracker._trackObject(this);
355
+ }
356
+ // ---- StateTarget interface (internal) ----
357
+ /** @internal */
358
+ get state() {
359
+ return this._state;
360
+ }
361
+ /** @internal */
362
+ _setState(value) {
363
+ this._state = value;
364
+ }
365
+ /** @internal */
366
+ _getDirtyCounter() {
367
+ return this._dirtyCounter;
368
+ }
369
+ /** @internal */
370
+ _setDirtyCounter(value) {
371
+ this._dirtyCounter = value;
372
+ }
373
+ // ---- Public API ----
374
+ get validationMessages() {
375
+ return this._validationMessages ?? /* @__PURE__ */ new Map();
376
+ }
377
+ set validationMessages(value) {
378
+ this._validationMessages = value;
379
+ }
380
+ get isValid() {
381
+ return this._isValid;
382
+ }
383
+ set isValid(value) {
384
+ const wasValid = this._isValid;
385
+ this._isValid = value;
386
+ if (wasValid !== value) {
387
+ this.tracker._onValidityChanged(wasValid, value);
388
+ }
389
+ }
390
+ get isDirty() {
391
+ return this._dirtyCounter !== 0;
392
+ }
393
+ get dirtyCounter() {
394
+ return this._dirtyCounter;
395
+ }
396
+ set dirtyCounter(value) {
397
+ this._dirtyCounter = value;
398
+ }
399
+ /** @internal */
400
+ _onCommitted(lastOp, keys) {
401
+ const autoIdProp = getAutoIdProperty(Object.getPrototypeOf(this));
402
+ const context = buildCommittedContext(this, autoIdProp, keys);
403
+ const redoFn = () => applyStateTransition(this, "committed", "do", context);
404
+ const undoFn = () => applyStateTransition(this, "committed", "undo", context);
405
+ if (lastOp) {
406
+ lastOp.updateOrAdd(redoFn, undoFn, new OperationProperties(this, "__state__", 4 /* Object */));
407
+ }
408
+ redoFn();
409
+ }
410
+ /** @internal */
411
+ _markRemoved() {
412
+ const prevState = this._state;
413
+ const prevDirtyCounter = this._dirtyCounter;
414
+ this.tracker._doAndTrack(
415
+ () => applyStateTransition(this, "removed", "do"),
416
+ () => applyStateTransition(this, "removed", "undo", { prevState, prevDirtyCounter }),
417
+ new OperationProperties(this, "__state__", 4 /* Object */)
418
+ );
419
+ }
420
+ /** @internal */
421
+ _markAdded() {
422
+ if (this._state !== "Unchanged" /* Unchanged */) return;
423
+ if (this.tracker._isTrackingSuppressed) return;
424
+ this.tracker._doAndTrack(
425
+ () => applyStateTransition(this, "added", "do"),
426
+ () => applyStateTransition(this, "added", "undo"),
427
+ new OperationProperties(this, "__state__", 4 /* Object */)
428
+ );
429
+ }
430
+ /** @internal */
431
+ _validate(property, errorMessage) {
432
+ if (errorMessage) {
433
+ this.validationMessages.set(property, errorMessage);
434
+ } else {
435
+ this.validationMessages.delete(property);
436
+ }
437
+ this.validationMessages = new Map(this.validationMessages);
438
+ this.isValid = this.validationMessages.size === 0;
439
+ }
440
+ /** @internal */
441
+ _applyValidation(messages) {
442
+ this.validationMessages = messages;
443
+ this.isValid = messages.size === 0;
444
+ }
445
+ destroy() {
446
+ DependencyTracker.clearDeps(this);
447
+ this.tracker._untrackObject(this);
448
+ }
449
+ };
450
+
451
+ // src/TrackerSession.ts
452
+ var TrackerSession = class {
453
+ constructor(scope, _end, _rollback) {
454
+ this._end = _end;
455
+ this._rollback = _rollback;
456
+ this._isDirty = false;
457
+ if (scope && scope.length > 0) {
458
+ this._scope = new Map(scope.map(([obj, props]) => [obj, new Set(props)]));
459
+ }
460
+ }
461
+ /** @internal */
462
+ _onWrite(obj, property) {
463
+ if (this._scope === void 0) return;
464
+ const declaredProps = this._scope.get(obj);
465
+ if (declaredProps === void 0 || !declaredProps.has(property)) return;
466
+ this._isDirty = true;
467
+ }
468
+ get isDirty() {
469
+ if (this._scope === void 0) return void 0;
470
+ return this._isDirty;
471
+ }
472
+ get isValid() {
473
+ if (this._scope === void 0) return void 0;
474
+ for (const [obj, props] of this._scope) {
475
+ for (const prop of props) {
476
+ if (obj.validationMessages.has(prop)) return false;
477
+ }
478
+ }
479
+ return true;
480
+ }
481
+ end() {
482
+ this._end();
483
+ }
484
+ rollback() {
485
+ this._rollback();
486
+ }
487
+ };
488
+
252
489
  // src/Tracker.ts
253
490
  var Tracker = class {
254
491
  constructor() {
@@ -422,6 +659,9 @@ var Tracker = class {
422
659
  properties
423
660
  );
424
661
  redoAction();
662
+ if (this._currentSession !== void 0 && properties.property !== void 0 && properties.trackedObject instanceof TrackedObject) {
663
+ this._currentSession._onWrite(properties.trackedObject, properties.property);
664
+ }
425
665
  if (this.isEndingCurrentOperation(properties)) {
426
666
  this._currentOperation = void 0;
427
667
  this._currentOperationOwner = void 0;
@@ -484,13 +724,20 @@ var Tracker = class {
484
724
  this.canRedo = this._redoOperations.length > 0;
485
725
  this.isDirty = CollectionUtilities.getLast(this._undoOperations) !== this._commitStateOperation;
486
726
  }
487
- startComposing() {
488
- if (this._composingBaseIndex !== void 0) return;
727
+ startSession(scope) {
728
+ if (this._currentSession !== void 0) return this._currentSession;
489
729
  this._composingBaseIndex = this._undoOperations.length;
490
730
  this._composingRedoLength = this._redoOperations.length;
731
+ this._currentSession = new TrackerSession(
732
+ scope,
733
+ () => this.endComposing(),
734
+ () => this.rollbackComposing()
735
+ );
736
+ return this._currentSession;
491
737
  }
492
738
  endComposing() {
493
739
  if (this._composingBaseIndex === void 0) return;
740
+ this._currentSession = void 0;
494
741
  const composed = this._undoOperations.splice(this._composingBaseIndex);
495
742
  this._redoOperations.splice(this._composingRedoLength);
496
743
  this._composingBaseIndex = void 0;
@@ -515,6 +762,7 @@ var Tracker = class {
515
762
  }
516
763
  rollbackComposing() {
517
764
  if (this._composingBaseIndex === void 0) return;
765
+ this._currentSession = void 0;
518
766
  const toRevert = this._undoOperations.splice(this._composingBaseIndex);
519
767
  this._redoOperations.splice(this._composingRedoLength);
520
768
  this._composingBaseIndex = void 0;
@@ -566,204 +814,6 @@ var Tracker = class {
566
814
  }
567
815
  };
568
816
 
569
- // src/OperationProperties.ts
570
- var OperationProperties = class {
571
- constructor(trackedObject, property, type, validator, coalesceWithin) {
572
- this.trackedObject = trackedObject;
573
- this.property = property;
574
- this.type = type;
575
- this.validator = validator;
576
- this.coalesceWithin = coalesceWithin;
577
- }
578
- };
579
-
580
- // src/ExternallyAssigned.ts
581
- var AUTO_ID = /* @__PURE__ */ Symbol("autoId");
582
- function AutoId(_target, context) {
583
- context.addInitializer(function() {
584
- Object.defineProperty(Object.getPrototypeOf(this), AUTO_ID, {
585
- value: String(context.name),
586
- configurable: true
587
- });
588
- });
589
- }
590
- function getAutoIdProperty(proto) {
591
- return AUTO_ID in proto ? proto[AUTO_ID] : void 0;
592
- }
593
-
594
- // src/TrackedObjectStateMachine.ts
595
- function applyStateTransition(obj, event, direction, context) {
596
- if (event === "added") {
597
- applyAdded(obj, direction);
598
- } else if (event === "removed") {
599
- applyRemoved(obj, direction, context);
600
- } else {
601
- applyCommitted(obj, direction, context);
602
- }
603
- }
604
- function applyAdded(obj, direction) {
605
- if (direction === "undo") {
606
- obj._setState("Unchanged" /* Unchanged */);
607
- } else {
608
- obj._setState("Insert" /* Insert */);
609
- }
610
- }
611
- function applyRemoved(obj, direction, context) {
612
- if (direction === "undo") {
613
- const prev = context?.prevState ?? "Unchanged" /* Unchanged */;
614
- obj._setState(prev);
615
- if (prev === "Insert" /* Insert */) {
616
- if (context?.prevDirtyCounter !== void 0) {
617
- obj._setDirtyCounter(context.prevDirtyCounter);
618
- }
619
- }
620
- } else {
621
- if (obj.state === "Insert" /* Insert */) {
622
- obj._setState("Unchanged" /* Unchanged */);
623
- obj._setDirtyCounter(0);
624
- } else {
625
- obj._setState("Deleted" /* Deleted */);
626
- }
627
- }
628
- }
629
- function applyCommitted(obj, direction, context) {
630
- if (direction === "undo") {
631
- const prev = context?.prevState ?? "Unchanged" /* Unchanged */;
632
- if (prev === "Insert" /* Insert */) {
633
- obj._setState("Deleted" /* Deleted */);
634
- } else if (prev === "Deleted" /* Deleted */) {
635
- obj._setState("Insert" /* Insert */);
636
- } else if (prev === "Changed" /* Changed */) {
637
- obj._setState("Changed" /* Changed */);
638
- }
639
- } else {
640
- if ((context?.prevState === "Insert" /* Insert */ || context?.prevState === "Changed" /* Changed */) && context.autoIdProp && context.realId !== void 0) {
641
- obj[context.autoIdProp] = context.realId;
642
- }
643
- obj._setState("Unchanged" /* Unchanged */);
644
- obj._setDirtyCounter(0);
645
- }
646
- }
647
- function buildCommittedContext(obj, autoIdProp, keys) {
648
- const prevState = obj.state;
649
- let realId;
650
- if ((prevState === "Insert" /* Insert */ || prevState === "Changed" /* Changed */) && keys) {
651
- realId = keys.find((k) => k.trackingId === obj.trackingId)?.value;
652
- }
653
- return { prevState, autoIdProp, realId };
654
- }
655
-
656
- // src/TrackedObject.ts
657
- var TrackedObject = class {
658
- constructor(tracker) {
659
- this.tracker = tracker;
660
- this._dirtyCounter = 0;
661
- this._isValid = true;
662
- this._state = "Unchanged" /* Unchanged */;
663
- this.changed = new TypedEvent();
664
- this.trackedChanged = new TypedEvent();
665
- if (!tracker._isConstructing) {
666
- throw new Error(`${this.constructor.name} must be created inside tracker.construct()`);
667
- }
668
- this.trackingId = tracker._nextTrackingId();
669
- this.validationMessages = /* @__PURE__ */ new Map();
670
- tracker._trackObject(this);
671
- }
672
- // ---- StateTarget interface (internal) ----
673
- /** @internal */
674
- get state() {
675
- return this._state;
676
- }
677
- /** @internal */
678
- _setState(value) {
679
- this._state = value;
680
- }
681
- /** @internal */
682
- _getDirtyCounter() {
683
- return this._dirtyCounter;
684
- }
685
- /** @internal */
686
- _setDirtyCounter(value) {
687
- this._dirtyCounter = value;
688
- }
689
- // ---- Public API ----
690
- get validationMessages() {
691
- return this._validationMessages ?? /* @__PURE__ */ new Map();
692
- }
693
- set validationMessages(value) {
694
- this._validationMessages = value;
695
- }
696
- get isValid() {
697
- return this._isValid;
698
- }
699
- set isValid(value) {
700
- const wasValid = this._isValid;
701
- this._isValid = value;
702
- if (wasValid !== value) {
703
- this.tracker._onValidityChanged(wasValid, value);
704
- }
705
- }
706
- get isDirty() {
707
- return this._dirtyCounter !== 0;
708
- }
709
- get dirtyCounter() {
710
- return this._dirtyCounter;
711
- }
712
- set dirtyCounter(value) {
713
- this._dirtyCounter = value;
714
- }
715
- /** @internal */
716
- _onCommitted(lastOp, keys) {
717
- const autoIdProp = getAutoIdProperty(Object.getPrototypeOf(this));
718
- const context = buildCommittedContext(this, autoIdProp, keys);
719
- const redoFn = () => applyStateTransition(this, "committed", "do", context);
720
- const undoFn = () => applyStateTransition(this, "committed", "undo", context);
721
- if (lastOp) {
722
- lastOp.updateOrAdd(redoFn, undoFn, new OperationProperties(this, "__state__", 4 /* Object */));
723
- }
724
- redoFn();
725
- }
726
- /** @internal */
727
- _markRemoved() {
728
- const prevState = this._state;
729
- const prevDirtyCounter = this._dirtyCounter;
730
- this.tracker._doAndTrack(
731
- () => applyStateTransition(this, "removed", "do"),
732
- () => applyStateTransition(this, "removed", "undo", { prevState, prevDirtyCounter }),
733
- new OperationProperties(this, "__state__", 4 /* Object */)
734
- );
735
- }
736
- /** @internal */
737
- _markAdded() {
738
- if (this._state !== "Unchanged" /* Unchanged */) return;
739
- if (this.tracker._isTrackingSuppressed) return;
740
- this.tracker._doAndTrack(
741
- () => applyStateTransition(this, "added", "do"),
742
- () => applyStateTransition(this, "added", "undo"),
743
- new OperationProperties(this, "__state__", 4 /* Object */)
744
- );
745
- }
746
- /** @internal */
747
- _validate(property, errorMessage) {
748
- if (errorMessage) {
749
- this.validationMessages.set(property, errorMessage);
750
- } else {
751
- this.validationMessages.delete(property);
752
- }
753
- this.validationMessages = new Map(this.validationMessages);
754
- this.isValid = this.validationMessages.size === 0;
755
- }
756
- /** @internal */
757
- _applyValidation(messages) {
758
- this.validationMessages = messages;
759
- this.isValid = messages.size === 0;
760
- }
761
- destroy() {
762
- DependencyTracker.clearDeps(this);
763
- this.tracker._untrackObject(this);
764
- }
765
- };
766
-
767
817
  // src/Tracked.ts
768
818
  function Tracked(validator, onChange, options) {
769
819
  function decorator(target, context) {
@@ -1260,6 +1310,7 @@ var TrackedCollectionChanged = class {
1260
1310
  TrackedCollectionChanged,
1261
1311
  TrackedObject,
1262
1312
  Tracker,
1313
+ TrackerSession,
1263
1314
  TypedEvent
1264
1315
  });
1265
1316
  //# sourceMappingURL=index.cjs.map