@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.
- package/.github/workflows/ci.yml +40 -0
- package/.husky/commit-msg +1 -0
- package/.husky/pre-commit +1 -0
- package/.versionrc.json +21 -0
- package/.vscode/settings.json +3 -0
- package/CHANGELOG.md +81 -0
- package/LICENSE +21 -0
- package/README.md +712 -0
- package/commitlint.config.js +23 -0
- package/dist/base-entity.d.ts +67 -0
- package/dist/base-entity.d.ts.map +1 -0
- package/dist/base-entity.js +309 -0
- package/dist/base-entity.js.map +1 -0
- package/dist/constants.d.ts +3 -0
- package/dist/constants.d.ts.map +1 -0
- package/dist/constants.js +6 -0
- package/dist/constants.js.map +1 -0
- package/dist/criteria.d.ts +60 -0
- package/dist/criteria.d.ts.map +1 -0
- package/dist/criteria.js +214 -0
- package/dist/criteria.js.map +1 -0
- package/dist/deep-proxy.d.ts +34 -0
- package/dist/deep-proxy.d.ts.map +1 -0
- package/dist/deep-proxy.js +297 -0
- package/dist/deep-proxy.js.map +1 -0
- package/dist/domain-event-bus.d.ts +57 -0
- package/dist/domain-event-bus.d.ts.map +1 -0
- package/dist/domain-event-bus.js +112 -0
- package/dist/domain-event-bus.js.map +1 -0
- package/dist/domain-event.d.ts +55 -0
- package/dist/domain-event.d.ts.map +1 -0
- package/dist/domain-event.js +42 -0
- package/dist/domain-event.js.map +1 -0
- package/dist/entity.d.ts +13 -0
- package/dist/entity.d.ts.map +1 -0
- package/dist/entity.js +15 -0
- package/dist/entity.js.map +1 -0
- package/dist/filtering.d.ts +107 -0
- package/dist/filtering.d.ts.map +1 -0
- package/dist/filtering.js +202 -0
- package/dist/filtering.js.map +1 -0
- package/dist/id.d.ts +51 -0
- package/dist/id.d.ts.map +1 -0
- package/dist/id.js +84 -0
- package/dist/id.js.map +1 -0
- package/dist/index.d.ts +15 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +25 -0
- package/dist/index.js.map +1 -0
- package/dist/ordering.d.ts +93 -0
- package/dist/ordering.d.ts.map +1 -0
- package/dist/ordering.js +154 -0
- package/dist/ordering.js.map +1 -0
- package/dist/paginated-result.d.ts +62 -0
- package/dist/paginated-result.d.ts.map +1 -0
- package/dist/paginated-result.js +201 -0
- package/dist/paginated-result.js.map +1 -0
- package/dist/pagination.d.ts +218 -0
- package/dist/pagination.d.ts.map +1 -0
- package/dist/pagination.js +281 -0
- package/dist/pagination.js.map +1 -0
- package/dist/repository/base-repository.d.ts +77 -0
- package/dist/repository/base-repository.d.ts.map +1 -0
- package/dist/repository/base-repository.js +80 -0
- package/dist/repository/base-repository.js.map +1 -0
- package/dist/repository/in-memory-repository.d.ts +46 -0
- package/dist/repository/in-memory-repository.d.ts.map +1 -0
- package/dist/repository/in-memory-repository.js +85 -0
- package/dist/repository/in-memory-repository.js.map +1 -0
- package/dist/repository/index.d.ts +42 -0
- package/dist/repository/index.d.ts.map +1 -0
- package/dist/repository/index.js +47 -0
- package/dist/repository/index.js.map +1 -0
- package/dist/repository/mapper.d.ts +56 -0
- package/dist/repository/mapper.d.ts.map +1 -0
- package/dist/repository/mapper.js +15 -0
- package/dist/repository/mapper.js.map +1 -0
- package/dist/repository/types.d.ts +87 -0
- package/dist/repository/types.d.ts.map +1 -0
- package/dist/repository/types.js +6 -0
- package/dist/repository/types.js.map +1 -0
- package/dist/repository/unit-of-work.d.ts +70 -0
- package/dist/repository/unit-of-work.d.ts.map +1 -0
- package/dist/repository/unit-of-work.js +122 -0
- package/dist/repository/unit-of-work.js.map +1 -0
- package/dist/repository.d.ts +2 -0
- package/dist/repository.d.ts.map +1 -0
- package/dist/repository.js +21 -0
- package/dist/repository.js.map +1 -0
- package/dist/specification.d.ts +102 -0
- package/dist/specification.d.ts.map +1 -0
- package/dist/specification.js +187 -0
- package/dist/specification.js.map +1 -0
- package/dist/types/criteria.d.ts +35 -0
- package/dist/types/criteria.d.ts.map +1 -0
- package/dist/types/criteria.js +17 -0
- package/dist/types/criteria.js.map +1 -0
- package/dist/types/domain.d.ts +30 -0
- package/dist/types/domain.d.ts.map +1 -0
- package/dist/types/domain.js +2 -0
- package/dist/types/domain.js.map +1 -0
- package/dist/types/history-tracker.d.ts +36 -0
- package/dist/types/history-tracker.d.ts.map +1 -0
- package/dist/types/history-tracker.js +2 -0
- package/dist/types/history-tracker.js.map +1 -0
- package/dist/types/index.d.ts +8 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +8 -0
- package/dist/types/index.js.map +1 -0
- package/dist/types/repository.d.ts +43 -0
- package/dist/types/repository.d.ts.map +1 -0
- package/dist/types/repository.js +2 -0
- package/dist/types/repository.js.map +1 -0
- package/dist/types/standard-schema.d.ts +15 -0
- package/dist/types/standard-schema.d.ts.map +1 -0
- package/dist/types/standard-schema.js +2 -0
- package/dist/types/standard-schema.js.map +1 -0
- package/dist/types/unit-of-work.d.ts +39 -0
- package/dist/types/unit-of-work.d.ts.map +1 -0
- package/dist/types/unit-of-work.js +2 -0
- package/dist/types/unit-of-work.js.map +1 -0
- package/dist/types/utils.d.ts +14 -0
- package/dist/types/utils.d.ts.map +1 -0
- package/dist/types/utils.js +2 -0
- package/dist/types/utils.js.map +1 -0
- package/dist/types.d.ts +88 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +12 -0
- package/dist/types.js.map +1 -0
- package/dist/validation-error.d.ts +42 -0
- package/dist/validation-error.d.ts.map +1 -0
- package/dist/validation-error.js +73 -0
- package/dist/validation-error.js.map +1 -0
- package/dist/value-object.d.ts +47 -0
- package/dist/value-object.d.ts.map +1 -0
- package/dist/value-object.js +136 -0
- package/dist/value-object.js.map +1 -0
- package/eslint.config.js +51 -0
- package/jest.config.js +21 -0
- package/package.json +58 -0
- package/src/base-entity.ts +401 -0
- package/src/constants.ts +7 -0
- package/src/criteria.ts +291 -0
- package/src/deep-proxy.ts +339 -0
- package/src/domain-event-bus.ts +166 -0
- package/src/domain-event.ts +90 -0
- package/src/entity.ts +16 -0
- package/src/id.ts +94 -0
- package/src/index.ts +33 -0
- package/src/paginated-result.ts +274 -0
- package/src/repository/base-repository.ts +152 -0
- package/src/repository/in-memory-repository.ts +104 -0
- package/src/repository/index.ts +55 -0
- package/src/repository/mapper.ts +74 -0
- package/src/repository/unit-of-work.ts +148 -0
- package/src/types/criteria.ts +79 -0
- package/src/types/domain.ts +37 -0
- package/src/types/history-tracker.ts +45 -0
- package/src/types/index.ts +7 -0
- package/src/types/repository.ts +51 -0
- package/src/types/standard-schema.ts +19 -0
- package/src/types/unit-of-work.ts +46 -0
- package/src/types/utils.ts +29 -0
- package/src/validation-error.ts +97 -0
- package/src/value-object.ts +187 -0
- package/tests/criteria.test.ts +432 -0
- package/tests/domain-events.test.ts +445 -0
- package/tests/entity-equality.test.ts +487 -0
- package/tests/entity-validation.test.ts +339 -0
- package/tests/entity.test.ts +33 -0
- package/tests/history-tracker.spec.ts +667 -0
- package/tests/id.test.ts +341 -0
- package/tests/repository.test.ts +641 -0
- package/tests/to-json.test.ts +91 -0
- package/tests/utils.ts +151 -0
- package/tests/value-object-validation.test.ts +228 -0
- package/tests/value-objects.test.ts +52 -0
- 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"}
|
package/dist/index.d.ts
ADDED
|
@@ -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"}
|
package/dist/ordering.js
ADDED
|
@@ -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
|