@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
package/dist/entity.js
DELETED
|
@@ -1,115 +0,0 @@
|
|
|
1
|
-
import { t as InvariantViolationException } from "./invariant-violation-exception.js";
|
|
2
|
-
import { i as version, n as __decorate } from "./immutable.js";
|
|
3
|
-
import { n as cloneDate, t as assertValidDate } from "./temporal-guards.js";
|
|
4
|
-
//#region src/core/shared/aggregate-version.ts
|
|
5
|
-
function assertValidAggregateVersion(aggregateVersion) {
|
|
6
|
-
if (!Number.isInteger(aggregateVersion) || aggregateVersion < 0) throw new InvariantViolationException(`aggregateVersion must be a non-negative integer. Received: ${aggregateVersion}`);
|
|
7
|
-
}
|
|
8
|
-
//#endregion
|
|
9
|
-
//#region src/core/domain/entity.ts
|
|
10
|
-
var _Entity;
|
|
11
|
-
/**
|
|
12
|
-
* Base class for domain entities — objects defined by a stable identity rather
|
|
13
|
-
* than by their attributes. Two entities are "the same" when their `id`
|
|
14
|
-
* matches, even if every other field differs; this is the opposite of a
|
|
15
|
-
* {@link ValueObject}, which is defined entirely by its contents.
|
|
16
|
-
*
|
|
17
|
-
* The class owns the bookkeeping common to every aggregate root: a creation
|
|
18
|
-
* timestamp that never changes, a last-modified timestamp, and an
|
|
19
|
-
* `aggregateVersion` counter used for optimistic concurrency control. All three
|
|
20
|
-
* are validated on construction and the dates are defensively cloned, so an
|
|
21
|
-
* entity can never be built into — or leak — an inconsistent temporal state.
|
|
22
|
-
*
|
|
23
|
-
* @typeParam TIdentifier - The identity type (e.g. a branded string id or a VO).
|
|
24
|
-
*/
|
|
25
|
-
let Entity = class Entity {
|
|
26
|
-
static {
|
|
27
|
-
_Entity = this;
|
|
28
|
-
}
|
|
29
|
-
/**
|
|
30
|
-
* Reconstitutes an entity from its persisted {@link EntityState}.
|
|
31
|
-
*
|
|
32
|
-
* Validates the temporal invariants up front so an invalid entity is
|
|
33
|
-
* impossible to construct: both dates must be valid, the aggregate version
|
|
34
|
-
* must be a non-negative integer, and `updatedAt` may not predate
|
|
35
|
-
* `createdAt`. Both dates are cloned on the way in so a later mutation of
|
|
36
|
-
* the caller's `Date` objects cannot reach into the entity's internal state.
|
|
37
|
-
*
|
|
38
|
-
* Declared `protected` because entities are reconstituted through a
|
|
39
|
-
* subclass factory, never instantiated directly by application code.
|
|
40
|
-
*
|
|
41
|
-
* @throws {InvariantViolationException} When `updatedAt` is earlier than `createdAt`.
|
|
42
|
-
*/
|
|
43
|
-
constructor(state) {
|
|
44
|
-
assertValidDate("createdAt", state.createdAt);
|
|
45
|
-
assertValidDate("updatedAt", state.updatedAt);
|
|
46
|
-
assertValidAggregateVersion(state.aggregateVersion);
|
|
47
|
-
if (state.updatedAt.getTime() < state.createdAt.getTime()) throw new InvariantViolationException("updatedAt cannot be earlier than createdAt");
|
|
48
|
-
this._id = state.id;
|
|
49
|
-
this._createdAt = cloneDate(state.createdAt);
|
|
50
|
-
this._updatedAt = cloneDate(state.updatedAt);
|
|
51
|
-
this._aggregateVersion = state.aggregateVersion;
|
|
52
|
-
}
|
|
53
|
-
/** The entity's stable identity — the basis for equality between entities. */
|
|
54
|
-
get id() {
|
|
55
|
-
return this._id;
|
|
56
|
-
}
|
|
57
|
-
/**
|
|
58
|
-
* When the entity was first created.
|
|
59
|
-
*
|
|
60
|
-
* Returns a clone so callers cannot mutate the entity's internal `Date`;
|
|
61
|
-
* the value is fixed at construction and never changes thereafter.
|
|
62
|
-
*/
|
|
63
|
-
get createdAt() {
|
|
64
|
-
return cloneDate(this._createdAt);
|
|
65
|
-
}
|
|
66
|
-
/**
|
|
67
|
-
* When the entity was last modified.
|
|
68
|
-
*
|
|
69
|
-
* Returns a clone for the same encapsulation reason as {@link createdAt};
|
|
70
|
-
* the underlying value advances only through {@link markAsModified}.
|
|
71
|
-
*/
|
|
72
|
-
get updatedAt() {
|
|
73
|
-
return cloneDate(this._updatedAt);
|
|
74
|
-
}
|
|
75
|
-
/**
|
|
76
|
-
* Monotonic counter incremented on every mutation, used for optimistic
|
|
77
|
-
* concurrency control: a writer reads this value, and the persistence layer
|
|
78
|
-
* rejects the write if the stored version has moved on in the meantime.
|
|
79
|
-
*/
|
|
80
|
-
get aggregateVersion() {
|
|
81
|
-
return this._aggregateVersion;
|
|
82
|
-
}
|
|
83
|
-
/**
|
|
84
|
-
* The schema/contract version stamped on this entity type by the
|
|
85
|
-
* `@version` decorator — used to detect and migrate state persisted under
|
|
86
|
-
* an older shape.
|
|
87
|
-
*/
|
|
88
|
-
get contractVersion() {
|
|
89
|
-
return _Entity.CONTRACT_VERSION;
|
|
90
|
-
}
|
|
91
|
-
/**
|
|
92
|
-
* The single seam through which an entity records a mutation: it advances
|
|
93
|
-
* `updatedAt` and bumps {@link aggregateVersion} by one. Subclasses must
|
|
94
|
-
* call this from every state-changing method so the version counter stays
|
|
95
|
-
* an accurate optimistic-lock token.
|
|
96
|
-
*
|
|
97
|
-
* @param updatedAt - The modification instant; defaults to now. Validated
|
|
98
|
-
* and required not to predate `createdAt`, preserving the same invariant
|
|
99
|
-
* the constructor enforces.
|
|
100
|
-
* @returns The new aggregate version after the increment.
|
|
101
|
-
* @throws {InvariantViolationException} When `updatedAt` is earlier than `createdAt`.
|
|
102
|
-
*/
|
|
103
|
-
markAsModified(updatedAt = /* @__PURE__ */ new Date()) {
|
|
104
|
-
assertValidDate("updatedAt", updatedAt);
|
|
105
|
-
if (updatedAt.getTime() < this._createdAt.getTime()) throw new InvariantViolationException("updatedAt cannot be earlier than createdAt");
|
|
106
|
-
this._updatedAt = cloneDate(updatedAt);
|
|
107
|
-
this._aggregateVersion += 1;
|
|
108
|
-
return this._aggregateVersion;
|
|
109
|
-
}
|
|
110
|
-
};
|
|
111
|
-
Entity = _Entity = __decorate([version("1.0")], Entity);
|
|
112
|
-
//#endregion
|
|
113
|
-
export { assertValidAggregateVersion as n, Entity as t };
|
|
114
|
-
|
|
115
|
-
//# sourceMappingURL=entity.js.map
|
package/dist/entity.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"entity.js","names":[],"sources":["../src/core/shared/aggregate-version.ts","../src/core/domain/entity.ts"],"sourcesContent":["import { InvariantViolationException } from \"../exceptions/invariant-violation-exception.js\";\n\nfunction assertValidAggregateVersion(aggregateVersion: number): void {\n if (!Number.isInteger(aggregateVersion) || aggregateVersion < 0) {\n throw new InvariantViolationException(\n `aggregateVersion must be a non-negative integer. Received: ${aggregateVersion}`,\n );\n }\n}\n\nexport { assertValidAggregateVersion };\n","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\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 `updatedAt` is earlier than `createdAt`.\n */\n protected constructor(state: EntityState<TIdentifier>) {\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 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 * @param updatedAt - The modification instant; defaults to now. Validated\n * and required not to predate `createdAt`, preserving the same invariant\n * the constructor enforces.\n * @returns The new aggregate version after the increment.\n * @throws {InvariantViolationException} When `updatedAt` is earlier than `createdAt`.\n */\n protected markAsModified(updatedAt: Date = new Date()): number {\n assertValidDate(\"updatedAt\", updatedAt);\n\n if (updatedAt.getTime() < this._createdAt.getTime()) {\n throw new InvariantViolationException(\n \"updatedAt cannot be earlier than createdAt\",\n );\n }\n\n this._updatedAt = cloneDate(updatedAt);\n this._aggregateVersion += 1;\n\n return this._aggregateVersion;\n }\n}\n\nexport { Entity, type EntityState };\n"],"mappings":";;;;AAEA,SAAS,4BAA4B,kBAAgC;CACjE,IAAI,CAAC,OAAO,UAAU,gBAAgB,KAAK,mBAAmB,GAC1D,MAAM,IAAI,4BACN,8DAA8D,kBAClE;AAER;;;;;;;;;;;;;;;;;;AC0BA,IAAA,SAAA,MACe,OAAoB;;;;;;;;;;;;;;;;;;CAsB/B,YAAsB,OAAiC;EACnD,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,OAAA,QAAc;CAClB;;;;;;;;;;;;;CAcA,eAAyB,4BAAkB,IAAI,KAAK,GAAW;EAC3D,gBAAgB,aAAa,SAAS;EAEtC,IAAI,UAAU,QAAQ,IAAI,KAAK,WAAW,QAAQ,GAC9C,MAAM,IAAI,4BACN,4CACJ;EAGJ,KAAK,aAAa,UAAU,SAAS;EACrC,KAAK,qBAAqB;EAE1B,OAAO,KAAK;CAChB;AACJ;+BA7GC,QAAQ,KAAK,CAAA,GAAA,MAAA"}
|
package/dist/use-case.cjs
DELETED
|
@@ -1,96 +0,0 @@
|
|
|
1
|
-
const require_immutable = require("./immutable.cjs");
|
|
2
|
-
//#region src/core/application/use-case.ts
|
|
3
|
-
var _UseCase;
|
|
4
|
-
const EXECUTION_COUNTER = "use_case.executions";
|
|
5
|
-
const DURATION_HISTOGRAM = "use_case.duration_ms";
|
|
6
|
-
let UseCase = class UseCase {
|
|
7
|
-
static {
|
|
8
|
-
_UseCase = this;
|
|
9
|
-
}
|
|
10
|
-
get contractVersion() {
|
|
11
|
-
return _UseCase.CONTRACT_VERSION;
|
|
12
|
-
}
|
|
13
|
-
/**
|
|
14
|
-
* Runs the use case.
|
|
15
|
-
*
|
|
16
|
-
* `run()` is the single seam every use case crosses, so it is where
|
|
17
|
-
* cross-cutting instrumentation lives. When {@link observability} exposes
|
|
18
|
-
* any adapter, the call to `execute()` is wrapped with tracing, metrics and
|
|
19
|
-
* failure logging; otherwise it delegates directly with no overhead.
|
|
20
|
-
*
|
|
21
|
-
* Observability is strictly side-effecting: a misbehaving adapter never
|
|
22
|
-
* changes the business result nor masks a thrown error — its own failures
|
|
23
|
-
* are swallowed.
|
|
24
|
-
*/
|
|
25
|
-
async run(input) {
|
|
26
|
-
const observability = this.observability();
|
|
27
|
-
if (!observability.logger && !observability.metrics && !observability.tracer) return await this.execute(input);
|
|
28
|
-
return await this.runInstrumented(input, observability);
|
|
29
|
-
}
|
|
30
|
-
/**
|
|
31
|
-
* Adapters used to instrument {@link run}. Override to opt a use case into
|
|
32
|
-
* tracing/metrics/logging. Returns an empty object by default, which keeps
|
|
33
|
-
* the plain delegating behavior.
|
|
34
|
-
*/
|
|
35
|
-
observability() {
|
|
36
|
-
return {};
|
|
37
|
-
}
|
|
38
|
-
/**
|
|
39
|
-
* Stable identity used for span names and metric labels. Defaults to the
|
|
40
|
-
* runtime class name; override when bundling/minification would otherwise
|
|
41
|
-
* erase a meaningful name.
|
|
42
|
-
*/
|
|
43
|
-
get useCaseName() {
|
|
44
|
-
return this.constructor.name;
|
|
45
|
-
}
|
|
46
|
-
async runInstrumented(input, observability) {
|
|
47
|
-
const { logger, metrics, tracer } = observability;
|
|
48
|
-
const name = this.useCaseName;
|
|
49
|
-
const span = safely(() => tracer?.startSpan(name, { "use_case.name": name }));
|
|
50
|
-
const startedAt = Date.now();
|
|
51
|
-
try {
|
|
52
|
-
const output = await this.execute(input);
|
|
53
|
-
const outcome = output.isOk() ? "ok" : "error";
|
|
54
|
-
if (outcome === "error") safely(() => logger?.warn(`${name} returned a business error`, { useCase: name }));
|
|
55
|
-
safely(() => span?.setAttribute("use_case.outcome", outcome));
|
|
56
|
-
safely(() => metrics?.counter(EXECUTION_COUNTER, 1, {
|
|
57
|
-
useCase: name,
|
|
58
|
-
outcome
|
|
59
|
-
}));
|
|
60
|
-
return output;
|
|
61
|
-
} catch (error) {
|
|
62
|
-
safely(() => logger?.error(`${name} threw an unexpected error`, { useCase: name }));
|
|
63
|
-
safely(() => span?.setAttribute("use_case.outcome", "exception"));
|
|
64
|
-
safely(() => span?.recordException(error));
|
|
65
|
-
safely(() => metrics?.counter(EXECUTION_COUNTER, 1, {
|
|
66
|
-
useCase: name,
|
|
67
|
-
outcome: "exception"
|
|
68
|
-
}));
|
|
69
|
-
throw error;
|
|
70
|
-
} finally {
|
|
71
|
-
safely(() => metrics?.histogram(DURATION_HISTOGRAM, Date.now() - startedAt, { useCase: name }));
|
|
72
|
-
safely(() => span?.end());
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
};
|
|
76
|
-
UseCase = _UseCase = require_immutable.__decorate([require_immutable.version("1.0")], UseCase);
|
|
77
|
-
/**
|
|
78
|
-
* Invokes an observability side effect, isolating any adapter failure so it
|
|
79
|
-
* cannot alter the use case outcome.
|
|
80
|
-
*/
|
|
81
|
-
function safely(effect) {
|
|
82
|
-
try {
|
|
83
|
-
return effect();
|
|
84
|
-
} catch {
|
|
85
|
-
return;
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
//#endregion
|
|
89
|
-
Object.defineProperty(exports, "UseCase", {
|
|
90
|
-
enumerable: true,
|
|
91
|
-
get: function() {
|
|
92
|
-
return UseCase;
|
|
93
|
-
}
|
|
94
|
-
});
|
|
95
|
-
|
|
96
|
-
//# sourceMappingURL=use-case.cjs.map
|
package/dist/use-case.cjs.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"use-case.cjs","names":["version"],"sources":["../src/core/application/use-case.ts"],"sourcesContent":["import { type ContractVersion, version } from \"../versioning/version.js\";\nimport type { Result } from \"../result/result.js\";\n\nimport type { LoggerPort } from \"./ports/logger.port.js\";\nimport type { MetricsPort } from \"./ports/metrics.port.js\";\nimport type { TracerPort } from \"./ports/tracer.port.js\";\n\ntype MaybePromise<T> = T | Promise<T>;\n\n/**\n * Observability adapters a use case may opt into.\n *\n * All fields are optional: a use case that provides none keeps the plain\n * `input → Result` behavior with zero overhead. When provided, {@link UseCase.run}\n * instruments every execution around `execute()` — opening a span, recording\n * duration, counting outcomes, and logging business/unexpected failures.\n */\ninterface UseCaseObservability {\n readonly logger?: LoggerPort;\n readonly metrics?: MetricsPort;\n readonly tracer?: TracerPort;\n}\n\nconst EXECUTION_COUNTER = \"use_case.executions\";\nconst DURATION_HISTOGRAM = \"use_case.duration_ms\";\n\ntype ExecutionOutcome = \"ok\" | \"error\" | \"exception\";\n\n@version(\"1.0\")\nabstract class UseCase<\n Input = void,\n Output extends Result<unknown, unknown> = Result<void, never>,\n> {\n declare public static readonly CONTRACT_VERSION: ContractVersion;\n\n public get contractVersion(): ContractVersion {\n return UseCase.CONTRACT_VERSION;\n }\n\n /**\n * Runs the use case.\n *\n * `run()` is the single seam every use case crosses, so it is where\n * cross-cutting instrumentation lives. When {@link observability} exposes\n * any adapter, the call to `execute()` is wrapped with tracing, metrics and\n * failure logging; otherwise it delegates directly with no overhead.\n *\n * Observability is strictly side-effecting: a misbehaving adapter never\n * changes the business result nor masks a thrown error — its own failures\n * are swallowed.\n */\n public async run(input: Input): Promise<Output> {\n const observability = this.observability();\n if (\n !observability.logger &&\n !observability.metrics &&\n !observability.tracer\n ) {\n return await this.execute(input);\n }\n\n return await this.runInstrumented(input, observability);\n }\n\n /**\n * Adapters used to instrument {@link run}. Override to opt a use case into\n * tracing/metrics/logging. Returns an empty object by default, which keeps\n * the plain delegating behavior.\n */\n protected observability(): UseCaseObservability {\n return {};\n }\n\n /**\n * Stable identity used for span names and metric labels. Defaults to the\n * runtime class name; override when bundling/minification would otherwise\n * erase a meaningful name.\n */\n protected get useCaseName(): string {\n return this.constructor.name;\n }\n\n protected abstract execute(input: Input): MaybePromise<Output>;\n\n private async runInstrumented(\n input: Input,\n observability: UseCaseObservability,\n ): Promise<Output> {\n const { logger, metrics, tracer } = observability;\n const name = this.useCaseName;\n const span = safely(() =>\n tracer?.startSpan(name, { \"use_case.name\": name }),\n );\n const startedAt = Date.now();\n\n try {\n const output = await this.execute(input);\n const outcome: ExecutionOutcome = output.isOk() ? \"ok\" : \"error\";\n\n if (outcome === \"error\") {\n safely(() =>\n logger?.warn(`${name} returned a business error`, {\n useCase: name,\n }),\n );\n }\n safely(() => span?.setAttribute(\"use_case.outcome\", outcome));\n safely(() =>\n metrics?.counter(EXECUTION_COUNTER, 1, {\n useCase: name,\n outcome,\n }),\n );\n\n return output;\n } catch (error) {\n safely(() =>\n logger?.error(`${name} threw an unexpected error`, {\n useCase: name,\n }),\n );\n safely(() => span?.setAttribute(\"use_case.outcome\", \"exception\"));\n safely(() => span?.recordException(error));\n safely(() =>\n metrics?.counter(EXECUTION_COUNTER, 1, {\n useCase: name,\n outcome: \"exception\",\n }),\n );\n\n throw error;\n } finally {\n safely(() =>\n metrics?.histogram(DURATION_HISTOGRAM, Date.now() - startedAt, {\n useCase: name,\n }),\n );\n safely(() => span?.end());\n }\n }\n}\n\n/**\n * Invokes an observability side effect, isolating any adapter failure so it\n * cannot alter the use case outcome.\n */\nfunction safely<R>(effect: () => R): R | undefined {\n try {\n return effect();\n } catch {\n return undefined;\n }\n}\n\nexport { type MaybePromise, UseCase, type UseCaseObservability };\n"],"mappings":";;;AAuBA,MAAM,oBAAoB;AAC1B,MAAM,qBAAqB;AAI3B,IAAA,UAAA,MACe,QAGb;;;;CAGE,IAAW,kBAAmC;EAC1C,OAAA,SAAe;CACnB;;;;;;;;;;;;;CAcA,MAAa,IAAI,OAA+B;EAC5C,MAAM,gBAAgB,KAAK,cAAc;EACzC,IACI,CAAC,cAAc,UACf,CAAC,cAAc,WACf,CAAC,cAAc,QAEf,OAAO,MAAM,KAAK,QAAQ,KAAK;EAGnC,OAAO,MAAM,KAAK,gBAAgB,OAAO,aAAa;CAC1D;;;;;;CAOA,gBAAgD;EAC5C,OAAO,CAAC;CACZ;;;;;;CAOA,IAAc,cAAsB;EAChC,OAAO,KAAK,YAAY;CAC5B;CAIA,MAAc,gBACV,OACA,eACe;EACf,MAAM,EAAE,QAAQ,SAAS,WAAW;EACpC,MAAM,OAAO,KAAK;EAClB,MAAM,OAAO,aACT,QAAQ,UAAU,MAAM,EAAE,iBAAiB,KAAK,CAAC,CACrD;EACA,MAAM,YAAY,KAAK,IAAI;EAE3B,IAAI;GACA,MAAM,SAAS,MAAM,KAAK,QAAQ,KAAK;GACvC,MAAM,UAA4B,OAAO,KAAK,IAAI,OAAO;GAEzD,IAAI,YAAY,SACZ,aACI,QAAQ,KAAK,GAAG,KAAK,6BAA6B,EAC9C,SAAS,KACb,CAAC,CACL;GAEJ,aAAa,MAAM,aAAa,oBAAoB,OAAO,CAAC;GAC5D,aACI,SAAS,QAAQ,mBAAmB,GAAG;IACnC,SAAS;IACT;GACJ,CAAC,CACL;GAEA,OAAO;EACX,SAAS,OAAO;GACZ,aACI,QAAQ,MAAM,GAAG,KAAK,6BAA6B,EAC/C,SAAS,KACb,CAAC,CACL;GACA,aAAa,MAAM,aAAa,oBAAoB,WAAW,CAAC;GAChE,aAAa,MAAM,gBAAgB,KAAK,CAAC;GACzC,aACI,SAAS,QAAQ,mBAAmB,GAAG;IACnC,SAAS;IACT,SAAS;GACb,CAAC,CACL;GAEA,MAAM;EACV,UAAU;GACN,aACI,SAAS,UAAU,oBAAoB,KAAK,IAAI,IAAI,WAAW,EAC3D,SAAS,KACb,CAAC,CACL;GACA,aAAa,MAAM,IAAI,CAAC;EAC5B;CACJ;AACJ;mDAhHCA,kBAAAA,QAAQ,KAAK,CAAA,GAAA,OAAA;;;;;AAsHd,SAAS,OAAU,QAAgC;CAC/C,IAAI;EACA,OAAO,OAAO;CAClB,QAAQ;EACJ;CACJ;AACJ"}
|
package/dist/use-case.js
DELETED
|
@@ -1,91 +0,0 @@
|
|
|
1
|
-
import { i as version, n as __decorate } from "./immutable.js";
|
|
2
|
-
//#region src/core/application/use-case.ts
|
|
3
|
-
var _UseCase;
|
|
4
|
-
const EXECUTION_COUNTER = "use_case.executions";
|
|
5
|
-
const DURATION_HISTOGRAM = "use_case.duration_ms";
|
|
6
|
-
let UseCase = class UseCase {
|
|
7
|
-
static {
|
|
8
|
-
_UseCase = this;
|
|
9
|
-
}
|
|
10
|
-
get contractVersion() {
|
|
11
|
-
return _UseCase.CONTRACT_VERSION;
|
|
12
|
-
}
|
|
13
|
-
/**
|
|
14
|
-
* Runs the use case.
|
|
15
|
-
*
|
|
16
|
-
* `run()` is the single seam every use case crosses, so it is where
|
|
17
|
-
* cross-cutting instrumentation lives. When {@link observability} exposes
|
|
18
|
-
* any adapter, the call to `execute()` is wrapped with tracing, metrics and
|
|
19
|
-
* failure logging; otherwise it delegates directly with no overhead.
|
|
20
|
-
*
|
|
21
|
-
* Observability is strictly side-effecting: a misbehaving adapter never
|
|
22
|
-
* changes the business result nor masks a thrown error — its own failures
|
|
23
|
-
* are swallowed.
|
|
24
|
-
*/
|
|
25
|
-
async run(input) {
|
|
26
|
-
const observability = this.observability();
|
|
27
|
-
if (!observability.logger && !observability.metrics && !observability.tracer) return await this.execute(input);
|
|
28
|
-
return await this.runInstrumented(input, observability);
|
|
29
|
-
}
|
|
30
|
-
/**
|
|
31
|
-
* Adapters used to instrument {@link run}. Override to opt a use case into
|
|
32
|
-
* tracing/metrics/logging. Returns an empty object by default, which keeps
|
|
33
|
-
* the plain delegating behavior.
|
|
34
|
-
*/
|
|
35
|
-
observability() {
|
|
36
|
-
return {};
|
|
37
|
-
}
|
|
38
|
-
/**
|
|
39
|
-
* Stable identity used for span names and metric labels. Defaults to the
|
|
40
|
-
* runtime class name; override when bundling/minification would otherwise
|
|
41
|
-
* erase a meaningful name.
|
|
42
|
-
*/
|
|
43
|
-
get useCaseName() {
|
|
44
|
-
return this.constructor.name;
|
|
45
|
-
}
|
|
46
|
-
async runInstrumented(input, observability) {
|
|
47
|
-
const { logger, metrics, tracer } = observability;
|
|
48
|
-
const name = this.useCaseName;
|
|
49
|
-
const span = safely(() => tracer?.startSpan(name, { "use_case.name": name }));
|
|
50
|
-
const startedAt = Date.now();
|
|
51
|
-
try {
|
|
52
|
-
const output = await this.execute(input);
|
|
53
|
-
const outcome = output.isOk() ? "ok" : "error";
|
|
54
|
-
if (outcome === "error") safely(() => logger?.warn(`${name} returned a business error`, { useCase: name }));
|
|
55
|
-
safely(() => span?.setAttribute("use_case.outcome", outcome));
|
|
56
|
-
safely(() => metrics?.counter(EXECUTION_COUNTER, 1, {
|
|
57
|
-
useCase: name,
|
|
58
|
-
outcome
|
|
59
|
-
}));
|
|
60
|
-
return output;
|
|
61
|
-
} catch (error) {
|
|
62
|
-
safely(() => logger?.error(`${name} threw an unexpected error`, { useCase: name }));
|
|
63
|
-
safely(() => span?.setAttribute("use_case.outcome", "exception"));
|
|
64
|
-
safely(() => span?.recordException(error));
|
|
65
|
-
safely(() => metrics?.counter(EXECUTION_COUNTER, 1, {
|
|
66
|
-
useCase: name,
|
|
67
|
-
outcome: "exception"
|
|
68
|
-
}));
|
|
69
|
-
throw error;
|
|
70
|
-
} finally {
|
|
71
|
-
safely(() => metrics?.histogram(DURATION_HISTOGRAM, Date.now() - startedAt, { useCase: name }));
|
|
72
|
-
safely(() => span?.end());
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
};
|
|
76
|
-
UseCase = _UseCase = __decorate([version("1.0")], UseCase);
|
|
77
|
-
/**
|
|
78
|
-
* Invokes an observability side effect, isolating any adapter failure so it
|
|
79
|
-
* cannot alter the use case outcome.
|
|
80
|
-
*/
|
|
81
|
-
function safely(effect) {
|
|
82
|
-
try {
|
|
83
|
-
return effect();
|
|
84
|
-
} catch {
|
|
85
|
-
return;
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
//#endregion
|
|
89
|
-
export { UseCase as t };
|
|
90
|
-
|
|
91
|
-
//# sourceMappingURL=use-case.js.map
|
package/dist/use-case.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"use-case.js","names":[],"sources":["../src/core/application/use-case.ts"],"sourcesContent":["import { type ContractVersion, version } from \"../versioning/version.js\";\nimport type { Result } from \"../result/result.js\";\n\nimport type { LoggerPort } from \"./ports/logger.port.js\";\nimport type { MetricsPort } from \"./ports/metrics.port.js\";\nimport type { TracerPort } from \"./ports/tracer.port.js\";\n\ntype MaybePromise<T> = T | Promise<T>;\n\n/**\n * Observability adapters a use case may opt into.\n *\n * All fields are optional: a use case that provides none keeps the plain\n * `input → Result` behavior with zero overhead. When provided, {@link UseCase.run}\n * instruments every execution around `execute()` — opening a span, recording\n * duration, counting outcomes, and logging business/unexpected failures.\n */\ninterface UseCaseObservability {\n readonly logger?: LoggerPort;\n readonly metrics?: MetricsPort;\n readonly tracer?: TracerPort;\n}\n\nconst EXECUTION_COUNTER = \"use_case.executions\";\nconst DURATION_HISTOGRAM = \"use_case.duration_ms\";\n\ntype ExecutionOutcome = \"ok\" | \"error\" | \"exception\";\n\n@version(\"1.0\")\nabstract class UseCase<\n Input = void,\n Output extends Result<unknown, unknown> = Result<void, never>,\n> {\n declare public static readonly CONTRACT_VERSION: ContractVersion;\n\n public get contractVersion(): ContractVersion {\n return UseCase.CONTRACT_VERSION;\n }\n\n /**\n * Runs the use case.\n *\n * `run()` is the single seam every use case crosses, so it is where\n * cross-cutting instrumentation lives. When {@link observability} exposes\n * any adapter, the call to `execute()` is wrapped with tracing, metrics and\n * failure logging; otherwise it delegates directly with no overhead.\n *\n * Observability is strictly side-effecting: a misbehaving adapter never\n * changes the business result nor masks a thrown error — its own failures\n * are swallowed.\n */\n public async run(input: Input): Promise<Output> {\n const observability = this.observability();\n if (\n !observability.logger &&\n !observability.metrics &&\n !observability.tracer\n ) {\n return await this.execute(input);\n }\n\n return await this.runInstrumented(input, observability);\n }\n\n /**\n * Adapters used to instrument {@link run}. Override to opt a use case into\n * tracing/metrics/logging. Returns an empty object by default, which keeps\n * the plain delegating behavior.\n */\n protected observability(): UseCaseObservability {\n return {};\n }\n\n /**\n * Stable identity used for span names and metric labels. Defaults to the\n * runtime class name; override when bundling/minification would otherwise\n * erase a meaningful name.\n */\n protected get useCaseName(): string {\n return this.constructor.name;\n }\n\n protected abstract execute(input: Input): MaybePromise<Output>;\n\n private async runInstrumented(\n input: Input,\n observability: UseCaseObservability,\n ): Promise<Output> {\n const { logger, metrics, tracer } = observability;\n const name = this.useCaseName;\n const span = safely(() =>\n tracer?.startSpan(name, { \"use_case.name\": name }),\n );\n const startedAt = Date.now();\n\n try {\n const output = await this.execute(input);\n const outcome: ExecutionOutcome = output.isOk() ? \"ok\" : \"error\";\n\n if (outcome === \"error\") {\n safely(() =>\n logger?.warn(`${name} returned a business error`, {\n useCase: name,\n }),\n );\n }\n safely(() => span?.setAttribute(\"use_case.outcome\", outcome));\n safely(() =>\n metrics?.counter(EXECUTION_COUNTER, 1, {\n useCase: name,\n outcome,\n }),\n );\n\n return output;\n } catch (error) {\n safely(() =>\n logger?.error(`${name} threw an unexpected error`, {\n useCase: name,\n }),\n );\n safely(() => span?.setAttribute(\"use_case.outcome\", \"exception\"));\n safely(() => span?.recordException(error));\n safely(() =>\n metrics?.counter(EXECUTION_COUNTER, 1, {\n useCase: name,\n outcome: \"exception\",\n }),\n );\n\n throw error;\n } finally {\n safely(() =>\n metrics?.histogram(DURATION_HISTOGRAM, Date.now() - startedAt, {\n useCase: name,\n }),\n );\n safely(() => span?.end());\n }\n }\n}\n\n/**\n * Invokes an observability side effect, isolating any adapter failure so it\n * cannot alter the use case outcome.\n */\nfunction safely<R>(effect: () => R): R | undefined {\n try {\n return effect();\n } catch {\n return undefined;\n }\n}\n\nexport { type MaybePromise, UseCase, type UseCaseObservability };\n"],"mappings":";;;AAuBA,MAAM,oBAAoB;AAC1B,MAAM,qBAAqB;AAI3B,IAAA,UAAA,MACe,QAGb;;;;CAGE,IAAW,kBAAmC;EAC1C,OAAA,SAAe;CACnB;;;;;;;;;;;;;CAcA,MAAa,IAAI,OAA+B;EAC5C,MAAM,gBAAgB,KAAK,cAAc;EACzC,IACI,CAAC,cAAc,UACf,CAAC,cAAc,WACf,CAAC,cAAc,QAEf,OAAO,MAAM,KAAK,QAAQ,KAAK;EAGnC,OAAO,MAAM,KAAK,gBAAgB,OAAO,aAAa;CAC1D;;;;;;CAOA,gBAAgD;EAC5C,OAAO,CAAC;CACZ;;;;;;CAOA,IAAc,cAAsB;EAChC,OAAO,KAAK,YAAY;CAC5B;CAIA,MAAc,gBACV,OACA,eACe;EACf,MAAM,EAAE,QAAQ,SAAS,WAAW;EACpC,MAAM,OAAO,KAAK;EAClB,MAAM,OAAO,aACT,QAAQ,UAAU,MAAM,EAAE,iBAAiB,KAAK,CAAC,CACrD;EACA,MAAM,YAAY,KAAK,IAAI;EAE3B,IAAI;GACA,MAAM,SAAS,MAAM,KAAK,QAAQ,KAAK;GACvC,MAAM,UAA4B,OAAO,KAAK,IAAI,OAAO;GAEzD,IAAI,YAAY,SACZ,aACI,QAAQ,KAAK,GAAG,KAAK,6BAA6B,EAC9C,SAAS,KACb,CAAC,CACL;GAEJ,aAAa,MAAM,aAAa,oBAAoB,OAAO,CAAC;GAC5D,aACI,SAAS,QAAQ,mBAAmB,GAAG;IACnC,SAAS;IACT;GACJ,CAAC,CACL;GAEA,OAAO;EACX,SAAS,OAAO;GACZ,aACI,QAAQ,MAAM,GAAG,KAAK,6BAA6B,EAC/C,SAAS,KACb,CAAC,CACL;GACA,aAAa,MAAM,aAAa,oBAAoB,WAAW,CAAC;GAChE,aAAa,MAAM,gBAAgB,KAAK,CAAC;GACzC,aACI,SAAS,QAAQ,mBAAmB,GAAG;IACnC,SAAS;IACT,SAAS;GACb,CAAC,CACL;GAEA,MAAM;EACV,UAAU;GACN,aACI,SAAS,UAAU,oBAAoB,KAAK,IAAI,IAAI,WAAW,EAC3D,SAAS,KACb,CAAC,CACL;GACA,aAAa,MAAM,IAAI,CAAC;EAC5B;CACJ;AACJ;iCAhHC,QAAQ,KAAK,CAAA,GAAA,OAAA;;;;;AAsHd,SAAS,OAAU,QAAgC;CAC/C,IAAI;EACA,OAAO,OAAO;CAClB,QAAQ;EACJ;CACJ;AACJ"}
|