@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/core-config.d.cts
CHANGED
|
@@ -2,7 +2,8 @@ import { r as Result } from "./result.cjs";
|
|
|
2
2
|
import { n as Outcome } from "./outcome.cjs";
|
|
3
3
|
|
|
4
4
|
//#region src/core/policies/engines/v1/condition-types.d.ts
|
|
5
|
-
|
|
5
|
+
declare const CONDITION_OPS: readonly ["eq", "neq", "gt", "gte", "lt", "lte", "in", "notIn", "isNull", "isNotNull"];
|
|
6
|
+
type ConditionOp = (typeof CONDITION_OPS)[number];
|
|
6
7
|
interface ConditionLeafNode {
|
|
7
8
|
readonly field: string;
|
|
8
9
|
readonly op: ConditionOp;
|
|
@@ -143,7 +144,39 @@ interface PolicyEvaluationCompletedEvent {
|
|
|
143
144
|
readonly engineKind: "GATE" | "COMPUTE";
|
|
144
145
|
readonly occurredAt: Date;
|
|
145
146
|
}
|
|
146
|
-
|
|
147
|
+
/**
|
|
148
|
+
* A resolved ABAC decision (both PERMIT and DENY). Emitted best-effort by the
|
|
149
|
+
* `AbacAuthorizer` so a host can build an authorization audit trail without an
|
|
150
|
+
* adapter of its own; silent by default like every other event. Types are kept
|
|
151
|
+
* self-contained (no import from `core/abac`) to avoid a config→abac cycle.
|
|
152
|
+
*/
|
|
153
|
+
interface AbacDecisionEvent {
|
|
154
|
+
readonly kind: "abac-decision";
|
|
155
|
+
readonly level: "info";
|
|
156
|
+
readonly action: string;
|
|
157
|
+
readonly resource?: {
|
|
158
|
+
readonly type: string;
|
|
159
|
+
readonly id?: string;
|
|
160
|
+
};
|
|
161
|
+
readonly actorId: string;
|
|
162
|
+
readonly effect: "PERMIT" | "DENY";
|
|
163
|
+
/** Deciding rule id, or `"<default>"` / `"<default-deny>"` when the set's default decided. */
|
|
164
|
+
readonly decidingRuleId: string;
|
|
165
|
+
readonly decidingRuleVersion?: number;
|
|
166
|
+
readonly algorithm: string;
|
|
167
|
+
readonly occurredAt: Date;
|
|
168
|
+
}
|
|
169
|
+
type PolicyEvent = ConditionEvalEvent | PolicyResolutionEvent | PolicyEvaluationFailedEvent | PolicyEvaluationCompletedEvent | AbacDecisionEvent;
|
|
170
|
+
/**
|
|
171
|
+
* Sink for policy telemetry. The core never logs on its own — it hands typed
|
|
172
|
+
* events here and a host implementation decides what to do with them.
|
|
173
|
+
*
|
|
174
|
+
* Redaction is the host's responsibility: a `condition-eval` event carries the
|
|
175
|
+
* evaluated field path and value in `details` (e.g. `student.flags.*`), so in
|
|
176
|
+
* an ERP those payloads can contain PII. Sanitize before persisting or shipping
|
|
177
|
+
* events off-box; the core deliberately keeps no redaction seam so it stays
|
|
178
|
+
* pure and unopinionated about your logging stack.
|
|
179
|
+
*/
|
|
147
180
|
interface PolicyReporter {
|
|
148
181
|
report(event: PolicyEvent): void;
|
|
149
182
|
}
|
|
@@ -156,17 +189,31 @@ interface CoreConfigOptions {
|
|
|
156
189
|
readonly observability?: CoreObservabilityConfig;
|
|
157
190
|
}
|
|
158
191
|
/**
|
|
159
|
-
*
|
|
160
|
-
*
|
|
192
|
+
* The core's config namespace. Today it holds a single section —
|
|
193
|
+
* `observability` — whose whole job is to decide *where policy telemetry goes*
|
|
194
|
+
* while guaranteeing telemetry is best-effort (a throwing reporter can never
|
|
195
|
+
* abort a domain flow). App config (env, settings, feature flags) is
|
|
196
|
+
* deliberately absent for now: when the core needs it, it joins here as a
|
|
197
|
+
* sibling section rather than a new module — which is why this is `config`,
|
|
198
|
+
* not `observability`. It holds the active {@link PolicyReporter} (silent by
|
|
199
|
+
* default, so the core carries no logging dependency), bridges it to the
|
|
200
|
+
* engines' {@link ConditionEvaluatorReporter}, and offers `reportSafely` as
|
|
201
|
+
* the single guarded emit path shared with {@link PolicyService}.
|
|
161
202
|
*/
|
|
162
203
|
declare class CoreConfig {
|
|
163
204
|
#private;
|
|
164
205
|
constructor(options?: CoreConfigOptions);
|
|
206
|
+
/**
|
|
207
|
+
* Swaps the active reporter. Pass `observability.reporter` to enable
|
|
208
|
+
* telemetry, or set it explicitly to `undefined` to go silent again.
|
|
209
|
+
* Omitting `observability` (or its `reporter` key) is a no-op.
|
|
210
|
+
*/
|
|
165
211
|
configure(options: CoreConfigOptions): this;
|
|
166
212
|
reset(): this;
|
|
167
|
-
|
|
213
|
+
/** Emits a policy event through the active reporter, best-effort. */
|
|
214
|
+
reportSafely(event: PolicyEvent): void;
|
|
168
215
|
getConditionEvaluationOptions(engineVersion: number): ConditionEvaluationOptions;
|
|
169
216
|
}
|
|
170
217
|
//#endregion
|
|
171
|
-
export {
|
|
218
|
+
export { ConditionNode as A, GateViolationTrace as C, GatePayload as D, VersionedGateEngine as E, GatePayloadV1 as O, GateTraceNodeSnapshot as S, PolicyViolation as T, ConditionEvaluatorReporter as _, PolicyDefinitionId as a, GateStatus as b, asPolicyDecisionId as c, asTenantId as d, ConditionEvaluationCause as f, ConditionEvaluationReportTag as g, ConditionEvaluationReportLevel as h, PolicyDecisionId as i, ConditionLeafNode as k, asPolicyDefinitionId as l, ConditionEvaluationReport as m, CoreConfigOptions as n, SchoolId as o, ConditionEvaluationOptions as p, CoreObservabilityConfig as r, TenantId as s, CoreConfig as t, asSchoolId as u, GateOutcome as v, PolicyContext as w, GateTraceLeafSnapshot as x, GateOutcomeData as y };
|
|
172
219
|
//# sourceMappingURL=core-config.d.cts.map
|
package/dist/core-config.d.ts
CHANGED
|
@@ -2,7 +2,8 @@ import { r as Result } from "./result.js";
|
|
|
2
2
|
import { n as Outcome } from "./outcome.js";
|
|
3
3
|
|
|
4
4
|
//#region src/core/policies/engines/v1/condition-types.d.ts
|
|
5
|
-
|
|
5
|
+
declare const CONDITION_OPS: readonly ["eq", "neq", "gt", "gte", "lt", "lte", "in", "notIn", "isNull", "isNotNull"];
|
|
6
|
+
type ConditionOp = (typeof CONDITION_OPS)[number];
|
|
6
7
|
interface ConditionLeafNode {
|
|
7
8
|
readonly field: string;
|
|
8
9
|
readonly op: ConditionOp;
|
|
@@ -143,7 +144,39 @@ interface PolicyEvaluationCompletedEvent {
|
|
|
143
144
|
readonly engineKind: "GATE" | "COMPUTE";
|
|
144
145
|
readonly occurredAt: Date;
|
|
145
146
|
}
|
|
146
|
-
|
|
147
|
+
/**
|
|
148
|
+
* A resolved ABAC decision (both PERMIT and DENY). Emitted best-effort by the
|
|
149
|
+
* `AbacAuthorizer` so a host can build an authorization audit trail without an
|
|
150
|
+
* adapter of its own; silent by default like every other event. Types are kept
|
|
151
|
+
* self-contained (no import from `core/abac`) to avoid a config→abac cycle.
|
|
152
|
+
*/
|
|
153
|
+
interface AbacDecisionEvent {
|
|
154
|
+
readonly kind: "abac-decision";
|
|
155
|
+
readonly level: "info";
|
|
156
|
+
readonly action: string;
|
|
157
|
+
readonly resource?: {
|
|
158
|
+
readonly type: string;
|
|
159
|
+
readonly id?: string;
|
|
160
|
+
};
|
|
161
|
+
readonly actorId: string;
|
|
162
|
+
readonly effect: "PERMIT" | "DENY";
|
|
163
|
+
/** Deciding rule id, or `"<default>"` / `"<default-deny>"` when the set's default decided. */
|
|
164
|
+
readonly decidingRuleId: string;
|
|
165
|
+
readonly decidingRuleVersion?: number;
|
|
166
|
+
readonly algorithm: string;
|
|
167
|
+
readonly occurredAt: Date;
|
|
168
|
+
}
|
|
169
|
+
type PolicyEvent = ConditionEvalEvent | PolicyResolutionEvent | PolicyEvaluationFailedEvent | PolicyEvaluationCompletedEvent | AbacDecisionEvent;
|
|
170
|
+
/**
|
|
171
|
+
* Sink for policy telemetry. The core never logs on its own — it hands typed
|
|
172
|
+
* events here and a host implementation decides what to do with them.
|
|
173
|
+
*
|
|
174
|
+
* Redaction is the host's responsibility: a `condition-eval` event carries the
|
|
175
|
+
* evaluated field path and value in `details` (e.g. `student.flags.*`), so in
|
|
176
|
+
* an ERP those payloads can contain PII. Sanitize before persisting or shipping
|
|
177
|
+
* events off-box; the core deliberately keeps no redaction seam so it stays
|
|
178
|
+
* pure and unopinionated about your logging stack.
|
|
179
|
+
*/
|
|
147
180
|
interface PolicyReporter {
|
|
148
181
|
report(event: PolicyEvent): void;
|
|
149
182
|
}
|
|
@@ -156,17 +189,31 @@ interface CoreConfigOptions {
|
|
|
156
189
|
readonly observability?: CoreObservabilityConfig;
|
|
157
190
|
}
|
|
158
191
|
/**
|
|
159
|
-
*
|
|
160
|
-
*
|
|
192
|
+
* The core's config namespace. Today it holds a single section —
|
|
193
|
+
* `observability` — whose whole job is to decide *where policy telemetry goes*
|
|
194
|
+
* while guaranteeing telemetry is best-effort (a throwing reporter can never
|
|
195
|
+
* abort a domain flow). App config (env, settings, feature flags) is
|
|
196
|
+
* deliberately absent for now: when the core needs it, it joins here as a
|
|
197
|
+
* sibling section rather than a new module — which is why this is `config`,
|
|
198
|
+
* not `observability`. It holds the active {@link PolicyReporter} (silent by
|
|
199
|
+
* default, so the core carries no logging dependency), bridges it to the
|
|
200
|
+
* engines' {@link ConditionEvaluatorReporter}, and offers `reportSafely` as
|
|
201
|
+
* the single guarded emit path shared with {@link PolicyService}.
|
|
161
202
|
*/
|
|
162
203
|
declare class CoreConfig {
|
|
163
204
|
#private;
|
|
164
205
|
constructor(options?: CoreConfigOptions);
|
|
206
|
+
/**
|
|
207
|
+
* Swaps the active reporter. Pass `observability.reporter` to enable
|
|
208
|
+
* telemetry, or set it explicitly to `undefined` to go silent again.
|
|
209
|
+
* Omitting `observability` (or its `reporter` key) is a no-op.
|
|
210
|
+
*/
|
|
165
211
|
configure(options: CoreConfigOptions): this;
|
|
166
212
|
reset(): this;
|
|
167
|
-
|
|
213
|
+
/** Emits a policy event through the active reporter, best-effort. */
|
|
214
|
+
reportSafely(event: PolicyEvent): void;
|
|
168
215
|
getConditionEvaluationOptions(engineVersion: number): ConditionEvaluationOptions;
|
|
169
216
|
}
|
|
170
217
|
//#endregion
|
|
171
|
-
export {
|
|
218
|
+
export { ConditionNode as A, GateViolationTrace as C, GatePayload as D, VersionedGateEngine as E, GatePayloadV1 as O, GateTraceNodeSnapshot as S, PolicyViolation as T, ConditionEvaluatorReporter as _, PolicyDefinitionId as a, GateStatus as b, asPolicyDecisionId as c, asTenantId as d, ConditionEvaluationCause as f, ConditionEvaluationReportTag as g, ConditionEvaluationReportLevel as h, PolicyDecisionId as i, ConditionLeafNode as k, asPolicyDefinitionId as l, ConditionEvaluationReport as m, CoreConfigOptions as n, SchoolId as o, ConditionEvaluationOptions as p, CoreObservabilityConfig as r, TenantId as s, CoreConfig as t, asSchoolId as u, GateOutcome as v, PolicyContext as w, GateTraceLeafSnapshot as x, GateOutcomeData as y };
|
|
172
219
|
//# sourceMappingURL=core-config.d.ts.map
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
//#region \0@oxc-project+runtime@0.132.0/helpers/decorate.js
|
|
2
|
+
function __decorate(decorators, target, key, desc) {
|
|
3
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
4
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
5
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
6
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
7
|
+
}
|
|
8
|
+
//#endregion
|
|
9
|
+
Object.defineProperty(exports, "__decorate", {
|
|
10
|
+
enumerable: true,
|
|
11
|
+
get: function() {
|
|
12
|
+
return __decorate;
|
|
13
|
+
}
|
|
14
|
+
});
|
package/dist/decorate.js
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
//#region \0@oxc-project+runtime@0.132.0/helpers/decorate.js
|
|
2
|
+
function __decorate(decorators, target, key, desc) {
|
|
3
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
4
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
5
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
6
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
7
|
+
}
|
|
8
|
+
//#endregion
|
|
9
|
+
export { __decorate as t };
|
package/dist/domain/index.cjs
CHANGED
|
@@ -1,11 +1,105 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
-
const
|
|
2
|
+
const require_invariant_violation_exception = require("../invariant-violation-exception.cjs");
|
|
3
|
+
const require_temporal_guards = require("../temporal-guards.cjs");
|
|
4
|
+
const require_immutable = require("../immutable.cjs");
|
|
3
5
|
const require_value_object = require("../value-object.cjs");
|
|
4
6
|
const require_uuid_identifier = require("../uuid-identifier.cjs");
|
|
7
|
+
//#region src/core/domain/temporal/half-open-interval.ts
|
|
8
|
+
/**
|
|
9
|
+
* Shared core of the bitemporal primitives: a half-open interval
|
|
10
|
+
* `[start, end)` whose end, when present, must be strictly later than its
|
|
11
|
+
* start. `ValidTime` and `TransactionTime` are the same shape under different
|
|
12
|
+
* (deliberate) vocabularies — the invariant lives here once so the two files
|
|
13
|
+
* cannot drift apart.
|
|
14
|
+
*
|
|
15
|
+
* Internal to `domain/temporal`; not exported from the barrel.
|
|
16
|
+
*/
|
|
17
|
+
function assertHalfOpenInterval(startLabel, start, endLabel, end) {
|
|
18
|
+
require_temporal_guards.assertValidDate(startLabel, start);
|
|
19
|
+
if (end === void 0) return;
|
|
20
|
+
require_temporal_guards.assertValidDate(endLabel, end);
|
|
21
|
+
if (end.getTime() <= start.getTime()) throw new require_invariant_violation_exception.InvariantViolationException(`${endLabel} must be later than ${startLabel}`);
|
|
22
|
+
}
|
|
23
|
+
//#endregion
|
|
24
|
+
//#region src/core/domain/temporal/transaction-time.ts
|
|
25
|
+
function assertTransactionTime(txTime, fieldName = "txTime") {
|
|
26
|
+
assertHalfOpenInterval(`${fieldName}.recordedAt`, txTime.recordedAt, `${fieldName}.supersededAt`, txTime.supersededAt);
|
|
27
|
+
}
|
|
28
|
+
function createTransactionTime(input) {
|
|
29
|
+
assertTransactionTime(input, "txTime");
|
|
30
|
+
if (input.supersededAt === void 0) return require_immutable.makeImmutable({ recordedAt: input.recordedAt });
|
|
31
|
+
return require_immutable.makeImmutable({
|
|
32
|
+
recordedAt: input.recordedAt,
|
|
33
|
+
supersededAt: input.supersededAt
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
//#endregion
|
|
37
|
+
//#region src/core/domain/temporal/valid-time.ts
|
|
38
|
+
function assertValidTime(validTime, fieldName = "validTime") {
|
|
39
|
+
assertHalfOpenInterval(`${fieldName}.from`, validTime.from, `${fieldName}.to`, validTime.to);
|
|
40
|
+
}
|
|
41
|
+
function createValidTime(input) {
|
|
42
|
+
assertValidTime(input, "validTime");
|
|
43
|
+
if (input.to === void 0) return require_immutable.makeImmutable({ from: input.from });
|
|
44
|
+
return require_immutable.makeImmutable({
|
|
45
|
+
from: input.from,
|
|
46
|
+
to: input.to
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
//#endregion
|
|
50
|
+
//#region src/core/domain/temporal/temporal-range.ts
|
|
51
|
+
/**
|
|
52
|
+
* Checks whether a temporal range is "open" (has no end date).
|
|
53
|
+
* Useful for identifying information that is still in effect / active.
|
|
54
|
+
*/
|
|
55
|
+
function isOpen(range) {
|
|
56
|
+
assertValidTime(range, "range");
|
|
57
|
+
return range.to === void 0;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Checks whether a temporal range is "closed" (has a defined end date).
|
|
61
|
+
*/
|
|
62
|
+
function isClosed(range) {
|
|
63
|
+
return !isOpen(range);
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Checks whether a specific date is contained within the temporal range.
|
|
67
|
+
* The lower bound (`from`) is inclusive, while the upper bound (`to`) is exclusive.
|
|
68
|
+
*/
|
|
69
|
+
function contains(range, date) {
|
|
70
|
+
assertValidTime(range, "range");
|
|
71
|
+
require_temporal_guards.assertValidDate("date", date);
|
|
72
|
+
const fromTime = range.from.getTime();
|
|
73
|
+
const toTime = range.to?.getTime() ?? Number.POSITIVE_INFINITY;
|
|
74
|
+
const targetTime = date.getTime();
|
|
75
|
+
return targetTime >= fromTime && targetTime < toTime;
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Checks whether two temporal ranges overlap at any point.
|
|
79
|
+
* Useful for validating whether a new rule conflicts with an existing one.
|
|
80
|
+
*/
|
|
81
|
+
function overlaps(left, right) {
|
|
82
|
+
assertValidTime(left, "left");
|
|
83
|
+
assertValidTime(right, "right");
|
|
84
|
+
const leftFrom = left.from.getTime();
|
|
85
|
+
const leftTo = left.to?.getTime() ?? Number.POSITIVE_INFINITY;
|
|
86
|
+
const rightFrom = right.from.getTime();
|
|
87
|
+
return leftFrom < (right.to?.getTime() ?? Number.POSITIVE_INFINITY) && rightFrom < leftTo;
|
|
88
|
+
}
|
|
89
|
+
//#endregion
|
|
90
|
+
//#region src/core/domain/temporal/temporal-snapshot.ts
|
|
91
|
+
function createTemporalSnapshot(input) {
|
|
92
|
+
return Object.freeze({
|
|
93
|
+
data: require_immutable.makeImmutable(input.data),
|
|
94
|
+
validTime: createValidTime(input.validTime),
|
|
95
|
+
txTime: createTransactionTime(input.txTime)
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
//#endregion
|
|
5
99
|
Object.defineProperty(exports, "Entity", {
|
|
6
100
|
enumerable: true,
|
|
7
101
|
get: function() {
|
|
8
|
-
return
|
|
102
|
+
return require_uuid_identifier.Entity;
|
|
9
103
|
}
|
|
10
104
|
});
|
|
11
105
|
exports.UuidIdentifier = require_uuid_identifier.UuidIdentifier;
|
|
@@ -15,3 +109,14 @@ Object.defineProperty(exports, "ValueObject", {
|
|
|
15
109
|
return require_value_object.ValueObject;
|
|
16
110
|
}
|
|
17
111
|
});
|
|
112
|
+
exports.assertTransactionTime = assertTransactionTime;
|
|
113
|
+
exports.assertValidTime = assertValidTime;
|
|
114
|
+
exports.contains = contains;
|
|
115
|
+
exports.createTemporalSnapshot = createTemporalSnapshot;
|
|
116
|
+
exports.createTransactionTime = createTransactionTime;
|
|
117
|
+
exports.createValidTime = createValidTime;
|
|
118
|
+
exports.isClosed = isClosed;
|
|
119
|
+
exports.isOpen = isOpen;
|
|
120
|
+
exports.overlaps = overlaps;
|
|
121
|
+
|
|
122
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.cjs","names":["InvariantViolationException","makeImmutable","makeImmutable","makeImmutable"],"sources":["../../src/core/domain/temporal/half-open-interval.ts","../../src/core/domain/temporal/transaction-time.ts","../../src/core/domain/temporal/valid-time.ts","../../src/core/domain/temporal/temporal-range.ts","../../src/core/domain/temporal/temporal-snapshot.ts"],"sourcesContent":["import { InvariantViolationException } from \"../../exceptions/invariant-violation-exception.js\";\nimport { assertValidDate } from \"../../shared/temporal-guards.js\";\n\n/**\n * Shared core of the bitemporal primitives: a half-open interval\n * `[start, end)` whose end, when present, must be strictly later than its\n * start. `ValidTime` and `TransactionTime` are the same shape under different\n * (deliberate) vocabularies — the invariant lives here once so the two files\n * cannot drift apart.\n *\n * Internal to `domain/temporal`; not exported from the barrel.\n */\nfunction assertHalfOpenInterval(\n startLabel: string,\n start: Date,\n endLabel: string,\n end: Date | undefined,\n): void {\n assertValidDate(startLabel, start);\n\n if (end === undefined) {\n return;\n }\n\n assertValidDate(endLabel, end);\n\n if (end.getTime() <= start.getTime()) {\n throw new InvariantViolationException(\n `${endLabel} must be later than ${startLabel}`,\n );\n }\n}\n\nexport { assertHalfOpenInterval };\n","import { makeImmutable } from \"../../shared/immutable.js\";\n\nimport { assertHalfOpenInterval } from \"./half-open-interval.js\";\n\n/**\n * Represents transaction time (Transaction Time).\n * Defines when the system learned about or recorded a piece of information.\n * Useful for auditing and reconstructing system state at a specific past moment.\n *\n * The window is half-open — `recordedAt` inclusive, `supersededAt` exclusive —\n * and strictly non-empty: `supersededAt` must be later than `recordedAt`, so a\n * record superseded in the same instant it was written is not representable.\n *\n * One caveat mirrors {@link ValueObject}: `createTransactionTime` clones the\n * input dates (no aliasing) but the inner `Date`s are NOT frozen —\n * `Object.freeze` cannot block `setTime`/`setFullYear`, so a caller can still\n * mutate `recordedAt` in place and corrupt the audit record after creation.\n * `readonly` only prevents reassignment. Treat the dates as read-only, or keep\n * instants as ISO/epoch in your own state when that guarantee matters.\n */\ninterface TransactionTime {\n /** When the information was first recorded in the system. */\n readonly recordedAt: Date;\n /** When this information was superseded or invalidated by a newer version. */\n readonly supersededAt?: Date;\n}\n\ntype CreateTransactionTimeInput = TransactionTime;\n\nfunction assertTransactionTime(\n txTime: TransactionTime,\n fieldName: string = \"txTime\",\n): void {\n assertHalfOpenInterval(\n `${fieldName}.recordedAt`,\n txTime.recordedAt,\n `${fieldName}.supersededAt`,\n txTime.supersededAt,\n );\n}\n\nfunction createTransactionTime(\n input: CreateTransactionTimeInput,\n): TransactionTime {\n assertTransactionTime(input, \"txTime\");\n\n if (input.supersededAt === undefined) {\n return makeImmutable({\n recordedAt: input.recordedAt,\n });\n }\n\n return makeImmutable({\n recordedAt: input.recordedAt,\n supersededAt: input.supersededAt,\n });\n}\n\nexport {\n assertTransactionTime,\n createTransactionTime,\n type CreateTransactionTimeInput,\n type TransactionTime,\n};\n","import { makeImmutable } from \"../../shared/immutable.js\";\n\nimport { assertHalfOpenInterval } from \"./half-open-interval.js\";\n\n/**\n * Represents business time (Valid Time).\n * Defines when a piece of information is considered true or in effect in the real world.\n *\n * The window is half-open — `from` inclusive, `to` exclusive — and strictly\n * non-empty: `to` must be later than `from`, so a zero-duration range `[t, t)`\n * is deliberately not representable. Model an instantaneous event as an open\n * range closed by the next fact, not as an empty window.\n *\n * One caveat mirrors {@link ValueObject}: `createValidTime` clones the input\n * dates (no aliasing) but the inner `Date`s are NOT frozen — `Object.freeze`\n * cannot block `setTime`/`setFullYear`, so a caller can still mutate `from`\n * in place and corrupt the range after creation. `readonly` only prevents\n * reassignment. Treat the dates as read-only, or keep instants as ISO/epoch in\n * your own state when that guarantee matters.\n */\ninterface ValidTime {\n /** Start of the validity window (inclusive). */\n readonly from: Date;\n /** End of the validity window (exclusive). When absent, the information is still in effect. */\n readonly to?: Date;\n}\n\ntype CreateValidTimeInput = ValidTime;\n\nfunction assertValidTime(\n validTime: ValidTime,\n fieldName: string = \"validTime\",\n): void {\n assertHalfOpenInterval(\n `${fieldName}.from`,\n validTime.from,\n `${fieldName}.to`,\n validTime.to,\n );\n}\n\nfunction createValidTime(input: CreateValidTimeInput): ValidTime {\n assertValidTime(input, \"validTime\");\n\n if (input.to === undefined) {\n return makeImmutable({\n from: input.from,\n });\n }\n\n return makeImmutable({\n from: input.from,\n to: input.to,\n });\n}\n\nexport {\n assertValidTime,\n createValidTime,\n type CreateValidTimeInput,\n type ValidTime,\n};\n","import { assertValidDate } from \"../../shared/temporal-guards.js\";\n\nimport { assertValidTime, type ValidTime } from \"./valid-time.js\";\n\n/**\n * Checks whether a temporal range is \"open\" (has no end date).\n * Useful for identifying information that is still in effect / active.\n */\nfunction isOpen(range: ValidTime): boolean {\n assertValidTime(range, \"range\");\n\n return range.to === undefined;\n}\n\n/**\n * Checks whether a temporal range is \"closed\" (has a defined end date).\n */\nfunction isClosed(range: ValidTime): boolean {\n return !isOpen(range);\n}\n\n/**\n * Checks whether a specific date is contained within the temporal range.\n * The lower bound (`from`) is inclusive, while the upper bound (`to`) is exclusive.\n */\nfunction contains(range: ValidTime, date: Date): boolean {\n assertValidTime(range, \"range\");\n assertValidDate(\"date\", date);\n\n const fromTime = range.from.getTime();\n const toTime = range.to?.getTime() ?? Number.POSITIVE_INFINITY;\n const targetTime = date.getTime();\n\n return targetTime >= fromTime && targetTime < toTime;\n}\n\n/**\n * Checks whether two temporal ranges overlap at any point.\n * Useful for validating whether a new rule conflicts with an existing one.\n */\nfunction overlaps(left: ValidTime, right: ValidTime): boolean {\n assertValidTime(left, \"left\");\n assertValidTime(right, \"right\");\n\n const leftFrom = left.from.getTime();\n const leftTo = left.to?.getTime() ?? Number.POSITIVE_INFINITY;\n const rightFrom = right.from.getTime();\n const rightTo = right.to?.getTime() ?? Number.POSITIVE_INFINITY;\n\n return leftFrom < rightTo && rightFrom < leftTo;\n}\n\nexport { contains, isClosed, isOpen, overlaps };\n","import { type DeepReadonly, makeImmutable } from \"../../shared/immutable.js\";\n\nimport {\n createTransactionTime,\n type CreateTransactionTimeInput,\n type TransactionTime,\n} from \"./transaction-time.js\";\nimport {\n createValidTime,\n type CreateValidTimeInput,\n type ValidTime,\n} from \"./valid-time.js\";\n\n/**\n * Pairs a domain datum with its time dimensions (Business and Transaction).\n * It is the representation of an entry in the append-only (historical) table.\n *\n * `data` must be plain, structured-cloneable state — the primitive shape of an\n * aggregate, not the aggregate itself. `createTemporalSnapshot` deep-clones it\n * via `structuredClone`: functions and symbols inside `data` throw an\n * `InvariantViolationException`, and class instances (a `ValueObject`, an\n * `Entity`) are NOT rejected — they are silently flattened to plain objects,\n * losing their prototype (and with it `equals`/`toPrimitive`). Project rich\n * objects down first, e.g. with `toPrimitive()`.\n */\ninterface TemporalSnapshot<T> {\n /** The data or state captured by the snapshot. */\n readonly data: DeepReadonly<T>;\n /** Period during which this datum was valid in the real world. */\n readonly validTime: ValidTime;\n /** Period during which this record was the state known to the system. */\n readonly txTime: TransactionTime;\n}\n\ninterface CreateTemporalSnapshotInput<T> {\n readonly data: T;\n readonly validTime: CreateValidTimeInput;\n readonly txTime: CreateTransactionTimeInput;\n}\n\nfunction createTemporalSnapshot<T>(\n input: CreateTemporalSnapshotInput<T>,\n): TemporalSnapshot<T> {\n // The time dimensions come out of their factories already cloned and\n // frozen; only `data` still needs the clone+freeze. Freezing the shell by\n // hand avoids a second structuredClone pass over the whole snapshot.\n return Object.freeze({\n data: makeImmutable(input.data),\n validTime: createValidTime(input.validTime),\n txTime: createTransactionTime(input.txTime),\n });\n}\n\nexport {\n createTemporalSnapshot,\n type CreateTemporalSnapshotInput,\n type TemporalSnapshot,\n};\n"],"mappings":";;;;;;;;;;;;;;;;AAYA,SAAS,uBACL,YACA,OACA,UACA,KACI;CACJ,wBAAA,gBAAgB,YAAY,KAAK;CAEjC,IAAI,QAAQ,KAAA,GACR;CAGJ,wBAAA,gBAAgB,UAAU,GAAG;CAE7B,IAAI,IAAI,QAAQ,KAAK,MAAM,QAAQ,GAC/B,MAAM,IAAIA,sCAAAA,4BACN,GAAG,SAAS,sBAAsB,YACtC;AAER;;;ACFA,SAAS,sBACL,QACA,YAAoB,UAChB;CACJ,uBACI,GAAG,UAAU,cACb,OAAO,YACP,GAAG,UAAU,gBACb,OAAO,YACX;AACJ;AAEA,SAAS,sBACL,OACe;CACf,sBAAsB,OAAO,QAAQ;CAErC,IAAI,MAAM,iBAAiB,KAAA,GACvB,OAAOC,kBAAAA,cAAc,EACjB,YAAY,MAAM,WACtB,CAAC;CAGL,OAAOA,kBAAAA,cAAc;EACjB,YAAY,MAAM;EAClB,cAAc,MAAM;CACxB,CAAC;AACL;;;AC3BA,SAAS,gBACL,WACA,YAAoB,aAChB;CACJ,uBACI,GAAG,UAAU,QACb,UAAU,MACV,GAAG,UAAU,MACb,UAAU,EACd;AACJ;AAEA,SAAS,gBAAgB,OAAwC;CAC7D,gBAAgB,OAAO,WAAW;CAElC,IAAI,MAAM,OAAO,KAAA,GACb,OAAOC,kBAAAA,cAAc,EACjB,MAAM,MAAM,KAChB,CAAC;CAGL,OAAOA,kBAAAA,cAAc;EACjB,MAAM,MAAM;EACZ,IAAI,MAAM;CACd,CAAC;AACL;;;;;;;AC9CA,SAAS,OAAO,OAA2B;CACvC,gBAAgB,OAAO,OAAO;CAE9B,OAAO,MAAM,OAAO,KAAA;AACxB;;;;AAKA,SAAS,SAAS,OAA2B;CACzC,OAAO,CAAC,OAAO,KAAK;AACxB;;;;;AAMA,SAAS,SAAS,OAAkB,MAAqB;CACrD,gBAAgB,OAAO,OAAO;CAC9B,wBAAA,gBAAgB,QAAQ,IAAI;CAE5B,MAAM,WAAW,MAAM,KAAK,QAAQ;CACpC,MAAM,SAAS,MAAM,IAAI,QAAQ,KAAK,OAAO;CAC7C,MAAM,aAAa,KAAK,QAAQ;CAEhC,OAAO,cAAc,YAAY,aAAa;AAClD;;;;;AAMA,SAAS,SAAS,MAAiB,OAA2B;CAC1D,gBAAgB,MAAM,MAAM;CAC5B,gBAAgB,OAAO,OAAO;CAE9B,MAAM,WAAW,KAAK,KAAK,QAAQ;CACnC,MAAM,SAAS,KAAK,IAAI,QAAQ,KAAK,OAAO;CAC5C,MAAM,YAAY,MAAM,KAAK,QAAQ;CAGrC,OAAO,YAFS,MAAM,IAAI,QAAQ,KAAK,OAAO,sBAEjB,YAAY;AAC7C;;;ACVA,SAAS,uBACL,OACmB;CAInB,OAAO,OAAO,OAAO;EACjB,MAAMC,kBAAAA,cAAc,MAAM,IAAI;EAC9B,WAAW,gBAAgB,MAAM,SAAS;EAC1C,QAAQ,sBAAsB,MAAM,MAAM;CAC9C,CAAC;AACL"}
|
package/dist/domain/index.d.cts
CHANGED
|
@@ -1,4 +1,28 @@
|
|
|
1
1
|
import { t as DeepReadonly } from "../immutable.cjs";
|
|
2
2
|
import { n as ValueObjectPluginContract, t as ValueObject } from "../value-object.cjs";
|
|
3
|
+
import { a as ValidTime, c as CreateTransactionTimeInput, d as createTransactionTime, i as CreateValidTimeInput, l as TransactionTime, n as TemporalSnapshot, o as assertValidTime, r as createTemporalSnapshot, s as createValidTime, t as CreateTemporalSnapshotInput, u as assertTransactionTime } from "../temporal-snapshot.cjs";
|
|
3
4
|
import { n as Entity, r as EntityState, t as UuidIdentifier } from "../uuid-identifier.cjs";
|
|
4
|
-
|
|
5
|
+
|
|
6
|
+
//#region src/core/domain/temporal/temporal-range.d.ts
|
|
7
|
+
/**
|
|
8
|
+
* Checks whether a temporal range is "open" (has no end date).
|
|
9
|
+
* Useful for identifying information that is still in effect / active.
|
|
10
|
+
*/
|
|
11
|
+
declare function isOpen(range: ValidTime): boolean;
|
|
12
|
+
/**
|
|
13
|
+
* Checks whether a temporal range is "closed" (has a defined end date).
|
|
14
|
+
*/
|
|
15
|
+
declare function isClosed(range: ValidTime): boolean;
|
|
16
|
+
/**
|
|
17
|
+
* Checks whether a specific date is contained within the temporal range.
|
|
18
|
+
* The lower bound (`from`) is inclusive, while the upper bound (`to`) is exclusive.
|
|
19
|
+
*/
|
|
20
|
+
declare function contains(range: ValidTime, date: Date): boolean;
|
|
21
|
+
/**
|
|
22
|
+
* Checks whether two temporal ranges overlap at any point.
|
|
23
|
+
* Useful for validating whether a new rule conflicts with an existing one.
|
|
24
|
+
*/
|
|
25
|
+
declare function overlaps(left: ValidTime, right: ValidTime): boolean;
|
|
26
|
+
//#endregion
|
|
27
|
+
export { CreateTemporalSnapshotInput, CreateTransactionTimeInput, CreateValidTimeInput, type DeepReadonly, Entity, type EntityState, TemporalSnapshot, TransactionTime, UuidIdentifier, ValidTime, ValueObject, type ValueObjectPluginContract, assertTransactionTime, assertValidTime, contains, createTemporalSnapshot, createTransactionTime, createValidTime, isClosed, isOpen, overlaps };
|
|
28
|
+
//# sourceMappingURL=index.d.cts.map
|
package/dist/domain/index.d.ts
CHANGED
|
@@ -1,4 +1,28 @@
|
|
|
1
1
|
import { t as DeepReadonly } from "../immutable.js";
|
|
2
2
|
import { n as ValueObjectPluginContract, t as ValueObject } from "../value-object.js";
|
|
3
|
+
import { a as ValidTime, c as CreateTransactionTimeInput, d as createTransactionTime, i as CreateValidTimeInput, l as TransactionTime, n as TemporalSnapshot, o as assertValidTime, r as createTemporalSnapshot, s as createValidTime, t as CreateTemporalSnapshotInput, u as assertTransactionTime } from "../temporal-snapshot.js";
|
|
3
4
|
import { n as Entity, r as EntityState, t as UuidIdentifier } from "../uuid-identifier.js";
|
|
4
|
-
|
|
5
|
+
|
|
6
|
+
//#region src/core/domain/temporal/temporal-range.d.ts
|
|
7
|
+
/**
|
|
8
|
+
* Checks whether a temporal range is "open" (has no end date).
|
|
9
|
+
* Useful for identifying information that is still in effect / active.
|
|
10
|
+
*/
|
|
11
|
+
declare function isOpen(range: ValidTime): boolean;
|
|
12
|
+
/**
|
|
13
|
+
* Checks whether a temporal range is "closed" (has a defined end date).
|
|
14
|
+
*/
|
|
15
|
+
declare function isClosed(range: ValidTime): boolean;
|
|
16
|
+
/**
|
|
17
|
+
* Checks whether a specific date is contained within the temporal range.
|
|
18
|
+
* The lower bound (`from`) is inclusive, while the upper bound (`to`) is exclusive.
|
|
19
|
+
*/
|
|
20
|
+
declare function contains(range: ValidTime, date: Date): boolean;
|
|
21
|
+
/**
|
|
22
|
+
* Checks whether two temporal ranges overlap at any point.
|
|
23
|
+
* Useful for validating whether a new rule conflicts with an existing one.
|
|
24
|
+
*/
|
|
25
|
+
declare function overlaps(left: ValidTime, right: ValidTime): boolean;
|
|
26
|
+
//#endregion
|
|
27
|
+
export { CreateTemporalSnapshotInput, CreateTransactionTimeInput, CreateValidTimeInput, type DeepReadonly, Entity, type EntityState, TemporalSnapshot, TransactionTime, UuidIdentifier, ValidTime, ValueObject, type ValueObjectPluginContract, assertTransactionTime, assertValidTime, contains, createTemporalSnapshot, createTransactionTime, createValidTime, isClosed, isOpen, overlaps };
|
|
28
|
+
//# sourceMappingURL=index.d.ts.map
|
package/dist/domain/index.js
CHANGED
|
@@ -1,4 +1,100 @@
|
|
|
1
|
-
import { t as
|
|
1
|
+
import { t as InvariantViolationException } from "../invariant-violation-exception.js";
|
|
2
|
+
import { t as assertValidDate } from "../temporal-guards.js";
|
|
3
|
+
import { n as makeImmutable } from "../immutable.js";
|
|
2
4
|
import { t as ValueObject } from "../value-object.js";
|
|
3
|
-
import { t as UuidIdentifier } from "../uuid-identifier.js";
|
|
4
|
-
|
|
5
|
+
import { n as Entity, t as UuidIdentifier } from "../uuid-identifier.js";
|
|
6
|
+
//#region src/core/domain/temporal/half-open-interval.ts
|
|
7
|
+
/**
|
|
8
|
+
* Shared core of the bitemporal primitives: a half-open interval
|
|
9
|
+
* `[start, end)` whose end, when present, must be strictly later than its
|
|
10
|
+
* start. `ValidTime` and `TransactionTime` are the same shape under different
|
|
11
|
+
* (deliberate) vocabularies — the invariant lives here once so the two files
|
|
12
|
+
* cannot drift apart.
|
|
13
|
+
*
|
|
14
|
+
* Internal to `domain/temporal`; not exported from the barrel.
|
|
15
|
+
*/
|
|
16
|
+
function assertHalfOpenInterval(startLabel, start, endLabel, end) {
|
|
17
|
+
assertValidDate(startLabel, start);
|
|
18
|
+
if (end === void 0) return;
|
|
19
|
+
assertValidDate(endLabel, end);
|
|
20
|
+
if (end.getTime() <= start.getTime()) throw new InvariantViolationException(`${endLabel} must be later than ${startLabel}`);
|
|
21
|
+
}
|
|
22
|
+
//#endregion
|
|
23
|
+
//#region src/core/domain/temporal/transaction-time.ts
|
|
24
|
+
function assertTransactionTime(txTime, fieldName = "txTime") {
|
|
25
|
+
assertHalfOpenInterval(`${fieldName}.recordedAt`, txTime.recordedAt, `${fieldName}.supersededAt`, txTime.supersededAt);
|
|
26
|
+
}
|
|
27
|
+
function createTransactionTime(input) {
|
|
28
|
+
assertTransactionTime(input, "txTime");
|
|
29
|
+
if (input.supersededAt === void 0) return makeImmutable({ recordedAt: input.recordedAt });
|
|
30
|
+
return makeImmutable({
|
|
31
|
+
recordedAt: input.recordedAt,
|
|
32
|
+
supersededAt: input.supersededAt
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
//#endregion
|
|
36
|
+
//#region src/core/domain/temporal/valid-time.ts
|
|
37
|
+
function assertValidTime(validTime, fieldName = "validTime") {
|
|
38
|
+
assertHalfOpenInterval(`${fieldName}.from`, validTime.from, `${fieldName}.to`, validTime.to);
|
|
39
|
+
}
|
|
40
|
+
function createValidTime(input) {
|
|
41
|
+
assertValidTime(input, "validTime");
|
|
42
|
+
if (input.to === void 0) return makeImmutable({ from: input.from });
|
|
43
|
+
return makeImmutable({
|
|
44
|
+
from: input.from,
|
|
45
|
+
to: input.to
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
//#endregion
|
|
49
|
+
//#region src/core/domain/temporal/temporal-range.ts
|
|
50
|
+
/**
|
|
51
|
+
* Checks whether a temporal range is "open" (has no end date).
|
|
52
|
+
* Useful for identifying information that is still in effect / active.
|
|
53
|
+
*/
|
|
54
|
+
function isOpen(range) {
|
|
55
|
+
assertValidTime(range, "range");
|
|
56
|
+
return range.to === void 0;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Checks whether a temporal range is "closed" (has a defined end date).
|
|
60
|
+
*/
|
|
61
|
+
function isClosed(range) {
|
|
62
|
+
return !isOpen(range);
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Checks whether a specific date is contained within the temporal range.
|
|
66
|
+
* The lower bound (`from`) is inclusive, while the upper bound (`to`) is exclusive.
|
|
67
|
+
*/
|
|
68
|
+
function contains(range, date) {
|
|
69
|
+
assertValidTime(range, "range");
|
|
70
|
+
assertValidDate("date", date);
|
|
71
|
+
const fromTime = range.from.getTime();
|
|
72
|
+
const toTime = range.to?.getTime() ?? Number.POSITIVE_INFINITY;
|
|
73
|
+
const targetTime = date.getTime();
|
|
74
|
+
return targetTime >= fromTime && targetTime < toTime;
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Checks whether two temporal ranges overlap at any point.
|
|
78
|
+
* Useful for validating whether a new rule conflicts with an existing one.
|
|
79
|
+
*/
|
|
80
|
+
function overlaps(left, right) {
|
|
81
|
+
assertValidTime(left, "left");
|
|
82
|
+
assertValidTime(right, "right");
|
|
83
|
+
const leftFrom = left.from.getTime();
|
|
84
|
+
const leftTo = left.to?.getTime() ?? Number.POSITIVE_INFINITY;
|
|
85
|
+
const rightFrom = right.from.getTime();
|
|
86
|
+
return leftFrom < (right.to?.getTime() ?? Number.POSITIVE_INFINITY) && rightFrom < leftTo;
|
|
87
|
+
}
|
|
88
|
+
//#endregion
|
|
89
|
+
//#region src/core/domain/temporal/temporal-snapshot.ts
|
|
90
|
+
function createTemporalSnapshot(input) {
|
|
91
|
+
return Object.freeze({
|
|
92
|
+
data: makeImmutable(input.data),
|
|
93
|
+
validTime: createValidTime(input.validTime),
|
|
94
|
+
txTime: createTransactionTime(input.txTime)
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
//#endregion
|
|
98
|
+
export { Entity, UuidIdentifier, ValueObject, assertTransactionTime, assertValidTime, contains, createTemporalSnapshot, createTransactionTime, createValidTime, isClosed, isOpen, overlaps };
|
|
99
|
+
|
|
100
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../../src/core/domain/temporal/half-open-interval.ts","../../src/core/domain/temporal/transaction-time.ts","../../src/core/domain/temporal/valid-time.ts","../../src/core/domain/temporal/temporal-range.ts","../../src/core/domain/temporal/temporal-snapshot.ts"],"sourcesContent":["import { InvariantViolationException } from \"../../exceptions/invariant-violation-exception.js\";\nimport { assertValidDate } from \"../../shared/temporal-guards.js\";\n\n/**\n * Shared core of the bitemporal primitives: a half-open interval\n * `[start, end)` whose end, when present, must be strictly later than its\n * start. `ValidTime` and `TransactionTime` are the same shape under different\n * (deliberate) vocabularies — the invariant lives here once so the two files\n * cannot drift apart.\n *\n * Internal to `domain/temporal`; not exported from the barrel.\n */\nfunction assertHalfOpenInterval(\n startLabel: string,\n start: Date,\n endLabel: string,\n end: Date | undefined,\n): void {\n assertValidDate(startLabel, start);\n\n if (end === undefined) {\n return;\n }\n\n assertValidDate(endLabel, end);\n\n if (end.getTime() <= start.getTime()) {\n throw new InvariantViolationException(\n `${endLabel} must be later than ${startLabel}`,\n );\n }\n}\n\nexport { assertHalfOpenInterval };\n","import { makeImmutable } from \"../../shared/immutable.js\";\n\nimport { assertHalfOpenInterval } from \"./half-open-interval.js\";\n\n/**\n * Represents transaction time (Transaction Time).\n * Defines when the system learned about or recorded a piece of information.\n * Useful for auditing and reconstructing system state at a specific past moment.\n *\n * The window is half-open — `recordedAt` inclusive, `supersededAt` exclusive —\n * and strictly non-empty: `supersededAt` must be later than `recordedAt`, so a\n * record superseded in the same instant it was written is not representable.\n *\n * One caveat mirrors {@link ValueObject}: `createTransactionTime` clones the\n * input dates (no aliasing) but the inner `Date`s are NOT frozen —\n * `Object.freeze` cannot block `setTime`/`setFullYear`, so a caller can still\n * mutate `recordedAt` in place and corrupt the audit record after creation.\n * `readonly` only prevents reassignment. Treat the dates as read-only, or keep\n * instants as ISO/epoch in your own state when that guarantee matters.\n */\ninterface TransactionTime {\n /** When the information was first recorded in the system. */\n readonly recordedAt: Date;\n /** When this information was superseded or invalidated by a newer version. */\n readonly supersededAt?: Date;\n}\n\ntype CreateTransactionTimeInput = TransactionTime;\n\nfunction assertTransactionTime(\n txTime: TransactionTime,\n fieldName: string = \"txTime\",\n): void {\n assertHalfOpenInterval(\n `${fieldName}.recordedAt`,\n txTime.recordedAt,\n `${fieldName}.supersededAt`,\n txTime.supersededAt,\n );\n}\n\nfunction createTransactionTime(\n input: CreateTransactionTimeInput,\n): TransactionTime {\n assertTransactionTime(input, \"txTime\");\n\n if (input.supersededAt === undefined) {\n return makeImmutable({\n recordedAt: input.recordedAt,\n });\n }\n\n return makeImmutable({\n recordedAt: input.recordedAt,\n supersededAt: input.supersededAt,\n });\n}\n\nexport {\n assertTransactionTime,\n createTransactionTime,\n type CreateTransactionTimeInput,\n type TransactionTime,\n};\n","import { makeImmutable } from \"../../shared/immutable.js\";\n\nimport { assertHalfOpenInterval } from \"./half-open-interval.js\";\n\n/**\n * Represents business time (Valid Time).\n * Defines when a piece of information is considered true or in effect in the real world.\n *\n * The window is half-open — `from` inclusive, `to` exclusive — and strictly\n * non-empty: `to` must be later than `from`, so a zero-duration range `[t, t)`\n * is deliberately not representable. Model an instantaneous event as an open\n * range closed by the next fact, not as an empty window.\n *\n * One caveat mirrors {@link ValueObject}: `createValidTime` clones the input\n * dates (no aliasing) but the inner `Date`s are NOT frozen — `Object.freeze`\n * cannot block `setTime`/`setFullYear`, so a caller can still mutate `from`\n * in place and corrupt the range after creation. `readonly` only prevents\n * reassignment. Treat the dates as read-only, or keep instants as ISO/epoch in\n * your own state when that guarantee matters.\n */\ninterface ValidTime {\n /** Start of the validity window (inclusive). */\n readonly from: Date;\n /** End of the validity window (exclusive). When absent, the information is still in effect. */\n readonly to?: Date;\n}\n\ntype CreateValidTimeInput = ValidTime;\n\nfunction assertValidTime(\n validTime: ValidTime,\n fieldName: string = \"validTime\",\n): void {\n assertHalfOpenInterval(\n `${fieldName}.from`,\n validTime.from,\n `${fieldName}.to`,\n validTime.to,\n );\n}\n\nfunction createValidTime(input: CreateValidTimeInput): ValidTime {\n assertValidTime(input, \"validTime\");\n\n if (input.to === undefined) {\n return makeImmutable({\n from: input.from,\n });\n }\n\n return makeImmutable({\n from: input.from,\n to: input.to,\n });\n}\n\nexport {\n assertValidTime,\n createValidTime,\n type CreateValidTimeInput,\n type ValidTime,\n};\n","import { assertValidDate } from \"../../shared/temporal-guards.js\";\n\nimport { assertValidTime, type ValidTime } from \"./valid-time.js\";\n\n/**\n * Checks whether a temporal range is \"open\" (has no end date).\n * Useful for identifying information that is still in effect / active.\n */\nfunction isOpen(range: ValidTime): boolean {\n assertValidTime(range, \"range\");\n\n return range.to === undefined;\n}\n\n/**\n * Checks whether a temporal range is \"closed\" (has a defined end date).\n */\nfunction isClosed(range: ValidTime): boolean {\n return !isOpen(range);\n}\n\n/**\n * Checks whether a specific date is contained within the temporal range.\n * The lower bound (`from`) is inclusive, while the upper bound (`to`) is exclusive.\n */\nfunction contains(range: ValidTime, date: Date): boolean {\n assertValidTime(range, \"range\");\n assertValidDate(\"date\", date);\n\n const fromTime = range.from.getTime();\n const toTime = range.to?.getTime() ?? Number.POSITIVE_INFINITY;\n const targetTime = date.getTime();\n\n return targetTime >= fromTime && targetTime < toTime;\n}\n\n/**\n * Checks whether two temporal ranges overlap at any point.\n * Useful for validating whether a new rule conflicts with an existing one.\n */\nfunction overlaps(left: ValidTime, right: ValidTime): boolean {\n assertValidTime(left, \"left\");\n assertValidTime(right, \"right\");\n\n const leftFrom = left.from.getTime();\n const leftTo = left.to?.getTime() ?? Number.POSITIVE_INFINITY;\n const rightFrom = right.from.getTime();\n const rightTo = right.to?.getTime() ?? Number.POSITIVE_INFINITY;\n\n return leftFrom < rightTo && rightFrom < leftTo;\n}\n\nexport { contains, isClosed, isOpen, overlaps };\n","import { type DeepReadonly, makeImmutable } from \"../../shared/immutable.js\";\n\nimport {\n createTransactionTime,\n type CreateTransactionTimeInput,\n type TransactionTime,\n} from \"./transaction-time.js\";\nimport {\n createValidTime,\n type CreateValidTimeInput,\n type ValidTime,\n} from \"./valid-time.js\";\n\n/**\n * Pairs a domain datum with its time dimensions (Business and Transaction).\n * It is the representation of an entry in the append-only (historical) table.\n *\n * `data` must be plain, structured-cloneable state — the primitive shape of an\n * aggregate, not the aggregate itself. `createTemporalSnapshot` deep-clones it\n * via `structuredClone`: functions and symbols inside `data` throw an\n * `InvariantViolationException`, and class instances (a `ValueObject`, an\n * `Entity`) are NOT rejected — they are silently flattened to plain objects,\n * losing their prototype (and with it `equals`/`toPrimitive`). Project rich\n * objects down first, e.g. with `toPrimitive()`.\n */\ninterface TemporalSnapshot<T> {\n /** The data or state captured by the snapshot. */\n readonly data: DeepReadonly<T>;\n /** Period during which this datum was valid in the real world. */\n readonly validTime: ValidTime;\n /** Period during which this record was the state known to the system. */\n readonly txTime: TransactionTime;\n}\n\ninterface CreateTemporalSnapshotInput<T> {\n readonly data: T;\n readonly validTime: CreateValidTimeInput;\n readonly txTime: CreateTransactionTimeInput;\n}\n\nfunction createTemporalSnapshot<T>(\n input: CreateTemporalSnapshotInput<T>,\n): TemporalSnapshot<T> {\n // The time dimensions come out of their factories already cloned and\n // frozen; only `data` still needs the clone+freeze. Freezing the shell by\n // hand avoids a second structuredClone pass over the whole snapshot.\n return Object.freeze({\n data: makeImmutable(input.data),\n validTime: createValidTime(input.validTime),\n txTime: createTransactionTime(input.txTime),\n });\n}\n\nexport {\n createTemporalSnapshot,\n type CreateTemporalSnapshotInput,\n type TemporalSnapshot,\n};\n"],"mappings":";;;;;;;;;;;;;;;AAYA,SAAS,uBACL,YACA,OACA,UACA,KACI;CACJ,gBAAgB,YAAY,KAAK;CAEjC,IAAI,QAAQ,KAAA,GACR;CAGJ,gBAAgB,UAAU,GAAG;CAE7B,IAAI,IAAI,QAAQ,KAAK,MAAM,QAAQ,GAC/B,MAAM,IAAI,4BACN,GAAG,SAAS,sBAAsB,YACtC;AAER;;;ACFA,SAAS,sBACL,QACA,YAAoB,UAChB;CACJ,uBACI,GAAG,UAAU,cACb,OAAO,YACP,GAAG,UAAU,gBACb,OAAO,YACX;AACJ;AAEA,SAAS,sBACL,OACe;CACf,sBAAsB,OAAO,QAAQ;CAErC,IAAI,MAAM,iBAAiB,KAAA,GACvB,OAAO,cAAc,EACjB,YAAY,MAAM,WACtB,CAAC;CAGL,OAAO,cAAc;EACjB,YAAY,MAAM;EAClB,cAAc,MAAM;CACxB,CAAC;AACL;;;AC3BA,SAAS,gBACL,WACA,YAAoB,aAChB;CACJ,uBACI,GAAG,UAAU,QACb,UAAU,MACV,GAAG,UAAU,MACb,UAAU,EACd;AACJ;AAEA,SAAS,gBAAgB,OAAwC;CAC7D,gBAAgB,OAAO,WAAW;CAElC,IAAI,MAAM,OAAO,KAAA,GACb,OAAO,cAAc,EACjB,MAAM,MAAM,KAChB,CAAC;CAGL,OAAO,cAAc;EACjB,MAAM,MAAM;EACZ,IAAI,MAAM;CACd,CAAC;AACL;;;;;;;AC9CA,SAAS,OAAO,OAA2B;CACvC,gBAAgB,OAAO,OAAO;CAE9B,OAAO,MAAM,OAAO,KAAA;AACxB;;;;AAKA,SAAS,SAAS,OAA2B;CACzC,OAAO,CAAC,OAAO,KAAK;AACxB;;;;;AAMA,SAAS,SAAS,OAAkB,MAAqB;CACrD,gBAAgB,OAAO,OAAO;CAC9B,gBAAgB,QAAQ,IAAI;CAE5B,MAAM,WAAW,MAAM,KAAK,QAAQ;CACpC,MAAM,SAAS,MAAM,IAAI,QAAQ,KAAK,OAAO;CAC7C,MAAM,aAAa,KAAK,QAAQ;CAEhC,OAAO,cAAc,YAAY,aAAa;AAClD;;;;;AAMA,SAAS,SAAS,MAAiB,OAA2B;CAC1D,gBAAgB,MAAM,MAAM;CAC5B,gBAAgB,OAAO,OAAO;CAE9B,MAAM,WAAW,KAAK,KAAK,QAAQ;CACnC,MAAM,SAAS,KAAK,IAAI,QAAQ,KAAK,OAAO;CAC5C,MAAM,YAAY,MAAM,KAAK,QAAQ;CAGrC,OAAO,YAFS,MAAM,IAAI,QAAQ,KAAK,OAAO,sBAEjB,YAAY;AAC7C;;;ACVA,SAAS,uBACL,OACmB;CAInB,OAAO,OAAO,OAAO;EACjB,MAAM,cAAc,MAAM,IAAI;EAC9B,WAAW,gBAAgB,MAAM,SAAS;EAC1C,QAAQ,sBAAsB,MAAM,MAAM;CAC9C,CAAC;AACL"}
|
|
@@ -1,17 +1,21 @@
|
|
|
1
|
-
const
|
|
2
|
-
const
|
|
3
|
-
const require_value_object = require("./value-object.cjs");
|
|
1
|
+
const require_immutable = require("./immutable.cjs");
|
|
2
|
+
const require_aggregate_version = require("./aggregate-version.cjs");
|
|
4
3
|
//#region src/core/versioning/domain-event-contracts.ts
|
|
5
4
|
function buildDomainEventContractVersions(selection) {
|
|
6
5
|
const contractVersions = {};
|
|
7
|
-
if (selection.entity) contractVersions.entity_contract =
|
|
8
|
-
if (selection.valueObject) contractVersions.value_object_contract =
|
|
9
|
-
if (selection.useCase) contractVersions.use_case_contract =
|
|
6
|
+
if (selection.entity) contractVersions.entity_contract = selection.entity.CONTRACT_VERSION;
|
|
7
|
+
if (selection.valueObject) contractVersions.value_object_contract = selection.valueObject.CONTRACT_VERSION;
|
|
8
|
+
if (selection.useCase) contractVersions.use_case_contract = selection.useCase.CONTRACT_VERSION;
|
|
10
9
|
return Object.freeze(contractVersions);
|
|
11
10
|
}
|
|
11
|
+
/**
|
|
12
|
+
* Builds a frozen domain-event envelope. The whole envelope is deep-frozen,
|
|
13
|
+
* so the `payload` handed in becomes immutable too — an event is an immutable
|
|
14
|
+
* fact, and the caller must not mutate the payload after wrapping it.
|
|
15
|
+
*/
|
|
12
16
|
function createDomainEventEnvelope(input) {
|
|
13
|
-
|
|
14
|
-
return
|
|
17
|
+
require_aggregate_version.assertValidAggregateVersion(input.aggregateVersion);
|
|
18
|
+
return require_immutable.deepFreeze({
|
|
15
19
|
aggregate_version: input.aggregateVersion,
|
|
16
20
|
payload: input.payload,
|
|
17
21
|
...buildDomainEventContractVersions(input.contracts)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"domain-event-contracts.cjs","names":["
|
|
1
|
+
{"version":3,"file":"domain-event-contracts.cjs","names":["deepFreeze"],"sources":["../src/core/versioning/domain-event-contracts.ts"],"sourcesContent":["import { assertValidAggregateVersion } from \"../shared/aggregate-version.js\";\nimport { deepFreeze } from \"../shared/immutable.js\";\nimport { type ContractVersion } from \"./version.js\";\n\n/** Anything carrying a `CONTRACT_VERSION` — a class decorated with `@version`. */\ninterface ContractVersioned {\n readonly CONTRACT_VERSION: ContractVersion;\n}\n\n/**\n * The classes whose contract version should be recorded on the envelope.\n *\n * Pass the **concrete** aggregate/value-object/use-case class (not the base),\n * so the envelope records the version the caller actually annotated with\n * `@version`. A base class handed here would report only the framework's\n * version, not the concrete shape that produced the event.\n */\ninterface DomainEventContractSelection {\n readonly entity?: ContractVersioned;\n readonly valueObject?: ContractVersioned;\n readonly useCase?: ContractVersioned;\n}\n\n// Wire-format shape: `snake_case` keys, deliberately distinct from the\n// `camelCase` builder input above — these travel in the serialized event.\ninterface DomainEventContractVersions {\n readonly entity_contract?: ContractVersion;\n readonly value_object_contract?: ContractVersion;\n readonly use_case_contract?: ContractVersion;\n}\n\ntype MutableContractVersions = {\n -readonly [K in keyof DomainEventContractVersions]: DomainEventContractVersions[K];\n};\n\ninterface CreateDomainEventEnvelopeInput<TPayload> {\n readonly aggregateVersion: number;\n readonly payload: TPayload;\n readonly contracts: DomainEventContractSelection;\n}\n\n/**\n * A deliberately minimal domain-event envelope: it carries only the aggregate\n * version and the structural contract versions needed for state/event\n * migration. It intentionally omits `eventId`, `eventType`, `occurredAt` and\n * correlation ids — those require a clock/id source and belong to the\n * consumer's event infrastructure. Treat this as a versioning core to extend,\n * not a complete event record.\n */\ninterface DomainEventEnvelope<TPayload> extends DomainEventContractVersions {\n readonly aggregate_version: number;\n readonly payload: TPayload;\n}\n\nfunction buildDomainEventContractVersions(\n selection: DomainEventContractSelection,\n): DomainEventContractVersions {\n const contractVersions: MutableContractVersions = {};\n\n if (selection.entity) {\n contractVersions.entity_contract = selection.entity.CONTRACT_VERSION;\n }\n\n if (selection.valueObject) {\n contractVersions.value_object_contract =\n selection.valueObject.CONTRACT_VERSION;\n }\n\n if (selection.useCase) {\n contractVersions.use_case_contract = selection.useCase.CONTRACT_VERSION;\n }\n\n return Object.freeze(contractVersions);\n}\n\n/**\n * Builds a frozen domain-event envelope. The whole envelope is deep-frozen,\n * so the `payload` handed in becomes immutable too — an event is an immutable\n * fact, and the caller must not mutate the payload after wrapping it.\n */\nfunction createDomainEventEnvelope<TPayload>(\n input: CreateDomainEventEnvelopeInput<TPayload>,\n): DomainEventEnvelope<TPayload> {\n assertValidAggregateVersion(input.aggregateVersion);\n\n return deepFreeze({\n aggregate_version: input.aggregateVersion,\n payload: input.payload,\n ...buildDomainEventContractVersions(input.contracts),\n });\n}\n\nexport {\n buildDomainEventContractVersions,\n createDomainEventEnvelope,\n type ContractVersioned,\n type CreateDomainEventEnvelopeInput,\n type DomainEventContractSelection,\n type DomainEventContractVersions,\n type DomainEventEnvelope,\n};\n"],"mappings":";;;AAsDA,SAAS,iCACL,WAC2B;CAC3B,MAAM,mBAA4C,CAAC;CAEnD,IAAI,UAAU,QACV,iBAAiB,kBAAkB,UAAU,OAAO;CAGxD,IAAI,UAAU,aACV,iBAAiB,wBACb,UAAU,YAAY;CAG9B,IAAI,UAAU,SACV,iBAAiB,oBAAoB,UAAU,QAAQ;CAG3D,OAAO,OAAO,OAAO,gBAAgB;AACzC;;;;;;AAOA,SAAS,0BACL,OAC6B;CAC7B,0BAAA,4BAA4B,MAAM,gBAAgB;CAElD,OAAOA,kBAAAA,WAAW;EACd,mBAAmB,MAAM;EACzB,SAAS,MAAM;EACf,GAAG,iCAAiC,MAAM,SAAS;CACvD,CAAC;AACL"}
|