@pocket-architect/core 0.1.23 → 0.1.26

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/build/Entity.d.ts CHANGED
@@ -1,10 +1,17 @@
1
1
  import { EntityId } from './EntityId';
2
+ import { DiffFormatterOptions, DiffViewModel } from "./helpers/diff";
2
3
  export declare abstract class Entity<T, E, H extends EntityId<E>> {
3
4
  protected readonly _id: H;
4
5
  protected props: T;
5
6
  protected _snapshot: string;
6
7
  snapshot(): T;
7
- snapshotDiff(): Partial<T> | null;
8
+ /**
9
+ * Повертає diff у вигляді масиву обʼєктів (DiffViewModel),
10
+ * АЛЕ лише якщо Entity перевизначив toDiff().
11
+ *
12
+ * За замовчуванням toDiff() = null → snapshotDiff() = null.
13
+ */
14
+ snapshotDiff(): DiffViewModel[] | null;
8
15
  isDirty(): boolean;
9
16
  protected constructor(props: T, id?: H | E);
10
17
  equals(object?: Entity<T, E, H>): boolean;
@@ -12,4 +19,5 @@ export declare abstract class Entity<T, E, H extends EntityId<E>> {
12
19
  toPrimitive(): T;
13
20
  toJSON(): any;
14
21
  toSnapshot(): any;
22
+ toDiff(): DiffFormatterOptions | null;
15
23
  }
package/build/Entity.js CHANGED
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.Entity = void 0;
4
4
  var EntityId_1 = require("./EntityId");
5
5
  var helpers_1 = require("./helpers");
6
+ var diff_1 = require("./helpers/diff");
6
7
  var isEntity = function (v) {
7
8
  return v instanceof Entity;
8
9
  };
@@ -17,24 +18,31 @@ var Entity = /** @class */ (function () {
17
18
  this.props = props;
18
19
  }
19
20
  Entity.prototype.snapshot = function () {
20
- this._snapshot = JSON.stringify(this.props);
21
+ // важливо: snapshot має бути стабільним (createSnapshot)
22
+ var snapObj = (0, helpers_1.createSnapshot)(this.props);
23
+ this._snapshot = JSON.stringify(snapObj);
21
24
  var copy = JSON.parse(this._snapshot);
22
25
  Object.freeze(copy);
23
26
  return copy;
24
27
  };
28
+ /**
29
+ * Повертає diff у вигляді масиву обʼєктів (DiffViewModel),
30
+ * АЛЕ лише якщо Entity перевизначив toDiff().
31
+ *
32
+ * За замовчуванням toDiff() = null → snapshotDiff() = null.
33
+ */
25
34
  Entity.prototype.snapshotDiff = function () {
26
- if (!this._snapshot) {
35
+ if (!this._snapshot)
27
36
  return null;
28
- }
29
- var diffs = {};
30
- var copy = JSON.parse(this._snapshot);
31
- var snapshotObj = (0, helpers_1.createSnapshot)(this.props);
32
- for (var key in this.props) {
33
- if (JSON.stringify(snapshotObj[key]) !== JSON.stringify(copy[key])) {
34
- diffs[key] = this.props[key];
35
- }
36
- }
37
- return Object.keys(diffs).length > 0 ? diffs : null;
37
+ var diffOptions = this.toDiff();
38
+ if (!diffOptions)
39
+ return null;
40
+ var beforeObj = JSON.parse(this._snapshot);
41
+ var afterObj = (0, helpers_1.createSnapshot)(this.props);
42
+ var diffs = (0, diff_1.diffObjects)(beforeObj, afterObj);
43
+ if (diffs.length === 0)
44
+ return [];
45
+ return (0, diff_1.buildDiffViewModel)(diffs, diffOptions);
38
46
  };
39
47
  Entity.prototype.isDirty = function () {
40
48
  if (!this._snapshot) {
@@ -72,6 +80,9 @@ var Entity = /** @class */ (function () {
72
80
  Entity.prototype.toSnapshot = function () {
73
81
  return this.toPrimitive();
74
82
  };
83
+ Entity.prototype.toDiff = function () {
84
+ return null;
85
+ };
75
86
  return Entity;
76
87
  }());
77
88
  exports.Entity = Entity;
@@ -1 +1 @@
1
- {"version":3,"file":"Entity.js","sourceRoot":"","sources":["../src/Entity.ts"],"names":[],"mappings":";;;AAAA,uCAAsC;AACtC,qCAAyC;AAEzC,IAAM,QAAQ,GAAG,UAA8B,CAAkB;IAC/D,OAAO,CAAC,YAAY,MAAM,CAAA;AAC5B,CAAC,CAAA;AAED;IAkCE,gBAAsB,KAAQ,EAAE,EAAQ;QACtC,IAAI,EAAE,EAAE,CAAC;YACP,IAAI,CAAC,GAAG,GAAM,CAAC,OAAO,EAAE,KAAK,QAAQ,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,mBAAQ,CAAI,EAAE,CAAC,CAAC,CAAC;QACpE,CAAC;aAAM,CAAC;YACN,IAAI,CAAC,GAAG,GAAM,CAAC,IAAI,mBAAQ,EAAE,CAAC,CAAC;QACjC,CAAC;QACD,IAAI,CAAC,KAAK,GAAG,KAAK,CAAA;IACpB,CAAC;IApCD,yBAAQ,GAAR;QACE,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAC5C,IAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,SAAS,CAAM,CAAC;QAC7C,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;QACpB,OAAO,IAAI,CAAC;IACd,CAAC;IAED,6BAAY,GAAZ;QACE,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,CAAC;YACpB,OAAO,IAAI,CAAC;QACd,CAAC;QACD,IAAM,KAAK,GAAe,EAAE,CAAC;QAC7B,IAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;QACxC,IAAM,WAAW,GAAG,IAAA,wBAAc,EAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAC/C,KAAK,IAAM,GAAG,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC;YAC7B,IAAI,IAAI,CAAC,SAAS,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,KAAK,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC;gBACnE,KAAK,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;YAC/B,CAAC;QACH,CAAC;QACD,OAAO,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC;IACtD,CAAC;IAED,wBAAO,GAAP;QACE,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,CAAC;YACpB,OAAO,KAAK,CAAC;QACf,CAAC;QACD,OAAO,IAAI,CAAC,SAAS,KAAK,IAAI,CAAC,SAAS,CAAC,IAAA,wBAAc,EAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC;IACvE,CAAC;IAWM,uBAAM,GAAb,UAAc,MAAwB;QACpC,IAAI,MAAM,KAAK,IAAI,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;YAC5C,OAAO,KAAK,CAAA;QACd,CAAC;QAED,IAAI,IAAI,KAAK,MAAM,EAAE,CAAC;YACpB,OAAO,IAAI,CAAA;QACb,CAAC;QAED,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC;YACtB,OAAO,KAAK,CAAA;QACd,CAAC;QAED,OAAO,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;IACrC,CAAC;IAED,sBAAI,sBAAE;aAAN;YACE,OAAO,IAAI,CAAC,GAAG,CAAA;QACjB,CAAC;;;OAAA;IAED,4BAAW,GAAX;QACE,OAAO,IAAI,CAAC,KAAK,CAAA;IACnB,CAAC;IAED,8DAA8D;IAC9D,uBAAM,GAAN;QACE,OAAO,IAAI,CAAC,WAAW,EAAE,CAAC;IAC5B,CAAC;IAED,8DAA8D;IAC9D,2BAAU,GAAV;QACE,OAAO,IAAI,CAAC,WAAW,EAAE,CAAC;IAC5B,CAAC;IACH,aAAC;AAAD,CAAC,AA5ED,IA4EC;AA5EqB,wBAAM"}
1
+ {"version":3,"file":"Entity.js","sourceRoot":"","sources":["../src/Entity.ts"],"names":[],"mappings":"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"}
@@ -25,26 +25,81 @@ var TestEntity = /** @class */ (function (_super) {
25
25
  enumerable: false,
26
26
  configurable: true
27
27
  });
28
+ TestEntity.prototype.setFirstAmount = function (amount) {
29
+ this.props.parts[0].amount = amount;
30
+ };
31
+ TestEntity.prototype.toDiff = function () {
32
+ return {
33
+ resolveLabel: function (_a) {
34
+ var key = _a.key, index = _a.index, path = _a.path;
35
+ if (key === 'createdAt')
36
+ return null; // ignore
37
+ if (key === 'amount' && index !== null)
38
+ return "\u0426\u0456\u043D\u0430 (\u043F\u043E\u0437\u0438\u0446\u0456\u044F ".concat(index + 1, ")");
39
+ if (path === 'name')
40
+ return 'Імʼя';
41
+ return undefined;
42
+ },
43
+ formatValue: function (value, _a) {
44
+ var key = _a.key;
45
+ if (value instanceof EntityId_1.EntityId) {
46
+ return value.toHash();
47
+ }
48
+ if (key === 'amount' && typeof value === 'number')
49
+ return "".concat(value.toFixed(2), " \u0433\u0440\u043D");
50
+ return String(value);
51
+ },
52
+ };
53
+ };
28
54
  return TestEntity;
29
55
  }(Entity_1.Entity));
56
+ var TestEntity2 = /** @class */ (function (_super) {
57
+ tslib_1.__extends(TestEntity2, _super);
58
+ function TestEntity2() {
59
+ return _super !== null && _super.apply(this, arguments) || this;
60
+ }
61
+ TestEntity2.create = function (props, id) {
62
+ return new TestEntity2(props, id);
63
+ };
64
+ return TestEntity2;
65
+ }(Entity_1.Entity));
30
66
  describe('Entity', function () {
31
67
  var entity;
32
68
  beforeAll(function () {
33
- entity = TestEntity.create({ name: 'test', testId: new EntityId_1.EntityId('1') }, '1');
69
+ entity = TestEntity.create({
70
+ name: 'test', testId: new EntityId_1.EntityId('1'),
71
+ test2: TestEntity2.create({ name: 'test', testId: new EntityId_1.EntityId('1') }),
72
+ parts: [{
73
+ amount: 1000,
74
+ entity: TestEntity2.create({ name: 'part entity', testId: new EntityId_1.EntityId('2') })
75
+ }]
76
+ }, '1');
34
77
  });
35
78
  test('base', function () { return tslib_1.__awaiter(void 0, void 0, void 0, function () {
36
79
  var entityNew, entity2, entity3;
37
80
  return tslib_1.__generator(this, function (_a) {
38
- entityNew = TestEntity.create({ name: 'test2' });
81
+ entityNew = TestEntity.create({
82
+ name: 'test2',
83
+ test2: TestEntity2.create({ name: 'test', testId: new EntityId_1.EntityId('1') }),
84
+ parts: []
85
+ });
39
86
  expect(entityNew.id.toString().length).toEqual(36); // random id
40
- entity2 = TestEntity.create({ name: 'test2' }, '1');
87
+ entity2 = TestEntity.create({
88
+ name: 'test2',
89
+ test2: TestEntity2.create({ name: 'test', testId: new EntityId_1.EntityId('1') }),
90
+ parts: []
91
+ }, '1');
41
92
  expect(entity.equals(entity2)).toBeTruthy(); // same id
42
93
  expect(entity.equals(entity)).toBeTruthy(); // same instance
43
94
  expect(entity.equals(null)).toBeFalsy(); // null
44
95
  expect(entity.equals({ id: new EntityId_1.EntityId('1') })).toBeFalsy(); // not Entity
45
96
  expect(entity.id.toPrimitive()).toEqual('1');
46
97
  expect(entity.id.toString()).toEqual('6061cf31-edcb-5d22-9adc-927af7c186fa');
47
- entity3 = TestEntity.create({ name: 'test2' }, entity.id);
98
+ entity3 = TestEntity.create({
99
+ name: 'test2',
100
+ test2: TestEntity2.create({ name: 'test', testId: new EntityId_1.EntityId('1') }),
101
+ parts: []
102
+ }, entity.id);
48
103
  expect(entity.equals(entity3)).toBeTruthy(); // same id
49
104
  expect(entity3.id.toPrimitive()).toEqual('1');
50
105
  expect(entity3.id.toString()).toEqual('6061cf31-edcb-5d22-9adc-927af7c186fa');
@@ -55,15 +110,53 @@ describe('Entity', function () {
55
110
  var snapshot;
56
111
  return tslib_1.__generator(this, function (_a) {
57
112
  snapshot = entity.snapshot();
58
- expect(snapshot).toEqual({ name: 'test', testId: '3D4WX' });
113
+ expect(snapshot).toEqual({
114
+ "name": "test",
115
+ "parts": [
116
+ {
117
+ "amount": 1000,
118
+ "entity": {
119
+ "name": "part entity",
120
+ "testId": "QbPvX"
121
+ }
122
+ }
123
+ ],
124
+ "test2": {
125
+ "name": "test",
126
+ "testId": "3D4WX"
127
+ },
128
+ "testId": "3D4WX"
129
+ });
59
130
  expect(function () {
60
131
  snapshot.name = 'changed';
61
132
  }).toThrow();
62
133
  expect(entity.isDirty()).toEqual(false);
63
- expect(entity.snapshotDiff()).toEqual(null);
134
+ expect(entity.snapshotDiff()).toEqual([]);
64
135
  entity.name = 'test changed';
136
+ entity.setFirstAmount(200);
65
137
  expect(entity.isDirty()).toEqual(true);
66
- expect(entity.snapshotDiff()).toEqual({ "name": "test changed" });
138
+ expect(entity.snapshotDiff()).toEqual([
139
+ {
140
+ "after": "test changed",
141
+ "afterRaw": "test changed",
142
+ "before": "test",
143
+ "beforeRaw": "test",
144
+ "index": null,
145
+ "kind": "changed",
146
+ "label": "Імʼя",
147
+ "path": "name"
148
+ },
149
+ {
150
+ "after": "200.00 грн",
151
+ "afterRaw": 200,
152
+ "before": "1000.00 грн",
153
+ "beforeRaw": 1000,
154
+ "index": 0,
155
+ "kind": "changed",
156
+ "label": "Ціна (позиція 1)",
157
+ "path": "parts[0].amount"
158
+ }
159
+ ]);
67
160
  return [2 /*return*/];
68
161
  });
69
162
  }); });
@@ -1 +1 @@
1
- {"version":3,"file":"Entity.spec.js","sourceRoot":"","sources":["../src/Entity.spec.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"Entity.spec.js","sourceRoot":"","sources":["../src/Entity.spec.ts"],"names":[],"mappings":"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"}
package/build/EntityId.js CHANGED
@@ -58,7 +58,7 @@ var EntityId = /** @class */ (function () {
58
58
  return this._uuid;
59
59
  };
60
60
  EntityId.prototype.toJSON = function () {
61
- return this.toHash();
61
+ return this._recordId ? this.toHash() : null;
62
62
  };
63
63
  EntityId.prototype.equals = function (id) {
64
64
  return this._uuid === id._uuid;
@@ -1 +1 @@
1
- {"version":3,"file":"EntityId.js","sourceRoot":"","sources":["../src/EntityId.ts"],"names":[],"mappings":";;;;AAAA,0EAAuC;AACvC,8CAA+C;AAC/C,4DAA8B;AAC9B,+CAA4C;AAE5C;IAIE,kBAAY,QAAyB,EAAE,MAAsB,EAAE,IAAmB;QAAtE,yBAAA,EAAA,eAAyB;QAAE,uBAAA,EAAA,cAAsB;QAAE,qBAAA,EAAA,WAAmB;QAHxE,cAAS,GAAW,IAAI,CAAC;QACzB,UAAK,GAAW,IAAI,CAAC;QAG7B,IAAI,CAAC,SAAS,GAAG,QAAQ,CAAC,CAAC,CAAC,QAAa,CAAC,CAAC,CAAC,IAAI,CAAC;QACjD,IAAI,MAAM,EAAE,CAAC;YACX,IAAI,CAAC,QAAQ,CAAC,QAAkB,CAAC,CAAC;QACpC,CAAC;QACD,IAAI,CAAC,IAAI,EAAE,CAAC;YACV,IAAI,CAAC,UAAU,EAAE,CAAC;QACpB,CAAC;IACH,CAAC;IAEO,6BAAU,GAAlB;QACE,IAAI,CAAC,KAAK,GAAG,IAAA,wBAAU,EAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,IAAA,gBAAQ,GAAE,EAAE,IAAA,wBAAU,EAAC,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC;IACtH,CAAC;IAED,sBAAI,iCAAW;aAAf;YACE,OAAO,CAAC,IAAI,CAAC,WAAW,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC;QACpC,CAAC;;;OAAA;IAED,yBAAM,GAAN;QACE,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,CAAC;YACpB,MAAM,IAAI,KAAK,CAAC,+BAA+B,CAAC,CAAC;QACnD,CAAC;QACD,IAAM,OAAO,QAAO,iBAAO,YAAP,iBAAO,kCAAI,IAAI,CAAC,WAAW,YAAC,CAAC;QACjD,OAAO,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC,QAAQ,EAAE,CAAC,CAAC;IACvD,CAAC;IAES,2BAAQ,GAAlB,UAAmB,IAAY;QAC7B,IAAI,CAAC,IAAI,EAAE,CAAC;YACV,OAAO,IAAI,CAAC;QACd,CAAC;QACD,IAAM,OAAO,QAAO,iBAAO,YAAP,iBAAO,kCAAI,IAAI,CAAC,WAAW,YAAC,CAAC;QACjD,IAAM,GAAG,GAAG,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;QACpC,IAAI,CAAC,GAAG,EAAE,CAAC;YACT,MAAM,IAAI,qBAAS,CAAC,uBAAgB,IAAI,CAAE,CAAC,CAAC;QAC9C,CAAC;QACD,IAAI,CAAC,SAAS,GAAM,CAAC,OAAO,IAAI,CAAC,SAAS,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;QAChF,IAAI,CAAC,UAAU,EAAE,CAAC;QAClB,OAAO,IAAI,CAAC;IACd,CAAC;IAED,8BAAW,GAAX;QACE,OAAO,IAAI,CAAC,SAAS,CAAC;IACxB,CAAC;IAED,2BAAQ,GAAR;QACE,OAAO,IAAI,CAAC,KAAK,CAAC;IACpB,CAAC;IAED,yBAAM,GAAN;QACE,OAAO,IAAI,CAAC,MAAM,EAAE,CAAC;IACvB,CAAC;IAED,yBAAM,GAAN,UAAO,EAAe;QACpB,OAAO,IAAI,CAAC,KAAK,KAAK,EAAE,CAAC,KAAK,CAAC;IACjC,CAAC;IACH,eAAC;AAAD,CAAC,AA3DD,IA2DC;AA3DY,4BAAQ"}
1
+ {"version":3,"file":"EntityId.js","sourceRoot":"","sources":["../src/EntityId.ts"],"names":[],"mappings":";;;;AAAA,0EAAuC;AACvC,8CAA+C;AAC/C,4DAA8B;AAC9B,+CAA4C;AAE5C;IAIE,kBAAY,QAAyB,EAAE,MAAsB,EAAE,IAAmB;QAAtE,yBAAA,EAAA,eAAyB;QAAE,uBAAA,EAAA,cAAsB;QAAE,qBAAA,EAAA,WAAmB;QAHxE,cAAS,GAAW,IAAI,CAAC;QACzB,UAAK,GAAW,IAAI,CAAC;QAG7B,IAAI,CAAC,SAAS,GAAG,QAAQ,CAAC,CAAC,CAAC,QAAa,CAAC,CAAC,CAAC,IAAI,CAAC;QACjD,IAAI,MAAM,EAAE,CAAC;YACX,IAAI,CAAC,QAAQ,CAAC,QAAkB,CAAC,CAAC;QACpC,CAAC;QACD,IAAI,CAAC,IAAI,EAAE,CAAC;YACV,IAAI,CAAC,UAAU,EAAE,CAAC;QACpB,CAAC;IACH,CAAC;IAEO,6BAAU,GAAlB;QACE,IAAI,CAAC,KAAK,GAAG,IAAA,wBAAU,EAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,IAAA,gBAAQ,GAAE,EAAE,IAAA,wBAAU,EAAC,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC;IACtH,CAAC;IAED,sBAAI,iCAAW;aAAf;YACE,OAAO,CAAC,IAAI,CAAC,WAAW,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC;QACpC,CAAC;;;OAAA;IAED,yBAAM,GAAN;QACE,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,CAAC;YACpB,MAAM,IAAI,KAAK,CAAC,+BAA+B,CAAC,CAAC;QACnD,CAAC;QACD,IAAM,OAAO,QAAO,iBAAO,YAAP,iBAAO,kCAAI,IAAI,CAAC,WAAW,YAAC,CAAC;QACjD,OAAO,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC,QAAQ,EAAE,CAAC,CAAC;IACvD,CAAC;IAES,2BAAQ,GAAlB,UAAmB,IAAY;QAC7B,IAAI,CAAC,IAAI,EAAE,CAAC;YACV,OAAO,IAAI,CAAC;QACd,CAAC;QACD,IAAM,OAAO,QAAO,iBAAO,YAAP,iBAAO,kCAAI,IAAI,CAAC,WAAW,YAAC,CAAC;QACjD,IAAM,GAAG,GAAG,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;QACpC,IAAI,CAAC,GAAG,EAAE,CAAC;YACT,MAAM,IAAI,qBAAS,CAAC,uBAAgB,IAAI,CAAE,CAAC,CAAC;QAC9C,CAAC;QACD,IAAI,CAAC,SAAS,GAAM,CAAC,OAAO,IAAI,CAAC,SAAS,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;QAChF,IAAI,CAAC,UAAU,EAAE,CAAC;QAClB,OAAO,IAAI,CAAC;IACd,CAAC;IAED,8BAAW,GAAX;QACE,OAAO,IAAI,CAAC,SAAS,CAAC;IACxB,CAAC;IAED,2BAAQ,GAAR;QACE,OAAO,IAAI,CAAC,KAAK,CAAC;IACpB,CAAC;IAED,yBAAM,GAAN;QACE,OAAO,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;IAC/C,CAAC;IAED,yBAAM,GAAN,UAAO,EAAe;QACpB,OAAO,IAAI,CAAC,KAAK,KAAK,EAAE,CAAC,KAAK,CAAC;IACjC,CAAC;IACH,eAAC;AAAD,CAAC,AA3DD,IA2DC;AA3DY,4BAAQ"}
@@ -88,18 +88,30 @@ describe('EventBus', function () {
88
88
  enumerable: false,
89
89
  configurable: true
90
90
  });
91
+ User.prototype.toDiff = function () {
92
+ return {
93
+ resolveLabel: function (_a) {
94
+ var key = _a.key;
95
+ return key;
96
+ },
97
+ formatValue: function (value) {
98
+ return value === null || value === void 0 ? void 0 : value.toString();
99
+ },
100
+ };
101
+ };
91
102
  return User;
92
103
  }(Entity_1.Entity));
93
104
  UserCreatedEvent = /** @class */ (function (_super) {
94
105
  tslib_1.__extends(UserCreatedEvent, _super);
106
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
95
107
  function UserCreatedEvent() {
96
108
  var _this = _super !== null && _super.apply(this, arguments) || this;
97
109
  _this.eventName = 'UserCreatedEvent'; // без цього не буде працювати статична типізація
98
110
  return _this;
99
111
  }
100
- Object.defineProperty(UserCreatedEvent.prototype, "username", {
112
+ Object.defineProperty(UserCreatedEvent.prototype, "diff", {
101
113
  get: function () {
102
- return this.props.username;
114
+ return this.props.diff;
103
115
  },
104
116
  enumerable: false,
105
117
  configurable: true
@@ -117,7 +129,18 @@ describe('EventBus', function () {
117
129
  ChangeLoggerSubscriber.prototype.on = function (domainEvent) {
118
130
  return tslib_1.__awaiter(this, void 0, void 0, function () {
119
131
  return tslib_1.__generator(this, function (_a) {
120
- console.log('Username changed:', domainEvent.username);
132
+ expect(domainEvent.diff).toEqual([
133
+ {
134
+ kind: 'changed',
135
+ label: 'username',
136
+ path: 'username',
137
+ index: null,
138
+ beforeRaw: 'john_doe',
139
+ afterRaw: 'john_doe_updated',
140
+ before: 'john_doe',
141
+ after: 'john_doe_updated'
142
+ }
143
+ ]);
121
144
  return [2 /*return*/];
122
145
  });
123
146
  });
@@ -133,11 +156,12 @@ describe('EventBus', function () {
133
156
  user.snapshot();
134
157
  // Змінюємо ім'я користувача
135
158
  user.username = 'john_doe_updated';
159
+ expect(user.isDirty()).toBeTruthy();
136
160
  // Перевіряємо, чи є зміни
137
161
  if (user.isDirty()) {
138
162
  diff = user.snapshotDiff();
139
163
  if (diff) {
140
- event = new UserCreatedEvent(diff);
164
+ event = new UserCreatedEvent({ diff: diff });
141
165
  // записуємо подію в чергу (не publish). Бо якщо в циклі може бути багато подій і їх треба назбирати, а потім виконати, тому збираємо через push
142
166
  eventBus.push(event);
143
167
  }
@@ -1 +1 @@
1
- {"version":3,"file":"EventBus.spec.js","sourceRoot":"","sources":["../src/EventBus.spec.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"EventBus.spec.js","sourceRoot":"","sources":["../src/EventBus.spec.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,43 @@
1
+ export type DiffKind = 'added' | 'removed' | 'changed';
2
+ export type PathSegment = {
3
+ type: 'key';
4
+ key: string;
5
+ } | {
6
+ type: 'index';
7
+ index: number;
8
+ };
9
+ export interface DiffEntry {
10
+ path: string;
11
+ segments: PathSegment[];
12
+ kind: DiffKind;
13
+ before?: unknown;
14
+ after?: unknown;
15
+ }
16
+ export interface LabelContext {
17
+ key: string | null;
18
+ index: number | null;
19
+ path: string;
20
+ segments: PathSegment[];
21
+ entry: DiffEntry;
22
+ }
23
+ export interface DiffFormatterOptions {
24
+ /**
25
+ * - string → label (людська назва поля)
26
+ * - null → ignore
27
+ * - undefined → fallback на path
28
+ */
29
+ resolveLabel?: (ctx: LabelContext) => string | null | undefined;
30
+ formatValue?: (value: unknown, ctx: LabelContext) => string;
31
+ }
32
+ export interface DiffViewModel {
33
+ kind: DiffKind;
34
+ label: string;
35
+ path: string;
36
+ index: number | null;
37
+ beforeRaw?: unknown;
38
+ afterRaw?: unknown;
39
+ before?: string;
40
+ after?: string;
41
+ }
42
+ export declare function diffObjects(before: unknown, after: unknown): DiffEntry[];
43
+ export declare function buildDiffViewModel(diffs: DiffEntry[], options: DiffFormatterOptions): DiffViewModel[];
@@ -0,0 +1,154 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.buildDiffViewModel = exports.diffObjects = void 0;
4
+ var tslib_1 = require("tslib");
5
+ function diffObjects(before, after) {
6
+ var diffs = [];
7
+ walk(before, after, [], diffs);
8
+ return diffs;
9
+ }
10
+ exports.diffObjects = diffObjects;
11
+ function walk(before, after, segments, out) {
12
+ if (Object.is(before, after))
13
+ return;
14
+ var isPlainObject = function (v) {
15
+ return typeof v === 'object' && v !== null && !Array.isArray(v);
16
+ };
17
+ // arrays
18
+ if (Array.isArray(before) || Array.isArray(after)) {
19
+ var bArr = Array.isArray(before) ? before : [];
20
+ var aArr = Array.isArray(after) ? after : [];
21
+ var max = Math.max(bArr.length, aArr.length);
22
+ for (var i = 0; i < max; i++) {
23
+ walk(bArr[i], aArr[i], tslib_1.__spreadArray(tslib_1.__spreadArray([], segments, true), [{ type: 'index', index: i }], false), out);
24
+ }
25
+ return;
26
+ }
27
+ // primitives or mismatched types
28
+ if (!isPlainObject(before) || !isPlainObject(after)) {
29
+ out.push({
30
+ path: segmentsToPath(segments),
31
+ segments: segments,
32
+ kind: 'changed',
33
+ before: before,
34
+ after: after,
35
+ });
36
+ return;
37
+ }
38
+ // objects
39
+ var bObj = before;
40
+ var aObj = after;
41
+ var keys = tslib_1.__spreadArray(tslib_1.__spreadArray([], Object.keys(bObj), true), Object.keys(aObj), true);
42
+ for (var _i = 0, keys_1 = keys; _i < keys_1.length; _i++) {
43
+ var key = keys_1[_i];
44
+ var b = bObj[key];
45
+ var a = aObj[key];
46
+ var nextSeg = tslib_1.__spreadArray(tslib_1.__spreadArray([], segments, true), [{ type: 'key', key: key }], false);
47
+ if (b === undefined && a !== undefined) {
48
+ out.push({
49
+ path: segmentsToPath(nextSeg),
50
+ segments: nextSeg,
51
+ kind: 'added',
52
+ after: a,
53
+ });
54
+ continue;
55
+ }
56
+ if (b !== undefined && a === undefined) {
57
+ out.push({
58
+ path: segmentsToPath(nextSeg),
59
+ segments: nextSeg,
60
+ kind: 'removed',
61
+ before: b,
62
+ });
63
+ continue;
64
+ }
65
+ walk(b, a, nextSeg, out);
66
+ }
67
+ }
68
+ function segmentsToPath(segments) {
69
+ var path = '';
70
+ for (var _i = 0, segments_1 = segments; _i < segments_1.length; _i++) {
71
+ var s = segments_1[_i];
72
+ if (s.type === 'key') {
73
+ path += path ? ".".concat(s.key) : s.key;
74
+ }
75
+ else {
76
+ path += "[".concat(s.index, "]");
77
+ }
78
+ }
79
+ return path || '';
80
+ }
81
+ function buildLabelContext(entry) {
82
+ var key = null;
83
+ var index = null;
84
+ for (var i = entry.segments.length - 1; i >= 0; i--) {
85
+ var s = entry.segments[i];
86
+ if (s.type === 'key') {
87
+ key = s.key;
88
+ break;
89
+ }
90
+ }
91
+ for (var i = entry.segments.length - 1; i >= 0; i--) {
92
+ var s = entry.segments[i];
93
+ if (s.type === 'index') {
94
+ index = s.index;
95
+ break;
96
+ }
97
+ }
98
+ return {
99
+ key: key,
100
+ index: index,
101
+ path: entry.path,
102
+ segments: entry.segments,
103
+ entry: entry,
104
+ };
105
+ }
106
+ function defaultStringify(value) {
107
+ if (typeof value === 'string')
108
+ return "\"".concat(value, "\"");
109
+ if (typeof value === 'boolean')
110
+ return value ? 'true' : 'false';
111
+ if (value === null)
112
+ return 'null';
113
+ if (typeof value === 'object')
114
+ return JSON.stringify(value);
115
+ return String(value);
116
+ }
117
+ function buildDiffViewModel(diffs, options) {
118
+ var resolveLabel = options.resolveLabel, _a = options.formatValue, formatValue = _a === void 0 ? defaultStringify : _a;
119
+ var seen = new Set();
120
+ var result = [];
121
+ for (var _i = 0, diffs_1 = diffs; _i < diffs_1.length; _i++) {
122
+ var entry = diffs_1[_i];
123
+ var ctx = buildLabelContext(entry);
124
+ var resolvedLabel = resolveLabel === null || resolveLabel === void 0 ? void 0 : resolveLabel(ctx);
125
+ if (resolvedLabel === null)
126
+ continue;
127
+ var label = resolvedLabel !== null && resolvedLabel !== void 0 ? resolvedLabel : entry.path;
128
+ var beforeFormatted = entry.before !== undefined ? formatValue(entry.before, ctx) : undefined;
129
+ var afterFormatted = entry.after !== undefined ? formatValue(entry.after, ctx) : undefined;
130
+ // якщо форматовано однаково — не зміна
131
+ if (entry.kind === 'changed' && beforeFormatted === afterFormatted) {
132
+ continue;
133
+ }
134
+ // 👇 КЛЮЧ ДЕДУПЛІКАЦІЇ
135
+ var dedupeKey = "".concat(entry.kind, "|").concat(entry.path);
136
+ if (seen.has(dedupeKey)) {
137
+ continue;
138
+ }
139
+ seen.add(dedupeKey);
140
+ result.push({
141
+ kind: entry.kind,
142
+ label: label,
143
+ path: entry.path,
144
+ index: ctx.index,
145
+ beforeRaw: entry.before,
146
+ afterRaw: entry.after,
147
+ before: beforeFormatted,
148
+ after: afterFormatted,
149
+ });
150
+ }
151
+ return result;
152
+ }
153
+ exports.buildDiffViewModel = buildDiffViewModel;
154
+ //# sourceMappingURL=diff.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"diff.js","sourceRoot":"","sources":["../../src/helpers/diff.ts"],"names":[],"mappings":"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"}
package/build/helpers.js CHANGED
@@ -3,33 +3,35 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.createSnapshot = void 0;
4
4
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
5
5
  function createSnapshot(obj) {
6
- if (!obj) {
6
+ if (obj === null || obj === undefined) {
7
7
  return obj;
8
8
  }
9
- // eslint-disable-next-line @typescript-eslint/ban-ts-comment
10
- // @ts-ignore
9
+ // toSnapshot має найвищий пріоритет
10
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
11
11
  if (typeof obj.toSnapshot === 'function') {
12
- // eslint-disable-next-line @typescript-eslint/ban-ts-comment
13
- // @ts-ignore
12
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
14
13
  return obj.toSnapshot();
15
14
  }
16
- // eslint-disable-next-line @typescript-eslint/ban-ts-comment
17
- // @ts-ignore
15
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
18
16
  if (typeof obj.toJSON === 'function') {
19
- // eslint-disable-next-line @typescript-eslint/ban-ts-comment
20
- // @ts-ignore
17
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
21
18
  return obj.toJSON();
22
19
  }
20
+ // ✅ МАСИВИ
21
+ if (Array.isArray(obj)) {
22
+ return obj.map(function (item) { return createSnapshot(item); });
23
+ }
24
+ // primitives
25
+ if (typeof obj !== 'object') {
26
+ return obj;
27
+ }
28
+ // plain object
23
29
  var copy = {};
24
- for (var key in obj) {
25
- if (!Object.prototype.hasOwnProperty.call(obj, key)) {
26
- continue;
27
- }
28
- if (typeof obj[key] !== 'object') {
29
- copy[key] = obj[key];
30
- continue;
31
- }
32
- copy[key] = createSnapshot(obj[key]);
30
+ for (var _i = 0, _a = Object.keys(obj); _i < _a.length; _i++) {
31
+ var key = _a[_i];
32
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
33
+ var value = obj[key];
34
+ copy[key] = createSnapshot(value);
33
35
  }
34
36
  return copy;
35
37
  }
@@ -1 +1 @@
1
- {"version":3,"file":"helpers.js","sourceRoot":"","sources":["../src/helpers.ts"],"names":[],"mappings":";;;AAAA,8DAA8D;AAC9D,SAAgB,cAAc,CAAI,GAAM;IACtC,IAAI,CAAC,GAAG,EAAE,CAAC;QACT,OAAO,GAAG,CAAC;IACb,CAAC;IACD,6DAA6D;IAC7D,aAAa;IACb,IAAI,OAAO,GAAG,CAAC,UAAU,KAAK,UAAU,EAAE,CAAC;QACzC,6DAA6D;QAC7D,aAAa;QACb,OAAO,GAAG,CAAC,UAAU,EAAO,CAAC;IAC/B,CAAC;IACD,6DAA6D;IAC7D,aAAa;IACb,IAAI,OAAO,GAAG,CAAC,MAAM,KAAK,UAAU,EAAE,CAAC;QACrC,6DAA6D;QAC7D,aAAa;QACb,OAAO,GAAG,CAAC,MAAM,EAAO,CAAC;IAC3B,CAAC;IACD,IAAM,IAAI,GAAK,EAAO,CAAC;IACvB,KAAK,IAAM,GAAG,IAAI,GAAG,EAAE,CAAC;QACtB,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,cAAc,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,CAAC,EAAE,CAAC;YACpD,SAAS;QACX,CAAC;QACD,IAAI,OAAO,GAAG,CAAC,GAAG,CAAC,KAAK,QAAQ,EAAE,CAAC;YACjC,IAAI,CAAC,GAAG,CAAC,GAAG,GAAG,CAAC,GAAG,CAAC,CAAC;YACrB,SAAS;QACX,CAAC;QACD,IAAI,CAAC,GAAG,CAAC,GAAG,cAAc,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC;IACvC,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AA9BD,wCA8BC"}
1
+ {"version":3,"file":"helpers.js","sourceRoot":"","sources":["../src/helpers.ts"],"names":[],"mappings":";;;AAAA,8DAA8D;AAC9D,SAAgB,cAAc,CAAI,GAAM;IACtC,IAAI,GAAG,KAAK,IAAI,IAAI,GAAG,KAAK,SAAS,EAAE,CAAC;QACtC,OAAO,GAAG,CAAC;IACb,CAAC;IAED,oCAAoC;IACpC,8DAA8D;IAC9D,IAAI,OAAQ,GAAW,CAAC,UAAU,KAAK,UAAU,EAAE,CAAC;QAClD,8DAA8D;QAC9D,OAAQ,GAAW,CAAC,UAAU,EAAE,CAAC;IACnC,CAAC;IACD,8DAA8D;IAC9D,IAAI,OAAQ,GAAW,CAAC,MAAM,KAAK,UAAU,EAAE,CAAC;QAC9C,8DAA8D;QAC9D,OAAQ,GAAW,CAAC,MAAM,EAAE,CAAC;IAC/B,CAAC;IAED,WAAW;IACX,IAAI,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC;QACvB,OAAO,GAAG,CAAC,GAAG,CAAC,UAAA,IAAI,IAAI,OAAA,cAAc,CAAC,IAAI,CAAC,EAApB,CAAoB,CAAiB,CAAC;IAC/D,CAAC;IAED,aAAa;IACb,IAAI,OAAO,GAAG,KAAK,QAAQ,EAAE,CAAC;QAC5B,OAAO,GAAG,CAAC;IACb,CAAC;IAED,eAAe;IACf,IAAM,IAAI,GAAG,EAA6B,CAAC;IAE3C,KAAkB,UAA0B,EAA1B,KAAA,MAAM,CAAC,IAAI,CAAC,GAAa,CAAC,EAA1B,cAA0B,EAA1B,IAA0B,EAAE,CAAC;QAA1C,IAAM,GAAG,SAAA;QACZ,8DAA8D;QAC9D,IAAM,KAAK,GAAI,GAAW,CAAC,GAAG,CAAC,CAAC;QAChC,IAAI,CAAC,GAAG,CAAC,GAAG,cAAc,CAAC,KAAK,CAAC,CAAC;IACpC,CAAC;IAED,OAAO,IAAS,CAAC;AACnB,CAAC;AArCD,wCAqCC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pocket-architect/core",
3
- "version": "0.1.23",
3
+ "version": "0.1.26",
4
4
  "description": "",
5
5
  "main": "build/index.js",
6
6
  "scripts": {
@@ -1,9 +1,19 @@
1
1
  import { Entity } from './Entity';
2
2
  import { EntityId } from './EntityId';
3
+ import {DiffFormatterOptions} from "./helpers/diff";
3
4
 
4
5
  interface TestProps {
5
6
  name: string;
6
7
  testId?: EntityId<string>;
8
+ test2: TestEntity2;
9
+ parts: {
10
+ amount: number;
11
+ entity: TestEntity2;
12
+ }[];
13
+ }
14
+ interface TestProps2 {
15
+ name: string;
16
+ testId?: EntityId<string>;
7
17
  }
8
18
  class TestEntity extends Entity<TestProps, string, EntityId<string>> {
9
19
  static create(props: TestProps, id?:EntityId<string>|string): TestEntity {
@@ -17,20 +27,64 @@ class TestEntity extends Entity<TestProps, string, EntityId<string>> {
17
27
  set testId(val: EntityId<string>) {
18
28
  this.props.testId = val;
19
29
  }
30
+
31
+ setFirstAmount(amount:number):void {
32
+ this.props.parts[0].amount = amount;
33
+ }
34
+
35
+ toDiff(): DiffFormatterOptions | null {
36
+ return {
37
+ resolveLabel({ key, index, path }) {
38
+ if (key === 'createdAt') return null; // ignore
39
+
40
+ if (key === 'amount' && index !== null) return `Ціна (позиція ${index + 1})`;
41
+ if (path === 'name') return 'Імʼя';
42
+
43
+ return undefined;
44
+ },
45
+ formatValue(value, { key }) {
46
+ if (value instanceof EntityId) {
47
+ return value.toHash();
48
+ }
49
+ if (key === 'amount' && typeof value === 'number') return `${value.toFixed(2)} грн`;
50
+ return String(value);
51
+ },
52
+ };
53
+ }
54
+ }
55
+ class TestEntity2 extends Entity<TestProps2, string, EntityId<string>> {
56
+ static create(props: TestProps2, id?:EntityId<string>|string): TestEntity2 {
57
+ return new TestEntity2(props, id);
58
+ }
20
59
  }
21
60
 
22
61
  describe('Entity', () => {
23
62
  let entity: TestEntity;
24
63
 
25
64
  beforeAll(() => {
26
- entity = TestEntity.create({ name: 'test', testId: new EntityId('1') }, '1');
65
+ entity = TestEntity.create({
66
+ name: 'test', testId: new EntityId('1'),
67
+ test2: TestEntity2.create({ name: 'test', testId: new EntityId('1') }),
68
+ parts: [{
69
+ amount: 1000,
70
+ entity: TestEntity2.create({ name: 'part entity', testId: new EntityId('2') })
71
+ }]
72
+ }, '1');
27
73
  });
28
74
 
29
75
  test('base', async () => {
30
- const entityNew = TestEntity.create({ name: 'test2' });
76
+ const entityNew = TestEntity.create({
77
+ name: 'test2',
78
+ test2: TestEntity2.create({ name: 'test', testId: new EntityId('1') }),
79
+ parts: []
80
+ });
31
81
  expect(entityNew.id.toString().length).toEqual(36); // random id
32
82
 
33
- const entity2 = TestEntity.create({ name: 'test2' }, '1');
83
+ const entity2 = TestEntity.create({
84
+ name: 'test2',
85
+ test2: TestEntity2.create({ name: 'test', testId: new EntityId('1') }),
86
+ parts: []
87
+ }, '1');
34
88
  expect(entity.equals(entity2)).toBeTruthy(); // same id
35
89
  expect(entity.equals(entity)).toBeTruthy(); // same instance
36
90
  expect(entity.equals(null)).toBeFalsy(); // null
@@ -38,7 +92,11 @@ describe('Entity', () => {
38
92
  expect(entity.id.toPrimitive()).toEqual('1');
39
93
  expect(entity.id.toString()).toEqual('6061cf31-edcb-5d22-9adc-927af7c186fa');
40
94
 
41
- const entity3 = TestEntity.create({ name: 'test2' }, entity.id);
95
+ const entity3 = TestEntity.create({
96
+ name: 'test2',
97
+ test2: TestEntity2.create({ name: 'test', testId: new EntityId('1') }),
98
+ parts: []
99
+ }, entity.id);
42
100
  expect(entity.equals(entity3)).toBeTruthy(); // same id
43
101
  expect(entity3.id.toPrimitive()).toEqual('1');
44
102
  expect(entity3.id.toString()).toEqual('6061cf31-edcb-5d22-9adc-927af7c186fa');
@@ -46,15 +104,53 @@ describe('Entity', () => {
46
104
 
47
105
  test('snapshot', async () => {
48
106
  const snapshot = entity.snapshot();
49
- expect(snapshot).toEqual({ name: 'test', testId: '3D4WX' });
107
+ expect(snapshot).toEqual({
108
+ "name": "test",
109
+ "parts": [
110
+ {
111
+ "amount": 1000,
112
+ "entity": {
113
+ "name": "part entity",
114
+ "testId": "QbPvX"
115
+ }
116
+ }
117
+ ],
118
+ "test2": {
119
+ "name": "test",
120
+ "testId": "3D4WX"
121
+ },
122
+ "testId": "3D4WX"
123
+ });
50
124
 
51
125
  expect(() => {
52
126
  snapshot.name = 'changed';
53
127
  }).toThrow()
54
128
  expect(entity.isDirty()).toEqual(false);
55
- expect(entity.snapshotDiff()).toEqual(null);
129
+ expect(entity.snapshotDiff()).toEqual([]);
56
130
  entity.name = 'test changed';
131
+ entity.setFirstAmount(200);
57
132
  expect(entity.isDirty()).toEqual(true);
58
- expect(entity.snapshotDiff()).toEqual({"name": "test changed"});
133
+ expect(entity.snapshotDiff()).toEqual([
134
+ {
135
+ "after": "test changed",
136
+ "afterRaw": "test changed",
137
+ "before": "test",
138
+ "beforeRaw": "test",
139
+ "index": null,
140
+ "kind": "changed",
141
+ "label": "Імʼя",
142
+ "path": "name"
143
+ },
144
+ {
145
+ "after": "200.00 грн",
146
+ "afterRaw": 200,
147
+ "before": "1000.00 грн",
148
+ "beforeRaw": 1000,
149
+ "index": 0,
150
+ "kind": "changed",
151
+ "label": "Ціна (позиція 1)",
152
+ "path": "parts[0].amount"
153
+ }
154
+ ]);
59
155
  });
60
156
  });
package/src/Entity.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  import { EntityId } from './EntityId';
2
2
  import {createSnapshot} from "./helpers";
3
+ import {buildDiffViewModel, DiffFormatterOptions, diffObjects, DiffViewModel} from "./helpers/diff";
3
4
 
4
5
  const isEntity = <T, E, M extends EntityId<E>>(v: Entity<T, E, M>): v is Entity<T, E, M> => {
5
6
  return v instanceof Entity
@@ -10,26 +11,37 @@ export abstract class Entity<T, E, H extends EntityId<E>> {
10
11
  protected props: T;
11
12
  protected _snapshot: string;
12
13
 
14
+
13
15
  snapshot(): T {
14
- this._snapshot = JSON.stringify(this.props);
16
+ // важливо: snapshot має бути стабільним (createSnapshot)
17
+ const snapObj = createSnapshot(this.props);
18
+ this._snapshot = JSON.stringify(snapObj);
19
+
15
20
  const copy = JSON.parse(this._snapshot) as T;
16
21
  Object.freeze(copy);
17
22
  return copy;
18
23
  }
19
24
 
20
- snapshotDiff(): Partial<T> | null {
21
- if (!this._snapshot) {
22
- return null;
23
- }
24
- const diffs: Partial<T> = {};
25
- const copy = JSON.parse(this._snapshot);
26
- const snapshotObj = createSnapshot(this.props);
27
- for (const key in this.props) {
28
- if (JSON.stringify(snapshotObj[key]) !== JSON.stringify(copy[key])) {
29
- diffs[key] = this.props[key];
30
- }
31
- }
32
- return Object.keys(diffs).length > 0 ? diffs : null;
25
+
26
+ /**
27
+ * Повертає diff у вигляді масиву обʼєктів (DiffViewModel),
28
+ * АЛЕ лише якщо Entity перевизначив toDiff().
29
+ *
30
+ * За замовчуванням toDiff() = null → snapshotDiff() = null.
31
+ */
32
+ snapshotDiff(): DiffViewModel[] | null {
33
+ if (!this._snapshot) return null;
34
+
35
+ const diffOptions = this.toDiff();
36
+ if (!diffOptions) return null;
37
+
38
+ const beforeObj = JSON.parse(this._snapshot) as unknown;
39
+ const afterObj = createSnapshot(this.props);
40
+
41
+ const diffs = diffObjects(beforeObj, afterObj);
42
+ if (diffs.length === 0) return [];
43
+
44
+ return buildDiffViewModel(diffs, diffOptions);
33
45
  }
34
46
 
35
47
  isDirty(): boolean {
@@ -81,4 +93,8 @@ export abstract class Entity<T, E, H extends EntityId<E>> {
81
93
  toSnapshot(): any {
82
94
  return this.toPrimitive();
83
95
  }
96
+
97
+ toDiff(): DiffFormatterOptions | null {
98
+ return null;
99
+ }
84
100
  }
package/src/EntityId.ts CHANGED
@@ -56,7 +56,7 @@ export class EntityId<T> {
56
56
  }
57
57
 
58
58
  toJSON(): string {
59
- return this.toHash();
59
+ return this._recordId ? this.toHash() : null;
60
60
  }
61
61
 
62
62
  equals(id: EntityId<T>): boolean {
@@ -3,6 +3,7 @@ import {DomainEventSubscriber} from "./DomainEventSubscriber";
3
3
  import {DomainEvent, DomainEventClass} from "./DomainEvent";
4
4
  import {EntityId} from "./EntityId";
5
5
  import {Entity} from "./Entity";
6
+ import {DiffFormatterOptions} from "./helpers/diff";
6
7
 
7
8
  describe('EventBus', () => {
8
9
  class NewUserCreated extends DomainEvent<{ username: string }> {
@@ -57,13 +58,25 @@ describe('EventBus', () => {
57
58
  get username(): string {
58
59
  return this.props.username;
59
60
  }
61
+
62
+ toDiff(): DiffFormatterOptions | null {
63
+ return {
64
+ resolveLabel({ key }) {
65
+ return key;
66
+ },
67
+ formatValue(value) {
68
+ return value?.toString();
69
+ },
70
+ };
71
+ }
60
72
  }
61
73
 
62
- class UserCreatedEvent extends DomainEvent<Partial<IUser>> {
74
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
75
+ class UserCreatedEvent extends DomainEvent<{ diff: any }> {
63
76
  readonly eventName = 'UserCreatedEvent'; // без цього не буде працювати статична типізація
64
77
 
65
- get username() {
66
- return this.props.username;
78
+ get diff() {
79
+ return this.props.diff;
67
80
  }
68
81
  }
69
82
 
@@ -75,7 +88,18 @@ describe('EventBus', () => {
75
88
 
76
89
  // Реалізуємо логіку обробки подій
77
90
  async on(domainEvent: UserCreatedEvent) {
78
- console.log('Username changed:', domainEvent.username);
91
+ expect(domainEvent.diff).toEqual([
92
+ {
93
+ kind: 'changed',
94
+ label: 'username',
95
+ path: 'username',
96
+ index: null,
97
+ beforeRaw: 'john_doe',
98
+ afterRaw: 'john_doe_updated',
99
+ before: 'john_doe',
100
+ after: 'john_doe_updated'
101
+ }
102
+ ]);
79
103
  }
80
104
  }
81
105
 
@@ -91,11 +115,12 @@ describe('EventBus', () => {
91
115
  // Змінюємо ім'я користувача
92
116
  user.username = 'john_doe_updated';
93
117
  let event;
118
+ expect(user.isDirty()).toBeTruthy();
94
119
  // Перевіряємо, чи є зміни
95
120
  if (user.isDirty()) {
96
121
  const diff = user.snapshotDiff();
97
122
  if (diff) {
98
- event = new UserCreatedEvent(diff);
123
+ event = new UserCreatedEvent({ diff });
99
124
  // записуємо подію в чергу (не publish). Бо якщо в циклі може бути багато подій і їх треба назбирати, а потім виконати, тому збираємо через push
100
125
  eventBus.push(event);
101
126
  }
@@ -0,0 +1,220 @@
1
+ export type DiffKind = 'added' | 'removed' | 'changed';
2
+
3
+ export type PathSegment =
4
+ | { type: 'key'; key: string }
5
+ | { type: 'index'; index: number };
6
+
7
+ export interface DiffEntry {
8
+ path: string;
9
+ segments: PathSegment[];
10
+ kind: DiffKind;
11
+ before?: unknown;
12
+ after?: unknown;
13
+ }
14
+
15
+ export interface LabelContext {
16
+ key: string | null; // назва поля (останній key)
17
+ index: number | null; // індекс масиву, якщо зміна всередині масиву
18
+ path: string; // items[2].price
19
+ segments: PathSegment[];
20
+ entry: DiffEntry;
21
+ }
22
+
23
+ export interface DiffFormatterOptions {
24
+ /**
25
+ * - string → label (людська назва поля)
26
+ * - null → ignore
27
+ * - undefined → fallback на path
28
+ */
29
+ resolveLabel?: (ctx: LabelContext) => string | null | undefined;
30
+
31
+ formatValue?: (value: unknown, ctx: LabelContext) => string;
32
+ }
33
+
34
+ export interface DiffViewModel {
35
+ kind: DiffKind;
36
+ label: string;
37
+ path: string;
38
+ index: number | null;
39
+
40
+ beforeRaw?: unknown;
41
+ afterRaw?: unknown;
42
+
43
+ before?: string;
44
+ after?: string;
45
+ }
46
+
47
+ export function diffObjects(before: unknown, after: unknown): DiffEntry[] {
48
+ const diffs: DiffEntry[] = [];
49
+ walk(before, after, [], diffs);
50
+ return diffs;
51
+ }
52
+
53
+ function walk(
54
+ before: unknown,
55
+ after: unknown,
56
+ segments: PathSegment[],
57
+ out: DiffEntry[]
58
+ ): void {
59
+ if (Object.is(before, after)) return;
60
+
61
+ const isPlainObject = (v: unknown) =>
62
+ typeof v === 'object' && v !== null && !Array.isArray(v);
63
+
64
+ // arrays
65
+ if (Array.isArray(before) || Array.isArray(after)) {
66
+ const bArr = Array.isArray(before) ? before : [];
67
+ const aArr = Array.isArray(after) ? after : [];
68
+ const max = Math.max(bArr.length, aArr.length);
69
+
70
+ for (let i = 0; i < max; i++) {
71
+ walk(bArr[i], aArr[i], [...segments, { type: 'index', index: i }], out);
72
+ }
73
+ return;
74
+ }
75
+
76
+ // primitives or mismatched types
77
+ if (!isPlainObject(before) || !isPlainObject(after)) {
78
+ out.push({
79
+ path: segmentsToPath(segments),
80
+ segments,
81
+ kind: 'changed',
82
+ before,
83
+ after,
84
+ });
85
+ return;
86
+ }
87
+
88
+ // objects
89
+ const bObj = before as Record<string, unknown>;
90
+ const aObj = after as Record<string, unknown>;
91
+ const keys = [...Object.keys(bObj), ...Object.keys(aObj)];
92
+
93
+ for (const key of keys) {
94
+ const b = bObj[key];
95
+ const a = aObj[key];
96
+ const nextSeg = [...segments, { type: 'key', key } as const];
97
+
98
+ if (b === undefined && a !== undefined) {
99
+ out.push({
100
+ path: segmentsToPath(nextSeg),
101
+ segments: nextSeg,
102
+ kind: 'added',
103
+ after: a,
104
+ });
105
+ continue;
106
+ }
107
+
108
+ if (b !== undefined && a === undefined) {
109
+ out.push({
110
+ path: segmentsToPath(nextSeg),
111
+ segments: nextSeg,
112
+ kind: 'removed',
113
+ before: b,
114
+ });
115
+ continue;
116
+ }
117
+
118
+ walk(b, a, nextSeg, out);
119
+ }
120
+ }
121
+
122
+ function segmentsToPath(segments: PathSegment[]): string {
123
+ let path = '';
124
+ for (const s of segments) {
125
+ if (s.type === 'key') {
126
+ path += path ? `.${s.key}` : s.key;
127
+ } else {
128
+ path += `[${s.index}]`;
129
+ }
130
+ }
131
+ return path || '';
132
+ }
133
+
134
+ function buildLabelContext(entry: DiffEntry): LabelContext {
135
+ let key: string | null = null;
136
+ let index: number | null = null;
137
+
138
+ for (let i = entry.segments.length - 1; i >= 0; i--) {
139
+ const s = entry.segments[i];
140
+ if (s.type === 'key') {
141
+ key = s.key;
142
+ break;
143
+ }
144
+ }
145
+
146
+ for (let i = entry.segments.length - 1; i >= 0; i--) {
147
+ const s = entry.segments[i];
148
+ if (s.type === 'index') {
149
+ index = s.index;
150
+ break;
151
+ }
152
+ }
153
+
154
+ return {
155
+ key,
156
+ index,
157
+ path: entry.path,
158
+ segments: entry.segments,
159
+ entry,
160
+ };
161
+ }
162
+
163
+ function defaultStringify(value: unknown): string {
164
+ if (typeof value === 'string') return `"${value}"`;
165
+ if (typeof value === 'boolean') return value ? 'true' : 'false';
166
+ if (value === null) return 'null';
167
+ if (typeof value === 'object') return JSON.stringify(value);
168
+ return String(value);
169
+ }
170
+
171
+ export function buildDiffViewModel(
172
+ diffs: DiffEntry[],
173
+ options: DiffFormatterOptions
174
+ ): DiffViewModel[] {
175
+ const { resolveLabel, formatValue = defaultStringify } = options;
176
+
177
+ const seen = new Set<string>();
178
+ const result: DiffViewModel[] = [];
179
+
180
+ for (const entry of diffs) {
181
+ const ctx = buildLabelContext(entry);
182
+
183
+ const resolvedLabel = resolveLabel?.(ctx);
184
+ if (resolvedLabel === null) continue;
185
+
186
+ const label = resolvedLabel ?? entry.path;
187
+
188
+ const beforeFormatted =
189
+ entry.before !== undefined ? formatValue(entry.before, ctx) : undefined;
190
+
191
+ const afterFormatted =
192
+ entry.after !== undefined ? formatValue(entry.after, ctx) : undefined;
193
+
194
+ // якщо форматовано однаково — не зміна
195
+ if (entry.kind === 'changed' && beforeFormatted === afterFormatted) {
196
+ continue;
197
+ }
198
+
199
+ // 👇 КЛЮЧ ДЕДУПЛІКАЦІЇ
200
+ const dedupeKey = `${entry.kind}|${entry.path}`;
201
+
202
+ if (seen.has(dedupeKey)) {
203
+ continue;
204
+ }
205
+ seen.add(dedupeKey);
206
+
207
+ result.push({
208
+ kind: entry.kind,
209
+ label,
210
+ path: entry.path,
211
+ index: ctx.index,
212
+ beforeRaw: entry.before,
213
+ afterRaw: entry.after,
214
+ before: beforeFormatted,
215
+ after: afterFormatted,
216
+ });
217
+ }
218
+
219
+ return result;
220
+ }
package/src/helpers.ts CHANGED
@@ -1,32 +1,39 @@
1
1
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
2
2
  export function createSnapshot<T>(obj: T): T {
3
- if (!obj) {
3
+ if (obj === null || obj === undefined) {
4
4
  return obj;
5
5
  }
6
- // eslint-disable-next-line @typescript-eslint/ban-ts-comment
7
- // @ts-ignore
8
- if (typeof obj.toSnapshot === 'function') {
9
- // eslint-disable-next-line @typescript-eslint/ban-ts-comment
10
- // @ts-ignore
11
- return obj.toSnapshot() as T;
6
+
7
+ // toSnapshot має найвищий пріоритет
8
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
9
+ if (typeof (obj as any).toSnapshot === 'function') {
10
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
11
+ return (obj as any).toSnapshot();
12
12
  }
13
- // eslint-disable-next-line @typescript-eslint/ban-ts-comment
14
- // @ts-ignore
15
- if (typeof obj.toJSON === 'function') {
16
- // eslint-disable-next-line @typescript-eslint/ban-ts-comment
17
- // @ts-ignore
18
- return obj.toJSON() as T;
13
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
14
+ if (typeof (obj as any).toJSON === 'function') {
15
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
16
+ return (obj as any).toJSON();
19
17
  }
20
- const copy:T = {} as T;
21
- for (const key in obj) {
22
- if (!Object.prototype.hasOwnProperty.call(obj, key)) {
23
- continue;
24
- }
25
- if (typeof obj[key] !== 'object') {
26
- copy[key] = obj[key];
27
- continue;
28
- }
29
- copy[key] = createSnapshot(obj[key]);
18
+
19
+ // МАСИВИ
20
+ if (Array.isArray(obj)) {
21
+ return obj.map(item => createSnapshot(item)) as unknown as T;
30
22
  }
31
- return copy;
23
+
24
+ // primitives
25
+ if (typeof obj !== 'object') {
26
+ return obj;
27
+ }
28
+
29
+ // plain object
30
+ const copy = {} as Record<string, unknown>;
31
+
32
+ for (const key of Object.keys(obj as object)) {
33
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
34
+ const value = (obj as any)[key];
35
+ copy[key] = createSnapshot(value);
36
+ }
37
+
38
+ return copy as T;
32
39
  }