@cullet/erp-core 1.4.0 → 2.0.0
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/KIT_CONTEXT.md +7 -1
- package/dist/abac/index.d.cts +2 -2
- package/dist/abac/index.d.ts +2 -2
- package/dist/aggregate-version.cjs +14 -0
- package/dist/aggregate-version.cjs.map +1 -0
- package/dist/aggregate-version.js +9 -0
- package/dist/aggregate-version.js.map +1 -0
- package/dist/app-error.cjs +21 -6
- package/dist/app-error.cjs.map +1 -1
- package/dist/app-error.js +21 -6
- package/dist/app-error.js.map +1 -1
- package/dist/application/index.cjs +8 -4
- package/dist/application/index.d.cts +2 -2
- package/dist/application/index.d.ts +2 -2
- package/dist/application/index.js +1 -2
- package/dist/authorization-error.cjs +67 -17
- package/dist/authorization-error.cjs.map +1 -1
- package/dist/authorization-error.d.cts +6 -2
- package/dist/authorization-error.d.ts +6 -2
- package/dist/authorization-error.js +50 -18
- package/dist/authorization-error.js.map +1 -1
- package/dist/authorizer.port.d.cts +18 -3
- package/dist/authorizer.port.d.ts +18 -3
- package/dist/composite-authorizer.d.cts +63 -10
- package/dist/composite-authorizer.d.ts +63 -10
- package/dist/condition-evaluator.cjs +117 -172
- package/dist/condition-evaluator.cjs.map +1 -1
- package/dist/condition-evaluator.js +118 -167
- package/dist/condition-evaluator.js.map +1 -1
- package/dist/core-config.d.cts +53 -6
- package/dist/core-config.d.ts +53 -6
- package/dist/decorate.cjs +14 -0
- package/dist/decorate.js +9 -0
- package/dist/domain/index.cjs +107 -2
- package/dist/domain/index.cjs.map +1 -0
- package/dist/domain/index.d.cts +25 -1
- package/dist/domain/index.d.ts +25 -1
- package/dist/domain/index.js +99 -3
- package/dist/domain/index.js.map +1 -0
- package/dist/domain-event-contracts.cjs +12 -8
- package/dist/domain-event-contracts.cjs.map +1 -1
- package/dist/domain-event-contracts.d.cts +29 -4
- package/dist/domain-event-contracts.d.ts +29 -4
- package/dist/domain-event-contracts.js +11 -7
- package/dist/domain-event-contracts.js.map +1 -1
- package/dist/domain-exception.cjs +2 -1
- package/dist/domain-exception.cjs.map +1 -1
- package/dist/domain-exception.js +2 -1
- package/dist/domain-exception.js.map +1 -1
- package/dist/errors/index.cjs +3 -2
- package/dist/errors/index.d.cts +1 -1
- package/dist/errors/index.d.ts +1 -1
- package/dist/errors/index.js +3 -2
- package/dist/exceptions/index.cjs +2 -2
- package/dist/exceptions/index.d.cts +1 -2
- package/dist/exceptions/index.d.ts +1 -2
- package/dist/exceptions/index.js +2 -2
- package/dist/gate-engine-registry.cjs.map +1 -1
- package/dist/gate-engine-registry.d.cts +1 -1
- package/dist/gate-engine-registry.d.ts +1 -1
- package/dist/gate-engine-registry.js.map +1 -1
- package/dist/gate-v1-payload.schema.cjs +11 -6
- package/dist/gate-v1-payload.schema.cjs.map +1 -1
- package/dist/gate-v1-payload.schema.js +11 -6
- package/dist/gate-v1-payload.schema.js.map +1 -1
- package/dist/hashing.cjs +2 -44
- package/dist/hashing.cjs.map +1 -1
- package/dist/hashing.js +3 -39
- package/dist/hashing.js.map +1 -1
- package/dist/immutable.cjs +25 -12
- package/dist/immutable.cjs.map +1 -1
- package/dist/immutable.js +24 -11
- package/dist/immutable.js.map +1 -1
- package/dist/index.cjs +15 -11
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +9 -10
- package/dist/index.d.ts +9 -10
- package/dist/index.js +8 -9
- package/dist/index.js.map +1 -1
- package/dist/invalid-state-transition-exception.cjs +6 -6
- package/dist/invalid-state-transition-exception.cjs.map +1 -1
- package/dist/invalid-state-transition-exception.js +6 -6
- package/dist/invalid-state-transition-exception.js.map +1 -1
- package/dist/invariant-violation-exception.cjs +2 -2
- package/dist/invariant-violation-exception.cjs.map +1 -1
- package/dist/invariant-violation-exception.js +2 -2
- package/dist/invariant-violation-exception.js.map +1 -1
- package/dist/not-found-error.cjs +6 -4
- package/dist/not-found-error.cjs.map +1 -1
- package/dist/not-found-error.js +6 -4
- package/dist/not-found-error.js.map +1 -1
- package/dist/outcome.cjs +5 -5
- package/dist/outcome.cjs.map +1 -1
- package/dist/outcome.js +5 -5
- package/dist/outcome.js.map +1 -1
- package/dist/parse-gate-payload.d.cts +1 -1
- package/dist/parse-gate-payload.d.ts +1 -1
- package/dist/path.d.cts +2 -2
- package/dist/path.d.ts +2 -2
- package/dist/plugin.cjs +23 -13
- package/dist/plugin.cjs.map +1 -1
- package/dist/plugin.d.cts +22 -8
- package/dist/plugin.d.ts +22 -8
- package/dist/plugin.js +23 -13
- package/dist/plugin.js.map +1 -1
- package/dist/policies/engines/index.d.cts +1 -1
- package/dist/policies/engines/index.d.ts +1 -1
- package/dist/policies/engines/v1/gate/index.d.cts +3 -13
- package/dist/policies/engines/v1/gate/index.d.ts +3 -13
- package/dist/policies/index.d.cts +1 -1
- package/dist/policies/index.d.ts +1 -1
- package/dist/policy-bridge.cjs +30 -2
- package/dist/policy-bridge.cjs.map +1 -1
- package/dist/policy-bridge.d.cts +18 -0
- package/dist/policy-bridge.d.ts +18 -0
- package/dist/policy-bridge.js +30 -2
- package/dist/policy-bridge.js.map +1 -1
- package/dist/policy-service.cjs +41 -46
- package/dist/policy-service.cjs.map +1 -1
- package/dist/policy-service.d.cts +72 -9
- package/dist/policy-service.d.ts +72 -9
- package/dist/policy-service.js +43 -48
- package/dist/policy-service.js.map +1 -1
- package/dist/requested-by.cjs +4 -4
- package/dist/requested-by.cjs.map +1 -1
- package/dist/requested-by.js +2 -2
- package/dist/requested-by.js.map +1 -1
- package/dist/result.cjs +29 -1
- package/dist/result.cjs.map +1 -1
- package/dist/result.d.cts +12 -0
- package/dist/result.d.ts +12 -0
- package/dist/result.js +29 -1
- package/dist/result.js.map +1 -1
- package/dist/rule.cjs +94 -24
- package/dist/rule.cjs.map +1 -1
- package/dist/rule.js +94 -24
- package/dist/rule.js.map +1 -1
- package/dist/ruleset-registry.cjs +19 -0
- package/dist/ruleset-registry.cjs.map +1 -1
- package/dist/ruleset-registry.js +19 -0
- package/dist/ruleset-registry.js.map +1 -1
- package/dist/stable-stringify.cjs +87 -0
- package/dist/stable-stringify.cjs.map +1 -0
- package/dist/stable-stringify.js +76 -0
- package/dist/stable-stringify.js.map +1 -0
- package/dist/temporal-snapshot.d.cts +87 -0
- package/dist/temporal-snapshot.d.ts +87 -0
- package/dist/temporal-use-case.cjs +174 -16
- package/dist/temporal-use-case.cjs.map +1 -1
- package/dist/temporal-use-case.d.cts +82 -44
- package/dist/temporal-use-case.d.ts +82 -44
- package/dist/temporal-use-case.js +167 -15
- package/dist/temporal-use-case.js.map +1 -1
- package/dist/unexpected-error.cjs +4 -2
- package/dist/unexpected-error.cjs.map +1 -1
- package/dist/unexpected-error.js +4 -2
- package/dist/unexpected-error.js.map +1 -1
- package/dist/uuid-identifier.cjs +141 -8
- package/dist/uuid-identifier.cjs.map +1 -1
- package/dist/uuid-identifier.d.cts +26 -7
- package/dist/uuid-identifier.d.ts +26 -7
- package/dist/uuid-identifier.js +135 -8
- package/dist/uuid-identifier.js.map +1 -1
- package/dist/uuid.cjs +23 -0
- package/dist/uuid.cjs.map +1 -0
- package/dist/uuid.js +18 -0
- package/dist/uuid.js.map +1 -0
- package/dist/validation-code.cjs +9 -0
- package/dist/validation-code.cjs.map +1 -1
- package/dist/validation-code.d.cts +3 -0
- package/dist/validation-code.d.ts +3 -0
- package/dist/validation-code.js +9 -0
- package/dist/validation-code.js.map +1 -1
- package/dist/validation-error.cjs +42 -67
- package/dist/validation-error.cjs.map +1 -1
- package/dist/validation-error.d.cts +29 -8
- package/dist/validation-error.d.ts +29 -8
- package/dist/validation-error.js +41 -66
- package/dist/validation-error.js.map +1 -1
- package/dist/validation-exception.cjs +17 -6
- package/dist/validation-exception.cjs.map +1 -1
- package/dist/validation-exception.d.cts +33 -9
- package/dist/validation-exception.d.ts +33 -9
- package/dist/validation-exception.js +17 -6
- package/dist/validation-exception.js.map +1 -1
- package/dist/validation-field.cjs +3 -0
- package/dist/validation-field.cjs.map +1 -1
- package/dist/validation-field.d.cts +1 -0
- package/dist/validation-field.d.ts +1 -0
- package/dist/validation-field.js +3 -0
- package/dist/validation-field.js.map +1 -1
- package/dist/value-object-ruleset.contracts.d.cts +10 -0
- package/dist/value-object-ruleset.contracts.d.ts +10 -0
- package/dist/value-object.cjs +23 -4
- package/dist/value-object.cjs.map +1 -1
- package/dist/value-object.d.cts +25 -1
- package/dist/value-object.d.ts +25 -1
- package/dist/value-object.js +23 -4
- package/dist/value-object.js.map +1 -1
- package/dist/version.d.cts +27 -0
- package/dist/version.d.ts +27 -0
- package/dist/versioning/index.d.cts +2 -2
- package/dist/versioning/index.d.ts +2 -2
- package/meta.json +5 -2
- package/package.json +1 -1
- package/src/core/abac/authorizer.ts +60 -10
- package/src/core/abac/domain/policy-set.ts +52 -5
- package/src/core/abac/domain/rule.ts +28 -16
- package/src/core/abac/index.ts +7 -1
- package/src/core/application/commands/command.ts +14 -1
- package/src/core/application/commands/requested-by.ts +13 -5
- package/src/core/application/index.ts +2 -1
- package/src/core/application/policy-error-mapper.ts +6 -0
- package/src/core/application/ports/index.ts +7 -0
- package/src/core/application/ports/temporal-repository.port.ts +35 -2
- package/src/core/application/queries/index.ts +1 -1
- package/src/core/application/queries/query.ts +25 -3
- package/src/core/application/temporal/temporal-use-case.ts +31 -4
- package/src/core/application/use-case.ts +45 -8
- package/src/core/config/core-config.ts +46 -25
- package/src/core/config/index.ts +1 -0
- package/src/core/config/policy-reporter.ts +32 -1
- package/src/core/domain/entity.ts +51 -8
- package/src/core/domain/rulesets/entity-ruleset.contracts.ts +0 -2
- package/src/core/domain/rulesets/ruleset-registry.ts +24 -0
- package/src/core/domain/rulesets/value-object-ruleset.contracts.ts +1 -7
- package/src/core/domain/temporal/half-open-interval.ts +34 -0
- package/src/core/domain/temporal/temporal-snapshot.ts +13 -2
- package/src/core/domain/temporal/transaction-time.ts +19 -15
- package/src/core/domain/temporal/valid-time.ts +20 -15
- package/src/core/domain/uuid-identifier.ts +6 -11
- package/src/core/domain/value-object.ts +29 -3
- package/src/core/errors/authentication-error.ts +5 -31
- package/src/core/errors/authorization-error.ts +12 -20
- package/src/core/errors/business-rule-violation-error.ts +4 -1
- package/src/core/errors/idempotency-error.ts +5 -2
- package/src/core/errors/index.ts +4 -0
- package/src/core/errors/integration-error.ts +4 -24
- package/src/core/errors/legacy-incompatible-error.ts +1 -0
- package/src/core/errors/not-found-error.ts +4 -1
- package/src/core/errors/temporal-error.ts +22 -20
- package/src/core/errors/unexpected-error.ts +5 -1
- package/src/core/errors/utils/factory-helpers.ts +70 -0
- package/src/core/errors/utils/index.ts +5 -0
- package/src/core/errors/utils/json-safe.ts +35 -12
- package/src/core/errors/validation-error.ts +4 -1
- package/src/core/exceptions/business-rule-violation-exception.ts +2 -1
- package/src/core/exceptions/domain-exception.ts +9 -1
- package/src/core/exceptions/entity-not-found-exception.ts +5 -1
- package/src/core/exceptions/invalid-state-transition-exception.ts +5 -2
- package/src/core/exceptions/invariant-violation-exception.ts +2 -2
- package/src/core/exceptions/validation-code.ts +14 -0
- package/src/core/exceptions/validation-exception.ts +44 -5
- package/src/core/exceptions/validation-field.ts +11 -1
- package/src/core/plugins/plugin.ts +25 -15
- package/src/core/plugins/types.ts +4 -2
- package/src/core/policies/asof/asof.ts +12 -0
- package/src/core/policies/catalog/policy-catalog-entry.ts +3 -1
- package/src/core/policies/catalog/policy-catalog.ts +5 -1
- package/src/core/policies/context/context-builder.ts +20 -0
- package/src/core/policies/context/context-resolver.ts +5 -0
- package/src/core/policies/context/context-seed.ts +11 -0
- package/src/core/policies/defs/in-memory-policy-definition-repo.ts +0 -2
- package/src/core/policies/engines/parse-gate-payload.ts +9 -0
- package/src/core/policies/engines/v1/condition-date-operands.ts +112 -0
- package/src/core/policies/engines/v1/condition-evaluator.ts +120 -291
- package/src/core/policies/engines/v1/condition-schema.ts +23 -19
- package/src/core/policies/engines/v1/condition-types.ts +18 -11
- package/src/core/policies/index.ts +4 -6
- package/src/core/policies/service/policy-service.ts +46 -35
- package/src/core/policies/utils/hash.ts +5 -69
- package/src/core/policies/utils/result.ts +1 -1
- package/src/core/rbac/access-request.ts +12 -2
- package/src/core/rbac/authorizer.ts +8 -1
- package/src/core/rbac/domain/role.ts +20 -0
- package/src/core/rbac/domain/scope.ts +6 -1
- package/src/core/result/index.ts +5 -0
- package/src/core/result/outcome.ts +5 -7
- package/src/core/result/result.ts +34 -1
- package/src/core/shared/hashing.ts +6 -57
- package/src/core/shared/immutable.ts +22 -4
- package/src/core/shared/stable-stringify.ts +144 -0
- package/src/core/shared/uuid.ts +16 -0
- package/src/core/versioning/domain-event-contracts.ts +43 -15
- package/src/core/versioning/index.ts +1 -0
- package/src/core/versioning/version.ts +30 -1
- package/src/domain/index.ts +5 -0
- package/src/examples/rulesets/entity/order-creation-rules-v1.ts +1 -1
- package/src/examples/rulesets/entity/order-invariants-v1.ts +2 -4
- package/src/examples/rulesets/entity/order-invariants-v2.ts +2 -4
- package/src/examples/rulesets/value-object/cpf-rules-v1.ts +2 -4
- package/src/examples/rulesets/value-object/cpf-rules-v2.ts +2 -4
- package/src/examples/rulesets/value-object/person-name-rules-v1.ts +2 -4
- package/src/examples/rulesets/value-object/person-name-rules-v2.ts +2 -4
- package/src/result/index.ts +7 -2
- package/src/version.ts +1 -1
- package/dist/domain-exception.d.cts +0 -7
- package/dist/domain-exception.d.ts +0 -7
- package/dist/entity.cjs +0 -126
- package/dist/entity.cjs.map +0 -1
- package/dist/entity.js +0 -115
- package/dist/entity.js.map +0 -1
- package/dist/use-case.cjs +0 -96
- package/dist/use-case.cjs.map +0 -1
- package/dist/use-case.js +0 -91
- package/dist/use-case.js.map +0 -1
|
@@ -49,7 +49,8 @@ declare abstract class Entity<TIdentifier> {
|
|
|
49
49
|
* Declared `protected` because entities are reconstituted through a
|
|
50
50
|
* subclass factory, never instantiated directly by application code.
|
|
51
51
|
*
|
|
52
|
-
* @throws {InvariantViolationException} When `
|
|
52
|
+
* @throws {InvariantViolationException} When `id` is absent, or when
|
|
53
|
+
* `updatedAt` is earlier than `createdAt`.
|
|
53
54
|
*/
|
|
54
55
|
protected constructor(state: EntityState<TIdentifier>);
|
|
55
56
|
/** The entity's stable identity — the basis for equality between entities. */
|
|
@@ -86,13 +87,29 @@ declare abstract class Entity<TIdentifier> {
|
|
|
86
87
|
* call this from every state-changing method so the version counter stays
|
|
87
88
|
* an accurate optimistic-lock token.
|
|
88
89
|
*
|
|
89
|
-
*
|
|
90
|
-
*
|
|
91
|
-
*
|
|
90
|
+
* `updatedAt` is monotonic: each call must be at or after the current
|
|
91
|
+
* `updatedAt` (equal is fine — two mutations can share a millisecond).
|
|
92
|
+
* Without this, the version counter would advance while the timestamp
|
|
93
|
+
* walked backwards, corrupting any audit trail or ordering that reads
|
|
94
|
+
* `updatedAt`. A caller replaying out-of-order events must sort them first.
|
|
95
|
+
*
|
|
96
|
+
* @param updatedAt - The modification instant; defaults to now.
|
|
92
97
|
* @returns The new aggregate version after the increment.
|
|
93
|
-
* @throws {InvariantViolationException} When `updatedAt` is earlier than
|
|
98
|
+
* @throws {InvariantViolationException} When `updatedAt` is earlier than
|
|
99
|
+
* the current `updatedAt`.
|
|
94
100
|
*/
|
|
95
101
|
protected markAsModified(updatedAt?: Date): number;
|
|
102
|
+
/**
|
|
103
|
+
* Identity-based equality — the semantics that define an entity. Two
|
|
104
|
+
* entities are equal when they are the same concrete class and their ids
|
|
105
|
+
* match, regardless of any other field.
|
|
106
|
+
*
|
|
107
|
+
* The class check keeps a `CustomerId`-bearing entity from equalling an
|
|
108
|
+
* unrelated entity that happens to reuse the same raw id. Value-object ids
|
|
109
|
+
* are compared through their own `equals`, so two independently
|
|
110
|
+
* reconstituted id instances still match; primitive ids use `===`.
|
|
111
|
+
*/
|
|
112
|
+
equals(other: Entity<TIdentifier> | null | undefined): boolean;
|
|
96
113
|
}
|
|
97
114
|
//#endregion
|
|
98
115
|
//#region src/core/domain/uuid-identifier.d.ts
|
|
@@ -137,8 +154,10 @@ declare abstract class UuidIdentifier<TBrand extends string = string> extends Va
|
|
|
137
154
|
/** The wrapped UUID string — convenient for logging and interpolation. */
|
|
138
155
|
toString(): string;
|
|
139
156
|
/**
|
|
140
|
-
* Whether `candidate` is a canonical UUID (versions 1–
|
|
141
|
-
*
|
|
157
|
+
* Whether `candidate` is a canonical UUID (versions 1–8, including the
|
|
158
|
+
* time-ordered v7; nil and max UUIDs are rejected as sentinels). The single
|
|
159
|
+
* source of truth for the format — see `shared/uuid.ts`; concrete
|
|
160
|
+
* identifiers call this from `create`.
|
|
142
161
|
*/
|
|
143
162
|
static isValid(candidate: string): boolean;
|
|
144
163
|
}
|
package/dist/uuid-identifier.js
CHANGED
|
@@ -1,11 +1,136 @@
|
|
|
1
|
+
import { t as InvariantViolationException } from "./invariant-violation-exception.js";
|
|
2
|
+
import { n as cloneDate, t as assertValidDate } from "./temporal-guards.js";
|
|
3
|
+
import { i as version } from "./immutable.js";
|
|
4
|
+
import { t as __decorate } from "./decorate.js";
|
|
5
|
+
import { t as UUID_PATTERN } from "./uuid.js";
|
|
6
|
+
import { t as assertValidAggregateVersion } from "./aggregate-version.js";
|
|
1
7
|
import { t as ValueObject } from "./value-object.js";
|
|
2
|
-
//#region src/core/domain/
|
|
8
|
+
//#region src/core/domain/entity.ts
|
|
3
9
|
/**
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
10
|
+
* Base class for domain entities — objects defined by a stable identity rather
|
|
11
|
+
* than by their attributes. Two entities are "the same" when their `id`
|
|
12
|
+
* matches, even if every other field differs; this is the opposite of a
|
|
13
|
+
* {@link ValueObject}, which is defined entirely by its contents.
|
|
14
|
+
*
|
|
15
|
+
* The class owns the bookkeeping common to every aggregate root: a creation
|
|
16
|
+
* timestamp that never changes, a last-modified timestamp, and an
|
|
17
|
+
* `aggregateVersion` counter used for optimistic concurrency control. All three
|
|
18
|
+
* are validated on construction and the dates are defensively cloned, so an
|
|
19
|
+
* entity can never be built into — or leak — an inconsistent temporal state.
|
|
20
|
+
*
|
|
21
|
+
* @typeParam TIdentifier - The identity type (e.g. a branded string id or a VO).
|
|
7
22
|
*/
|
|
8
|
-
|
|
23
|
+
let Entity = class Entity {
|
|
24
|
+
/**
|
|
25
|
+
* Reconstitutes an entity from its persisted {@link EntityState}.
|
|
26
|
+
*
|
|
27
|
+
* Validates the temporal invariants up front so an invalid entity is
|
|
28
|
+
* impossible to construct: both dates must be valid, the aggregate version
|
|
29
|
+
* must be a non-negative integer, and `updatedAt` may not predate
|
|
30
|
+
* `createdAt`. Both dates are cloned on the way in so a later mutation of
|
|
31
|
+
* the caller's `Date` objects cannot reach into the entity's internal state.
|
|
32
|
+
*
|
|
33
|
+
* Declared `protected` because entities are reconstituted through a
|
|
34
|
+
* subclass factory, never instantiated directly by application code.
|
|
35
|
+
*
|
|
36
|
+
* @throws {InvariantViolationException} When `id` is absent, or when
|
|
37
|
+
* `updatedAt` is earlier than `createdAt`.
|
|
38
|
+
*/
|
|
39
|
+
constructor(state) {
|
|
40
|
+
if (state.id == null) throw new InvariantViolationException("id is required: an entity cannot exist without an identity");
|
|
41
|
+
assertValidDate("createdAt", state.createdAt);
|
|
42
|
+
assertValidDate("updatedAt", state.updatedAt);
|
|
43
|
+
assertValidAggregateVersion(state.aggregateVersion);
|
|
44
|
+
if (state.updatedAt.getTime() < state.createdAt.getTime()) throw new InvariantViolationException("updatedAt cannot be earlier than createdAt");
|
|
45
|
+
this._id = state.id;
|
|
46
|
+
this._createdAt = cloneDate(state.createdAt);
|
|
47
|
+
this._updatedAt = cloneDate(state.updatedAt);
|
|
48
|
+
this._aggregateVersion = state.aggregateVersion;
|
|
49
|
+
}
|
|
50
|
+
/** The entity's stable identity — the basis for equality between entities. */
|
|
51
|
+
get id() {
|
|
52
|
+
return this._id;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* When the entity was first created.
|
|
56
|
+
*
|
|
57
|
+
* Returns a clone so callers cannot mutate the entity's internal `Date`;
|
|
58
|
+
* the value is fixed at construction and never changes thereafter.
|
|
59
|
+
*/
|
|
60
|
+
get createdAt() {
|
|
61
|
+
return cloneDate(this._createdAt);
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* When the entity was last modified.
|
|
65
|
+
*
|
|
66
|
+
* Returns a clone for the same encapsulation reason as {@link createdAt};
|
|
67
|
+
* the underlying value advances only through {@link markAsModified}.
|
|
68
|
+
*/
|
|
69
|
+
get updatedAt() {
|
|
70
|
+
return cloneDate(this._updatedAt);
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Monotonic counter incremented on every mutation, used for optimistic
|
|
74
|
+
* concurrency control: a writer reads this value, and the persistence layer
|
|
75
|
+
* rejects the write if the stored version has moved on in the meantime.
|
|
76
|
+
*/
|
|
77
|
+
get aggregateVersion() {
|
|
78
|
+
return this._aggregateVersion;
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* The schema/contract version stamped on this entity type by the
|
|
82
|
+
* `@version` decorator — used to detect and migrate state persisted under
|
|
83
|
+
* an older shape.
|
|
84
|
+
*/
|
|
85
|
+
get contractVersion() {
|
|
86
|
+
return this.constructor.CONTRACT_VERSION;
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* The single seam through which an entity records a mutation: it advances
|
|
90
|
+
* `updatedAt` and bumps {@link aggregateVersion} by one. Subclasses must
|
|
91
|
+
* call this from every state-changing method so the version counter stays
|
|
92
|
+
* an accurate optimistic-lock token.
|
|
93
|
+
*
|
|
94
|
+
* `updatedAt` is monotonic: each call must be at or after the current
|
|
95
|
+
* `updatedAt` (equal is fine — two mutations can share a millisecond).
|
|
96
|
+
* Without this, the version counter would advance while the timestamp
|
|
97
|
+
* walked backwards, corrupting any audit trail or ordering that reads
|
|
98
|
+
* `updatedAt`. A caller replaying out-of-order events must sort them first.
|
|
99
|
+
*
|
|
100
|
+
* @param updatedAt - The modification instant; defaults to now.
|
|
101
|
+
* @returns The new aggregate version after the increment.
|
|
102
|
+
* @throws {InvariantViolationException} When `updatedAt` is earlier than
|
|
103
|
+
* the current `updatedAt`.
|
|
104
|
+
*/
|
|
105
|
+
markAsModified(updatedAt = /* @__PURE__ */ new Date()) {
|
|
106
|
+
assertValidDate("updatedAt", updatedAt);
|
|
107
|
+
if (updatedAt.getTime() < this._updatedAt.getTime()) throw new InvariantViolationException("updatedAt cannot move backwards: it must be at or after the current updatedAt");
|
|
108
|
+
this._updatedAt = cloneDate(updatedAt);
|
|
109
|
+
this._aggregateVersion += 1;
|
|
110
|
+
return this._aggregateVersion;
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Identity-based equality — the semantics that define an entity. Two
|
|
114
|
+
* entities are equal when they are the same concrete class and their ids
|
|
115
|
+
* match, regardless of any other field.
|
|
116
|
+
*
|
|
117
|
+
* The class check keeps a `CustomerId`-bearing entity from equalling an
|
|
118
|
+
* unrelated entity that happens to reuse the same raw id. Value-object ids
|
|
119
|
+
* are compared through their own `equals`, so two independently
|
|
120
|
+
* reconstituted id instances still match; primitive ids use `===`.
|
|
121
|
+
*/
|
|
122
|
+
equals(other) {
|
|
123
|
+
if (other == null) return false;
|
|
124
|
+
if (other === this) return true;
|
|
125
|
+
if (other.constructor !== this.constructor) return false;
|
|
126
|
+
const id = this._id;
|
|
127
|
+
if (id instanceof ValueObject) return id.equals(other._id);
|
|
128
|
+
return this._id === other._id;
|
|
129
|
+
}
|
|
130
|
+
};
|
|
131
|
+
Entity = __decorate([version("1.0")], Entity);
|
|
132
|
+
//#endregion
|
|
133
|
+
//#region src/core/domain/uuid-identifier.ts
|
|
9
134
|
/**
|
|
10
135
|
* Base class for UUID-backed identity value objects.
|
|
11
136
|
*
|
|
@@ -47,14 +172,16 @@ var UuidIdentifier = class extends ValueObject {
|
|
|
47
172
|
return this.value;
|
|
48
173
|
}
|
|
49
174
|
/**
|
|
50
|
-
* Whether `candidate` is a canonical UUID (versions 1–
|
|
51
|
-
*
|
|
175
|
+
* Whether `candidate` is a canonical UUID (versions 1–8, including the
|
|
176
|
+
* time-ordered v7; nil and max UUIDs are rejected as sentinels). The single
|
|
177
|
+
* source of truth for the format — see `shared/uuid.ts`; concrete
|
|
178
|
+
* identifiers call this from `create`.
|
|
52
179
|
*/
|
|
53
180
|
static isValid(candidate) {
|
|
54
181
|
return UUID_PATTERN.test(candidate);
|
|
55
182
|
}
|
|
56
183
|
};
|
|
57
184
|
//#endregion
|
|
58
|
-
export { UuidIdentifier as t };
|
|
185
|
+
export { Entity as n, UuidIdentifier as t };
|
|
59
186
|
|
|
60
187
|
//# sourceMappingURL=uuid-identifier.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"uuid-identifier.js","names":[],"sources":["../src/core/domain/uuid-identifier.ts"],"sourcesContent":["import { ValueObject } from \"./value-object.js\";\n\n/**\n * Canonical RFC-4122 UUID (versions 1–5), matched case-insensitively. Declared\n * once here so the dozens of typed identifiers a domain accumulates never have\n * to re-state the pattern.\n */\nconst UUID_PATTERN =\n /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;\n\n/**\n * Base class for UUID-backed identity value objects.\n *\n * A domain typically has many of these — `OrderId`, `CustomerId`, `InvoiceId` —\n * all wrapping the same `string` shape. Two problems follow: the UUID format\n * check gets copy-pasted into every one, and because the wrapped shape is\n * identical, TypeScript's structural typing would happily accept an `OrderId`\n * where a `CustomerId` is expected. `UuidIdentifier` solves both: the format\n * lives here once ({@link isValid}), and the `TBrand` phantom tag makes each\n * subtype nominally distinct so the ids cannot be swapped for one another.\n *\n * It deliberately does not impose a factory. Identifiers vary in how they\n * report an invalid value (their own exception type, their own message), so a\n * concrete id adds a validating `create` that calls {@link isValid} plus a\n * `reconstitute` that trusts an already-persisted value:\n *\n * ```ts\n * class OrderId extends UuidIdentifier<\"OrderId\"> {\n * private constructor(value: string) { super(value); }\n * static create(raw: string): OrderId {\n * if (!UuidIdentifier.isValid(raw)) throw new InvalidOrderIdError(raw);\n * return new OrderId(raw);\n * }\n * static reconstitute(value: string): OrderId { return new OrderId(value); }\n * }\n * ```\n *\n * @typeParam TBrand - A unique string literal that nominally tags the subtype.\n */\nabstract class UuidIdentifier<\n TBrand extends string = string,\n> extends ValueObject<string, string> {\n /**\n * Phantom brand. Never assigned at runtime (`declare`), it exists only so\n * two identifiers with different brands are not interchangeable at the type\n * level despite sharing the same `string` value.\n */\n protected declare readonly __brand: TBrand;\n\n protected constructor(value: string) {\n super(value);\n }\n\n public toPrimitive(): string {\n return this.value;\n }\n\n /** The wrapped UUID string — convenient for logging and interpolation. */\n public toString(): string {\n return this.value;\n }\n\n /**\n * Whether `candidate` is a canonical UUID (versions 1–5). The single source\n * of truth for the format; concrete identifiers call this from `create`.\n */\n public static isValid(candidate: string): boolean {\n return UUID_PATTERN.test(candidate);\n }\n}\n\nexport { UuidIdentifier };\n"],"mappings":";;;;;;;AAOA,MAAM,eACF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA+BJ,IAAe,iBAAf,cAEU,YAA4B;CAQlC,YAAsB,OAAe;EACjC,MAAM,KAAK;CACf;CAEA,cAA6B;EACzB,OAAO,KAAK;CAChB;;CAGA,WAA0B;EACtB,OAAO,KAAK;CAChB;;;;;CAMA,OAAc,QAAQ,WAA4B;EAC9C,OAAO,aAAa,KAAK,SAAS;CACtC;AACJ"}
|
|
1
|
+
{"version":3,"file":"uuid-identifier.js","names":[],"sources":["../src/core/domain/entity.ts","../src/core/domain/uuid-identifier.ts"],"sourcesContent":["import { InvariantViolationException } from \"../exceptions/invariant-violation-exception.js\";\nimport { assertValidAggregateVersion } from \"../shared/aggregate-version.js\";\nimport { assertValidDate, cloneDate } from \"../shared/temporal-guards.js\";\nimport { type ContractVersion, version } from \"../versioning/version.js\";\n\nimport { ValueObject } from \"./value-object.js\";\n\n/**\n * The minimal persisted shape needed to reconstitute an {@link Entity}.\n *\n * This is the contract between storage and the domain: a repository maps a row\n * (or document) onto these four fields and hands them to a subclass constructor.\n * `aggregateVersion` travels with the state so optimistic-concurrency checks\n * survive a round-trip through the database.\n */\ninterface EntityState<TIdentifier> {\n readonly id: TIdentifier;\n readonly createdAt: Date;\n readonly updatedAt: Date;\n readonly aggregateVersion: number;\n}\n\n/**\n * Base class for domain entities — objects defined by a stable identity rather\n * than by their attributes. Two entities are \"the same\" when their `id`\n * matches, even if every other field differs; this is the opposite of a\n * {@link ValueObject}, which is defined entirely by its contents.\n *\n * The class owns the bookkeeping common to every aggregate root: a creation\n * timestamp that never changes, a last-modified timestamp, and an\n * `aggregateVersion` counter used for optimistic concurrency control. All three\n * are validated on construction and the dates are defensively cloned, so an\n * entity can never be built into — or leak — an inconsistent temporal state.\n *\n * @typeParam TIdentifier - The identity type (e.g. a branded string id or a VO).\n */\n@version(\"1.0\")\nabstract class Entity<TIdentifier> {\n declare public static readonly CONTRACT_VERSION: ContractVersion;\n\n private readonly _id: TIdentifier;\n private readonly _createdAt: Date;\n private _updatedAt: Date;\n private _aggregateVersion: number;\n\n /**\n * Reconstitutes an entity from its persisted {@link EntityState}.\n *\n * Validates the temporal invariants up front so an invalid entity is\n * impossible to construct: both dates must be valid, the aggregate version\n * must be a non-negative integer, and `updatedAt` may not predate\n * `createdAt`. Both dates are cloned on the way in so a later mutation of\n * the caller's `Date` objects cannot reach into the entity's internal state.\n *\n * Declared `protected` because entities are reconstituted through a\n * subclass factory, never instantiated directly by application code.\n *\n * @throws {InvariantViolationException} When `id` is absent, or when\n * `updatedAt` is earlier than `createdAt`.\n */\n protected constructor(state: EntityState<TIdentifier>) {\n if (state.id == null) {\n throw new InvariantViolationException(\n \"id is required: an entity cannot exist without an identity\",\n );\n }\n\n assertValidDate(\"createdAt\", state.createdAt);\n assertValidDate(\"updatedAt\", state.updatedAt);\n assertValidAggregateVersion(state.aggregateVersion);\n\n if (state.updatedAt.getTime() < state.createdAt.getTime()) {\n throw new InvariantViolationException(\n \"updatedAt cannot be earlier than createdAt\",\n );\n }\n\n this._id = state.id;\n this._createdAt = cloneDate(state.createdAt);\n this._updatedAt = cloneDate(state.updatedAt);\n this._aggregateVersion = state.aggregateVersion;\n }\n\n /** The entity's stable identity — the basis for equality between entities. */\n public get id(): TIdentifier {\n return this._id;\n }\n\n /**\n * When the entity was first created.\n *\n * Returns a clone so callers cannot mutate the entity's internal `Date`;\n * the value is fixed at construction and never changes thereafter.\n */\n public get createdAt(): Date {\n return cloneDate(this._createdAt);\n }\n\n /**\n * When the entity was last modified.\n *\n * Returns a clone for the same encapsulation reason as {@link createdAt};\n * the underlying value advances only through {@link markAsModified}.\n */\n public get updatedAt(): Date {\n return cloneDate(this._updatedAt);\n }\n\n /**\n * Monotonic counter incremented on every mutation, used for optimistic\n * concurrency control: a writer reads this value, and the persistence layer\n * rejects the write if the stored version has moved on in the meantime.\n */\n public get aggregateVersion(): number {\n return this._aggregateVersion;\n }\n\n /**\n * The schema/contract version stamped on this entity type by the\n * `@version` decorator — used to detect and migrate state persisted under\n * an older shape.\n */\n public get contractVersion(): ContractVersion {\n return (this.constructor as typeof Entity).CONTRACT_VERSION;\n }\n\n /**\n * The single seam through which an entity records a mutation: it advances\n * `updatedAt` and bumps {@link aggregateVersion} by one. Subclasses must\n * call this from every state-changing method so the version counter stays\n * an accurate optimistic-lock token.\n *\n * `updatedAt` is monotonic: each call must be at or after the current\n * `updatedAt` (equal is fine — two mutations can share a millisecond).\n * Without this, the version counter would advance while the timestamp\n * walked backwards, corrupting any audit trail or ordering that reads\n * `updatedAt`. A caller replaying out-of-order events must sort them first.\n *\n * @param updatedAt - The modification instant; defaults to now.\n * @returns The new aggregate version after the increment.\n * @throws {InvariantViolationException} When `updatedAt` is earlier than\n * the current `updatedAt`.\n */\n protected markAsModified(updatedAt: Date = new Date()): number {\n assertValidDate(\"updatedAt\", updatedAt);\n\n if (updatedAt.getTime() < this._updatedAt.getTime()) {\n throw new InvariantViolationException(\n \"updatedAt cannot move backwards: it must be at or after the current updatedAt\",\n );\n }\n\n this._updatedAt = cloneDate(updatedAt);\n this._aggregateVersion += 1;\n\n return this._aggregateVersion;\n }\n\n /**\n * Identity-based equality — the semantics that define an entity. Two\n * entities are equal when they are the same concrete class and their ids\n * match, regardless of any other field.\n *\n * The class check keeps a `CustomerId`-bearing entity from equalling an\n * unrelated entity that happens to reuse the same raw id. Value-object ids\n * are compared through their own `equals`, so two independently\n * reconstituted id instances still match; primitive ids use `===`.\n */\n public equals(other: Entity<TIdentifier> | null | undefined): boolean {\n if (other == null) {\n return false;\n }\n if (other === this) {\n return true;\n }\n if (other.constructor !== this.constructor) {\n return false;\n }\n\n const id: unknown = this._id;\n if (id instanceof ValueObject) {\n return id.equals(other._id as typeof id);\n }\n\n return this._id === other._id;\n }\n}\n\nexport { Entity, type EntityState };\n","import { UUID_PATTERN } from \"../shared/uuid.js\";\nimport { ValueObject } from \"./value-object.js\";\n\n/**\n * Base class for UUID-backed identity value objects.\n *\n * A domain typically has many of these — `OrderId`, `CustomerId`, `InvoiceId` —\n * all wrapping the same `string` shape. Two problems follow: the UUID format\n * check gets copy-pasted into every one, and because the wrapped shape is\n * identical, TypeScript's structural typing would happily accept an `OrderId`\n * where a `CustomerId` is expected. `UuidIdentifier` solves both: the format\n * lives here once ({@link isValid}), and the `TBrand` phantom tag makes each\n * subtype nominally distinct so the ids cannot be swapped for one another.\n *\n * It deliberately does not impose a factory. Identifiers vary in how they\n * report an invalid value (their own exception type, their own message), so a\n * concrete id adds a validating `create` that calls {@link isValid} plus a\n * `reconstitute` that trusts an already-persisted value:\n *\n * ```ts\n * class OrderId extends UuidIdentifier<\"OrderId\"> {\n * private constructor(value: string) { super(value); }\n * static create(raw: string): OrderId {\n * if (!UuidIdentifier.isValid(raw)) throw new InvalidOrderIdError(raw);\n * return new OrderId(raw);\n * }\n * static reconstitute(value: string): OrderId { return new OrderId(value); }\n * }\n * ```\n *\n * @typeParam TBrand - A unique string literal that nominally tags the subtype.\n */\nabstract class UuidIdentifier<\n TBrand extends string = string,\n> extends ValueObject<string, string> {\n /**\n * Phantom brand. Never assigned at runtime (`declare`), it exists only so\n * two identifiers with different brands are not interchangeable at the type\n * level despite sharing the same `string` value.\n */\n declare protected readonly __brand: TBrand;\n\n protected constructor(value: string) {\n super(value);\n }\n\n public toPrimitive(): string {\n return this.value;\n }\n\n /** The wrapped UUID string — convenient for logging and interpolation. */\n public toString(): string {\n return this.value;\n }\n\n /**\n * Whether `candidate` is a canonical UUID (versions 1–8, including the\n * time-ordered v7; nil and max UUIDs are rejected as sentinels). The single\n * source of truth for the format — see `shared/uuid.ts`; concrete\n * identifiers call this from `create`.\n */\n public static isValid(candidate: string): boolean {\n return UUID_PATTERN.test(candidate);\n }\n}\n\nexport { UuidIdentifier };\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;AAoCA,IAAA,SAAA,MACe,OAAoB;;;;;;;;;;;;;;;;CAuB/B,YAAsB,OAAiC;EACnD,IAAI,MAAM,MAAM,MACZ,MAAM,IAAI,4BACN,4DACJ;EAGJ,gBAAgB,aAAa,MAAM,SAAS;EAC5C,gBAAgB,aAAa,MAAM,SAAS;EAC5C,4BAA4B,MAAM,gBAAgB;EAElD,IAAI,MAAM,UAAU,QAAQ,IAAI,MAAM,UAAU,QAAQ,GACpD,MAAM,IAAI,4BACN,4CACJ;EAGJ,KAAK,MAAM,MAAM;EACjB,KAAK,aAAa,UAAU,MAAM,SAAS;EAC3C,KAAK,aAAa,UAAU,MAAM,SAAS;EAC3C,KAAK,oBAAoB,MAAM;CACnC;;CAGA,IAAW,KAAkB;EACzB,OAAO,KAAK;CAChB;;;;;;;CAQA,IAAW,YAAkB;EACzB,OAAO,UAAU,KAAK,UAAU;CACpC;;;;;;;CAQA,IAAW,YAAkB;EACzB,OAAO,UAAU,KAAK,UAAU;CACpC;;;;;;CAOA,IAAW,mBAA2B;EAClC,OAAO,KAAK;CAChB;;;;;;CAOA,IAAW,kBAAmC;EAC1C,OAAQ,KAAK,YAA8B;CAC/C;;;;;;;;;;;;;;;;;;CAmBA,eAAyB,4BAAkB,IAAI,KAAK,GAAW;EAC3D,gBAAgB,aAAa,SAAS;EAEtC,IAAI,UAAU,QAAQ,IAAI,KAAK,WAAW,QAAQ,GAC9C,MAAM,IAAI,4BACN,+EACJ;EAGJ,KAAK,aAAa,UAAU,SAAS;EACrC,KAAK,qBAAqB;EAE1B,OAAO,KAAK;CAChB;;;;;;;;;;;CAYA,OAAc,OAAwD;EAClE,IAAI,SAAS,MACT,OAAO;EAEX,IAAI,UAAU,MACV,OAAO;EAEX,IAAI,MAAM,gBAAgB,KAAK,aAC3B,OAAO;EAGX,MAAM,KAAc,KAAK;EACzB,IAAI,cAAc,aACd,OAAO,GAAG,OAAO,MAAM,GAAgB;EAG3C,OAAO,KAAK,QAAQ,MAAM;CAC9B;AACJ;qBAtJC,QAAQ,KAAK,CAAA,GAAA,MAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ACJd,IAAe,iBAAf,cAEU,YAA4B;CAQlC,YAAsB,OAAe;EACjC,MAAM,KAAK;CACf;CAEA,cAA6B;EACzB,OAAO,KAAK;CAChB;;CAGA,WAA0B;EACtB,OAAO,KAAK;CAChB;;;;;;;CAQA,OAAc,QAAQ,WAA4B;EAC9C,OAAO,aAAa,KAAK,SAAS;CACtC;AACJ"}
|
package/dist/uuid.cjs
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
//#region src/core/shared/uuid.ts
|
|
2
|
+
/**
|
|
3
|
+
* Canonical RFC-4122 / RFC-9562 UUID, matched case-insensitively. Accepts
|
|
4
|
+
* versions 1–8 (variant 8/9/a/b) — notably UUIDv7, the time-ordered default for
|
|
5
|
+
* sortable primary keys in modern schemas. The nil (`0000…`) and max (`ffff…`)
|
|
6
|
+
* UUIDs are intentionally rejected: they are sentinels, not real identities, and
|
|
7
|
+
* a domain id should never carry one.
|
|
8
|
+
*
|
|
9
|
+
* The single source of truth for the UUID format — `UuidIdentifier` (domain ids)
|
|
10
|
+
* and `RequestedBy` (command actor) both validate against this pattern, so an
|
|
11
|
+
* identity accepted at the application boundary is always one the domain also
|
|
12
|
+
* considers valid.
|
|
13
|
+
*/
|
|
14
|
+
const UUID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
|
|
15
|
+
//#endregion
|
|
16
|
+
Object.defineProperty(exports, "UUID_PATTERN", {
|
|
17
|
+
enumerable: true,
|
|
18
|
+
get: function() {
|
|
19
|
+
return UUID_PATTERN;
|
|
20
|
+
}
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
//# sourceMappingURL=uuid.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"uuid.cjs","names":[],"sources":["../src/core/shared/uuid.ts"],"sourcesContent":["/**\n * Canonical RFC-4122 / RFC-9562 UUID, matched case-insensitively. Accepts\n * versions 1–8 (variant 8/9/a/b) — notably UUIDv7, the time-ordered default for\n * sortable primary keys in modern schemas. The nil (`0000…`) and max (`ffff…`)\n * UUIDs are intentionally rejected: they are sentinels, not real identities, and\n * a domain id should never carry one.\n *\n * The single source of truth for the UUID format — `UuidIdentifier` (domain ids)\n * and `RequestedBy` (command actor) both validate against this pattern, so an\n * identity accepted at the application boundary is always one the domain also\n * considers valid.\n */\nconst UUID_PATTERN =\n /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;\n\nexport { UUID_PATTERN };\n"],"mappings":";;;;;;;;;;;;;AAYA,MAAM,eACF"}
|
package/dist/uuid.js
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
//#region src/core/shared/uuid.ts
|
|
2
|
+
/**
|
|
3
|
+
* Canonical RFC-4122 / RFC-9562 UUID, matched case-insensitively. Accepts
|
|
4
|
+
* versions 1–8 (variant 8/9/a/b) — notably UUIDv7, the time-ordered default for
|
|
5
|
+
* sortable primary keys in modern schemas. The nil (`0000…`) and max (`ffff…`)
|
|
6
|
+
* UUIDs are intentionally rejected: they are sentinels, not real identities, and
|
|
7
|
+
* a domain id should never carry one.
|
|
8
|
+
*
|
|
9
|
+
* The single source of truth for the UUID format — `UuidIdentifier` (domain ids)
|
|
10
|
+
* and `RequestedBy` (command actor) both validate against this pattern, so an
|
|
11
|
+
* identity accepted at the application boundary is always one the domain also
|
|
12
|
+
* considers valid.
|
|
13
|
+
*/
|
|
14
|
+
const UUID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
|
|
15
|
+
//#endregion
|
|
16
|
+
export { UUID_PATTERN as t };
|
|
17
|
+
|
|
18
|
+
//# sourceMappingURL=uuid.js.map
|
package/dist/uuid.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"uuid.js","names":[],"sources":["../src/core/shared/uuid.ts"],"sourcesContent":["/**\n * Canonical RFC-4122 / RFC-9562 UUID, matched case-insensitively. Accepts\n * versions 1–8 (variant 8/9/a/b) — notably UUIDv7, the time-ordered default for\n * sortable primary keys in modern schemas. The nil (`0000…`) and max (`ffff…`)\n * UUIDs are intentionally rejected: they are sentinels, not real identities, and\n * a domain id should never carry one.\n *\n * The single source of truth for the UUID format — `UuidIdentifier` (domain ids)\n * and `RequestedBy` (command actor) both validate against this pattern, so an\n * identity accepted at the application boundary is always one the domain also\n * considers valid.\n */\nconst UUID_PATTERN =\n /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;\n\nexport { UUID_PATTERN };\n"],"mappings":";;;;;;;;;;;;;AAYA,MAAM,eACF"}
|
package/dist/validation-code.cjs
CHANGED
|
@@ -6,6 +6,15 @@ var ValidationCode = class ValidationCode {
|
|
|
6
6
|
static of(value) {
|
|
7
7
|
return new ValidationCode(value);
|
|
8
8
|
}
|
|
9
|
+
toString() {
|
|
10
|
+
return this.value;
|
|
11
|
+
}
|
|
12
|
+
equals(other) {
|
|
13
|
+
return this.value === other.value;
|
|
14
|
+
}
|
|
15
|
+
toJSON() {
|
|
16
|
+
return this.value;
|
|
17
|
+
}
|
|
9
18
|
static {
|
|
10
19
|
this.BLANK = new ValidationCode("blank");
|
|
11
20
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"validation-code.cjs","names":[],"sources":["../src/core/exceptions/validation-code.ts"],"sourcesContent":["// Object-oriented representation of validation codes (similar to Kotlin inline class).\n// Provides common reusable codes while allowing custom codes via `of`.\n\nclass ValidationCode {\n public readonly value: string;\n\n private constructor(value: string) {\n this.value = value;\n }\n\n // Factory for custom codes\n public static of(value: string): ValidationCode {\n return new ValidationCode(value);\n }\n\n // Common, domain-friendly codes\n public static readonly BLANK = new ValidationCode(\"blank\");\n public static readonly REQUIRED = new ValidationCode(\"required\");\n public static readonly INVALID_FORMAT = new ValidationCode(\n \"invalid_format\",\n );\n public static readonly INVALID_CHARACTERS = new ValidationCode(\n \"invalid_characters\",\n );\n public static readonly INVALID_LENGTH = new ValidationCode(\n \"invalid_length\",\n );\n public static readonly TOO_SHORT = new ValidationCode(\"too_short\");\n public static readonly TOO_LONG = new ValidationCode(\"too_long\");\n public static readonly OUT_OF_RANGE = new ValidationCode(\"out_of_range\");\n public static readonly INVALID_CHECKSUM = new ValidationCode(\n \"invalid_checksum\",\n );\n public static readonly INVALID_CHECKING_DIGIT = new ValidationCode(\n \"invalid_checking_digit\",\n );\n public static readonly ALREADY_EXISTS = new ValidationCode(\n \"already_exists\",\n );\n public static readonly NOT_FOUND = new ValidationCode(\"not_found\");\n public static readonly NOT_ALLOWED = new ValidationCode(\"not_allowed\");\n public static readonly UNEXPECTED = new ValidationCode(\"unexpected\");\n}\n\nexport { ValidationCode };\n"],"mappings":";AAGA,IAAM,iBAAN,MAAM,eAAe;CAGjB,YAAoB,OAAe;EAC/B,KAAK,QAAQ;CACjB;CAGA,OAAc,GAAG,OAA+B;EAC5C,OAAO,IAAI,eAAe,KAAK;CACnC;;eAG+B,IAAI,eAAe,OAAO;;;kBACvB,IAAI,eAAe,UAAU;;;wBACvB,IAAI,eACxC,gBACJ;;;4BAC4C,IAAI,eAC5C,oBACJ;;;wBACwC,IAAI,eACxC,gBACJ;;;mBACmC,IAAI,eAAe,WAAW;;;kBAC/B,IAAI,eAAe,UAAU;;;sBACzB,IAAI,eAAe,cAAc;;;0BAC7B,IAAI,eAC1C,kBACJ;;;gCACgD,IAAI,eAChD,wBACJ;;;wBACwC,IAAI,eACxC,gBACJ;;;mBACmC,IAAI,eAAe,WAAW;;;qBAC5B,IAAI,eAAe,aAAa;;;oBACjC,IAAI,eAAe,YAAY;;AACvE"}
|
|
1
|
+
{"version":3,"file":"validation-code.cjs","names":[],"sources":["../src/core/exceptions/validation-code.ts"],"sourcesContent":["// Object-oriented representation of validation codes (similar to Kotlin inline class).\n// Provides common reusable codes while allowing custom codes via `of`.\n\nclass ValidationCode {\n public readonly value: string;\n\n private constructor(value: string) {\n this.value = value;\n }\n\n // Factory for custom codes\n public static of(value: string): ValidationCode {\n return new ValidationCode(value);\n }\n\n public toString(): string {\n return this.value;\n }\n\n public equals(other: ValidationCode): boolean {\n return this.value === other.value;\n }\n\n // Serialize to the bare code string, so JSON.stringify of a violation\n // yields \"blank\" rather than { \"value\": \"blank\" }. Mirrors ValidationField.\n public toJSON(): string {\n return this.value;\n }\n\n // Common, domain-friendly codes\n public static readonly BLANK = new ValidationCode(\"blank\");\n public static readonly REQUIRED = new ValidationCode(\"required\");\n public static readonly INVALID_FORMAT = new ValidationCode(\n \"invalid_format\",\n );\n public static readonly INVALID_CHARACTERS = new ValidationCode(\n \"invalid_characters\",\n );\n public static readonly INVALID_LENGTH = new ValidationCode(\n \"invalid_length\",\n );\n public static readonly TOO_SHORT = new ValidationCode(\"too_short\");\n public static readonly TOO_LONG = new ValidationCode(\"too_long\");\n public static readonly OUT_OF_RANGE = new ValidationCode(\"out_of_range\");\n public static readonly INVALID_CHECKSUM = new ValidationCode(\n \"invalid_checksum\",\n );\n public static readonly INVALID_CHECKING_DIGIT = new ValidationCode(\n \"invalid_checking_digit\",\n );\n public static readonly ALREADY_EXISTS = new ValidationCode(\n \"already_exists\",\n );\n public static readonly NOT_FOUND = new ValidationCode(\"not_found\");\n public static readonly NOT_ALLOWED = new ValidationCode(\"not_allowed\");\n public static readonly UNEXPECTED = new ValidationCode(\"unexpected\");\n}\n\nexport { ValidationCode };\n"],"mappings":";AAGA,IAAM,iBAAN,MAAM,eAAe;CAGjB,YAAoB,OAAe;EAC/B,KAAK,QAAQ;CACjB;CAGA,OAAc,GAAG,OAA+B;EAC5C,OAAO,IAAI,eAAe,KAAK;CACnC;CAEA,WAA0B;EACtB,OAAO,KAAK;CAChB;CAEA,OAAc,OAAgC;EAC1C,OAAO,KAAK,UAAU,MAAM;CAChC;CAIA,SAAwB;EACpB,OAAO,KAAK;CAChB;;eAG+B,IAAI,eAAe,OAAO;;;kBACvB,IAAI,eAAe,UAAU;;;wBACvB,IAAI,eACxC,gBACJ;;;4BAC4C,IAAI,eAC5C,oBACJ;;;wBACwC,IAAI,eACxC,gBACJ;;;mBACmC,IAAI,eAAe,WAAW;;;kBAC/B,IAAI,eAAe,UAAU;;;sBACzB,IAAI,eAAe,cAAc;;;0BAC7B,IAAI,eAC1C,kBACJ;;;gCACgD,IAAI,eAChD,wBACJ;;;wBACwC,IAAI,eACxC,gBACJ;;;mBACmC,IAAI,eAAe,WAAW;;;qBAC5B,IAAI,eAAe,aAAa;;;oBACjC,IAAI,eAAe,YAAY;;AACvE"}
|
|
@@ -3,6 +3,9 @@ declare class ValidationCode {
|
|
|
3
3
|
readonly value: string;
|
|
4
4
|
private constructor();
|
|
5
5
|
static of(value: string): ValidationCode;
|
|
6
|
+
toString(): string;
|
|
7
|
+
equals(other: ValidationCode): boolean;
|
|
8
|
+
toJSON(): string;
|
|
6
9
|
static readonly BLANK: ValidationCode;
|
|
7
10
|
static readonly REQUIRED: ValidationCode;
|
|
8
11
|
static readonly INVALID_FORMAT: ValidationCode;
|
|
@@ -3,6 +3,9 @@ declare class ValidationCode {
|
|
|
3
3
|
readonly value: string;
|
|
4
4
|
private constructor();
|
|
5
5
|
static of(value: string): ValidationCode;
|
|
6
|
+
toString(): string;
|
|
7
|
+
equals(other: ValidationCode): boolean;
|
|
8
|
+
toJSON(): string;
|
|
6
9
|
static readonly BLANK: ValidationCode;
|
|
7
10
|
static readonly REQUIRED: ValidationCode;
|
|
8
11
|
static readonly INVALID_FORMAT: ValidationCode;
|
package/dist/validation-code.js
CHANGED
|
@@ -6,6 +6,15 @@ var ValidationCode = class ValidationCode {
|
|
|
6
6
|
static of(value) {
|
|
7
7
|
return new ValidationCode(value);
|
|
8
8
|
}
|
|
9
|
+
toString() {
|
|
10
|
+
return this.value;
|
|
11
|
+
}
|
|
12
|
+
equals(other) {
|
|
13
|
+
return this.value === other.value;
|
|
14
|
+
}
|
|
15
|
+
toJSON() {
|
|
16
|
+
return this.value;
|
|
17
|
+
}
|
|
9
18
|
static {
|
|
10
19
|
this.BLANK = new ValidationCode("blank");
|
|
11
20
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"validation-code.js","names":[],"sources":["../src/core/exceptions/validation-code.ts"],"sourcesContent":["// Object-oriented representation of validation codes (similar to Kotlin inline class).\n// Provides common reusable codes while allowing custom codes via `of`.\n\nclass ValidationCode {\n public readonly value: string;\n\n private constructor(value: string) {\n this.value = value;\n }\n\n // Factory for custom codes\n public static of(value: string): ValidationCode {\n return new ValidationCode(value);\n }\n\n // Common, domain-friendly codes\n public static readonly BLANK = new ValidationCode(\"blank\");\n public static readonly REQUIRED = new ValidationCode(\"required\");\n public static readonly INVALID_FORMAT = new ValidationCode(\n \"invalid_format\",\n );\n public static readonly INVALID_CHARACTERS = new ValidationCode(\n \"invalid_characters\",\n );\n public static readonly INVALID_LENGTH = new ValidationCode(\n \"invalid_length\",\n );\n public static readonly TOO_SHORT = new ValidationCode(\"too_short\");\n public static readonly TOO_LONG = new ValidationCode(\"too_long\");\n public static readonly OUT_OF_RANGE = new ValidationCode(\"out_of_range\");\n public static readonly INVALID_CHECKSUM = new ValidationCode(\n \"invalid_checksum\",\n );\n public static readonly INVALID_CHECKING_DIGIT = new ValidationCode(\n \"invalid_checking_digit\",\n );\n public static readonly ALREADY_EXISTS = new ValidationCode(\n \"already_exists\",\n );\n public static readonly NOT_FOUND = new ValidationCode(\"not_found\");\n public static readonly NOT_ALLOWED = new ValidationCode(\"not_allowed\");\n public static readonly UNEXPECTED = new ValidationCode(\"unexpected\");\n}\n\nexport { ValidationCode };\n"],"mappings":";AAGA,IAAM,iBAAN,MAAM,eAAe;CAGjB,YAAoB,OAAe;EAC/B,KAAK,QAAQ;CACjB;CAGA,OAAc,GAAG,OAA+B;EAC5C,OAAO,IAAI,eAAe,KAAK;CACnC;;eAG+B,IAAI,eAAe,OAAO;;;kBACvB,IAAI,eAAe,UAAU;;;wBACvB,IAAI,eACxC,gBACJ;;;4BAC4C,IAAI,eAC5C,oBACJ;;;wBACwC,IAAI,eACxC,gBACJ;;;mBACmC,IAAI,eAAe,WAAW;;;kBAC/B,IAAI,eAAe,UAAU;;;sBACzB,IAAI,eAAe,cAAc;;;0BAC7B,IAAI,eAC1C,kBACJ;;;gCACgD,IAAI,eAChD,wBACJ;;;wBACwC,IAAI,eACxC,gBACJ;;;mBACmC,IAAI,eAAe,WAAW;;;qBAC5B,IAAI,eAAe,aAAa;;;oBACjC,IAAI,eAAe,YAAY;;AACvE"}
|
|
1
|
+
{"version":3,"file":"validation-code.js","names":[],"sources":["../src/core/exceptions/validation-code.ts"],"sourcesContent":["// Object-oriented representation of validation codes (similar to Kotlin inline class).\n// Provides common reusable codes while allowing custom codes via `of`.\n\nclass ValidationCode {\n public readonly value: string;\n\n private constructor(value: string) {\n this.value = value;\n }\n\n // Factory for custom codes\n public static of(value: string): ValidationCode {\n return new ValidationCode(value);\n }\n\n public toString(): string {\n return this.value;\n }\n\n public equals(other: ValidationCode): boolean {\n return this.value === other.value;\n }\n\n // Serialize to the bare code string, so JSON.stringify of a violation\n // yields \"blank\" rather than { \"value\": \"blank\" }. Mirrors ValidationField.\n public toJSON(): string {\n return this.value;\n }\n\n // Common, domain-friendly codes\n public static readonly BLANK = new ValidationCode(\"blank\");\n public static readonly REQUIRED = new ValidationCode(\"required\");\n public static readonly INVALID_FORMAT = new ValidationCode(\n \"invalid_format\",\n );\n public static readonly INVALID_CHARACTERS = new ValidationCode(\n \"invalid_characters\",\n );\n public static readonly INVALID_LENGTH = new ValidationCode(\n \"invalid_length\",\n );\n public static readonly TOO_SHORT = new ValidationCode(\"too_short\");\n public static readonly TOO_LONG = new ValidationCode(\"too_long\");\n public static readonly OUT_OF_RANGE = new ValidationCode(\"out_of_range\");\n public static readonly INVALID_CHECKSUM = new ValidationCode(\n \"invalid_checksum\",\n );\n public static readonly INVALID_CHECKING_DIGIT = new ValidationCode(\n \"invalid_checking_digit\",\n );\n public static readonly ALREADY_EXISTS = new ValidationCode(\n \"already_exists\",\n );\n public static readonly NOT_FOUND = new ValidationCode(\"not_found\");\n public static readonly NOT_ALLOWED = new ValidationCode(\"not_allowed\");\n public static readonly UNEXPECTED = new ValidationCode(\"unexpected\");\n}\n\nexport { ValidationCode };\n"],"mappings":";AAGA,IAAM,iBAAN,MAAM,eAAe;CAGjB,YAAoB,OAAe;EAC/B,KAAK,QAAQ;CACjB;CAGA,OAAc,GAAG,OAA+B;EAC5C,OAAO,IAAI,eAAe,KAAK;CACnC;CAEA,WAA0B;EACtB,OAAO,KAAK;CAChB;CAEA,OAAc,OAAgC;EAC1C,OAAO,KAAK,UAAU,MAAM;CAChC;CAIA,SAAwB;EACpB,OAAO,KAAK;CAChB;;eAG+B,IAAI,eAAe,OAAO;;;kBACvB,IAAI,eAAe,UAAU;;;wBACvB,IAAI,eACxC,gBACJ;;;4BAC4C,IAAI,eAC5C,oBACJ;;;wBACwC,IAAI,eACxC,gBACJ;;;mBACmC,IAAI,eAAe,WAAW;;;kBAC/B,IAAI,eAAe,UAAU;;;sBACzB,IAAI,eAAe,cAAc;;;0BAC7B,IAAI,eAC1C,kBACJ;;;gCACgD,IAAI,eAChD,wBACJ;;;wBACwC,IAAI,eACxC,gBACJ;;;mBACmC,IAAI,eAAe,WAAW;;;qBAC5B,IAAI,eAAe,aAAa;;;oBACjC,IAAI,eAAe,YAAY;;AACvE"}
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
const require_app_error = require("./app-error.cjs");
|
|
2
|
+
const require_authorization_error = require("./authorization-error.cjs");
|
|
3
|
+
const require_hashing = require("./hashing.cjs");
|
|
2
4
|
//#region src/core/errors/authentication-error.ts
|
|
3
5
|
var AuthenticationError = class AuthenticationError extends require_app_error.AppError {
|
|
4
6
|
constructor(params) {
|
|
@@ -24,8 +26,8 @@ var AuthenticationError = class AuthenticationError extends require_app_error.Ap
|
|
|
24
26
|
message: "Missing credentials",
|
|
25
27
|
code: require_app_error.ErrorCodes.authentication.missingToken,
|
|
26
28
|
reason: "missing_token",
|
|
27
|
-
metadata: compactMetadata
|
|
28
|
-
...
|
|
29
|
+
metadata: require_authorization_error.compactMetadata(options),
|
|
30
|
+
...require_authorization_error.pickAppErrorOptions(options)
|
|
29
31
|
});
|
|
30
32
|
}
|
|
31
33
|
static invalidToken(options) {
|
|
@@ -33,14 +35,14 @@ var AuthenticationError = class AuthenticationError extends require_app_error.Ap
|
|
|
33
35
|
message: "Invalid credentials",
|
|
34
36
|
code: require_app_error.ErrorCodes.authentication.invalidToken,
|
|
35
37
|
reason: "invalid_token",
|
|
36
|
-
metadata: compactMetadata
|
|
38
|
+
metadata: require_authorization_error.compactMetadata({
|
|
37
39
|
authScheme: options?.authScheme,
|
|
38
40
|
tokenLocation: options?.tokenLocation,
|
|
39
41
|
details: options?.details,
|
|
40
42
|
correlationId: options?.correlationId,
|
|
41
43
|
requestId: options?.requestId
|
|
42
44
|
}),
|
|
43
|
-
...
|
|
45
|
+
...require_authorization_error.pickAppErrorOptions(options)
|
|
44
46
|
});
|
|
45
47
|
}
|
|
46
48
|
static expiredToken(options) {
|
|
@@ -48,8 +50,8 @@ var AuthenticationError = class AuthenticationError extends require_app_error.Ap
|
|
|
48
50
|
message: "Expired credentials",
|
|
49
51
|
code: require_app_error.ErrorCodes.authentication.expiredToken,
|
|
50
52
|
reason: "expired_token",
|
|
51
|
-
metadata: compactMetadata
|
|
52
|
-
...
|
|
53
|
+
metadata: require_authorization_error.compactMetadata(options),
|
|
54
|
+
...require_authorization_error.pickAppErrorOptions(options)
|
|
53
55
|
});
|
|
54
56
|
}
|
|
55
57
|
static invalidCredentials(options) {
|
|
@@ -57,27 +59,11 @@ var AuthenticationError = class AuthenticationError extends require_app_error.Ap
|
|
|
57
59
|
message: "Invalid username or password",
|
|
58
60
|
code: require_app_error.ErrorCodes.authentication.invalidCredentials,
|
|
59
61
|
reason: "invalid_credentials",
|
|
60
|
-
metadata: compactMetadata
|
|
61
|
-
...
|
|
62
|
+
metadata: require_authorization_error.compactMetadata(options),
|
|
63
|
+
...require_authorization_error.pickAppErrorOptions(options)
|
|
62
64
|
});
|
|
63
65
|
}
|
|
64
66
|
};
|
|
65
|
-
function extractAppErrorOptions(options) {
|
|
66
|
-
return {
|
|
67
|
-
cause: options?.cause,
|
|
68
|
-
type: options?.type,
|
|
69
|
-
severity: options?.severity,
|
|
70
|
-
correlationId: options?.correlationId,
|
|
71
|
-
requestId: options?.requestId,
|
|
72
|
-
commandId: options?.commandId,
|
|
73
|
-
createdAtIso: options?.createdAtIso,
|
|
74
|
-
publicMessage: options?.publicMessage
|
|
75
|
-
};
|
|
76
|
-
}
|
|
77
|
-
function compactMetadata$1(input) {
|
|
78
|
-
if (!input) return void 0;
|
|
79
|
-
return Object.fromEntries(Object.entries(input).filter(([, value]) => value !== void 0));
|
|
80
|
-
}
|
|
81
67
|
//#endregion
|
|
82
68
|
//#region src/core/errors/conflict-error.ts
|
|
83
69
|
/**
|
|
@@ -408,9 +394,10 @@ var IdempotencyReplayNotSupportedError = class IdempotencyReplayNotSupportedErro
|
|
|
408
394
|
}
|
|
409
395
|
};
|
|
410
396
|
function resolveKeyIdentity(key, keyHash, keyPreview) {
|
|
397
|
+
const preview = keyPreview ?? (key ? key.length <= 8 ? key : key.slice(0, 8) : void 0);
|
|
411
398
|
return {
|
|
412
|
-
keyHash,
|
|
413
|
-
keyPreview:
|
|
399
|
+
keyHash: keyHash ?? (key ? require_hashing.sha256Hex(key) : void 0),
|
|
400
|
+
keyPreview: preview
|
|
414
401
|
};
|
|
415
402
|
}
|
|
416
403
|
//#endregion
|
|
@@ -450,7 +437,7 @@ var IntegrationError = class IntegrationError extends require_app_error.AppError
|
|
|
450
437
|
reason: "timeout",
|
|
451
438
|
...options
|
|
452
439
|
}),
|
|
453
|
-
...
|
|
440
|
+
...require_authorization_error.pickAppErrorOptions(options)
|
|
454
441
|
});
|
|
455
442
|
}
|
|
456
443
|
/**
|
|
@@ -466,7 +453,7 @@ var IntegrationError = class IntegrationError extends require_app_error.AppError
|
|
|
466
453
|
reason: "unreachable",
|
|
467
454
|
...options
|
|
468
455
|
}),
|
|
469
|
-
...
|
|
456
|
+
...require_authorization_error.pickAppErrorOptions(options)
|
|
470
457
|
});
|
|
471
458
|
}
|
|
472
459
|
/**
|
|
@@ -483,12 +470,12 @@ var IntegrationError = class IntegrationError extends require_app_error.AppError
|
|
|
483
470
|
reason: "bad_response",
|
|
484
471
|
...options
|
|
485
472
|
}),
|
|
486
|
-
...
|
|
473
|
+
...require_authorization_error.pickAppErrorOptions(options)
|
|
487
474
|
});
|
|
488
475
|
}
|
|
489
476
|
};
|
|
490
477
|
function buildMetadata(input) {
|
|
491
|
-
return compactMetadata({
|
|
478
|
+
return require_authorization_error.compactMetadata({
|
|
492
479
|
reason: input.reason,
|
|
493
480
|
provider: input.provider,
|
|
494
481
|
operation: input.operation,
|
|
@@ -503,21 +490,6 @@ function buildMetadata(input) {
|
|
|
503
490
|
responseCode: input.responseCode
|
|
504
491
|
});
|
|
505
492
|
}
|
|
506
|
-
function pickAppErrorFields(options) {
|
|
507
|
-
return {
|
|
508
|
-
cause: options.cause,
|
|
509
|
-
type: options.type,
|
|
510
|
-
severity: options.severity,
|
|
511
|
-
correlationId: options.correlationId,
|
|
512
|
-
requestId: options.requestId,
|
|
513
|
-
commandId: options.commandId,
|
|
514
|
-
createdAtIso: options.createdAtIso,
|
|
515
|
-
publicMessage: options.publicMessage
|
|
516
|
-
};
|
|
517
|
-
}
|
|
518
|
-
function compactMetadata(input) {
|
|
519
|
-
return Object.fromEntries(Object.entries(input).filter(([, value]) => value !== void 0));
|
|
520
|
-
}
|
|
521
493
|
//#endregion
|
|
522
494
|
//#region src/core/errors/legacy-incompatible-error.ts
|
|
523
495
|
var LegacyIncompatibleError = class extends require_app_error.AppError {
|
|
@@ -530,6 +502,7 @@ var LegacyIncompatibleError = class extends require_app_error.AppError {
|
|
|
530
502
|
...options,
|
|
531
503
|
metadata: mergedMetadata
|
|
532
504
|
});
|
|
505
|
+
Object.freeze(this);
|
|
533
506
|
}
|
|
534
507
|
};
|
|
535
508
|
//#endregion
|
|
@@ -644,17 +617,10 @@ var ExpiredError = class ExpiredError extends TemporalError {
|
|
|
644
617
|
code: require_app_error.ErrorCodes.temporal.expired,
|
|
645
618
|
kind: "expired",
|
|
646
619
|
metadata: {
|
|
647
|
-
...input,
|
|
620
|
+
...require_authorization_error.stripAppErrorOptions(input),
|
|
648
621
|
hint: input.hint ?? "The window has expired. Request a new link or try again within the valid period."
|
|
649
622
|
},
|
|
650
|
-
|
|
651
|
-
type: input.type,
|
|
652
|
-
severity: input.severity,
|
|
653
|
-
correlationId: input.correlationId,
|
|
654
|
-
requestId: input.requestId,
|
|
655
|
-
commandId: input.commandId,
|
|
656
|
-
createdAtIso: input.createdAtIso,
|
|
657
|
-
publicMessage: input.publicMessage
|
|
623
|
+
...require_authorization_error.pickAppErrorOptions(input)
|
|
658
624
|
});
|
|
659
625
|
}
|
|
660
626
|
};
|
|
@@ -668,31 +634,39 @@ var NotYetValidError = class NotYetValidError extends TemporalError {
|
|
|
668
634
|
code: require_app_error.ErrorCodes.temporal.notYetValid,
|
|
669
635
|
kind: "not_yet_valid",
|
|
670
636
|
metadata: {
|
|
671
|
-
...input,
|
|
637
|
+
...require_authorization_error.stripAppErrorOptions(input),
|
|
672
638
|
hint: input.hint ?? "Not yet within the valid period. Try again later."
|
|
673
639
|
},
|
|
674
|
-
|
|
675
|
-
type: input.type,
|
|
676
|
-
severity: input.severity,
|
|
677
|
-
correlationId: input.correlationId,
|
|
678
|
-
requestId: input.requestId,
|
|
679
|
-
commandId: input.commandId,
|
|
680
|
-
createdAtIso: input.createdAtIso,
|
|
681
|
-
publicMessage: input.publicMessage
|
|
640
|
+
...require_authorization_error.pickAppErrorOptions(input)
|
|
682
641
|
});
|
|
683
642
|
}
|
|
684
643
|
};
|
|
644
|
+
/**
|
|
645
|
+
* Evaluates whether `evaluatedAtIso` falls inside the [`validFromIso`,
|
|
646
|
+
* `validUntilIso`] window.
|
|
647
|
+
*
|
|
648
|
+
* Fails **closed**: a boundary that is present but unparseable is treated as if
|
|
649
|
+
* the window were closed on that side (a bad `validUntilIso` → expired, a bad
|
|
650
|
+
* `validFromIso` → not-yet-valid), never silently ignored — these checks guard
|
|
651
|
+
* access (invites, links, tokens), so a typo in an expiry must not disable
|
|
652
|
+
* expiry. A `null`/absent boundary means "no bound on that side", which is
|
|
653
|
+
* distinct from a malformed one. Returns `null` only when `evaluatedAtIso`
|
|
654
|
+
* itself is unparseable (nothing can be decided).
|
|
655
|
+
*
|
|
656
|
+
* For an inverted window (`validFrom` > `validUntil`) `expired` wins, since the
|
|
657
|
+
* upper-bound check short-circuits.
|
|
658
|
+
*/
|
|
685
659
|
function evaluateTemporalWindow(input) {
|
|
686
660
|
const evaluatedMs = Date.parse(input.evaluatedAtIso);
|
|
687
661
|
if (Number.isNaN(evaluatedMs)) return null;
|
|
688
662
|
let notYetValid = false;
|
|
689
663
|
if (input.validFromIso) {
|
|
690
664
|
const fromMs = Date.parse(input.validFromIso);
|
|
691
|
-
if (
|
|
665
|
+
if (Number.isNaN(fromMs) || evaluatedMs < fromMs) notYetValid = true;
|
|
692
666
|
}
|
|
693
667
|
if (input.validUntilIso) {
|
|
694
668
|
const untilMs = Date.parse(input.validUntilIso);
|
|
695
|
-
if (
|
|
669
|
+
if (Number.isNaN(untilMs) || evaluatedMs > untilMs) return {
|
|
696
670
|
expired: true,
|
|
697
671
|
notYetValid: false
|
|
698
672
|
};
|
|
@@ -729,13 +703,14 @@ var ValidationError = class extends require_app_error.AppError {
|
|
|
729
703
|
validationCode: code.value
|
|
730
704
|
};
|
|
731
705
|
const mergedMetadata = options?.metadata ? {
|
|
732
|
-
...
|
|
733
|
-
...
|
|
706
|
+
...options.metadata,
|
|
707
|
+
...baseMetadata
|
|
734
708
|
} : baseMetadata;
|
|
735
709
|
super(message, require_app_error.ErrorCodes.validation, {
|
|
736
710
|
...options,
|
|
737
711
|
metadata: mergedMetadata
|
|
738
712
|
});
|
|
713
|
+
Object.freeze(this);
|
|
739
714
|
}
|
|
740
715
|
};
|
|
741
716
|
//#endregion
|