@cullet/erp-core 1.0.1 → 1.0.3
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.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.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 -62
- 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 +547 -505
- 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
|
@@ -1,71 +1,71 @@
|
|
|
1
1
|
import { DomainException } from "../../exceptions/domain-exception";
|
|
2
2
|
import {
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
type Ruleset,
|
|
4
|
+
type RulesetRegistry as RulesetRegistryContract,
|
|
5
5
|
} from "./ruleset.contracts";
|
|
6
6
|
|
|
7
7
|
class RulesetRegistryError extends DomainException {}
|
|
8
8
|
|
|
9
9
|
function parseVersion(id: string): [number, number] {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
10
|
+
const atIdx = id.lastIndexOf("@");
|
|
11
|
+
const versionStr = id.slice(atIdx + 1);
|
|
12
|
+
const [major, minor] = versionStr.split(".").map(Number);
|
|
13
|
+
return [major, minor];
|
|
14
14
|
}
|
|
15
15
|
|
|
16
16
|
class RulesetRegistry implements RulesetRegistryContract {
|
|
17
|
-
|
|
18
|
-
|
|
17
|
+
private readonly _store = new Map<string, Ruleset>();
|
|
18
|
+
private _sealed = false;
|
|
19
19
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
20
|
+
register(ruleset: Ruleset): void {
|
|
21
|
+
if (this._sealed) {
|
|
22
|
+
throw new RulesetRegistryError(
|
|
23
|
+
"Registry is sealed. No new rulesets can be registered.",
|
|
24
|
+
);
|
|
25
|
+
}
|
|
26
|
+
if (this._store.has(ruleset.id)) {
|
|
27
|
+
throw new RulesetRegistryError(
|
|
28
|
+
`Ruleset with id "${ruleset.id}" is already registered.`,
|
|
29
|
+
);
|
|
30
|
+
}
|
|
31
|
+
this._store.set(ruleset.id, ruleset);
|
|
25
32
|
}
|
|
26
|
-
if (this._store.has(ruleset.id)) {
|
|
27
|
-
throw new RulesetRegistryError(
|
|
28
|
-
`Ruleset with id "${ruleset.id}" is already registered.`,
|
|
29
|
-
);
|
|
30
|
-
}
|
|
31
|
-
this._store.set(ruleset.id, ruleset);
|
|
32
|
-
}
|
|
33
33
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
34
|
+
seal(): void {
|
|
35
|
+
this._sealed = true;
|
|
36
|
+
}
|
|
37
37
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
38
|
+
get<T extends Ruleset>(id: string): T {
|
|
39
|
+
const ruleset = this._store.get(id);
|
|
40
|
+
if (!ruleset) {
|
|
41
|
+
const available = Array.from(this._store.keys()).join(", ");
|
|
42
|
+
throw new RulesetRegistryError(
|
|
43
|
+
`Ruleset "${id}" not found. Available: [${available}]`,
|
|
44
|
+
);
|
|
45
|
+
}
|
|
46
|
+
return ruleset as T;
|
|
45
47
|
}
|
|
46
|
-
return ruleset as T;
|
|
47
|
-
}
|
|
48
48
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
49
|
+
getCurrent<T extends Ruleset>(prefix: string): T {
|
|
50
|
+
const matchingEntries = Array.from(this._store.entries()).filter(
|
|
51
|
+
([key]) => key.startsWith(prefix + "@"),
|
|
52
|
+
);
|
|
53
53
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
54
|
+
if (matchingEntries.length === 0) {
|
|
55
|
+
throw new RulesetRegistryError(
|
|
56
|
+
`No rulesets found with prefix "${prefix}".`,
|
|
57
|
+
);
|
|
58
|
+
}
|
|
59
59
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
60
|
+
const sorted = matchingEntries.sort(([a], [b]) => {
|
|
61
|
+
const [aMajor, aMinor] = parseVersion(a);
|
|
62
|
+
const [bMajor, bMinor] = parseVersion(b);
|
|
63
|
+
if (aMajor !== bMajor) return aMajor - bMajor;
|
|
64
|
+
return aMinor - bMinor;
|
|
65
|
+
});
|
|
66
66
|
|
|
67
|
-
|
|
68
|
-
|
|
67
|
+
return sorted[sorted.length - 1][1] as T;
|
|
68
|
+
}
|
|
69
69
|
}
|
|
70
70
|
|
|
71
71
|
export { RulesetRegistry };
|
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
type RulesetId = `${string}@${number}.${number}`;
|
|
2
2
|
|
|
3
3
|
interface Ruleset {
|
|
4
|
-
|
|
5
|
-
|
|
4
|
+
readonly id: RulesetId;
|
|
5
|
+
readonly description: string;
|
|
6
6
|
}
|
|
7
7
|
|
|
8
8
|
interface RulesetRegistry {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
9
|
+
register(ruleset: Ruleset): void;
|
|
10
|
+
seal(): void;
|
|
11
|
+
get<T extends Ruleset>(id: string): T;
|
|
12
|
+
getCurrent<T extends Ruleset>(prefix: string): T;
|
|
13
13
|
}
|
|
14
14
|
|
|
15
15
|
export { type Ruleset, type RulesetId, type RulesetRegistry };
|
|
@@ -2,12 +2,12 @@ import { DomainException } from "../../exceptions/domain-exception";
|
|
|
2
2
|
import { type Ruleset, type RulesetId } from "./ruleset.contracts";
|
|
3
3
|
|
|
4
4
|
interface ValueObjectRuleset<T> extends Ruleset {
|
|
5
|
-
|
|
5
|
+
validate(value: T): void;
|
|
6
6
|
}
|
|
7
7
|
|
|
8
8
|
export {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
9
|
+
DomainException,
|
|
10
|
+
type Ruleset,
|
|
11
|
+
type RulesetId,
|
|
12
|
+
type ValueObjectRuleset,
|
|
13
13
|
};
|
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
export {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
2
|
+
assertTransactionTime,
|
|
3
|
+
createTransactionTime,
|
|
4
|
+
type CreateTransactionTimeInput,
|
|
5
|
+
type TransactionTime,
|
|
6
6
|
} from "./transaction-time";
|
|
7
7
|
export { contains, isClosed, isOpen, overlaps } from "./temporal-range";
|
|
8
8
|
export {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
9
|
+
createTemporalSnapshot,
|
|
10
|
+
type CreateTemporalSnapshotInput,
|
|
11
|
+
type TemporalSnapshot,
|
|
12
12
|
} from "./temporal-snapshot";
|
|
13
13
|
export {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
14
|
+
assertValidTime,
|
|
15
|
+
createValidTime,
|
|
16
|
+
type CreateValidTimeInput,
|
|
17
|
+
type ValidTime,
|
|
18
18
|
} from "./valid-time";
|
|
@@ -7,16 +7,16 @@ import { assertValidTime, type ValidTime } from "./valid-time";
|
|
|
7
7
|
* Useful for identifying information that is still in effect / active.
|
|
8
8
|
*/
|
|
9
9
|
function isOpen(range: ValidTime): boolean {
|
|
10
|
-
|
|
10
|
+
assertValidTime(range, "range");
|
|
11
11
|
|
|
12
|
-
|
|
12
|
+
return range.to === undefined;
|
|
13
13
|
}
|
|
14
14
|
|
|
15
15
|
/**
|
|
16
16
|
* Checks whether a temporal range is "closed" (has a defined end date).
|
|
17
17
|
*/
|
|
18
18
|
function isClosed(range: ValidTime): boolean {
|
|
19
|
-
|
|
19
|
+
return !isOpen(range);
|
|
20
20
|
}
|
|
21
21
|
|
|
22
22
|
/**
|
|
@@ -24,14 +24,14 @@ function isClosed(range: ValidTime): boolean {
|
|
|
24
24
|
* The lower bound (`from`) is inclusive, while the upper bound (`to`) is exclusive.
|
|
25
25
|
*/
|
|
26
26
|
function contains(range: ValidTime, date: Date): boolean {
|
|
27
|
-
|
|
28
|
-
|
|
27
|
+
assertValidTime(range, "range");
|
|
28
|
+
assertValidDate("date", date);
|
|
29
29
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
30
|
+
const fromTime = range.from.getTime();
|
|
31
|
+
const toTime = range.to?.getTime() ?? Number.POSITIVE_INFINITY;
|
|
32
|
+
const targetTime = date.getTime();
|
|
33
33
|
|
|
34
|
-
|
|
34
|
+
return targetTime >= fromTime && targetTime < toTime;
|
|
35
35
|
}
|
|
36
36
|
|
|
37
37
|
/**
|
|
@@ -39,15 +39,15 @@ function contains(range: ValidTime, date: Date): boolean {
|
|
|
39
39
|
* Useful for validating whether a new rule conflicts with an existing one.
|
|
40
40
|
*/
|
|
41
41
|
function overlaps(left: ValidTime, right: ValidTime): boolean {
|
|
42
|
-
|
|
43
|
-
|
|
42
|
+
assertValidTime(left, "left");
|
|
43
|
+
assertValidTime(right, "right");
|
|
44
44
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
45
|
+
const leftFrom = left.from.getTime();
|
|
46
|
+
const leftTo = left.to?.getTime() ?? Number.POSITIVE_INFINITY;
|
|
47
|
+
const rightFrom = right.from.getTime();
|
|
48
|
+
const rightTo = right.to?.getTime() ?? Number.POSITIVE_INFINITY;
|
|
49
49
|
|
|
50
|
-
|
|
50
|
+
return leftFrom < rightTo && rightFrom < leftTo;
|
|
51
51
|
}
|
|
52
52
|
|
|
53
53
|
export { contains, isClosed, isOpen, overlaps };
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
import { type DeepReadonly, makeImmutable } from "../../shared/immutable";
|
|
2
2
|
|
|
3
3
|
import {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
4
|
+
createTransactionTime,
|
|
5
|
+
type CreateTransactionTimeInput,
|
|
6
|
+
type TransactionTime,
|
|
7
7
|
} from "./transaction-time";
|
|
8
8
|
import {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
9
|
+
createValidTime,
|
|
10
|
+
type CreateValidTimeInput,
|
|
11
|
+
type ValidTime,
|
|
12
12
|
} from "./valid-time";
|
|
13
13
|
|
|
14
14
|
/**
|
|
@@ -16,32 +16,32 @@ import {
|
|
|
16
16
|
* It is the representation of an entry in the append-only (historical) table.
|
|
17
17
|
*/
|
|
18
18
|
interface TemporalSnapshot<T> {
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
19
|
+
/** The data or state captured by the snapshot. */
|
|
20
|
+
readonly data: DeepReadonly<T>;
|
|
21
|
+
/** Period during which this datum was valid in the real world. */
|
|
22
|
+
readonly validTime: ValidTime;
|
|
23
|
+
/** Period during which this record was the state known to the system. */
|
|
24
|
+
readonly txTime: TransactionTime;
|
|
25
25
|
}
|
|
26
26
|
|
|
27
27
|
interface CreateTemporalSnapshotInput<T> {
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
28
|
+
readonly data: T;
|
|
29
|
+
readonly validTime: CreateValidTimeInput;
|
|
30
|
+
readonly txTime: CreateTransactionTimeInput;
|
|
31
31
|
}
|
|
32
32
|
|
|
33
33
|
function createTemporalSnapshot<T>(
|
|
34
|
-
|
|
34
|
+
input: CreateTemporalSnapshotInput<T>,
|
|
35
35
|
): TemporalSnapshot<T> {
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
36
|
+
return makeImmutable({
|
|
37
|
+
data: input.data,
|
|
38
|
+
validTime: createValidTime(input.validTime),
|
|
39
|
+
txTime: createTransactionTime(input.txTime),
|
|
40
|
+
});
|
|
41
41
|
}
|
|
42
42
|
|
|
43
43
|
export {
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
44
|
+
createTemporalSnapshot,
|
|
45
|
+
type CreateTemporalSnapshotInput,
|
|
46
|
+
type TemporalSnapshot,
|
|
47
47
|
};
|
|
@@ -8,53 +8,53 @@ import { assertValidDate } from "../../shared/temporal-guards";
|
|
|
8
8
|
* Useful for auditing and reconstructing system state at a specific past moment.
|
|
9
9
|
*/
|
|
10
10
|
interface TransactionTime {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
11
|
+
/** When the information was first recorded in the system. */
|
|
12
|
+
readonly recordedAt: Date;
|
|
13
|
+
/** When this information was superseded or invalidated by a newer version. */
|
|
14
|
+
readonly supersededAt?: Date;
|
|
15
15
|
}
|
|
16
16
|
|
|
17
17
|
type CreateTransactionTimeInput = TransactionTime;
|
|
18
18
|
|
|
19
19
|
function assertTransactionTime(
|
|
20
|
-
|
|
21
|
-
|
|
20
|
+
txTime: TransactionTime,
|
|
21
|
+
fieldName: string = "txTime",
|
|
22
22
|
): void {
|
|
23
|
-
|
|
23
|
+
assertValidDate(`${fieldName}.recordedAt`, txTime.recordedAt);
|
|
24
24
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
25
|
+
if (txTime.supersededAt === undefined) {
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
28
|
|
|
29
|
-
|
|
29
|
+
assertValidDate(`${fieldName}.supersededAt`, txTime.supersededAt);
|
|
30
30
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
31
|
+
if (txTime.supersededAt.getTime() <= txTime.recordedAt.getTime()) {
|
|
32
|
+
throw new InvariantViolationException(
|
|
33
|
+
`${fieldName}.supersededAt must be later than ${fieldName}.recordedAt`,
|
|
34
|
+
);
|
|
35
|
+
}
|
|
36
36
|
}
|
|
37
37
|
|
|
38
38
|
function createTransactionTime(
|
|
39
|
-
|
|
39
|
+
input: CreateTransactionTimeInput,
|
|
40
40
|
): TransactionTime {
|
|
41
|
-
|
|
41
|
+
assertTransactionTime(input, "txTime");
|
|
42
|
+
|
|
43
|
+
if (input.supersededAt === undefined) {
|
|
44
|
+
return makeImmutable({
|
|
45
|
+
recordedAt: input.recordedAt,
|
|
46
|
+
});
|
|
47
|
+
}
|
|
42
48
|
|
|
43
|
-
if (input.supersededAt === undefined) {
|
|
44
49
|
return makeImmutable({
|
|
45
|
-
|
|
50
|
+
recordedAt: input.recordedAt,
|
|
51
|
+
supersededAt: input.supersededAt,
|
|
46
52
|
});
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
return makeImmutable({
|
|
50
|
-
recordedAt: input.recordedAt,
|
|
51
|
-
supersededAt: input.supersededAt,
|
|
52
|
-
});
|
|
53
53
|
}
|
|
54
54
|
|
|
55
55
|
export {
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
56
|
+
assertTransactionTime,
|
|
57
|
+
createTransactionTime,
|
|
58
|
+
type CreateTransactionTimeInput,
|
|
59
|
+
type TransactionTime,
|
|
60
60
|
};
|
|
@@ -7,51 +7,51 @@ import { assertValidDate } from "../../shared/temporal-guards";
|
|
|
7
7
|
* Defines when a piece of information is considered true or in effect in the real world.
|
|
8
8
|
*/
|
|
9
9
|
interface ValidTime {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
10
|
+
/** Start of the validity window (inclusive). */
|
|
11
|
+
readonly from: Date;
|
|
12
|
+
/** End of the validity window (exclusive). When absent, the information is still in effect. */
|
|
13
|
+
readonly to?: Date;
|
|
14
14
|
}
|
|
15
15
|
|
|
16
16
|
type CreateValidTimeInput = ValidTime;
|
|
17
17
|
|
|
18
18
|
function assertValidTime(
|
|
19
|
-
|
|
20
|
-
|
|
19
|
+
validTime: ValidTime,
|
|
20
|
+
fieldName: string = "validTime",
|
|
21
21
|
): void {
|
|
22
|
-
|
|
22
|
+
assertValidDate(`${fieldName}.from`, validTime.from);
|
|
23
23
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
24
|
+
if (validTime.to === undefined) {
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
27
|
|
|
28
|
-
|
|
28
|
+
assertValidDate(`${fieldName}.to`, validTime.to);
|
|
29
29
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
30
|
+
if (validTime.to.getTime() <= validTime.from.getTime()) {
|
|
31
|
+
throw new InvariantViolationException(
|
|
32
|
+
`${fieldName}.to must be later than ${fieldName}.from`,
|
|
33
|
+
);
|
|
34
|
+
}
|
|
35
35
|
}
|
|
36
36
|
|
|
37
37
|
function createValidTime(input: CreateValidTimeInput): ValidTime {
|
|
38
|
-
|
|
38
|
+
assertValidTime(input, "validTime");
|
|
39
|
+
|
|
40
|
+
if (input.to === undefined) {
|
|
41
|
+
return makeImmutable({
|
|
42
|
+
from: input.from,
|
|
43
|
+
});
|
|
44
|
+
}
|
|
39
45
|
|
|
40
|
-
if (input.to === undefined) {
|
|
41
46
|
return makeImmutable({
|
|
42
|
-
|
|
47
|
+
from: input.from,
|
|
48
|
+
to: input.to,
|
|
43
49
|
});
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
return makeImmutable({
|
|
47
|
-
from: input.from,
|
|
48
|
-
to: input.to,
|
|
49
|
-
});
|
|
50
50
|
}
|
|
51
51
|
|
|
52
52
|
export {
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
53
|
+
assertValidTime,
|
|
54
|
+
createValidTime,
|
|
55
|
+
type CreateValidTimeInput,
|
|
56
|
+
type ValidTime,
|
|
57
57
|
};
|
|
@@ -3,28 +3,28 @@ import { type ContractVersion, version } from "../versioning/version";
|
|
|
3
3
|
|
|
4
4
|
@version("1.0")
|
|
5
5
|
abstract class ValueObject<T, P> {
|
|
6
|
-
|
|
7
|
-
|
|
6
|
+
public static readonly CONTRACT_VERSION: ContractVersion;
|
|
7
|
+
public readonly value: DeepReadonly<T>;
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
9
|
+
protected constructor(value: T) {
|
|
10
|
+
this.value = makeImmutable(value);
|
|
11
|
+
}
|
|
12
12
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
13
|
+
public get contractVersion(): ContractVersion {
|
|
14
|
+
return ValueObject.CONTRACT_VERSION;
|
|
15
|
+
}
|
|
16
16
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
17
|
+
protected finalize(): void {
|
|
18
|
+
Object.freeze(this);
|
|
19
|
+
}
|
|
20
20
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
21
|
+
public toJSON(): P {
|
|
22
|
+
return this.toPrimitive();
|
|
23
|
+
}
|
|
24
24
|
|
|
25
|
-
|
|
25
|
+
public abstract equals(other: this): boolean;
|
|
26
26
|
|
|
27
|
-
|
|
27
|
+
public abstract toPrimitive(): P;
|
|
28
28
|
}
|
|
29
29
|
|
|
30
30
|
export { type DeepReadonly, ValueObject };
|