@katn30/trakr 1.1.0 → 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/dist/dev/index.js CHANGED
@@ -216,6 +216,242 @@ function validateSingleProperty(tracked, property) {
216
216
  return error;
217
217
  }
218
218
 
219
+ // src/OperationProperties.ts
220
+ var OperationProperties = class {
221
+ constructor(trackedObject, property, type, validator, coalesceWithin) {
222
+ this.trackedObject = trackedObject;
223
+ this.property = property;
224
+ this.type = type;
225
+ this.validator = validator;
226
+ this.coalesceWithin = coalesceWithin;
227
+ }
228
+ };
229
+
230
+ // src/ExternallyAssigned.ts
231
+ var AUTO_ID = /* @__PURE__ */ Symbol("autoId");
232
+ function AutoId(_target, context) {
233
+ context.addInitializer(function() {
234
+ Object.defineProperty(Object.getPrototypeOf(this), AUTO_ID, {
235
+ value: String(context.name),
236
+ configurable: true
237
+ });
238
+ });
239
+ }
240
+ function getAutoIdProperty(proto) {
241
+ return AUTO_ID in proto ? proto[AUTO_ID] : void 0;
242
+ }
243
+
244
+ // src/TrackedObjectStateMachine.ts
245
+ function applyStateTransition(obj, event, direction, context) {
246
+ if (event === "added") {
247
+ applyAdded(obj, direction);
248
+ } else if (event === "removed") {
249
+ applyRemoved(obj, direction, context);
250
+ } else {
251
+ applyCommitted(obj, direction, context);
252
+ }
253
+ }
254
+ function applyAdded(obj, direction) {
255
+ if (direction === "undo") {
256
+ obj._setState("Unchanged" /* Unchanged */);
257
+ } else {
258
+ obj._setState("Insert" /* Insert */);
259
+ }
260
+ }
261
+ function applyRemoved(obj, direction, context) {
262
+ if (direction === "undo") {
263
+ const prev = context?.prevState ?? "Unchanged" /* Unchanged */;
264
+ obj._setState(prev);
265
+ if (prev === "Insert" /* Insert */) {
266
+ if (context?.prevDirtyCounter !== void 0) {
267
+ obj._setDirtyCounter(context.prevDirtyCounter);
268
+ }
269
+ }
270
+ } else {
271
+ if (obj.state === "Insert" /* Insert */) {
272
+ obj._setState("Unchanged" /* Unchanged */);
273
+ obj._setDirtyCounter(0);
274
+ } else {
275
+ obj._setState("Deleted" /* Deleted */);
276
+ }
277
+ }
278
+ }
279
+ function applyCommitted(obj, direction, context) {
280
+ if (direction === "undo") {
281
+ const prev = context?.prevState ?? "Unchanged" /* Unchanged */;
282
+ if (prev === "Insert" /* Insert */) {
283
+ obj._setState("Deleted" /* Deleted */);
284
+ } else if (prev === "Deleted" /* Deleted */) {
285
+ obj._setState("Insert" /* Insert */);
286
+ } else if (prev === "Changed" /* Changed */) {
287
+ obj._setState("Changed" /* Changed */);
288
+ }
289
+ } else {
290
+ if ((context?.prevState === "Insert" /* Insert */ || context?.prevState === "Changed" /* Changed */) && context.autoIdProp && context.realId !== void 0) {
291
+ obj[context.autoIdProp] = context.realId;
292
+ }
293
+ obj._setState("Unchanged" /* Unchanged */);
294
+ obj._setDirtyCounter(0);
295
+ }
296
+ }
297
+ function buildCommittedContext(obj, autoIdProp, keys) {
298
+ const prevState = obj.state;
299
+ let realId;
300
+ if ((prevState === "Insert" /* Insert */ || prevState === "Changed" /* Changed */) && keys) {
301
+ realId = keys.find((k) => k.trackingId === obj.trackingId)?.value;
302
+ }
303
+ return { prevState, autoIdProp, realId };
304
+ }
305
+
306
+ // src/TrackedObject.ts
307
+ var TrackedObject = class {
308
+ constructor(tracker) {
309
+ this.tracker = tracker;
310
+ this._dirtyCounter = 0;
311
+ this._isValid = true;
312
+ this._state = "Unchanged" /* Unchanged */;
313
+ this.changed = new TypedEvent();
314
+ this.trackedChanged = new TypedEvent();
315
+ if (!tracker._isConstructing) {
316
+ throw new Error(`${this.constructor.name} must be created inside tracker.construct()`);
317
+ }
318
+ this.trackingId = tracker._nextTrackingId();
319
+ this.validationMessages = /* @__PURE__ */ new Map();
320
+ tracker._trackObject(this);
321
+ }
322
+ // ---- StateTarget interface (internal) ----
323
+ /** @internal */
324
+ get state() {
325
+ return this._state;
326
+ }
327
+ /** @internal */
328
+ _setState(value) {
329
+ this._state = value;
330
+ }
331
+ /** @internal */
332
+ _getDirtyCounter() {
333
+ return this._dirtyCounter;
334
+ }
335
+ /** @internal */
336
+ _setDirtyCounter(value) {
337
+ this._dirtyCounter = value;
338
+ }
339
+ // ---- Public API ----
340
+ get validationMessages() {
341
+ return this._validationMessages ?? /* @__PURE__ */ new Map();
342
+ }
343
+ set validationMessages(value) {
344
+ this._validationMessages = value;
345
+ }
346
+ get isValid() {
347
+ return this._isValid;
348
+ }
349
+ set isValid(value) {
350
+ const wasValid = this._isValid;
351
+ this._isValid = value;
352
+ if (wasValid !== value) {
353
+ this.tracker._onValidityChanged(wasValid, value);
354
+ }
355
+ }
356
+ get isDirty() {
357
+ return this._dirtyCounter !== 0;
358
+ }
359
+ get dirtyCounter() {
360
+ return this._dirtyCounter;
361
+ }
362
+ set dirtyCounter(value) {
363
+ this._dirtyCounter = value;
364
+ }
365
+ /** @internal */
366
+ _onCommitted(lastOp, keys) {
367
+ const autoIdProp = getAutoIdProperty(Object.getPrototypeOf(this));
368
+ const context = buildCommittedContext(this, autoIdProp, keys);
369
+ const redoFn = () => applyStateTransition(this, "committed", "do", context);
370
+ const undoFn = () => applyStateTransition(this, "committed", "undo", context);
371
+ if (lastOp) {
372
+ lastOp.updateOrAdd(redoFn, undoFn, new OperationProperties(this, "__state__", 4 /* Object */));
373
+ }
374
+ redoFn();
375
+ }
376
+ /** @internal */
377
+ _markRemoved() {
378
+ const prevState = this._state;
379
+ const prevDirtyCounter = this._dirtyCounter;
380
+ this.tracker._doAndTrack(
381
+ () => applyStateTransition(this, "removed", "do"),
382
+ () => applyStateTransition(this, "removed", "undo", { prevState, prevDirtyCounter }),
383
+ new OperationProperties(this, "__state__", 4 /* Object */)
384
+ );
385
+ }
386
+ /** @internal */
387
+ _markAdded() {
388
+ if (this._state !== "Unchanged" /* Unchanged */) return;
389
+ if (this.tracker._isTrackingSuppressed) return;
390
+ this.tracker._doAndTrack(
391
+ () => applyStateTransition(this, "added", "do"),
392
+ () => applyStateTransition(this, "added", "undo"),
393
+ new OperationProperties(this, "__state__", 4 /* Object */)
394
+ );
395
+ }
396
+ /** @internal */
397
+ _validate(property, errorMessage) {
398
+ if (errorMessage) {
399
+ this.validationMessages.set(property, errorMessage);
400
+ } else {
401
+ this.validationMessages.delete(property);
402
+ }
403
+ this.validationMessages = new Map(this.validationMessages);
404
+ this.isValid = this.validationMessages.size === 0;
405
+ }
406
+ /** @internal */
407
+ _applyValidation(messages) {
408
+ this.validationMessages = messages;
409
+ this.isValid = messages.size === 0;
410
+ }
411
+ destroy() {
412
+ DependencyTracker.clearDeps(this);
413
+ this.tracker._untrackObject(this);
414
+ }
415
+ };
416
+
417
+ // src/TrackerSession.ts
418
+ var TrackerSession = class {
419
+ constructor(scope, _end, _rollback) {
420
+ this._end = _end;
421
+ this._rollback = _rollback;
422
+ this._isDirty = false;
423
+ if (scope && scope.length > 0) {
424
+ this._scope = new Map(scope.map(([obj, props]) => [obj, new Set(props)]));
425
+ }
426
+ }
427
+ /** @internal */
428
+ _onWrite(obj, property) {
429
+ if (this._scope === void 0) return;
430
+ const declaredProps = this._scope.get(obj);
431
+ if (declaredProps === void 0 || !declaredProps.has(property)) return;
432
+ this._isDirty = true;
433
+ }
434
+ get isDirty() {
435
+ if (this._scope === void 0) return void 0;
436
+ return this._isDirty;
437
+ }
438
+ get isValid() {
439
+ if (this._scope === void 0) return void 0;
440
+ for (const [obj, props] of this._scope) {
441
+ for (const prop of props) {
442
+ if (obj.validationMessages.has(prop)) return false;
443
+ }
444
+ }
445
+ return true;
446
+ }
447
+ end() {
448
+ this._end();
449
+ }
450
+ rollback() {
451
+ this._rollback();
452
+ }
453
+ };
454
+
219
455
  // src/Tracker.ts
220
456
  var Tracker = class {
221
457
  constructor() {
@@ -372,6 +608,7 @@ var Tracker = class {
372
608
  this._currentOperationPropertyName = properties.property;
373
609
  if (this.shouldCoalesceChanges(properties)) {
374
610
  this._currentOperation = CollectionUtilities.getLast(this._undoOperations);
611
+ this._version++;
375
612
  this.versionChanged.emit(this._version);
376
613
  } else {
377
614
  this._currentOperation = new Operation();
@@ -388,6 +625,9 @@ var Tracker = class {
388
625
  properties
389
626
  );
390
627
  redoAction();
628
+ if (this._currentSession !== void 0 && properties.property !== void 0 && properties.trackedObject instanceof TrackedObject) {
629
+ this._currentSession._onWrite(properties.trackedObject, properties.property);
630
+ }
391
631
  if (this.isEndingCurrentOperation(properties)) {
392
632
  this._currentOperation = void 0;
393
633
  this._currentOperationOwner = void 0;
@@ -450,13 +690,20 @@ var Tracker = class {
450
690
  this.canRedo = this._redoOperations.length > 0;
451
691
  this.isDirty = CollectionUtilities.getLast(this._undoOperations) !== this._commitStateOperation;
452
692
  }
453
- startComposing() {
454
- if (this._composingBaseIndex !== void 0) return;
693
+ startSession(scope) {
694
+ if (this._currentSession !== void 0) return this._currentSession;
455
695
  this._composingBaseIndex = this._undoOperations.length;
456
696
  this._composingRedoLength = this._redoOperations.length;
697
+ this._currentSession = new TrackerSession(
698
+ scope,
699
+ () => this.endComposing(),
700
+ () => this.rollbackComposing()
701
+ );
702
+ return this._currentSession;
457
703
  }
458
704
  endComposing() {
459
705
  if (this._composingBaseIndex === void 0) return;
706
+ this._currentSession = void 0;
460
707
  const composed = this._undoOperations.splice(this._composingBaseIndex);
461
708
  this._redoOperations.splice(this._composingRedoLength);
462
709
  this._composingBaseIndex = void 0;
@@ -481,6 +728,7 @@ var Tracker = class {
481
728
  }
482
729
  rollbackComposing() {
483
730
  if (this._composingBaseIndex === void 0) return;
731
+ this._currentSession = void 0;
484
732
  const toRevert = this._undoOperations.splice(this._composingBaseIndex);
485
733
  this._redoOperations.splice(this._composingRedoLength);
486
734
  this._composingBaseIndex = void 0;
@@ -532,204 +780,6 @@ var Tracker = class {
532
780
  }
533
781
  };
534
782
 
535
- // src/OperationProperties.ts
536
- var OperationProperties = class {
537
- constructor(trackedObject, property, type, validator, coalesceWithin) {
538
- this.trackedObject = trackedObject;
539
- this.property = property;
540
- this.type = type;
541
- this.validator = validator;
542
- this.coalesceWithin = coalesceWithin;
543
- }
544
- };
545
-
546
- // src/ExternallyAssigned.ts
547
- var AUTO_ID = /* @__PURE__ */ Symbol("autoId");
548
- function AutoId(_target, context) {
549
- context.addInitializer(function() {
550
- Object.defineProperty(Object.getPrototypeOf(this), AUTO_ID, {
551
- value: String(context.name),
552
- configurable: true
553
- });
554
- });
555
- }
556
- function getAutoIdProperty(proto) {
557
- return AUTO_ID in proto ? proto[AUTO_ID] : void 0;
558
- }
559
-
560
- // src/TrackedObjectStateMachine.ts
561
- function applyStateTransition(obj, event, direction, context) {
562
- if (event === "added") {
563
- applyAdded(obj, direction);
564
- } else if (event === "removed") {
565
- applyRemoved(obj, direction, context);
566
- } else {
567
- applyCommitted(obj, direction, context);
568
- }
569
- }
570
- function applyAdded(obj, direction) {
571
- if (direction === "undo") {
572
- obj._setState("Unchanged" /* Unchanged */);
573
- } else {
574
- obj._setState("Insert" /* Insert */);
575
- }
576
- }
577
- function applyRemoved(obj, direction, context) {
578
- if (direction === "undo") {
579
- const prev = context?.prevState ?? "Unchanged" /* Unchanged */;
580
- obj._setState(prev);
581
- if (prev === "Insert" /* Insert */) {
582
- if (context?.prevDirtyCounter !== void 0) {
583
- obj._setDirtyCounter(context.prevDirtyCounter);
584
- }
585
- }
586
- } else {
587
- if (obj.state === "Insert" /* Insert */) {
588
- obj._setState("Unchanged" /* Unchanged */);
589
- obj._setDirtyCounter(0);
590
- } else {
591
- obj._setState("Deleted" /* Deleted */);
592
- }
593
- }
594
- }
595
- function applyCommitted(obj, direction, context) {
596
- if (direction === "undo") {
597
- const prev = context?.prevState ?? "Unchanged" /* Unchanged */;
598
- if (prev === "Insert" /* Insert */) {
599
- obj._setState("Deleted" /* Deleted */);
600
- } else if (prev === "Deleted" /* Deleted */) {
601
- obj._setState("Insert" /* Insert */);
602
- } else if (prev === "Changed" /* Changed */) {
603
- obj._setState("Changed" /* Changed */);
604
- }
605
- } else {
606
- if ((context?.prevState === "Insert" /* Insert */ || context?.prevState === "Changed" /* Changed */) && context.autoIdProp && context.realId !== void 0) {
607
- obj[context.autoIdProp] = context.realId;
608
- }
609
- obj._setState("Unchanged" /* Unchanged */);
610
- obj._setDirtyCounter(0);
611
- }
612
- }
613
- function buildCommittedContext(obj, autoIdProp, keys) {
614
- const prevState = obj.state;
615
- let realId;
616
- if ((prevState === "Insert" /* Insert */ || prevState === "Changed" /* Changed */) && keys) {
617
- realId = keys.find((k) => k.trackingId === obj.trackingId)?.value;
618
- }
619
- return { prevState, autoIdProp, realId };
620
- }
621
-
622
- // src/TrackedObject.ts
623
- var TrackedObject = class {
624
- constructor(tracker) {
625
- this.tracker = tracker;
626
- this._dirtyCounter = 0;
627
- this._isValid = true;
628
- this._state = "Unchanged" /* Unchanged */;
629
- this.changed = new TypedEvent();
630
- this.trackedChanged = new TypedEvent();
631
- if (!tracker._isConstructing) {
632
- throw new Error(`${this.constructor.name} must be created inside tracker.construct()`);
633
- }
634
- this.trackingId = tracker._nextTrackingId();
635
- this.validationMessages = /* @__PURE__ */ new Map();
636
- tracker._trackObject(this);
637
- }
638
- // ---- StateTarget interface (internal) ----
639
- /** @internal */
640
- get state() {
641
- return this._state;
642
- }
643
- /** @internal */
644
- _setState(value) {
645
- this._state = value;
646
- }
647
- /** @internal */
648
- _getDirtyCounter() {
649
- return this._dirtyCounter;
650
- }
651
- /** @internal */
652
- _setDirtyCounter(value) {
653
- this._dirtyCounter = value;
654
- }
655
- // ---- Public API ----
656
- get validationMessages() {
657
- return this._validationMessages ?? /* @__PURE__ */ new Map();
658
- }
659
- set validationMessages(value) {
660
- this._validationMessages = value;
661
- }
662
- get isValid() {
663
- return this._isValid;
664
- }
665
- set isValid(value) {
666
- const wasValid = this._isValid;
667
- this._isValid = value;
668
- if (wasValid !== value) {
669
- this.tracker._onValidityChanged(wasValid, value);
670
- }
671
- }
672
- get isDirty() {
673
- return this._dirtyCounter !== 0;
674
- }
675
- get dirtyCounter() {
676
- return this._dirtyCounter;
677
- }
678
- set dirtyCounter(value) {
679
- this._dirtyCounter = value;
680
- }
681
- /** @internal */
682
- _onCommitted(lastOp, keys) {
683
- const autoIdProp = getAutoIdProperty(Object.getPrototypeOf(this));
684
- const context = buildCommittedContext(this, autoIdProp, keys);
685
- const redoFn = () => applyStateTransition(this, "committed", "do", context);
686
- const undoFn = () => applyStateTransition(this, "committed", "undo", context);
687
- if (lastOp) {
688
- lastOp.updateOrAdd(redoFn, undoFn, new OperationProperties(this, "__state__", 4 /* Object */));
689
- }
690
- redoFn();
691
- }
692
- /** @internal */
693
- _markRemoved() {
694
- const prevState = this._state;
695
- const prevDirtyCounter = this._dirtyCounter;
696
- this.tracker._doAndTrack(
697
- () => applyStateTransition(this, "removed", "do"),
698
- () => applyStateTransition(this, "removed", "undo", { prevState, prevDirtyCounter }),
699
- new OperationProperties(this, "__state__", 4 /* Object */)
700
- );
701
- }
702
- /** @internal */
703
- _markAdded() {
704
- if (this._state !== "Unchanged" /* Unchanged */) return;
705
- if (this.tracker._isTrackingSuppressed) return;
706
- this.tracker._doAndTrack(
707
- () => applyStateTransition(this, "added", "do"),
708
- () => applyStateTransition(this, "added", "undo"),
709
- new OperationProperties(this, "__state__", 4 /* Object */)
710
- );
711
- }
712
- /** @internal */
713
- _validate(property, errorMessage) {
714
- if (errorMessage) {
715
- this.validationMessages.set(property, errorMessage);
716
- } else {
717
- this.validationMessages.delete(property);
718
- }
719
- this.validationMessages = new Map(this.validationMessages);
720
- this.isValid = this.validationMessages.size === 0;
721
- }
722
- /** @internal */
723
- _applyValidation(messages) {
724
- this.validationMessages = messages;
725
- this.isValid = messages.size === 0;
726
- }
727
- destroy() {
728
- DependencyTracker.clearDeps(this);
729
- this.tracker._untrackObject(this);
730
- }
731
- };
732
-
733
783
  // src/Tracked.ts
734
784
  function Tracked(validator, onChange, options) {
735
785
  function decorator(target, context) {
@@ -1225,6 +1275,7 @@ export {
1225
1275
  TrackedCollectionChanged,
1226
1276
  TrackedObject,
1227
1277
  Tracker,
1278
+ TrackerSession,
1228
1279
  TypedEvent
1229
1280
  };
1230
1281
  //# sourceMappingURL=index.js.map