@woltz/rich-domain 1.3.1 → 1.3.2

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/index.d.ts CHANGED
@@ -11,7 +11,7 @@ export { ValueObject } from "./value-object";
11
11
  export { Mapper } from "./mapper";
12
12
  export { EntitySchemaRegistry } from "./entity-schema-registry";
13
13
  export { AggregateChanges } from "./aggregate-changes";
14
- export type { DomainEventHandler, EntityHooks, Filter, EntityValidation, IDomainEvent, VOValidation, VOHooks, ValidationConfig, Primitive, TransactionContext, PaginationMeta, Pagination, OrderDirection, Order, IUnitOfWork, IDomainEventHandler, FieldPath, FilterOperator, Search, FilterValueFor, PathValue, OperatorsForType, DateOperators, NumberOperators, StringOperators, BooleanOperators, ArrayOperators, CriteriaOptions, } from "./types";
14
+ export type { DomainEventHandler, EntityHooks, Filter, EntityValidation, IDomainEvent, VOValidation, VOHooks, ValidationConfig, Primitive, TransactionContext, PaginationMeta, Pagination, OrderDirection, Order, IUnitOfWork, IDomainEventHandler, FieldPath, FilterOperator, Search, FilterValueFor, PathValue, OperatorsForType, DateOperators, NumberOperators, StringOperators, BooleanOperators, ArrayOperators, CriteriaOptions, DeepJsonResult, } from "./types";
15
15
  export { ARRAY_OPERATORS, BOOLEAN_OPERATORS, DATE_OPERATORS, NUMBER_OPERATORS, STRING_OPERATORS, FILTER_OPERATORS, } from "./constants";
16
16
  export { isValidOperatorForType } from "./utils/criteria-operator-validation";
17
17
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,oBAAoB,CAAC;AACnC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,oBAAoB,CAAC;AACnC,cAAc,cAAc,CAAC;AAC7B,cAAc,YAAY,CAAC;AAC3B,cAAc,oBAAoB,CAAC;AACnC,cAAc,cAAc,CAAC;AAC7B,OAAO,EAAE,EAAE,EAAE,MAAM,MAAM,CAAC;AAC1B,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AAC7C,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAC7C,OAAO,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAClC,OAAO,EAAE,oBAAoB,EAAE,MAAM,0BAA0B,CAAC;AAChE,OAAO,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AACvD,YAAY,EACV,kBAAkB,EAClB,WAAW,EACX,MAAM,EACN,gBAAgB,EAChB,YAAY,EACZ,YAAY,EACZ,OAAO,EACP,gBAAgB,EAChB,SAAS,EACT,kBAAkB,EAClB,cAAc,EACd,UAAU,EACV,cAAc,EACd,KAAK,EACL,WAAW,EACX,mBAAmB,EACnB,SAAS,EACT,cAAc,EACd,MAAM,EACN,cAAc,EACd,SAAS,EACT,gBAAgB,EAChB,aAAa,EACb,eAAe,EACf,eAAe,EACf,gBAAgB,EAChB,cAAc,EACd,eAAe,GAChB,MAAM,SAAS,CAAC;AACjB,OAAO,EACL,eAAe,EACf,iBAAiB,EACjB,cAAc,EACd,gBAAgB,EAChB,gBAAgB,EAChB,gBAAgB,GACjB,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,sBAAsB,EAAE,MAAM,sCAAsC,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,oBAAoB,CAAC;AACnC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,oBAAoB,CAAC;AACnC,cAAc,cAAc,CAAC;AAC7B,cAAc,YAAY,CAAC;AAC3B,cAAc,oBAAoB,CAAC;AACnC,cAAc,cAAc,CAAC;AAC7B,OAAO,EAAE,EAAE,EAAE,MAAM,MAAM,CAAC;AAC1B,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AAC7C,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAC7C,OAAO,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAClC,OAAO,EAAE,oBAAoB,EAAE,MAAM,0BAA0B,CAAC;AAChE,OAAO,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AACvD,YAAY,EACV,kBAAkB,EAClB,WAAW,EACX,MAAM,EACN,gBAAgB,EAChB,YAAY,EACZ,YAAY,EACZ,OAAO,EACP,gBAAgB,EAChB,SAAS,EACT,kBAAkB,EAClB,cAAc,EACd,UAAU,EACV,cAAc,EACd,KAAK,EACL,WAAW,EACX,mBAAmB,EACnB,SAAS,EACT,cAAc,EACd,MAAM,EACN,cAAc,EACd,SAAS,EACT,gBAAgB,EAChB,aAAa,EACb,eAAe,EACf,eAAe,EACf,gBAAgB,EAChB,cAAc,EACd,eAAe,EACf,cAAc,GACf,MAAM,SAAS,CAAC;AACjB,OAAO,EACL,eAAe,EACf,iBAAiB,EACjB,cAAc,EACd,gBAAgB,EAChB,gBAAgB,EAChB,gBAAgB,GACjB,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,sBAAsB,EAAE,MAAM,sCAAsC,CAAC"}
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,oBAAoB,CAAC;AACnC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,oBAAoB,CAAC;AACnC,cAAc,cAAc,CAAC;AAC7B,cAAc,YAAY,CAAC;AAC3B,cAAc,oBAAoB,CAAC;AACnC,cAAc,cAAc,CAAC;AAC7B,OAAO,EAAE,EAAE,EAAE,MAAM,MAAM,CAAC;AAC1B,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AAC7C,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAC7C,OAAO,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAClC,OAAO,EAAE,oBAAoB,EAAE,MAAM,0BAA0B,CAAC;AAChE,OAAO,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AA+BvD,OAAO,EACL,eAAe,EACf,iBAAiB,EACjB,cAAc,EACd,gBAAgB,EAChB,gBAAgB,EAChB,gBAAgB,GACjB,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,sBAAsB,EAAE,MAAM,sCAAsC,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,oBAAoB,CAAC;AACnC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,oBAAoB,CAAC;AACnC,cAAc,cAAc,CAAC;AAC7B,cAAc,YAAY,CAAC;AAC3B,cAAc,oBAAoB,CAAC;AACnC,cAAc,cAAc,CAAC;AAC7B,OAAO,EAAE,EAAE,EAAE,MAAM,MAAM,CAAC;AAC1B,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AAC7C,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAC7C,OAAO,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAClC,OAAO,EAAE,oBAAoB,EAAE,MAAM,0BAA0B,CAAC;AAChE,OAAO,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAgCvD,OAAO,EACL,eAAe,EACf,iBAAiB,EACjB,cAAc,EACd,gBAAgB,EAChB,gBAAgB,EAChB,gBAAgB,GACjB,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,sBAAsB,EAAE,MAAM,sCAAsC,CAAC"}
package/package.json CHANGED
@@ -1,11 +1,17 @@
1
1
  {
2
2
  "name": "@woltz/rich-domain",
3
- "version": "1.3.1",
3
+ "version": "1.3.2",
4
4
  "description": "Rich Domain Library with Standard Schema validation support",
5
5
  "homepage": "https://woltz.mintlify.app",
6
6
  "type": "module",
7
7
  "main": "dist/index.js",
8
8
  "types": "dist/index.d.ts",
9
+ "exports": {
10
+ ".": {
11
+ "types": "./dist/index.d.ts",
12
+ "import": "./dist/index.js"
13
+ }
14
+ },
9
15
  "author": "Tarcisio Andrade",
10
16
  "license": "MIT",
11
17
  "publishConfig": {
@@ -26,7 +32,7 @@
26
32
  "check": "tsc --noEmit",
27
33
  "coverage": "jest --coverage",
28
34
  "lint": "eslint src",
29
- "clean": "rm -rf dist coverage node_modules",
35
+ "clean": "rm -rf dist coverage node_modules tsconfig.tsbuildinfo",
30
36
  "prepublishOnly": "npm run build",
31
37
  "release": "standard-version",
32
38
  "release:minor": "standard-version --release-as minor",
@@ -44,15 +50,7 @@
44
50
  "typescript"
45
51
  ],
46
52
  "files": [
47
- "dist",
48
- "src",
49
- "**/*.js",
50
- "**/*.mjs",
51
- "**/*.cjs",
52
- "**/*.d.ts",
53
- "**/*.d.mts",
54
- "**/*.d.cts",
55
- "**/package.json"
53
+ "dist"
56
54
  ],
57
55
  "devDependencies": {
58
56
  "@faker-js/faker": "^9.9.0",
package/eslint.config.js DELETED
@@ -1,57 +0,0 @@
1
- import tsParser from "@typescript-eslint/parser";
2
- import tsPlugin from "@typescript-eslint/eslint-plugin";
3
- import { dirname } from "path";
4
- import { fileURLToPath } from "url";
5
-
6
- const __filename = fileURLToPath(import.meta.url);
7
- const __dirname = dirname(__filename);
8
-
9
- export default [
10
- {
11
- ignores: [
12
- "dist/**",
13
- "node_modules/**",
14
- "coverage/**",
15
- "**/*.test.ts",
16
- "**/*.spec.ts",
17
- ],
18
- },
19
- {
20
- files: ["src/**/*.ts"],
21
- languageOptions: {
22
- parser: tsParser,
23
- parserOptions: {
24
- ecmaVersion: 2020,
25
- sourceType: "module",
26
- project: "./tsconfig.json",
27
- tsconfigRootDir: __dirname,
28
- },
29
- globals: {
30
- console: "readonly",
31
- process: "readonly",
32
- Buffer: "readonly",
33
- __dirname: "readonly",
34
- __filename: "readonly",
35
- module: "readonly",
36
- require: "readonly",
37
- exports: "readonly",
38
- },
39
- },
40
- plugins: {
41
- "@typescript-eslint": tsPlugin,
42
- },
43
- rules: {
44
- ...tsPlugin.configs.recommended.rules,
45
- "@typescript-eslint/no-explicit-any": "off",
46
- "@typescript-eslint/no-unused-vars": [
47
- "warn",
48
- { argsIgnorePattern: "^_" },
49
- ],
50
- "@typescript-eslint/explicit-function-return-type": "off",
51
- "@typescript-eslint/explicit-module-boundary-types": "off",
52
- "@typescript-eslint/no-this-alias": "off",
53
- "@typescript-eslint/no-unsafe-function-type": "off",
54
- "no-console": "off",
55
- },
56
- },
57
- ];
package/jest.config.js DELETED
@@ -1,21 +0,0 @@
1
- /** @type {import('ts-jest').JestConfigWithTsJest} */
2
- export default {
3
- preset: "ts-jest",
4
- testEnvironment: "node",
5
- roots: ["<rootDir>/tests", "<rootDir>/src"],
6
- testMatch: ["**/*.test.ts", "**/*.spec.ts"],
7
- transform: {
8
- "^.+\\.tsx?$": "ts-jest",
9
- },
10
- coveragePathIgnorePatterns: [
11
- "/node_modules/",
12
- "/dist/",
13
- "/tests/",
14
- "**/examples/**",
15
- ],
16
- moduleFileExtensions: ["ts", "tsx", "js", "jsx", "json", "node"],
17
- collectCoverageFrom: ["src/**/*.ts", "!src/**/*.d.ts", "!src/index.ts"],
18
- coverageDirectory: "coverage",
19
- coverageReporters: ["text", "lcov", "clover"],
20
- verbose: true,
21
- };
@@ -1,444 +0,0 @@
1
- import {
2
- Operation,
3
- CreateOperation,
4
- UpdateOperation,
5
- DeleteOperation,
6
- BatchOperations,
7
- BatchCreateItem,
8
- BatchUpdateItem,
9
- } from "./types/change-tracker";
10
- import { EntityChanges } from "./entity-changes";
11
-
12
- /**
13
- * Manages and organizes the changes of an Aggregate.
14
- *
15
- * Responsibilities:
16
- * - Stores all operations (create, update, delete)
17
- * - Orders operations respecting FK dependencies
18
- * - Groups operations by entity for batch execution
19
- * - Provides query and iteration methods
20
- * - Includes relationField, parentId, parentEntity for N:N support
21
- *
22
- * @example
23
- * ```typescript
24
- * // Define an entity map for type-safe operations
25
- * type UserEntities = {
26
- * User: User;
27
- * Post: Post;
28
- * Comment: Comment;
29
- * };
30
- *
31
- * // Getting changes with types
32
- * const changes = user.getChanges<UserEntities>();
33
- *
34
- * // Filtering by entity with autocompletion
35
- * const postChanges = changes.for('Post');
36
- * postChanges.creates.forEach(post => {
37
- * console.log(post.title);
38
- * });
39
- * ```
40
- */
41
- export class AggregateChanges<TEntityMap = Record<string, any>> {
42
- private ops: Operation[] = [];
43
-
44
- constructor(operations: Operation[] = []) {
45
- this.ops = [...operations];
46
- }
47
-
48
- /**
49
- * Adds a create operation.
50
- *
51
- * @param entity - Entity name
52
- * @param data - Entity data
53
- * @param depth - Depth in the aggregate tree
54
- * @param parentId - Parent entity ID (for FK)
55
- * @param parentEntity - Parent entity name
56
- * @param relationField - Name of the relation field in parent (e.g., 'tags', 'comments')
57
- */
58
- addCreate<T>(
59
- entity: string,
60
- data: T,
61
- depth: number,
62
- parentId?: string,
63
- parentEntity?: string,
64
- relationField?: string
65
- ): void {
66
- this.ops.push({
67
- type: "create",
68
- entity,
69
- data,
70
- depth,
71
- parentId,
72
- parentEntity,
73
- relationField,
74
- } as CreateOperation<T>);
75
- }
76
-
77
- /**
78
- * Adds an update operation.
79
- */
80
- addUpdate<T>(
81
- entity: string,
82
- id: string,
83
- data: T,
84
- changedFields: Record<string, any>,
85
- depth: number
86
- ): void {
87
- this.ops.push({
88
- type: "update",
89
- entity,
90
- id,
91
- data,
92
- changedFields,
93
- depth,
94
- } as UpdateOperation<T>);
95
- }
96
-
97
- /**
98
- * Adds a delete operation.
99
- *
100
- * @param entity - Entity name
101
- * @param id - Entity ID
102
- * @param data - Entity data (for reference)
103
- * @param depth - Depth in the aggregate tree
104
- * @param relationField - Name of the relation field in parent (e.g., 'tags', 'comments')
105
- * @param parentId - Parent entity ID (for N:N disconnect)
106
- * @param parentEntity - Parent entity name (for N:N disconnect)
107
- */
108
- addDelete<T>(
109
- entity: string,
110
- id: string,
111
- data: T,
112
- depth: number,
113
- relationField?: string,
114
- parentId?: string,
115
- parentEntity?: string
116
- ): void {
117
- this.ops.push({
118
- type: "delete",
119
- entity,
120
- id,
121
- data,
122
- depth,
123
- relationField,
124
- parentId,
125
- parentEntity,
126
- } as DeleteOperation<T>);
127
- }
128
-
129
- /**
130
- * Returns all create operations, sorted by ascending depth (root → leaf).
131
- */
132
- creates(): CreateOperation[] {
133
- return this.ops
134
- .filter((op): op is CreateOperation => op.type === "create")
135
- .sort((a, b) => a.depth - b.depth);
136
- }
137
-
138
- /**
139
- * Returns all update operations.
140
- */
141
- updates(): UpdateOperation[] {
142
- return this.ops.filter((op): op is UpdateOperation => op.type === "update");
143
- }
144
-
145
- /**
146
- * Returns all delete operations, sorted by descending depth (leaf → root).
147
- */
148
- deletes(): DeleteOperation[] {
149
- return this.ops
150
- .filter((op): op is DeleteOperation => op.type === "delete")
151
- .sort((a, b) => b.depth - a.depth);
152
- }
153
-
154
- /**
155
- * Iterator that returns operations in the correct execution order:
156
- * 1. Deletes (leaf → root)
157
- * 2. Creates (root → leaf)
158
- * 3. Updates
159
- */
160
- *operations(): Generator<Operation> {
161
- yield* this.deletes();
162
- yield* this.creates();
163
- yield* this.updates();
164
- }
165
-
166
- /**
167
- * Returns all operations as an array in execution order.
168
- */
169
- toArray(): Operation[] {
170
- return [...this.operations()];
171
- }
172
-
173
- /**
174
- * Converts the changes into BatchOperations for optimized execution.
175
- *
176
- * Groups operations by entity and sorts by depth:
177
- * - Deletes: depth DESC (leaf → root), grouped by entity + relationField + parentId
178
- * - Creates: depth ASC (root → leaf), grouped by entity + relationField
179
- * - Updates: grouped by entity
180
- *
181
- * @example
182
- * ```typescript
183
- * const batch = changes.toBatchOperations();
184
- *
185
- * // Run deletes
186
- * for (const del of batch.deletes) {
187
- * if (registry.isReferenceCollection(del.parentEntity, del.relationField)) {
188
- * // N:N - disconnect only
189
- * await prisma[del.parentEntity].update({
190
- * where: { id: del.parentId },
191
- * data: { [del.relationField]: { disconnect: del.ids.map(id => ({ id })) } }
192
- * });
193
- * } else {
194
- * // 1:N - delete entities
195
- * await prisma[del.entity].deleteMany({ where: { id: { in: del.ids } } });
196
- * }
197
- * }
198
- * ```
199
- */
200
- toBatchOperations(): BatchOperations {
201
- return {
202
- deletes: this.groupDeletes(),
203
- creates: this.groupCreates(),
204
- updates: this.groupUpdates(),
205
- };
206
- }
207
-
208
- /**
209
- * Groups deletes by entity + relationField + parentId, sorted by descending depth.
210
- *
211
- * For N:N relations, we need to group by parentId because disconnect
212
- * operations are performed on the parent entity.
213
- */
214
- private groupDeletes(): BatchOperations["deletes"] {
215
- const deleteOps = this.deletes();
216
- const grouped = new Map<
217
- string,
218
- {
219
- depth: number;
220
- ids: string[];
221
- relationField?: string;
222
- parentEntity?: string;
223
- parentId?: string;
224
- }
225
- >();
226
-
227
- for (const op of deleteOps) {
228
- // Group by entity + relationField + parentId
229
- // This ensures N:N disconnects are grouped per parent
230
- const key = `${op.entity}:${op.relationField ?? ""}:${op.parentId ?? ""}`;
231
-
232
- if (!grouped.has(key)) {
233
- grouped.set(key, {
234
- depth: op.depth,
235
- ids: [],
236
- relationField: op.relationField,
237
- parentEntity: op.parentEntity,
238
- parentId: op.parentId,
239
- });
240
- }
241
- grouped.get(key)!.ids.push(op.id);
242
- }
243
-
244
- return Array.from(grouped.entries())
245
- .map(([key, { depth, ids, relationField, parentEntity, parentId }]) => {
246
- const entity = key.split(":")[0];
247
- return { entity, depth, ids, parentId, relationField, parentEntity };
248
- })
249
- .sort((a, b) => b.depth - a.depth);
250
- }
251
-
252
- /**
253
- * Groups creates by entity + relationField, sorted by ascending depth.
254
- *
255
- * Preserves parentEntity for N:N connect operations.
256
- */
257
- private groupCreates(): BatchOperations["creates"] {
258
- const createOps = this.creates();
259
- const grouped = new Map<
260
- string,
261
- {
262
- depth: number;
263
- items: BatchCreateItem[];
264
- relationField?: string;
265
- parentEntity?: string;
266
- }
267
- >();
268
-
269
- for (const op of createOps) {
270
- const key = `${op.entity}:${op.relationField ?? ""}`;
271
-
272
- if (!grouped.has(key)) {
273
- grouped.set(key, {
274
- depth: op.depth,
275
- items: [],
276
- relationField: op.relationField,
277
- parentEntity: op.parentEntity,
278
- });
279
- }
280
- grouped.get(key)!.items.push({
281
- data: op.data,
282
- parentId: op.parentId,
283
- parentEntity: op.parentEntity,
284
- relationField: op.relationField,
285
- });
286
- }
287
-
288
- return Array.from(grouped.entries())
289
- .map(([key, { depth, items, relationField, parentEntity }]) => {
290
- const entity = key.split(":")[0];
291
- return { entity, depth, items, relationField, parentEntity };
292
- })
293
- .sort((a, b) => a.depth - b.depth);
294
- }
295
-
296
- /**
297
- * Groups updates by entity.
298
- */
299
- private groupUpdates(): BatchOperations["updates"] {
300
- const updateOps = this.updates();
301
- const grouped = new Map<string, BatchUpdateItem[]>();
302
-
303
- for (const op of updateOps) {
304
- if (!grouped.has(op.entity)) {
305
- grouped.set(op.entity, []);
306
- }
307
- grouped.get(op.entity)!.push({
308
- id: op.id,
309
- changedFields: op.changedFields,
310
- });
311
- }
312
-
313
- return Array.from(grouped.entries()).map(([entity, items]) => ({
314
- entity,
315
- items,
316
- }));
317
- }
318
-
319
- /**
320
- * Filters changes by entity name.
321
- *
322
- * @param entityName - Name of the entity (e.g., 'Post', 'Comment')
323
- * @returns EntityChanges containing only the operations for this entity
324
- *
325
- * @example
326
- * ```typescript
327
- * const postChanges = changes.for('Post');
328
- *
329
- * if (postChanges.hasCreates()) {
330
- * postChanges.creates.forEach(post => {
331
- * console.log('New post:', post.title);
332
- * });
333
- * }
334
- * ```
335
- */
336
- for<K extends keyof TEntityMap>(entityName: K): EntityChanges<TEntityMap[K]> {
337
- const filtered = this.ops.filter((op) => op.entity === entityName);
338
- return new EntityChanges<TEntityMap[K]>(filtered);
339
- }
340
-
341
- /**
342
- * Filters changes by relation field.
343
- *
344
- * @param relationField - Name of the relation field (e.g., 'tags', 'comments')
345
- * @returns New AggregateChanges containing only operations for this relation
346
- *
347
- * @example
348
- * ```typescript
349
- * const tagChanges = changes.forRelation('tags');
350
- * // Contains only creates/deletes for the 'tags' relation
351
- * ```
352
- */
353
- forRelation(relationField: string): AggregateChanges<TEntityMap> {
354
- const filtered = this.ops.filter(
355
- (op) => op.relationField === relationField
356
- );
357
- return new AggregateChanges<TEntityMap>(filtered);
358
- }
359
-
360
- /**
361
- * Checks if there are create operations.
362
- */
363
- hasCreates(): boolean {
364
- return this.ops.some((op) => op.type === "create");
365
- }
366
-
367
- /**
368
- * Checks if there are update operations.
369
- */
370
- hasUpdates(): boolean {
371
- return this.ops.some((op) => op.type === "update");
372
- }
373
-
374
- /**
375
- * Checks if there are delete operations.
376
- */
377
- hasDeletes(): boolean {
378
- return this.ops.some((op) => op.type === "delete");
379
- }
380
-
381
- /**
382
- * Checks if there are any operations.
383
- */
384
- hasChanges(): boolean {
385
- return this.ops.length > 0;
386
- }
387
-
388
- /**
389
- * Checks if there are no operations.
390
- */
391
- isEmpty(): boolean {
392
- return this.ops.length === 0;
393
- }
394
-
395
- /**
396
- * Returns the total number of operations.
397
- */
398
- get count(): number {
399
- return this.ops.length;
400
- }
401
-
402
- /**
403
- * Returns the raw operations (for debug/testing).
404
- */
405
- get rawOperations(): Operation[] {
406
- return [...this.ops];
407
- }
408
-
409
- /**
410
- * Lists all entities that have changes.
411
- */
412
- getAffectedEntities(): string[] {
413
- const entities = new Set<string>();
414
- this.ops.forEach((op) => entities.add(op.entity));
415
- return Array.from(entities);
416
- }
417
-
418
- /**
419
- * Lists all relation fields that have changes.
420
- */
421
- getAffectedRelations(): string[] {
422
- const relations = new Set<string>();
423
- this.ops.forEach((op) => {
424
- if (op.relationField) {
425
- relations.add(op.relationField);
426
- }
427
- });
428
- return Array.from(relations);
429
- }
430
-
431
- /**
432
- * Clears all operations.
433
- */
434
- clear(): void {
435
- this.ops = [];
436
- }
437
-
438
- /**
439
- * Creates a copy of the changes.
440
- */
441
- clone(): AggregateChanges<TEntityMap> {
442
- return new AggregateChanges<TEntityMap>([...this.ops]);
443
- }
444
- }