@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.
@@ -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 (false) {
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 (false) {
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