@cullet/erp-core 1.0.2 → 1.0.4
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/README.md +24 -24
- package/dist/gate-engine-registry.js.map +1 -1
- package/dist/gate-v1-payload.schema.js +5 -0
- package/dist/gate-v1-payload.schema.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/outcome.js.map +1 -1
- package/dist/policies/engines/v1/gate/index.d.ts +1 -0
- package/dist/policies/engines/v1/gate/index.js.map +1 -1
- package/dist/policy-service.js.map +1 -1
- package/dist/validation-code.js.map +1 -1
- package/dist/validation-error.js.map +1 -1
- package/dist/validation-field.js.map +1 -1
- package/meta.json +67 -67
- package/package.json +56 -56
- package/src/core/application/commands/command.ts +3 -3
- package/src/core/application/commands/requested-by.ts +60 -60
- package/src/core/application/index.ts +21 -21
- package/src/core/application/policy-error-mapper.ts +54 -54
- package/src/core/application/ports/index.ts +6 -6
- package/src/core/application/ports/logger.port.ts +4 -4
- package/src/core/application/ports/metrics.port.ts +3 -3
- package/src/core/application/ports/policy-port.ts +7 -7
- package/src/core/application/ports/repository.port.ts +3 -3
- package/src/core/application/ports/temporal-repository.port.ts +4 -4
- package/src/core/application/ports/tracer.port.ts +7 -7
- package/src/core/application/queries/query.ts +13 -13
- package/src/core/application/temporal/index.ts +7 -7
- package/src/core/application/temporal/temporal-context.ts +20 -20
- package/src/core/application/temporal/temporal-use-case.ts +22 -22
- package/src/core/application/use-case.ts +10 -10
- package/src/core/config/core-config.instance.ts +1 -1
- package/src/core/config/core-config.ts +59 -59
- package/src/core/config/index.ts +6 -6
- package/src/core/config/policy-reporter.ts +31 -31
- package/src/core/config/silent-policy-reporter.ts +1 -1
- package/src/core/domain/entity.ts +44 -44
- package/src/core/domain/rulesets/entity-ruleset.contracts.ts +6 -6
- package/src/core/domain/rulesets/ruleset-registry.ts +49 -49
- package/src/core/domain/rulesets/ruleset.contracts.ts +6 -6
- package/src/core/domain/rulesets/value-object-ruleset.contracts.ts +5 -5
- package/src/core/domain/temporal/index.ts +11 -11
- package/src/core/domain/temporal/temporal-range.ts +16 -16
- package/src/core/domain/temporal/temporal-snapshot.ts +24 -24
- package/src/core/domain/temporal/transaction-time.ts +30 -30
- package/src/core/domain/temporal/valid-time.ts +29 -29
- package/src/core/domain/value-object.ts +16 -16
- package/src/core/errors/app-error.ts +64 -64
- package/src/core/errors/authentication-error.ts +134 -134
- package/src/core/errors/authorization-error.ts +154 -154
- package/src/core/errors/business-rule-violation-error.ts +15 -15
- package/src/core/errors/conflict-error.ts +202 -201
- package/src/core/errors/error-codes.ts +48 -48
- package/src/core/errors/idempotency-error.ts +324 -320
- package/src/core/errors/index.ts +50 -50
- package/src/core/errors/integration-error.ts +120 -120
- package/src/core/errors/legacy-incompatible-error.ts +13 -13
- package/src/core/errors/not-found-error.ts +14 -14
- package/src/core/errors/serialization-error.ts +167 -167
- package/src/core/errors/temporal-error.ts +109 -107
- package/src/core/errors/types.ts +35 -35
- package/src/core/errors/unexpected-error.ts +10 -10
- package/src/core/errors/utils/index.ts +2 -2
- package/src/core/errors/utils/json-safe.ts +53 -53
- package/src/core/errors/validation-error.ts +18 -18
- package/src/core/exceptions/business-rule-violation-exception.ts +6 -6
- package/src/core/exceptions/domain-exception.ts +5 -5
- package/src/core/exceptions/entity-not-found-exception.ts +6 -6
- package/src/core/exceptions/invalid-state-transition-exception.ts +8 -8
- package/src/core/exceptions/invariant-violation-exception.ts +3 -3
- package/src/core/exceptions/validation-code.ts +35 -29
- package/src/core/exceptions/validation-exception.ts +19 -19
- package/src/core/exceptions/validation-field.ts +20 -19
- package/src/core/index.ts +7 -7
- package/src/core/policies/asof/asof.ts +47 -47
- package/src/core/policies/catalog/catalog.instance.ts +8 -6
- package/src/core/policies/catalog/index.ts +4 -4
- package/src/core/policies/catalog/policy-catalog-entry.ts +199 -197
- package/src/core/policies/catalog/policy-catalog.ts +101 -101
- package/src/core/policies/catalog/policy-key.ts +51 -51
- package/src/core/policies/context/context-builder.ts +335 -313
- package/src/core/policies/context/context-registry.instance.ts +9 -9
- package/src/core/policies/context/context-registry.ts +61 -61
- package/src/core/policies/context/context-resolver.ts +12 -12
- package/src/core/policies/context/context-seed.ts +19 -15
- package/src/core/policies/context/index.ts +9 -9
- package/src/core/policies/context/path.ts +135 -128
- package/src/core/policies/defs/in-memory-policy-definition-repo.ts +56 -55
- package/src/core/policies/defs/index.ts +10 -10
- package/src/core/policies/defs/policy-definition-repository.ts +3 -3
- package/src/core/policies/defs/policy-definition.ts +143 -142
- package/src/core/policies/defs/policy-scope.ts +21 -19
- package/src/core/policies/defs/policy-semver.ts +51 -51
- package/src/core/policies/engines/compute-engine-registry.ts +43 -43
- package/src/core/policies/engines/compute-evaluator-registry.ts +20 -20
- package/src/core/policies/engines/compute-registry.ts +45 -45
- package/src/core/policies/engines/compute-types.ts +15 -15
- package/src/core/policies/engines/condition-evaluator-reporter.ts +17 -17
- package/src/core/policies/engines/gate-engine-registry.ts +27 -25
- package/src/core/policies/engines/gate-types.ts +28 -28
- package/src/core/policies/engines/index.ts +28 -28
- package/src/core/policies/engines/parse-compute-payload.ts +51 -45
- package/src/core/policies/engines/parse-gate-payload.ts +42 -42
- package/src/core/policies/engines/v1/compute/compute-engine-v1.ts +39 -37
- package/src/core/policies/engines/v1/compute/compute-payload.schema.ts +43 -41
- package/src/core/policies/engines/v1/compute/index.ts +6 -6
- package/src/core/policies/engines/v1/compute/resolve-decision-table-v1.ts +41 -39
- package/src/core/policies/engines/v1/condition-evaluator.ts +557 -504
- package/src/core/policies/engines/v1/condition-schema.ts +35 -35
- package/src/core/policies/engines/v1/condition-types.ts +21 -21
- package/src/core/policies/engines/v1/gate/gate-engine-v1.ts +134 -130
- package/src/core/policies/engines/v1/gate/gate-types-v1.ts +5 -5
- package/src/core/policies/engines/v1/gate/gate-v1-payload.schema.ts +32 -30
- package/src/core/policies/engines/v1/gate/index.ts +4 -4
- package/src/core/policies/engines/v1/index.ts +8 -8
- package/src/core/policies/index.ts +72 -72
- package/src/core/policies/package/policy-package.ts +3 -3
- package/src/core/policies/policy-ids.ts +25 -25
- package/src/core/policies/resolver/policy-resolver.ts +34 -34
- package/src/core/policies/service/index.ts +6 -6
- package/src/core/policies/service/policy-evaluation-error.ts +141 -133
- package/src/core/policies/service/policy-service.ts +432 -419
- package/src/core/policies/utils/date.ts +3 -3
- package/src/core/policies/utils/hash.ts +61 -61
- package/src/core/result/outcome.ts +131 -126
- package/src/core/result/result.ts +130 -127
- package/src/core/shared/aggregate-version.ts +5 -5
- package/src/core/shared/hashing.ts +18 -18
- package/src/core/shared/immutable.ts +29 -29
- package/src/core/shared/temporal-guards.ts +9 -9
- package/src/core/versioning/domain-event-contracts.ts +40 -40
- package/src/core/versioning/version.ts +19 -19
- package/src/examples/rulesets/entity/order-creation-rules-v1.ts +18 -18
- package/src/examples/rulesets/entity/order-invariants-v1.ts +31 -29
- package/src/examples/rulesets/entity/order-invariants-v2.ts +12 -12
- package/src/examples/rulesets/entity/order.ts +127 -127
- package/src/examples/rulesets/value-object/cpf-rules-v1.ts +41 -39
- package/src/examples/rulesets/value-object/cpf-rules-v2.ts +14 -12
- package/src/examples/rulesets/value-object/cpf.ts +24 -24
- package/src/examples/rulesets/value-object/customer.ts +42 -39
- package/src/examples/rulesets/value-object/person-name-rules-v1.ts +27 -27
- package/src/examples/rulesets/value-object/person-name-rules-v2.ts +13 -13
- package/src/examples/rulesets/value-object/person-name.ts +29 -26
- package/src/index.ts +2 -2
- package/src/version.ts +1 -1
|
@@ -4,28 +4,28 @@ import { type OrderCreationData, type OrderCreationRuleset } from "./order";
|
|
|
4
4
|
class OrderCreationValidationError extends DomainException {}
|
|
5
5
|
|
|
6
6
|
class OrderCreationRulesV1 implements OrderCreationRuleset {
|
|
7
|
-
|
|
8
|
-
|
|
7
|
+
readonly id = "order-creation@1.0" as const;
|
|
8
|
+
readonly description = "Order creation rules — version 1.0";
|
|
9
9
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
10
|
+
validate(data: OrderCreationData): void {
|
|
11
|
+
if (!data.customerId) {
|
|
12
|
+
throw new OrderCreationValidationError("customerId is required.");
|
|
13
|
+
}
|
|
14
14
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
15
|
+
if (!data.items || data.items.length === 0) {
|
|
16
|
+
throw new OrderCreationValidationError(
|
|
17
|
+
"The order must contain at least one item.",
|
|
18
|
+
);
|
|
19
|
+
}
|
|
20
20
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
21
|
+
for (const item of data.items) {
|
|
22
|
+
if (item.quantity <= 0) {
|
|
23
|
+
throw new OrderCreationValidationError(
|
|
24
|
+
`Item "${item.sku}" quantity must be greater than zero. Received: ${item.quantity}.`,
|
|
25
|
+
);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
27
28
|
}
|
|
28
|
-
}
|
|
29
29
|
}
|
|
30
30
|
|
|
31
31
|
export { OrderCreationRulesV1 };
|
|
@@ -1,48 +1,50 @@
|
|
|
1
1
|
import {
|
|
2
|
-
|
|
3
|
-
|
|
2
|
+
DomainException,
|
|
3
|
+
type RulesetId,
|
|
4
4
|
} from "../../../core/domain/rulesets/entity-ruleset.contracts";
|
|
5
5
|
import {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
6
|
+
type Order,
|
|
7
|
+
type OrderInvariantRuleset,
|
|
8
|
+
type OrderItem,
|
|
9
9
|
} from "./order";
|
|
10
10
|
|
|
11
11
|
class OrderInvariantError extends DomainException {}
|
|
12
12
|
|
|
13
13
|
class OrderInvariantsV1 implements OrderInvariantRuleset {
|
|
14
|
-
|
|
15
|
-
|
|
14
|
+
readonly id: RulesetId = "order-invariants@1.0";
|
|
15
|
+
readonly description: string = "Order invariants — version 1.0";
|
|
16
16
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
17
|
+
assertCanAddItem(order: Order, _item: OrderItem): void {
|
|
18
|
+
if (order.status !== "open") {
|
|
19
|
+
throw new OrderInvariantError(
|
|
20
|
+
`Cannot add an item to an order with status "${order.status}". The order must be open.`,
|
|
21
|
+
);
|
|
22
|
+
}
|
|
22
23
|
}
|
|
23
|
-
}
|
|
24
24
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
25
|
+
assertCanCancel(order: Order): void {
|
|
26
|
+
if (order.status === "delivered") {
|
|
27
|
+
throw new OrderInvariantError(
|
|
28
|
+
"Cannot cancel an order that has already been delivered.",
|
|
29
|
+
);
|
|
30
|
+
}
|
|
30
31
|
}
|
|
31
|
-
}
|
|
32
32
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
33
|
+
assertCanCheckout(order: Order): void {
|
|
34
|
+
if (order.items.length === 0) {
|
|
35
|
+
throw new OrderInvariantError(
|
|
36
|
+
"Cannot check out an order with no items.",
|
|
37
|
+
);
|
|
38
|
+
}
|
|
36
39
|
}
|
|
37
|
-
}
|
|
38
40
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
41
|
+
assertCanMarkAsDelivered(order: Order): void {
|
|
42
|
+
if (order.status !== "shipped") {
|
|
43
|
+
throw new OrderInvariantError(
|
|
44
|
+
`Cannot mark as delivered an order with status "${order.status}". The order must be shipped.`,
|
|
45
|
+
);
|
|
46
|
+
}
|
|
44
47
|
}
|
|
45
|
-
}
|
|
46
48
|
}
|
|
47
49
|
|
|
48
50
|
export { OrderInvariantsV1 };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
|
-
|
|
3
|
-
|
|
2
|
+
DomainException,
|
|
3
|
+
type RulesetId,
|
|
4
4
|
} from "../../../core/domain/rulesets/entity-ruleset.contracts";
|
|
5
5
|
import { type Order } from "./order";
|
|
6
6
|
import { OrderInvariantsV1 } from "./order-invariants-v1";
|
|
@@ -10,19 +10,19 @@ class OrderCancellationWindowError extends DomainException {}
|
|
|
10
10
|
const FORTY_EIGHT_HOURS_MS = 48 * 60 * 60 * 1000;
|
|
11
11
|
|
|
12
12
|
class OrderInvariantsV2 extends OrderInvariantsV1 {
|
|
13
|
-
|
|
14
|
-
|
|
13
|
+
override readonly id: RulesetId = "order-invariants@2.0";
|
|
14
|
+
override readonly description = "Order invariants — version 2.0";
|
|
15
15
|
|
|
16
|
-
|
|
17
|
-
|
|
16
|
+
override assertCanCancel(order: Order): void {
|
|
17
|
+
super.assertCanCancel(order);
|
|
18
18
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
19
|
+
const ageMs = Date.now() - order.createdAt.getTime();
|
|
20
|
+
if (ageMs > FORTY_EIGHT_HOURS_MS) {
|
|
21
|
+
throw new OrderCancellationWindowError(
|
|
22
|
+
"Cannot cancel an order created more than 48 hours ago.",
|
|
23
|
+
);
|
|
24
|
+
}
|
|
24
25
|
}
|
|
25
|
-
}
|
|
26
26
|
}
|
|
27
27
|
|
|
28
28
|
export { OrderInvariantsV2 };
|
|
@@ -1,32 +1,32 @@
|
|
|
1
1
|
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
2
|
+
type CreationRuleset,
|
|
3
|
+
type InvariantRuleset,
|
|
4
|
+
type RulesetId,
|
|
5
5
|
} from "../../../core/domain/rulesets/entity-ruleset.contracts";
|
|
6
6
|
|
|
7
7
|
type OrderStatus = "open" | "cancelled" | "shipped" | "delivered";
|
|
8
8
|
|
|
9
9
|
interface OrderItem {
|
|
10
|
-
|
|
11
|
-
|
|
10
|
+
readonly sku: string;
|
|
11
|
+
readonly quantity: number;
|
|
12
12
|
}
|
|
13
13
|
|
|
14
14
|
interface OrderCreationData {
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
15
|
+
readonly id: string;
|
|
16
|
+
readonly customerId: string;
|
|
17
|
+
readonly items: readonly OrderItem[];
|
|
18
|
+
readonly createdAt: Date;
|
|
19
19
|
}
|
|
20
20
|
|
|
21
21
|
interface OrderSnapshot extends OrderCreationData {
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
22
|
+
readonly status: OrderStatus;
|
|
23
|
+
readonly creationRulesetId: RulesetId;
|
|
24
|
+
readonly aggregateVersion: number;
|
|
25
25
|
}
|
|
26
26
|
|
|
27
27
|
interface OrderCancelledEvent {
|
|
28
|
-
|
|
29
|
-
|
|
28
|
+
readonly type: "OrderCancelled";
|
|
29
|
+
readonly invariantRulesetId: RulesetId;
|
|
30
30
|
}
|
|
31
31
|
|
|
32
32
|
type OrderDomainEvent = OrderCancelledEvent;
|
|
@@ -34,129 +34,129 @@ type OrderDomainEvent = OrderCancelledEvent;
|
|
|
34
34
|
type OrderCreationRuleset = CreationRuleset<OrderCreationData>;
|
|
35
35
|
|
|
36
36
|
interface OrderInvariantRuleset extends InvariantRuleset {
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
37
|
+
assertCanAddItem(order: Order, item: OrderItem): void;
|
|
38
|
+
assertCanCancel(order: Order): void;
|
|
39
|
+
assertCanCheckout(order: Order): void;
|
|
40
|
+
assertCanMarkAsDelivered(order: Order): void;
|
|
41
41
|
}
|
|
42
42
|
|
|
43
43
|
interface OrderStatic {
|
|
44
|
-
|
|
45
|
-
|
|
44
|
+
create(data: OrderCreationData, ruleset: OrderCreationRuleset): Order;
|
|
45
|
+
reconstitute(snapshot: OrderSnapshot): Order;
|
|
46
46
|
}
|
|
47
47
|
|
|
48
48
|
class Order {
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
49
|
+
private readonly _id: string;
|
|
50
|
+
private readonly _customerId: string;
|
|
51
|
+
private _status: OrderStatus;
|
|
52
|
+
private readonly _items: OrderItem[];
|
|
53
|
+
private readonly _createdAt: Date;
|
|
54
|
+
private readonly _creationRulesetId: RulesetId;
|
|
55
|
+
private _aggregateVersion: number;
|
|
56
|
+
private readonly _domainEvents: OrderDomainEvent[] = [];
|
|
57
|
+
|
|
58
|
+
private constructor(snapshot: OrderSnapshot) {
|
|
59
|
+
this._id = snapshot.id;
|
|
60
|
+
this._customerId = snapshot.customerId;
|
|
61
|
+
this._status = snapshot.status;
|
|
62
|
+
this._items = [...snapshot.items];
|
|
63
|
+
this._createdAt = new Date(snapshot.createdAt.getTime());
|
|
64
|
+
this._creationRulesetId = snapshot.creationRulesetId;
|
|
65
|
+
this._aggregateVersion = snapshot.aggregateVersion;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
get id(): string {
|
|
69
|
+
return this._id;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
get customerId(): string {
|
|
73
|
+
return this._customerId;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
get status(): OrderStatus {
|
|
77
|
+
return this._status;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
get items(): readonly OrderItem[] {
|
|
81
|
+
return this._items;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
get createdAt(): Date {
|
|
85
|
+
return new Date(this._createdAt.getTime());
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
get creationRulesetId(): RulesetId {
|
|
89
|
+
return this._creationRulesetId;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
get aggregateVersion(): number {
|
|
93
|
+
return this._aggregateVersion;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
addItem(item: OrderItem, ruleset: OrderInvariantRuleset): void {
|
|
97
|
+
ruleset.assertCanAddItem(this, item);
|
|
98
|
+
this._items.push(item);
|
|
99
|
+
this._aggregateVersion += 1;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
cancel(ruleset: OrderInvariantRuleset): void {
|
|
103
|
+
ruleset.assertCanCancel(this);
|
|
104
|
+
this._status = "cancelled";
|
|
105
|
+
this._aggregateVersion += 1;
|
|
106
|
+
this._domainEvents.push({
|
|
107
|
+
type: "OrderCancelled",
|
|
108
|
+
invariantRulesetId: ruleset.id,
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
checkout(ruleset: OrderInvariantRuleset): void {
|
|
113
|
+
ruleset.assertCanCheckout(this);
|
|
114
|
+
this._status = "shipped";
|
|
115
|
+
this._aggregateVersion += 1;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
markAsDelivered(ruleset: OrderInvariantRuleset): void {
|
|
119
|
+
ruleset.assertCanMarkAsDelivered(this);
|
|
120
|
+
this._status = "delivered";
|
|
121
|
+
this._aggregateVersion += 1;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
pullDomainEvents(): readonly OrderDomainEvent[] {
|
|
125
|
+
const events = [...this._domainEvents];
|
|
126
|
+
this._domainEvents.length = 0;
|
|
127
|
+
return events;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
static create(
|
|
131
|
+
data: OrderCreationData,
|
|
132
|
+
ruleset: CreationRuleset<OrderCreationData>,
|
|
133
|
+
): Order {
|
|
134
|
+
ruleset.validate(data);
|
|
135
|
+
return new Order({
|
|
136
|
+
...data,
|
|
137
|
+
status: "open",
|
|
138
|
+
creationRulesetId: ruleset.id,
|
|
139
|
+
aggregateVersion: 0,
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
static reconstitute(snapshot: OrderSnapshot): Order {
|
|
144
|
+
return new Order(snapshot);
|
|
145
|
+
}
|
|
146
146
|
}
|
|
147
147
|
|
|
148
148
|
const _typeCheck: OrderStatic = Order;
|
|
149
149
|
void _typeCheck;
|
|
150
150
|
|
|
151
151
|
export {
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
152
|
+
Order,
|
|
153
|
+
type OrderCancelledEvent,
|
|
154
|
+
type OrderCreationData,
|
|
155
|
+
type OrderCreationRuleset,
|
|
156
|
+
type OrderDomainEvent,
|
|
157
|
+
type OrderInvariantRuleset,
|
|
158
|
+
type OrderItem,
|
|
159
|
+
type OrderSnapshot,
|
|
160
|
+
type OrderStatic,
|
|
161
|
+
type OrderStatus,
|
|
162
162
|
};
|
|
@@ -1,52 +1,54 @@
|
|
|
1
1
|
import {
|
|
2
|
-
|
|
3
|
-
|
|
2
|
+
DomainException,
|
|
3
|
+
type ValueObjectRuleset,
|
|
4
4
|
} from "../../../core/domain/rulesets/value-object-ruleset.contracts";
|
|
5
5
|
|
|
6
6
|
class CPFValidationError extends DomainException {}
|
|
7
7
|
|
|
8
8
|
function validateCheckDigits(digits: number[]): boolean {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
}
|
|
14
|
-
const rem1 = sum % 11;
|
|
15
|
-
const expected1 = rem1 < 2 ? 0 : 11 - rem1;
|
|
16
|
-
if (digits[9] !== expected1) return false;
|
|
17
|
-
|
|
18
|
-
// Second check digit: multiply first 10 digits by weights 11..2
|
|
19
|
-
sum = 0;
|
|
20
|
-
for (let i = 0; i < 10; i++) {
|
|
21
|
-
sum += digits[i]! * (11 - i);
|
|
22
|
-
}
|
|
23
|
-
const rem2 = sum % 11;
|
|
24
|
-
const expected2 = rem2 < 2 ? 0 : 11 - rem2;
|
|
25
|
-
return digits[10] === expected2;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
class CPFRulesV1 implements ValueObjectRuleset<string> {
|
|
29
|
-
readonly id = "cpf-rules@1.0" as const;
|
|
30
|
-
readonly description = "CPF validation using the check-digit algorithm";
|
|
31
|
-
|
|
32
|
-
validate(value: string): void {
|
|
33
|
-
if (!/^\d{11}$/.test(value)) {
|
|
34
|
-
throw new CPFValidationError(
|
|
35
|
-
`CPF must contain exactly 11 numeric digits. Received: "${value}".`,
|
|
36
|
-
);
|
|
9
|
+
// First check digit: multiply first 9 digits by weights 10..2
|
|
10
|
+
let sum = 0;
|
|
11
|
+
for (let i = 0; i < 9; i++) {
|
|
12
|
+
sum += digits[i]! * (10 - i);
|
|
37
13
|
}
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
14
|
+
const rem1 = sum % 11;
|
|
15
|
+
const expected1 = rem1 < 2 ? 0 : 11 - rem1;
|
|
16
|
+
if (digits[9] !== expected1) return false;
|
|
17
|
+
|
|
18
|
+
// Second check digit: multiply first 10 digits by weights 11..2
|
|
19
|
+
sum = 0;
|
|
20
|
+
for (let i = 0; i < 10; i++) {
|
|
21
|
+
sum += digits[i]! * (11 - i);
|
|
43
22
|
}
|
|
23
|
+
const rem2 = sum % 11;
|
|
24
|
+
const expected2 = rem2 < 2 ? 0 : 11 - rem2;
|
|
25
|
+
return digits[10] === expected2;
|
|
26
|
+
}
|
|
44
27
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
28
|
+
class CPFRulesV1 implements ValueObjectRuleset<string> {
|
|
29
|
+
readonly id = "cpf-rules@1.0" as const;
|
|
30
|
+
readonly description = "CPF validation using the check-digit algorithm";
|
|
31
|
+
|
|
32
|
+
validate(value: string): void {
|
|
33
|
+
if (!/^\d{11}$/.test(value)) {
|
|
34
|
+
throw new CPFValidationError(
|
|
35
|
+
`CPF must contain exactly 11 numeric digits. Received: "${value}".`,
|
|
36
|
+
);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
if (/^(\d)\1{10}$/.test(value)) {
|
|
40
|
+
throw new CPFValidationError(
|
|
41
|
+
`A CPF with all repeating digits is invalid: "${value}".`,
|
|
42
|
+
);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const digits = value.split("").map(Number);
|
|
46
|
+
if (!validateCheckDigits(digits)) {
|
|
47
|
+
throw new CPFValidationError(
|
|
48
|
+
`Invalid check digits for CPF "${value}".`,
|
|
49
|
+
);
|
|
50
|
+
}
|
|
48
51
|
}
|
|
49
|
-
}
|
|
50
52
|
}
|
|
51
53
|
|
|
52
54
|
export { CPFRulesV1 };
|
|
@@ -1,27 +1,29 @@
|
|
|
1
1
|
import {
|
|
2
|
-
|
|
3
|
-
|
|
2
|
+
DomainException,
|
|
3
|
+
type ValueObjectRuleset,
|
|
4
4
|
} from "../../../core/domain/rulesets/value-object-ruleset.contracts";
|
|
5
5
|
import { CPFRulesV1 } from "./cpf-rules-v1";
|
|
6
6
|
|
|
7
7
|
class CPFBlocklistError extends DomainException {}
|
|
8
8
|
|
|
9
9
|
class CPFRulesV2 implements ValueObjectRuleset<string> {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
10
|
+
readonly id = "cpf-rules@2.0" as const;
|
|
11
|
+
readonly description =
|
|
12
|
+
"CPF validation with check-digit algorithm and blocklist";
|
|
13
13
|
|
|
14
|
-
|
|
14
|
+
private readonly v1 = new CPFRulesV1();
|
|
15
15
|
|
|
16
|
-
|
|
16
|
+
constructor(private readonly blocklist: readonly string[]) {}
|
|
17
17
|
|
|
18
|
-
|
|
19
|
-
|
|
18
|
+
validate(value: string): void {
|
|
19
|
+
this.v1.validate(value);
|
|
20
20
|
|
|
21
|
-
|
|
22
|
-
|
|
21
|
+
if (this.blocklist.includes(value)) {
|
|
22
|
+
throw new CPFBlocklistError(
|
|
23
|
+
`CPF "${value}" appears on the blocklist.`,
|
|
24
|
+
);
|
|
25
|
+
}
|
|
23
26
|
}
|
|
24
|
-
}
|
|
25
27
|
}
|
|
26
28
|
|
|
27
29
|
export { CPFRulesV2 };
|
|
@@ -1,34 +1,34 @@
|
|
|
1
1
|
import {
|
|
2
|
-
|
|
3
|
-
|
|
2
|
+
type RulesetId,
|
|
3
|
+
type ValueObjectRuleset,
|
|
4
4
|
} from "../../../core/domain/rulesets/value-object-ruleset.contracts";
|
|
5
5
|
|
|
6
6
|
interface CPFStatic {
|
|
7
|
-
|
|
8
|
-
|
|
7
|
+
create(raw: string, ruleset: ValueObjectRuleset<string>): CPF;
|
|
8
|
+
reconstitute(value: string, rulesetId: RulesetId): CPF;
|
|
9
9
|
}
|
|
10
10
|
|
|
11
11
|
class CPF {
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
12
|
+
readonly value: string;
|
|
13
|
+
readonly appliedRulesetId: RulesetId;
|
|
14
|
+
|
|
15
|
+
private constructor(value: string, appliedRulesetId: RulesetId) {
|
|
16
|
+
this.value = value;
|
|
17
|
+
this.appliedRulesetId = appliedRulesetId;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
equals(other: CPF): boolean {
|
|
21
|
+
return this.value === other.value;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
static create(raw: string, ruleset: ValueObjectRuleset<string>): CPF {
|
|
25
|
+
ruleset.validate(raw);
|
|
26
|
+
return new CPF(raw, ruleset.id);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
static reconstitute(value: string, rulesetId: RulesetId): CPF {
|
|
30
|
+
return new CPF(value, rulesetId);
|
|
31
|
+
}
|
|
32
32
|
}
|
|
33
33
|
|
|
34
34
|
const _typeCheck: CPFStatic = CPF;
|