@woltz/rich-domain 0.2.1

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.
Files changed (178) hide show
  1. package/.github/workflows/ci.yml +40 -0
  2. package/.husky/commit-msg +1 -0
  3. package/.husky/pre-commit +1 -0
  4. package/.versionrc.json +21 -0
  5. package/.vscode/settings.json +3 -0
  6. package/CHANGELOG.md +81 -0
  7. package/LICENSE +21 -0
  8. package/README.md +712 -0
  9. package/commitlint.config.js +23 -0
  10. package/dist/base-entity.d.ts +67 -0
  11. package/dist/base-entity.d.ts.map +1 -0
  12. package/dist/base-entity.js +309 -0
  13. package/dist/base-entity.js.map +1 -0
  14. package/dist/constants.d.ts +3 -0
  15. package/dist/constants.d.ts.map +1 -0
  16. package/dist/constants.js +6 -0
  17. package/dist/constants.js.map +1 -0
  18. package/dist/criteria.d.ts +60 -0
  19. package/dist/criteria.d.ts.map +1 -0
  20. package/dist/criteria.js +214 -0
  21. package/dist/criteria.js.map +1 -0
  22. package/dist/deep-proxy.d.ts +34 -0
  23. package/dist/deep-proxy.d.ts.map +1 -0
  24. package/dist/deep-proxy.js +297 -0
  25. package/dist/deep-proxy.js.map +1 -0
  26. package/dist/domain-event-bus.d.ts +57 -0
  27. package/dist/domain-event-bus.d.ts.map +1 -0
  28. package/dist/domain-event-bus.js +112 -0
  29. package/dist/domain-event-bus.js.map +1 -0
  30. package/dist/domain-event.d.ts +55 -0
  31. package/dist/domain-event.d.ts.map +1 -0
  32. package/dist/domain-event.js +42 -0
  33. package/dist/domain-event.js.map +1 -0
  34. package/dist/entity.d.ts +13 -0
  35. package/dist/entity.d.ts.map +1 -0
  36. package/dist/entity.js +15 -0
  37. package/dist/entity.js.map +1 -0
  38. package/dist/filtering.d.ts +107 -0
  39. package/dist/filtering.d.ts.map +1 -0
  40. package/dist/filtering.js +202 -0
  41. package/dist/filtering.js.map +1 -0
  42. package/dist/id.d.ts +51 -0
  43. package/dist/id.d.ts.map +1 -0
  44. package/dist/id.js +84 -0
  45. package/dist/id.js.map +1 -0
  46. package/dist/index.d.ts +15 -0
  47. package/dist/index.d.ts.map +1 -0
  48. package/dist/index.js +25 -0
  49. package/dist/index.js.map +1 -0
  50. package/dist/ordering.d.ts +93 -0
  51. package/dist/ordering.d.ts.map +1 -0
  52. package/dist/ordering.js +154 -0
  53. package/dist/ordering.js.map +1 -0
  54. package/dist/paginated-result.d.ts +62 -0
  55. package/dist/paginated-result.d.ts.map +1 -0
  56. package/dist/paginated-result.js +201 -0
  57. package/dist/paginated-result.js.map +1 -0
  58. package/dist/pagination.d.ts +218 -0
  59. package/dist/pagination.d.ts.map +1 -0
  60. package/dist/pagination.js +281 -0
  61. package/dist/pagination.js.map +1 -0
  62. package/dist/repository/base-repository.d.ts +77 -0
  63. package/dist/repository/base-repository.d.ts.map +1 -0
  64. package/dist/repository/base-repository.js +80 -0
  65. package/dist/repository/base-repository.js.map +1 -0
  66. package/dist/repository/in-memory-repository.d.ts +46 -0
  67. package/dist/repository/in-memory-repository.d.ts.map +1 -0
  68. package/dist/repository/in-memory-repository.js +85 -0
  69. package/dist/repository/in-memory-repository.js.map +1 -0
  70. package/dist/repository/index.d.ts +42 -0
  71. package/dist/repository/index.d.ts.map +1 -0
  72. package/dist/repository/index.js +47 -0
  73. package/dist/repository/index.js.map +1 -0
  74. package/dist/repository/mapper.d.ts +56 -0
  75. package/dist/repository/mapper.d.ts.map +1 -0
  76. package/dist/repository/mapper.js +15 -0
  77. package/dist/repository/mapper.js.map +1 -0
  78. package/dist/repository/types.d.ts +87 -0
  79. package/dist/repository/types.d.ts.map +1 -0
  80. package/dist/repository/types.js +6 -0
  81. package/dist/repository/types.js.map +1 -0
  82. package/dist/repository/unit-of-work.d.ts +70 -0
  83. package/dist/repository/unit-of-work.d.ts.map +1 -0
  84. package/dist/repository/unit-of-work.js +122 -0
  85. package/dist/repository/unit-of-work.js.map +1 -0
  86. package/dist/repository.d.ts +2 -0
  87. package/dist/repository.d.ts.map +1 -0
  88. package/dist/repository.js +21 -0
  89. package/dist/repository.js.map +1 -0
  90. package/dist/specification.d.ts +102 -0
  91. package/dist/specification.d.ts.map +1 -0
  92. package/dist/specification.js +187 -0
  93. package/dist/specification.js.map +1 -0
  94. package/dist/types/criteria.d.ts +35 -0
  95. package/dist/types/criteria.d.ts.map +1 -0
  96. package/dist/types/criteria.js +17 -0
  97. package/dist/types/criteria.js.map +1 -0
  98. package/dist/types/domain.d.ts +30 -0
  99. package/dist/types/domain.d.ts.map +1 -0
  100. package/dist/types/domain.js +2 -0
  101. package/dist/types/domain.js.map +1 -0
  102. package/dist/types/history-tracker.d.ts +36 -0
  103. package/dist/types/history-tracker.d.ts.map +1 -0
  104. package/dist/types/history-tracker.js +2 -0
  105. package/dist/types/history-tracker.js.map +1 -0
  106. package/dist/types/index.d.ts +8 -0
  107. package/dist/types/index.d.ts.map +1 -0
  108. package/dist/types/index.js +8 -0
  109. package/dist/types/index.js.map +1 -0
  110. package/dist/types/repository.d.ts +43 -0
  111. package/dist/types/repository.d.ts.map +1 -0
  112. package/dist/types/repository.js +2 -0
  113. package/dist/types/repository.js.map +1 -0
  114. package/dist/types/standard-schema.d.ts +15 -0
  115. package/dist/types/standard-schema.d.ts.map +1 -0
  116. package/dist/types/standard-schema.js +2 -0
  117. package/dist/types/standard-schema.js.map +1 -0
  118. package/dist/types/unit-of-work.d.ts +39 -0
  119. package/dist/types/unit-of-work.d.ts.map +1 -0
  120. package/dist/types/unit-of-work.js +2 -0
  121. package/dist/types/unit-of-work.js.map +1 -0
  122. package/dist/types/utils.d.ts +14 -0
  123. package/dist/types/utils.d.ts.map +1 -0
  124. package/dist/types/utils.js +2 -0
  125. package/dist/types/utils.js.map +1 -0
  126. package/dist/types.d.ts +88 -0
  127. package/dist/types.d.ts.map +1 -0
  128. package/dist/types.js +12 -0
  129. package/dist/types.js.map +1 -0
  130. package/dist/validation-error.d.ts +42 -0
  131. package/dist/validation-error.d.ts.map +1 -0
  132. package/dist/validation-error.js +73 -0
  133. package/dist/validation-error.js.map +1 -0
  134. package/dist/value-object.d.ts +47 -0
  135. package/dist/value-object.d.ts.map +1 -0
  136. package/dist/value-object.js +136 -0
  137. package/dist/value-object.js.map +1 -0
  138. package/eslint.config.js +51 -0
  139. package/jest.config.js +21 -0
  140. package/package.json +58 -0
  141. package/src/base-entity.ts +401 -0
  142. package/src/constants.ts +7 -0
  143. package/src/criteria.ts +291 -0
  144. package/src/deep-proxy.ts +339 -0
  145. package/src/domain-event-bus.ts +166 -0
  146. package/src/domain-event.ts +90 -0
  147. package/src/entity.ts +16 -0
  148. package/src/id.ts +94 -0
  149. package/src/index.ts +33 -0
  150. package/src/paginated-result.ts +274 -0
  151. package/src/repository/base-repository.ts +152 -0
  152. package/src/repository/in-memory-repository.ts +104 -0
  153. package/src/repository/index.ts +55 -0
  154. package/src/repository/mapper.ts +74 -0
  155. package/src/repository/unit-of-work.ts +148 -0
  156. package/src/types/criteria.ts +79 -0
  157. package/src/types/domain.ts +37 -0
  158. package/src/types/history-tracker.ts +45 -0
  159. package/src/types/index.ts +7 -0
  160. package/src/types/repository.ts +51 -0
  161. package/src/types/standard-schema.ts +19 -0
  162. package/src/types/unit-of-work.ts +46 -0
  163. package/src/types/utils.ts +29 -0
  164. package/src/validation-error.ts +97 -0
  165. package/src/value-object.ts +187 -0
  166. package/tests/criteria.test.ts +432 -0
  167. package/tests/domain-events.test.ts +445 -0
  168. package/tests/entity-equality.test.ts +487 -0
  169. package/tests/entity-validation.test.ts +339 -0
  170. package/tests/entity.test.ts +33 -0
  171. package/tests/history-tracker.spec.ts +667 -0
  172. package/tests/id.test.ts +341 -0
  173. package/tests/repository.test.ts +641 -0
  174. package/tests/to-json.test.ts +91 -0
  175. package/tests/utils.ts +151 -0
  176. package/tests/value-object-validation.test.ts +228 -0
  177. package/tests/value-objects.test.ts +52 -0
  178. package/tsconfig.json +31 -0
package/dist/id.js ADDED
@@ -0,0 +1,84 @@
1
+ import { randomUUID } from 'crypto';
2
+ // ============================================================================
3
+ // Id Class - Smart Identity Management
4
+ // ============================================================================
5
+ export class Id {
6
+ /**
7
+ * Create a new Id
8
+ * @param value - Optional existing ID value. If not provided, generates a new UUID.
9
+ *
10
+ * @example
11
+ * // New entity (generates UUID)
12
+ * const newId = new Id();
13
+ * newId.isNew // true
14
+ *
15
+ * // Existing entity (uses provided ID)
16
+ * const existingId = new Id("550e8400-e29b-41d4-a716-446655440000");
17
+ * existingId.isNew // false
18
+ */
19
+ constructor(value) {
20
+ if (value !== undefined) {
21
+ // ID was provided - this is an existing entity
22
+ this._value = value;
23
+ this._isNew = false;
24
+ }
25
+ else {
26
+ // No ID provided - generate new one, this is a new entity
27
+ this._value = this.generateUUID();
28
+ this._isNew = true;
29
+ }
30
+ }
31
+ /**
32
+ * Get the string value of the ID
33
+ */
34
+ get value() {
35
+ return this._value;
36
+ }
37
+ /**
38
+ * Check if this ID represents a new entity
39
+ */
40
+ get isNew() {
41
+ return this._isNew;
42
+ }
43
+ /**
44
+ * Convert to string (for JSON serialization and comparisons)
45
+ */
46
+ toString() {
47
+ return this._value;
48
+ }
49
+ /**
50
+ * Convert to JSON (returns the string value)
51
+ */
52
+ toJSON() {
53
+ return this._value;
54
+ }
55
+ /**
56
+ * Check equality with another Id or string
57
+ */
58
+ equals(other) {
59
+ if (other instanceof Id) {
60
+ return this._value === other._value;
61
+ }
62
+ return this._value === other;
63
+ }
64
+ /**
65
+ * Generate a UUID v4
66
+ */
67
+ generateUUID() {
68
+ // Simple UUID v4 implementation
69
+ return randomUUID();
70
+ }
71
+ /**
72
+ * Create a new Id (convenience static method)
73
+ */
74
+ static create() {
75
+ return new Id();
76
+ }
77
+ /**
78
+ * Create an Id from an existing value
79
+ */
80
+ static from(value) {
81
+ return new Id(value);
82
+ }
83
+ }
84
+ //# sourceMappingURL=id.js.map
package/dist/id.js.map ADDED
@@ -0,0 +1 @@
1
+ {"version":3,"file":"id.js","sourceRoot":"","sources":["../src/id.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,QAAQ,CAAC;AACpC,+EAA+E;AAC/E,uCAAuC;AACvC,+EAA+E;AAE/E,MAAM,OAAO,EAAE;IAIb;;;;;;;;;;;;OAYG;IACH,YAAY,KAAc;QACxB,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;YACxB,+CAA+C;YAC/C,IAAI,CAAC,MAAM,GAAG,KAAK,CAAC;YACpB,IAAI,CAAC,MAAM,GAAG,KAAK,CAAC;QACtB,CAAC;aAAM,CAAC;YACN,0DAA0D;YAC1D,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC,YAAY,EAAE,CAAC;YAClC,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC;QACrB,CAAC;IACH,CAAC;IAED;;OAEG;IACH,IAAI,KAAK;QACP,OAAO,IAAI,CAAC,MAAM,CAAC;IACrB,CAAC;IAED;;OAEG;IACH,IAAI,KAAK;QACP,OAAO,IAAI,CAAC,MAAM,CAAC;IACrB,CAAC;IAED;;OAEG;IACH,QAAQ;QACN,OAAO,IAAI,CAAC,MAAM,CAAC;IACrB,CAAC;IAED;;OAEG;IACH,MAAM;QACJ,OAAO,IAAI,CAAC,MAAM,CAAC;IACrB,CAAC;IAED;;OAEG;IACH,MAAM,CAAC,KAAkB;QACvB,IAAI,KAAK,YAAY,EAAE,EAAE,CAAC;YACxB,OAAO,IAAI,CAAC,MAAM,KAAK,KAAK,CAAC,MAAM,CAAC;QACtC,CAAC;QACD,OAAO,IAAI,CAAC,MAAM,KAAK,KAAK,CAAC;IAC/B,CAAC;IAED;;OAEG;IACK,YAAY;QAClB,gCAAgC;QAChC,OAAO,UAAU,EAAE,CAAC;IACtB,CAAC;IAED;;OAEG;IACH,MAAM,CAAC,MAAM;QACX,OAAO,IAAI,EAAE,EAAE,CAAC;IAClB,CAAC;IAED;;OAEG;IACH,MAAM,CAAC,IAAI,CAAC,KAAa;QACvB,OAAO,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC;IACvB,CAAC;CACF"}
@@ -0,0 +1,15 @@
1
+ export { Id } from "./id";
2
+ export { BaseEntity } from "./base-entity";
3
+ export { Entity, Aggregate } from "./entity";
4
+ export { ValueObject } from "./value-object";
5
+ export * from "./validation-error";
6
+ export * from "./domain-event";
7
+ export * from "./domain-event-bus";
8
+ export * from "./criteria";
9
+ export * from "./paginated-result";
10
+ export * from "./repository";
11
+ export { InMemoryRepository } from "./repository";
12
+ export * from "./types";
13
+ export * from "./constants";
14
+ export { DeepProxy } from "./deep-proxy";
15
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,EAAE,EAAE,MAAM,MAAM,CAAC;AAC1B,OAAO,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAC3C,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AAC7C,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAE7C,cAAc,oBAAoB,CAAC;AAGnC,cAAc,gBAAgB,CAAC;AAE/B,cAAc,oBAAoB,CAAC;AAGnC,cAAc,YAAY,CAAC;AAC3B,cAAc,oBAAoB,CAAC;AAGnC,cAAc,cAAc,CAAC;AAE7B,OAAO,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAGlD,cAAc,SAAS,CAAC;AAExB,cAAc,aAAa,CAAC;AAG5B,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,25 @@
1
+ // ============================================================================
2
+ // Rich Domain Library - Main Exports
3
+ // ============================================================================
4
+ // Core Classes
5
+ export { Id } from "./id";
6
+ export { BaseEntity } from "./base-entity";
7
+ export { Entity, Aggregate } from "./entity";
8
+ export { ValueObject } from "./value-object";
9
+ export * from "./validation-error";
10
+ // Domain Events
11
+ export * from "./domain-event";
12
+ export * from "./domain-event-bus";
13
+ // Criteria & Repository
14
+ export * from "./criteria";
15
+ export * from "./paginated-result";
16
+ // Repository
17
+ export * from "./repository";
18
+ // Backward compatibility - re-export InMemoryRepository at top level
19
+ export { InMemoryRepository } from "./repository";
20
+ // Types
21
+ export * from "./types";
22
+ export * from "./constants";
23
+ // Internal (for advanced usage)
24
+ export { DeepProxy } from "./deep-proxy";
25
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,+EAA+E;AAC/E,qCAAqC;AACrC,+EAA+E;AAE/E,eAAe;AACf,OAAO,EAAE,EAAE,EAAE,MAAM,MAAM,CAAC;AAC1B,OAAO,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAC3C,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AAC7C,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAE7C,cAAc,oBAAoB,CAAC;AAEnC,gBAAgB;AAChB,cAAc,gBAAgB,CAAC;AAE/B,cAAc,oBAAoB,CAAC;AAEnC,wBAAwB;AACxB,cAAc,YAAY,CAAC;AAC3B,cAAc,oBAAoB,CAAC;AAEnC,aAAa;AACb,cAAc,cAAc,CAAC;AAC7B,qEAAqE;AACrE,OAAO,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAElD,QAAQ;AACR,cAAc,SAAS,CAAC;AAExB,cAAc,aAAa,CAAC;AAE5B,gCAAgC;AAChC,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC"}
@@ -0,0 +1,93 @@
1
+ /**
2
+ * Direction for ordering
3
+ */
4
+ export type OrderDirection = "asc" | "desc";
5
+ /**
6
+ * Configuration for a single field ordering
7
+ */
8
+ export interface OrderByConfig<T> {
9
+ field: keyof T | string;
10
+ direction: OrderDirection;
11
+ }
12
+ /**
13
+ * Represents ordering configuration for a specific field
14
+ * Can be used for in-memory sorting or converted to database-specific queries
15
+ *
16
+ * @example
17
+ * ```typescript
18
+ * const orderBy = new OrderBy('name', 'asc');
19
+ * const items = users.sort(orderBy.compareFn());
20
+ * ```
21
+ */
22
+ export declare class OrderBy<T> {
23
+ private readonly _field;
24
+ private readonly _direction;
25
+ constructor(field: keyof T | string, direction?: OrderDirection);
26
+ /**
27
+ * Gets the field to order by
28
+ */
29
+ get field(): keyof T | string;
30
+ /**
31
+ * Gets the order direction
32
+ */
33
+ get direction(): OrderDirection;
34
+ /**
35
+ * Returns configuration object for serialization
36
+ */
37
+ toConfig(): OrderByConfig<T>;
38
+ /**
39
+ * Creates a comparison function for in-memory sorting
40
+ * Handles nested properties using dot notation (e.g., "address.city")
41
+ *
42
+ * @example
43
+ * ```typescript
44
+ * const orderBy = new OrderBy<User>('name', 'asc');
45
+ * const sorted = users.sort(orderBy.compareFn());
46
+ * ```
47
+ */
48
+ compareFn(): (a: T, b: T) => number;
49
+ /**
50
+ * Gets nested property value using dot notation
51
+ * @private
52
+ */
53
+ private getNestedValue;
54
+ /**
55
+ * Creates a new OrderBy with reversed direction
56
+ */
57
+ reverse(): OrderBy<T>;
58
+ }
59
+ /**
60
+ * Represents multiple ordering configurations
61
+ * Allows sorting by multiple fields with different directions
62
+ *
63
+ * @example
64
+ * ```typescript
65
+ * const multiOrder = new MultiOrderBy<User>([
66
+ * new OrderBy('status', 'asc'),
67
+ * new OrderBy('name', 'asc')
68
+ * ]);
69
+ * const sorted = users.sort(multiOrder.compareFn());
70
+ * ```
71
+ */
72
+ export declare class MultiOrderBy<T> {
73
+ private readonly _orderings;
74
+ constructor(orderings: OrderBy<T>[]);
75
+ /**
76
+ * Gets all ordering configurations
77
+ */
78
+ get orderings(): readonly OrderBy<T>[];
79
+ /**
80
+ * Returns array of configuration objects for serialization
81
+ */
82
+ toConfig(): OrderByConfig<T>[];
83
+ /**
84
+ * Creates a comparison function that applies all orderings in sequence
85
+ * If first ordering produces a tie, moves to next ordering, and so on
86
+ */
87
+ compareFn(): (a: T, b: T) => number;
88
+ /**
89
+ * Adds another ordering to the end of the list
90
+ */
91
+ thenBy(field: keyof T | string, direction?: OrderDirection): MultiOrderBy<T>;
92
+ }
93
+ //# sourceMappingURL=ordering.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ordering.d.ts","sourceRoot":"","sources":["../src/ordering.ts"],"names":[],"mappings":"AAMA;;GAEG;AACH,MAAM,MAAM,cAAc,GAAG,KAAK,GAAG,MAAM,CAAC;AAE5C;;GAEG;AACH,MAAM,WAAW,aAAa,CAAC,CAAC;IAC9B,KAAK,EAAE,MAAM,CAAC,GAAG,MAAM,CAAC;IACxB,SAAS,EAAE,cAAc,CAAC;CAC3B;AAMD;;;;;;;;;GASG;AACH,qBAAa,OAAO,CAAC,CAAC;IACpB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAmB;IAC1C,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAiB;gBAEhC,KAAK,EAAE,MAAM,CAAC,GAAG,MAAM,EAAE,SAAS,GAAE,cAAsB;IAKtE;;OAEG;IACH,IAAI,KAAK,IAAI,MAAM,CAAC,GAAG,MAAM,CAE5B;IAED;;OAEG;IACH,IAAI,SAAS,IAAI,cAAc,CAE9B;IAED;;OAEG;IACH,QAAQ,IAAI,aAAa,CAAC,CAAC,CAAC;IAO5B;;;;;;;;;OASG;IACH,SAAS,IAAI,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,KAAK,MAAM;IAsBnC;;;OAGG;IACH,OAAO,CAAC,cAAc;IAItB;;OAEG;IACH,OAAO,IAAI,OAAO,CAAC,CAAC,CAAC;CAItB;AAMD;;;;;;;;;;;;GAYG;AACH,qBAAa,YAAY,CAAC,CAAC;IACzB,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAe;gBAE9B,SAAS,EAAE,OAAO,CAAC,CAAC,CAAC,EAAE;IAOnC;;OAEG;IACH,IAAI,SAAS,IAAI,SAAS,OAAO,CAAC,CAAC,CAAC,EAAE,CAErC;IAED;;OAEG;IACH,QAAQ,IAAI,aAAa,CAAC,CAAC,CAAC,EAAE;IAI9B;;;OAGG;IACH,SAAS,IAAI,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,KAAK,MAAM;IAYnC;;OAEG;IACH,MAAM,CAAC,KAAK,EAAE,MAAM,CAAC,GAAG,MAAM,EAAE,SAAS,GAAE,cAAsB,GAAG,YAAY,CAAC,CAAC,CAAC;CAIpF"}
@@ -0,0 +1,154 @@
1
+ "use strict";
2
+ // ============================================================================
3
+ // Ordering System
4
+ // ============================================================================
5
+ // Provides ordering/sorting capabilities for queries and in-memory collections
6
+ // Compatible with Entity, Aggregate, and ValueObject types
7
+ Object.defineProperty(exports, "__esModule", { value: true });
8
+ exports.MultiOrderBy = exports.OrderBy = void 0;
9
+ // ============================================================================
10
+ // OrderBy Class
11
+ // ============================================================================
12
+ /**
13
+ * Represents ordering configuration for a specific field
14
+ * Can be used for in-memory sorting or converted to database-specific queries
15
+ *
16
+ * @example
17
+ * ```typescript
18
+ * const orderBy = new OrderBy('name', 'asc');
19
+ * const items = users.sort(orderBy.compareFn());
20
+ * ```
21
+ */
22
+ class OrderBy {
23
+ constructor(field, direction = "asc") {
24
+ this._field = field;
25
+ this._direction = direction;
26
+ }
27
+ /**
28
+ * Gets the field to order by
29
+ */
30
+ get field() {
31
+ return this._field;
32
+ }
33
+ /**
34
+ * Gets the order direction
35
+ */
36
+ get direction() {
37
+ return this._direction;
38
+ }
39
+ /**
40
+ * Returns configuration object for serialization
41
+ */
42
+ toConfig() {
43
+ return {
44
+ field: this._field,
45
+ direction: this._direction,
46
+ };
47
+ }
48
+ /**
49
+ * Creates a comparison function for in-memory sorting
50
+ * Handles nested properties using dot notation (e.g., "address.city")
51
+ *
52
+ * @example
53
+ * ```typescript
54
+ * const orderBy = new OrderBy<User>('name', 'asc');
55
+ * const sorted = users.sort(orderBy.compareFn());
56
+ * ```
57
+ */
58
+ compareFn() {
59
+ return (a, b) => {
60
+ const aValue = this.getNestedValue(a, this._field);
61
+ const bValue = this.getNestedValue(b, this._field);
62
+ // Handle null/undefined values
63
+ if (aValue === null || aValue === undefined)
64
+ return 1;
65
+ if (bValue === null || bValue === undefined)
66
+ return -1;
67
+ // Compare values
68
+ let comparison = 0;
69
+ if (aValue > bValue) {
70
+ comparison = 1;
71
+ }
72
+ else if (aValue < bValue) {
73
+ comparison = -1;
74
+ }
75
+ // Apply direction
76
+ return this._direction === "asc" ? comparison : -comparison;
77
+ };
78
+ }
79
+ /**
80
+ * Gets nested property value using dot notation
81
+ * @private
82
+ */
83
+ getNestedValue(obj, path) {
84
+ return path.split(".").reduce((current, prop) => current?.[prop], obj);
85
+ }
86
+ /**
87
+ * Creates a new OrderBy with reversed direction
88
+ */
89
+ reverse() {
90
+ const newDirection = this._direction === "asc" ? "desc" : "asc";
91
+ return new OrderBy(this._field, newDirection);
92
+ }
93
+ }
94
+ exports.OrderBy = OrderBy;
95
+ // ============================================================================
96
+ // MultiOrderBy Class
97
+ // ============================================================================
98
+ /**
99
+ * Represents multiple ordering configurations
100
+ * Allows sorting by multiple fields with different directions
101
+ *
102
+ * @example
103
+ * ```typescript
104
+ * const multiOrder = new MultiOrderBy<User>([
105
+ * new OrderBy('status', 'asc'),
106
+ * new OrderBy('name', 'asc')
107
+ * ]);
108
+ * const sorted = users.sort(multiOrder.compareFn());
109
+ * ```
110
+ */
111
+ class MultiOrderBy {
112
+ constructor(orderings) {
113
+ if (orderings.length === 0) {
114
+ throw new Error("At least one OrderBy is required");
115
+ }
116
+ this._orderings = orderings;
117
+ }
118
+ /**
119
+ * Gets all ordering configurations
120
+ */
121
+ get orderings() {
122
+ return this._orderings;
123
+ }
124
+ /**
125
+ * Returns array of configuration objects for serialization
126
+ */
127
+ toConfig() {
128
+ return this._orderings.map((o) => o.toConfig());
129
+ }
130
+ /**
131
+ * Creates a comparison function that applies all orderings in sequence
132
+ * If first ordering produces a tie, moves to next ordering, and so on
133
+ */
134
+ compareFn() {
135
+ return (a, b) => {
136
+ for (const ordering of this._orderings) {
137
+ const comparison = ordering.compareFn()(a, b);
138
+ if (comparison !== 0) {
139
+ return comparison;
140
+ }
141
+ }
142
+ return 0;
143
+ };
144
+ }
145
+ /**
146
+ * Adds another ordering to the end of the list
147
+ */
148
+ thenBy(field, direction = "asc") {
149
+ const newOrderBy = new OrderBy(field, direction);
150
+ return new MultiOrderBy([...this._orderings, newOrderBy]);
151
+ }
152
+ }
153
+ exports.MultiOrderBy = MultiOrderBy;
154
+ //# sourceMappingURL=ordering.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ordering.js","sourceRoot":"","sources":["../src/ordering.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,62 @@
1
+ import type { Criteria } from "./criteria";
2
+ import type { Pagination, PaginationMeta } from "./types";
3
+ /**
4
+ * Infers the JSON result type from T
5
+ * - If T has toJson(), returns its return type
6
+ * - Otherwise returns T as-is
7
+ */
8
+ type InferJsonResult<T> = T extends {
9
+ toJson(): infer R;
10
+ } ? R : T;
11
+ /**
12
+ * Type for the serialized result of PaginatedResult.toJSON()
13
+ */
14
+ export type PaginatedJsonResult<T> = {
15
+ data: InferJsonResult<T>[];
16
+ meta: PaginationMeta;
17
+ };
18
+ export declare class PaginatedResult<T> {
19
+ readonly data: T[];
20
+ readonly meta: PaginationMeta;
21
+ constructor(data: T[], meta: PaginationMeta);
22
+ /**
23
+ * Creates a PaginatedResult with calculated metadata
24
+ */
25
+ static create<T>(data: T[], pagination: Pagination, total: number): PaginatedResult<T>;
26
+ /**
27
+ * Creates pagination metadata from total count
28
+ */
29
+ static createMeta(pagination: Pagination, total: number): PaginationMeta;
30
+ /**
31
+ * Applies criteria to an in-memory array (useful for testing)
32
+ */
33
+ static fromArray<T>(items: T[], criteria: Criteria<T>): PaginatedResult<T>;
34
+ /**
35
+ * Converts the result to JSON, deeply serializing all entities/aggregates/value objects
36
+ * - Entities/Aggregates → calls toJson() recursively
37
+ * - Value Objects → calls toJson()
38
+ * - Id → converts to string
39
+ * - Arrays → maps recursively
40
+ * - Plain objects → serializes properties recursively
41
+ * - Primitives → returns as-is
42
+ */
43
+ toJSON(): PaginatedJsonResult<T>;
44
+ /**
45
+ * Deep serialization logic (similar to BaseEntity.deepToJson)
46
+ */
47
+ private deepSerialize;
48
+ /**
49
+ * Transform each item in the result using a mapper function
50
+ */
51
+ map<U>(fn: (item: T) => U): PaginatedResult<U>;
52
+ /**
53
+ * Check if result has no data
54
+ */
55
+ get isEmpty(): boolean;
56
+ /**
57
+ * Check if there are more pages available
58
+ */
59
+ get hasMore(): boolean;
60
+ }
61
+ export {};
62
+ //# sourceMappingURL=paginated-result.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"paginated-result.d.ts","sourceRoot":"","sources":["../src/paginated-result.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAC3C,OAAO,KAAK,EAAE,UAAU,EAAE,cAAc,EAAU,MAAM,SAAS,CAAC;AAMlE;;;;GAIG;AACH,KAAK,eAAe,CAAC,CAAC,IAAI,CAAC,SAAS;IAAE,MAAM,IAAI,MAAM,CAAC,CAAA;CAAE,GAAG,CAAC,GAAG,CAAC,CAAC;AAElE;;GAEG;AACH,MAAM,MAAM,mBAAmB,CAAC,CAAC,IAAI;IACnC,IAAI,EAAE,eAAe,CAAC,CAAC,CAAC,EAAE,CAAC;IAC3B,IAAI,EAAE,cAAc,CAAC;CACtB,CAAC;AAMF,qBAAa,eAAe,CAAC,CAAC;aAEV,IAAI,EAAE,CAAC,EAAE;aACT,IAAI,EAAE,cAAc;gBADpB,IAAI,EAAE,CAAC,EAAE,EACT,IAAI,EAAE,cAAc;IAGtC;;OAEG;IACH,MAAM,CAAC,MAAM,CAAC,CAAC,EACb,IAAI,EAAE,CAAC,EAAE,EACT,UAAU,EAAE,UAAU,EACtB,KAAK,EAAE,MAAM,GACZ,eAAe,CAAC,CAAC,CAAC;IAKrB;;OAEG;IACH,MAAM,CAAC,UAAU,CAAC,UAAU,EAAE,UAAU,EAAE,KAAK,EAAE,MAAM,GAAG,cAAc;IAaxE;;OAEG;IACH,MAAM,CAAC,SAAS,CAAC,CAAC,EAChB,KAAK,EAAE,CAAC,EAAE,EACV,QAAQ,EAAE,QAAQ,CAAC,CAAC,CAAC,GACpB,eAAe,CAAC,CAAC,CAAC;IA0CrB;;;;;;;;OAQG;IACH,MAAM,IAAI,mBAAmB,CAAC,CAAC,CAAC;IAShC;;OAEG;IACH,OAAO,CAAC,aAAa;IA+BrB;;OAEG;IACH,GAAG,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,GAAG,eAAe,CAAC,CAAC,CAAC;IAI9C;;OAEG;IACH,IAAI,OAAO,IAAI,OAAO,CAErB;IAED;;OAEG;IACH,IAAI,OAAO,IAAI,OAAO,CAErB;CACF"}
@@ -0,0 +1,201 @@
1
+ // ============================================================================
2
+ // PaginatedResult - Container for paginated data with deep serialization
3
+ // ============================================================================
4
+ import { Id } from "./id";
5
+ // ============================================================================
6
+ // PaginatedResult Class
7
+ // ============================================================================
8
+ export class PaginatedResult {
9
+ constructor(data, meta) {
10
+ this.data = data;
11
+ this.meta = meta;
12
+ }
13
+ /**
14
+ * Creates a PaginatedResult with calculated metadata
15
+ */
16
+ static create(data, pagination, total) {
17
+ const meta = this.createMeta(pagination, total);
18
+ return new PaginatedResult(data, meta);
19
+ }
20
+ /**
21
+ * Creates pagination metadata from total count
22
+ */
23
+ static createMeta(pagination, total) {
24
+ const totalPages = Math.ceil(total / pagination.limit);
25
+ return {
26
+ page: pagination.page,
27
+ limit: pagination.limit,
28
+ total,
29
+ totalPages,
30
+ hasNext: pagination.page < totalPages,
31
+ hasPrevious: pagination.page > 1,
32
+ };
33
+ }
34
+ /**
35
+ * Applies criteria to an in-memory array (useful for testing)
36
+ */
37
+ static fromArray(items, criteria) {
38
+ let result = [...items];
39
+ // Apply filters
40
+ for (const filter of criteria.getFilters()) {
41
+ result = result.filter((item) => applyFilter(item, filter));
42
+ }
43
+ const total = result.length;
44
+ // Apply ordering
45
+ for (const order of criteria.getOrders().reverse()) {
46
+ result.sort((a, b) => {
47
+ const aVal = getNestedValue(a, order.field);
48
+ const bVal = getNestedValue(b, order.field);
49
+ let comparison = 0;
50
+ if (aVal < bVal)
51
+ comparison = -1;
52
+ if (aVal > bVal)
53
+ comparison = 1;
54
+ return order.direction === "desc" ? -comparison : comparison;
55
+ });
56
+ }
57
+ // Apply pagination
58
+ const pagination = criteria.getPagination();
59
+ if (pagination) {
60
+ result = result.slice(pagination.offset, pagination.offset + pagination.limit);
61
+ return PaginatedResult.create(result, pagination, total);
62
+ }
63
+ // No pagination - return all with default meta
64
+ return PaginatedResult.create(result, { page: 1, limit: result.length, offset: 0 }, total);
65
+ }
66
+ /**
67
+ * Converts the result to JSON, deeply serializing all entities/aggregates/value objects
68
+ * - Entities/Aggregates → calls toJson() recursively
69
+ * - Value Objects → calls toJson()
70
+ * - Id → converts to string
71
+ * - Arrays → maps recursively
72
+ * - Plain objects → serializes properties recursively
73
+ * - Primitives → returns as-is
74
+ */
75
+ toJSON() {
76
+ return {
77
+ data: this.data.map((item) => this.deepSerialize(item)),
78
+ meta: this.meta,
79
+ };
80
+ }
81
+ /**
82
+ * Deep serialization logic (similar to BaseEntity.deepToJson)
83
+ */
84
+ deepSerialize(obj) {
85
+ if (obj === null || obj === undefined)
86
+ return obj;
87
+ // Id → string
88
+ if (obj instanceof Id)
89
+ return obj.value;
90
+ // Arrays → map recursively
91
+ if (Array.isArray(obj)) {
92
+ return obj.map((item) => this.deepSerialize(item));
93
+ }
94
+ // Objects with toJson() method (Entity/Aggregate/ValueObject)
95
+ if (obj && typeof obj.toJson === "function") {
96
+ return obj.toJson();
97
+ }
98
+ // Plain objects → serialize properties recursively
99
+ if (typeof obj === "object") {
100
+ const result = {};
101
+ for (const key in obj) {
102
+ if (obj.hasOwnProperty(key)) {
103
+ result[key] = this.deepSerialize(obj[key]);
104
+ }
105
+ }
106
+ return result;
107
+ }
108
+ // Primitives
109
+ return obj;
110
+ }
111
+ /**
112
+ * Transform each item in the result using a mapper function
113
+ */
114
+ map(fn) {
115
+ return new PaginatedResult(this.data.map(fn), this.meta);
116
+ }
117
+ /**
118
+ * Check if result has no data
119
+ */
120
+ get isEmpty() {
121
+ return this.data.length === 0;
122
+ }
123
+ /**
124
+ * Check if there are more pages available
125
+ */
126
+ get hasMore() {
127
+ return this.meta.hasNext;
128
+ }
129
+ }
130
+ // ============================================================================
131
+ // Helper Functions (moved from criteria.ts)
132
+ // ============================================================================
133
+ function applyFilter(item, filter) {
134
+ const value = getNestedValue(item, filter.field);
135
+ const isValueDate = value instanceof Date;
136
+ const parseValue = (v) => {
137
+ if (isValueDate && typeof v === "string")
138
+ return new Date(v);
139
+ if (isValueDate && typeof v === "number")
140
+ return new Date(v);
141
+ return v;
142
+ };
143
+ switch (filter.operator) {
144
+ case "equals":
145
+ return value === filter.value;
146
+ case "notEquals":
147
+ return value !== filter.value;
148
+ case "greaterThan": {
149
+ const compareTo = parseValue(filter.value);
150
+ return isValueDate ? value > compareTo : value > filter.value;
151
+ }
152
+ case "greaterThanOrEqual": {
153
+ const compareTo = parseValue(filter.value);
154
+ return isValueDate ? value >= compareTo : value >= filter.value;
155
+ }
156
+ case "lessThan":
157
+ const lt = parseValue(filter.value);
158
+ return isValueDate ? value < lt : value < filter.value;
159
+ case "lessThanOrEqual":
160
+ const lte = parseValue(filter.value);
161
+ return isValueDate ? value <= lte : value <= filter.value;
162
+ case "contains":
163
+ return String(value)
164
+ .toLowerCase()
165
+ .includes(String(filter.value).toLowerCase());
166
+ case "startsWith":
167
+ return String(value)
168
+ .toLowerCase()
169
+ .startsWith(String(filter.value).toLowerCase());
170
+ case "endsWith":
171
+ return String(value)
172
+ .toLowerCase()
173
+ .endsWith(String(filter.value).toLowerCase());
174
+ case "in":
175
+ return Array.isArray(filter.value) && filter.value.includes(value);
176
+ case "notIn":
177
+ return Array.isArray(filter.value) && !filter.value.includes(value);
178
+ case "between":
179
+ if (Array.isArray(filter.value) && filter.value.length === 2) {
180
+ const [min, max] = filter.value.map(parseValue);
181
+ return isValueDate
182
+ ? value >= min && value <= max
183
+ : value >= filter.value[0] && value <= filter.value[1];
184
+ }
185
+ return false;
186
+ case "isNull":
187
+ return value === null || value === undefined;
188
+ case "isNotNull":
189
+ return value !== null && value !== undefined;
190
+ default:
191
+ return true;
192
+ }
193
+ }
194
+ function getNestedValue(obj, path) {
195
+ return path.split(".").reduce((current, key) => {
196
+ if (current === null || current === undefined)
197
+ return undefined;
198
+ return current[key];
199
+ }, obj);
200
+ }
201
+ //# sourceMappingURL=paginated-result.js.map