@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
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ruleset-registry.js","names":[],"sources":["../src/core/domain/rulesets/ruleset-registry.ts"],"sourcesContent":["import { DomainException } from \"../../exceptions/domain-exception.js\";\nimport {\n type Ruleset,\n type RulesetRegistry as RulesetRegistryContract,\n} from \"./ruleset.contracts.js\";\n\nclass RulesetRegistryError extends DomainException {}\n\nfunction parseVersion(id: string): [number, number] {\n const atIdx = id.lastIndexOf(\"@\");\n const versionStr = id.slice(atIdx + 1);\n const [major, minor] = versionStr.split(\".\").map(Number);\n return [major, minor];\n}\n\nclass RulesetRegistry implements RulesetRegistryContract {\n private readonly _store = new Map<string, Ruleset>();\n private _sealed = false;\n\n register(ruleset: Ruleset): void {\n if (this._sealed) {\n throw new RulesetRegistryError(\n \"Registry is sealed. No new rulesets can be registered.\",\n );\n }\n if (this._store.has(ruleset.id)) {\n throw new RulesetRegistryError(\n `Ruleset with id \"${ruleset.id}\" is already registered.`,\n );\n }\n this._store.set(ruleset.id, ruleset);\n }\n\n seal(): void {\n this._sealed = true;\n }\n\n get<T extends Ruleset>(id: string): T {\n const ruleset = this._store.get(id);\n if (!ruleset) {\n const available = Array.from(this._store.keys()).join(\", \");\n throw new RulesetRegistryError(\n `Ruleset \"${id}\" not found. Available: [${available}]`,\n );\n }\n return ruleset as T;\n }\n\n getCurrent<T extends Ruleset>(prefix: string): T {\n const matchingEntries = Array.from(this._store.entries()).filter(\n ([key]) => key.startsWith(prefix + \"@\"),\n );\n\n if (matchingEntries.length === 0) {\n throw new RulesetRegistryError(\n `No rulesets found with prefix \"${prefix}\".`,\n );\n }\n\n const sorted = matchingEntries.sort(([a], [b]) => {\n const [aMajor, aMinor] = parseVersion(a);\n const [bMajor, bMinor] = parseVersion(b);\n if (aMajor !== bMajor) return aMajor - bMajor;\n return aMinor - bMinor;\n });\n\n return sorted[sorted.length - 1][1] as T;\n }\n}\n\nexport { RulesetRegistry };\n"],"mappings":";;AAMA,IAAM,uBAAN,cAAmC,gBAAgB,CAAC;
|
|
1
|
+
{"version":3,"file":"ruleset-registry.js","names":[],"sources":["../src/core/domain/rulesets/ruleset-registry.ts"],"sourcesContent":["import { DomainException } from \"../../exceptions/domain-exception.js\";\nimport {\n type Ruleset,\n type RulesetRegistry as RulesetRegistryContract,\n} from \"./ruleset.contracts.js\";\n\nclass RulesetRegistryError extends DomainException {}\n\n/**\n * Runtime guard for the `RulesetId` shape (`name@major.minor`). The template\n * type only protects TypeScript callers — a JS consumer (or a cast) could\n * register `\"foo\"` or `\"foo@1.5.2\"`, and either would silently corrupt\n * {@link RulesetRegistry.getCurrent}'s version ordering via `NaN`/ignored\n * segments. Same idea as `CONTRACT_VERSION_PATTERN` in `versioning/version.ts`.\n */\nconst RULESET_ID_PATTERN = /^.+@\\d+\\.\\d+$/;\n\nfunction parseVersion(id: string): [number, number] {\n const atIdx = id.lastIndexOf(\"@\");\n const versionStr = id.slice(atIdx + 1);\n const [major, minor] = versionStr.split(\".\").map(Number);\n return [major, minor];\n}\n\nclass RulesetRegistry implements RulesetRegistryContract {\n private readonly _store = new Map<string, Ruleset>();\n private _sealed = false;\n\n register(ruleset: Ruleset): void {\n if (this._sealed) {\n throw new RulesetRegistryError(\n \"Registry is sealed. No new rulesets can be registered.\",\n );\n }\n if (!RULESET_ID_PATTERN.test(ruleset.id)) {\n throw new RulesetRegistryError(\n `Invalid ruleset id \"${ruleset.id}\". Expected \"<name>@<major>.<minor>\", e.g. \"order-creation@1.0\".`,\n );\n }\n if (this._store.has(ruleset.id)) {\n throw new RulesetRegistryError(\n `Ruleset with id \"${ruleset.id}\" is already registered.`,\n );\n }\n this._store.set(ruleset.id, ruleset);\n }\n\n seal(): void {\n this._sealed = true;\n }\n\n /**\n * Looks up a ruleset by exact id. The `T` parameter is a caller-asserted\n * cast, not a runtime check: asking for the wrong `T` compiles and returns\n * the object mistyped — the standard registry trade-off. Keep the id and\n * the expected type paired at the call site.\n */\n get<T extends Ruleset>(id: string): T {\n const ruleset = this._store.get(id);\n if (!ruleset) {\n const available = Array.from(this._store.keys()).join(\", \");\n throw new RulesetRegistryError(\n `Ruleset \"${id}\" not found. Available: [${available}]`,\n );\n }\n return ruleset as T;\n }\n\n /**\n * Returns the highest `major.minor` version registered under `prefix`.\n * The same caller-asserted `T` cast as {@link get} applies.\n */\n getCurrent<T extends Ruleset>(prefix: string): T {\n const matchingEntries = Array.from(this._store.entries()).filter(\n ([key]) => key.startsWith(prefix + \"@\"),\n );\n\n if (matchingEntries.length === 0) {\n throw new RulesetRegistryError(\n `No rulesets found with prefix \"${prefix}\".`,\n );\n }\n\n const sorted = matchingEntries.sort(([a], [b]) => {\n const [aMajor, aMinor] = parseVersion(a);\n const [bMajor, bMinor] = parseVersion(b);\n if (aMajor !== bMajor) return aMajor - bMajor;\n return aMinor - bMinor;\n });\n\n return sorted[sorted.length - 1][1] as T;\n }\n}\n\nexport { RulesetRegistry };\n"],"mappings":";;AAMA,IAAM,uBAAN,cAAmC,gBAAgB,CAAC;;;;;;;;AASpD,MAAM,qBAAqB;AAE3B,SAAS,aAAa,IAA8B;CAChD,MAAM,QAAQ,GAAG,YAAY,GAAG;CAEhC,MAAM,CAAC,OAAO,SADK,GAAG,MAAM,QAAQ,CACJ,EAAE,MAAM,GAAG,EAAE,IAAI,MAAM;CACvD,OAAO,CAAC,OAAO,KAAK;AACxB;AAEA,IAAM,kBAAN,MAAyD;;gCAC3B,IAAI,IAAqB;iBACjC;;CAElB,SAAS,SAAwB;EAC7B,IAAI,KAAK,SACL,MAAM,IAAI,qBACN,wDACJ;EAEJ,IAAI,CAAC,mBAAmB,KAAK,QAAQ,EAAE,GACnC,MAAM,IAAI,qBACN,uBAAuB,QAAQ,GAAG,iEACtC;EAEJ,IAAI,KAAK,OAAO,IAAI,QAAQ,EAAE,GAC1B,MAAM,IAAI,qBACN,oBAAoB,QAAQ,GAAG,yBACnC;EAEJ,KAAK,OAAO,IAAI,QAAQ,IAAI,OAAO;CACvC;CAEA,OAAa;EACT,KAAK,UAAU;CACnB;;;;;;;CAQA,IAAuB,IAAe;EAClC,MAAM,UAAU,KAAK,OAAO,IAAI,EAAE;EAClC,IAAI,CAAC,SAED,MAAM,IAAI,qBACN,YAAY,GAAG,2BAFD,MAAM,KAAK,KAAK,OAAO,KAAK,CAAC,EAAE,KAAK,IAEA,EAAE,EACxD;EAEJ,OAAO;CACX;;;;;CAMA,WAA8B,QAAmB;EAC7C,MAAM,kBAAkB,MAAM,KAAK,KAAK,OAAO,QAAQ,CAAC,EAAE,QACrD,CAAC,SAAS,IAAI,WAAW,SAAS,GAAG,CAC1C;EAEA,IAAI,gBAAgB,WAAW,GAC3B,MAAM,IAAI,qBACN,kCAAkC,OAAO,GAC7C;EAGJ,MAAM,SAAS,gBAAgB,MAAM,CAAC,IAAI,CAAC,OAAO;GAC9C,MAAM,CAAC,QAAQ,UAAU,aAAa,CAAC;GACvC,MAAM,CAAC,QAAQ,UAAU,aAAa,CAAC;GACvC,IAAI,WAAW,QAAQ,OAAO,SAAS;GACvC,OAAO,SAAS;EACpB,CAAC;EAED,OAAO,OAAO,OAAO,SAAS,GAAG;CACrC;AACJ"}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
const require_temporal_guards = require("./temporal-guards.cjs");
|
|
2
|
+
//#region src/core/shared/stable-stringify.ts
|
|
3
|
+
const CIRCULAR_STRUCTURE_MESSAGE = "Cannot stably stringify a circular structure";
|
|
4
|
+
function sortKeysDeep(value, seen) {
|
|
5
|
+
if (Array.isArray(value)) {
|
|
6
|
+
if (seen.has(value)) throw new TypeError(CIRCULAR_STRUCTURE_MESSAGE);
|
|
7
|
+
seen.add(value);
|
|
8
|
+
const mapped = value.map((item) => sortKeysDeep(item, seen));
|
|
9
|
+
seen.delete(value);
|
|
10
|
+
return mapped;
|
|
11
|
+
}
|
|
12
|
+
if (value instanceof Date) return value;
|
|
13
|
+
if (value && typeof value === "object") {
|
|
14
|
+
if (seen.has(value)) throw new TypeError(CIRCULAR_STRUCTURE_MESSAGE);
|
|
15
|
+
seen.add(value);
|
|
16
|
+
const record = value;
|
|
17
|
+
const ordered = Object.create(null);
|
|
18
|
+
for (const key of Object.keys(record).sort()) ordered[key] = sortKeysDeep(record[key], seen);
|
|
19
|
+
seen.delete(value);
|
|
20
|
+
return ordered;
|
|
21
|
+
}
|
|
22
|
+
return value;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Deterministic JSON string with object keys sorted recursively, so key order
|
|
26
|
+
* does not affect the output.
|
|
27
|
+
*
|
|
28
|
+
* Follows `JSON.stringify` semantics for the values it serializes: `undefined`,
|
|
29
|
+
* functions and symbols are dropped, non-finite numbers become `null`, `bigint`
|
|
30
|
+
* throws, and `Map`/`Set` serialize as `{}`. Distinct payloads can therefore
|
|
31
|
+
* collapse to the same string — this is fine for the structural-equality use it
|
|
32
|
+
* serves (comparing two value objects), but **not** for hashing. For a
|
|
33
|
+
* collision-resistant canonical form use {@link canonicalStringify}, which
|
|
34
|
+
* rejects those lossy values up front.
|
|
35
|
+
*
|
|
36
|
+
* @throws {TypeError} On circular references (mirroring `JSON.stringify`).
|
|
37
|
+
*/
|
|
38
|
+
function stableStringify(value) {
|
|
39
|
+
return JSON.stringify(sortKeysDeep(value, /* @__PURE__ */ new WeakSet()));
|
|
40
|
+
}
|
|
41
|
+
function assertHashable(value, path, seen) {
|
|
42
|
+
if (value === null) return;
|
|
43
|
+
const type = typeof value;
|
|
44
|
+
if (type === "undefined") throw new TypeError(`canonicalStringify does not accept undefined values (at ${path})`);
|
|
45
|
+
if (type === "number" && !Number.isFinite(value)) throw new TypeError(`canonicalStringify does not accept non-finite numbers (at ${path}, value: ${String(value)})`);
|
|
46
|
+
if (type === "bigint" || type === "function" || type === "symbol") throw new TypeError(`canonicalStringify does not accept ${type} values (at ${path})`);
|
|
47
|
+
if (type !== "object") return;
|
|
48
|
+
if (value instanceof Date) {
|
|
49
|
+
if (!require_temporal_guards.isValidDate(value)) throw new TypeError(`canonicalStringify does not accept Invalid Date (at ${path})`);
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
if (seen.has(value)) throw new TypeError(`canonicalStringify does not accept circular references (at ${path})`);
|
|
53
|
+
seen.add(value);
|
|
54
|
+
if (Array.isArray(value)) value.forEach((item, index) => {
|
|
55
|
+
assertHashable(item, `${path}[${index}]`, seen);
|
|
56
|
+
});
|
|
57
|
+
else for (const [key, nested] of Object.entries(value)) assertHashable(nested, `${path}.${key}`, seen);
|
|
58
|
+
seen.delete(value);
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Strict counterpart to {@link stableStringify}: same deterministic output, but
|
|
62
|
+
* throws on any value `JSON.stringify` would drop or coerce (`undefined`,
|
|
63
|
+
* non-finite numbers, `bigint`, functions, symbols, `Invalid Date`) and on
|
|
64
|
+
* circular references. Use this whenever the string feeds a hash or integrity
|
|
65
|
+
* check, so distinct payloads cannot collide.
|
|
66
|
+
*
|
|
67
|
+
* @throws {TypeError} On lossy values or circular references.
|
|
68
|
+
*/
|
|
69
|
+
function canonicalStringify(value) {
|
|
70
|
+
assertHashable(value, "$", /* @__PURE__ */ new WeakSet());
|
|
71
|
+
return stableStringify(value);
|
|
72
|
+
}
|
|
73
|
+
//#endregion
|
|
74
|
+
Object.defineProperty(exports, "canonicalStringify", {
|
|
75
|
+
enumerable: true,
|
|
76
|
+
get: function() {
|
|
77
|
+
return canonicalStringify;
|
|
78
|
+
}
|
|
79
|
+
});
|
|
80
|
+
Object.defineProperty(exports, "stableStringify", {
|
|
81
|
+
enumerable: true,
|
|
82
|
+
get: function() {
|
|
83
|
+
return stableStringify;
|
|
84
|
+
}
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
//# sourceMappingURL=stable-stringify.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stable-stringify.cjs","names":["isValidDate"],"sources":["../src/core/shared/stable-stringify.ts"],"sourcesContent":["import { isValidDate } from \"./temporal-guards.js\";\n\nconst CIRCULAR_STRUCTURE_MESSAGE =\n \"Cannot stably stringify a circular structure\";\n\n// `seen` holds the current traversal branch so true cycles are rejected while\n// repeated (acyclic) references are still serialized — matching JSON.stringify.\nfunction sortKeysDeep(value: unknown, seen: WeakSet<object>): unknown {\n if (Array.isArray(value)) {\n if (seen.has(value)) {\n throw new TypeError(CIRCULAR_STRUCTURE_MESSAGE);\n }\n seen.add(value);\n const mapped = value.map((item) => sortKeysDeep(item, seen));\n seen.delete(value);\n return mapped;\n }\n\n if (value instanceof Date) {\n return value;\n }\n\n if (value && typeof value === \"object\") {\n if (seen.has(value)) {\n throw new TypeError(CIRCULAR_STRUCTURE_MESSAGE);\n }\n seen.add(value);\n const record = value as Record<string, unknown>;\n const ordered = Object.create(null) as Record<string, unknown>;\n\n for (const key of Object.keys(record).sort()) {\n ordered[key] = sortKeysDeep(record[key], seen);\n }\n seen.delete(value);\n\n return ordered;\n }\n\n return value;\n}\n\n/**\n * Deterministic JSON string with object keys sorted recursively, so key order\n * does not affect the output.\n *\n * Follows `JSON.stringify` semantics for the values it serializes: `undefined`,\n * functions and symbols are dropped, non-finite numbers become `null`, `bigint`\n * throws, and `Map`/`Set` serialize as `{}`. Distinct payloads can therefore\n * collapse to the same string — this is fine for the structural-equality use it\n * serves (comparing two value objects), but **not** for hashing. For a\n * collision-resistant canonical form use {@link canonicalStringify}, which\n * rejects those lossy values up front.\n *\n * @throws {TypeError} On circular references (mirroring `JSON.stringify`).\n */\nfunction stableStringify(value: unknown): string {\n return JSON.stringify(sortKeysDeep(value, new WeakSet<object>()));\n}\n\n// Rejects every value `JSON.stringify` would silently drop or coerce, so\n// semantically different payloads can never collapse to the same canonical\n// string. Owned here (rather than in policies) so `payloadHash` and\n// `PolicyHashing` share one strict definition instead of each re-implementing\n// the deep guard.\nfunction assertHashable(\n value: unknown,\n path: string,\n seen: WeakSet<object>,\n): void {\n if (value === null) {\n return;\n }\n\n const type = typeof value;\n\n if (type === \"undefined\") {\n throw new TypeError(\n `canonicalStringify does not accept undefined values (at ${path})`,\n );\n }\n\n if (type === \"number\" && !Number.isFinite(value)) {\n throw new TypeError(\n `canonicalStringify does not accept non-finite numbers (at ${path}, value: ${String(value)})`,\n );\n }\n\n if (type === \"bigint\" || type === \"function\" || type === \"symbol\") {\n throw new TypeError(\n `canonicalStringify does not accept ${type} values (at ${path})`,\n );\n }\n\n if (type !== \"object\") {\n return;\n }\n\n if (value instanceof Date) {\n if (!isValidDate(value)) {\n throw new TypeError(\n `canonicalStringify does not accept Invalid Date (at ${path})`,\n );\n }\n return;\n }\n\n if (seen.has(value as object)) {\n throw new TypeError(\n `canonicalStringify does not accept circular references (at ${path})`,\n );\n }\n seen.add(value as object);\n\n if (Array.isArray(value)) {\n value.forEach((item, index) => {\n assertHashable(item, `${path}[${index}]`, seen);\n });\n } else {\n for (const [key, nested] of Object.entries(\n value as Record<string, unknown>,\n )) {\n assertHashable(nested, `${path}.${key}`, seen);\n }\n }\n\n seen.delete(value as object);\n}\n\n/**\n * Strict counterpart to {@link stableStringify}: same deterministic output, but\n * throws on any value `JSON.stringify` would drop or coerce (`undefined`,\n * non-finite numbers, `bigint`, functions, symbols, `Invalid Date`) and on\n * circular references. Use this whenever the string feeds a hash or integrity\n * check, so distinct payloads cannot collide.\n *\n * @throws {TypeError} On lossy values or circular references.\n */\nfunction canonicalStringify(value: unknown): string {\n assertHashable(value, \"$\", new WeakSet<object>());\n\n return stableStringify(value);\n}\n\nexport { assertHashable, canonicalStringify, stableStringify };\n"],"mappings":";;AAEA,MAAM,6BACF;AAIJ,SAAS,aAAa,OAAgB,MAAgC;CAClE,IAAI,MAAM,QAAQ,KAAK,GAAG;EACtB,IAAI,KAAK,IAAI,KAAK,GACd,MAAM,IAAI,UAAU,0BAA0B;EAElD,KAAK,IAAI,KAAK;EACd,MAAM,SAAS,MAAM,KAAK,SAAS,aAAa,MAAM,IAAI,CAAC;EAC3D,KAAK,OAAO,KAAK;EACjB,OAAO;CACX;CAEA,IAAI,iBAAiB,MACjB,OAAO;CAGX,IAAI,SAAS,OAAO,UAAU,UAAU;EACpC,IAAI,KAAK,IAAI,KAAK,GACd,MAAM,IAAI,UAAU,0BAA0B;EAElD,KAAK,IAAI,KAAK;EACd,MAAM,SAAS;EACf,MAAM,UAAU,OAAO,OAAO,IAAI;EAElC,KAAK,MAAM,OAAO,OAAO,KAAK,MAAM,EAAE,KAAK,GACvC,QAAQ,OAAO,aAAa,OAAO,MAAM,IAAI;EAEjD,KAAK,OAAO,KAAK;EAEjB,OAAO;CACX;CAEA,OAAO;AACX;;;;;;;;;;;;;;;AAgBA,SAAS,gBAAgB,OAAwB;CAC7C,OAAO,KAAK,UAAU,aAAa,uBAAO,IAAI,QAAgB,CAAC,CAAC;AACpE;AAOA,SAAS,eACL,OACA,MACA,MACI;CACJ,IAAI,UAAU,MACV;CAGJ,MAAM,OAAO,OAAO;CAEpB,IAAI,SAAS,aACT,MAAM,IAAI,UACN,2DAA2D,KAAK,EACpE;CAGJ,IAAI,SAAS,YAAY,CAAC,OAAO,SAAS,KAAK,GAC3C,MAAM,IAAI,UACN,6DAA6D,KAAK,WAAW,OAAO,KAAK,EAAE,EAC/F;CAGJ,IAAI,SAAS,YAAY,SAAS,cAAc,SAAS,UACrD,MAAM,IAAI,UACN,sCAAsC,KAAK,cAAc,KAAK,EAClE;CAGJ,IAAI,SAAS,UACT;CAGJ,IAAI,iBAAiB,MAAM;EACvB,IAAI,CAACA,wBAAAA,YAAY,KAAK,GAClB,MAAM,IAAI,UACN,uDAAuD,KAAK,EAChE;EAEJ;CACJ;CAEA,IAAI,KAAK,IAAI,KAAe,GACxB,MAAM,IAAI,UACN,8DAA8D,KAAK,EACvE;CAEJ,KAAK,IAAI,KAAe;CAExB,IAAI,MAAM,QAAQ,KAAK,GACnB,MAAM,SAAS,MAAM,UAAU;EAC3B,eAAe,MAAM,GAAG,KAAK,GAAG,MAAM,IAAI,IAAI;CAClD,CAAC;MAED,KAAK,MAAM,CAAC,KAAK,WAAW,OAAO,QAC/B,KACJ,GACI,eAAe,QAAQ,GAAG,KAAK,GAAG,OAAO,IAAI;CAIrD,KAAK,OAAO,KAAe;AAC/B;;;;;;;;;;AAWA,SAAS,mBAAmB,OAAwB;CAChD,eAAe,OAAO,qBAAK,IAAI,QAAgB,CAAC;CAEhD,OAAO,gBAAgB,KAAK;AAChC"}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { r as isValidDate } from "./temporal-guards.js";
|
|
2
|
+
//#region src/core/shared/stable-stringify.ts
|
|
3
|
+
const CIRCULAR_STRUCTURE_MESSAGE = "Cannot stably stringify a circular structure";
|
|
4
|
+
function sortKeysDeep(value, seen) {
|
|
5
|
+
if (Array.isArray(value)) {
|
|
6
|
+
if (seen.has(value)) throw new TypeError(CIRCULAR_STRUCTURE_MESSAGE);
|
|
7
|
+
seen.add(value);
|
|
8
|
+
const mapped = value.map((item) => sortKeysDeep(item, seen));
|
|
9
|
+
seen.delete(value);
|
|
10
|
+
return mapped;
|
|
11
|
+
}
|
|
12
|
+
if (value instanceof Date) return value;
|
|
13
|
+
if (value && typeof value === "object") {
|
|
14
|
+
if (seen.has(value)) throw new TypeError(CIRCULAR_STRUCTURE_MESSAGE);
|
|
15
|
+
seen.add(value);
|
|
16
|
+
const record = value;
|
|
17
|
+
const ordered = Object.create(null);
|
|
18
|
+
for (const key of Object.keys(record).sort()) ordered[key] = sortKeysDeep(record[key], seen);
|
|
19
|
+
seen.delete(value);
|
|
20
|
+
return ordered;
|
|
21
|
+
}
|
|
22
|
+
return value;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Deterministic JSON string with object keys sorted recursively, so key order
|
|
26
|
+
* does not affect the output.
|
|
27
|
+
*
|
|
28
|
+
* Follows `JSON.stringify` semantics for the values it serializes: `undefined`,
|
|
29
|
+
* functions and symbols are dropped, non-finite numbers become `null`, `bigint`
|
|
30
|
+
* throws, and `Map`/`Set` serialize as `{}`. Distinct payloads can therefore
|
|
31
|
+
* collapse to the same string — this is fine for the structural-equality use it
|
|
32
|
+
* serves (comparing two value objects), but **not** for hashing. For a
|
|
33
|
+
* collision-resistant canonical form use {@link canonicalStringify}, which
|
|
34
|
+
* rejects those lossy values up front.
|
|
35
|
+
*
|
|
36
|
+
* @throws {TypeError} On circular references (mirroring `JSON.stringify`).
|
|
37
|
+
*/
|
|
38
|
+
function stableStringify(value) {
|
|
39
|
+
return JSON.stringify(sortKeysDeep(value, /* @__PURE__ */ new WeakSet()));
|
|
40
|
+
}
|
|
41
|
+
function assertHashable(value, path, seen) {
|
|
42
|
+
if (value === null) return;
|
|
43
|
+
const type = typeof value;
|
|
44
|
+
if (type === "undefined") throw new TypeError(`canonicalStringify does not accept undefined values (at ${path})`);
|
|
45
|
+
if (type === "number" && !Number.isFinite(value)) throw new TypeError(`canonicalStringify does not accept non-finite numbers (at ${path}, value: ${String(value)})`);
|
|
46
|
+
if (type === "bigint" || type === "function" || type === "symbol") throw new TypeError(`canonicalStringify does not accept ${type} values (at ${path})`);
|
|
47
|
+
if (type !== "object") return;
|
|
48
|
+
if (value instanceof Date) {
|
|
49
|
+
if (!isValidDate(value)) throw new TypeError(`canonicalStringify does not accept Invalid Date (at ${path})`);
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
if (seen.has(value)) throw new TypeError(`canonicalStringify does not accept circular references (at ${path})`);
|
|
53
|
+
seen.add(value);
|
|
54
|
+
if (Array.isArray(value)) value.forEach((item, index) => {
|
|
55
|
+
assertHashable(item, `${path}[${index}]`, seen);
|
|
56
|
+
});
|
|
57
|
+
else for (const [key, nested] of Object.entries(value)) assertHashable(nested, `${path}.${key}`, seen);
|
|
58
|
+
seen.delete(value);
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Strict counterpart to {@link stableStringify}: same deterministic output, but
|
|
62
|
+
* throws on any value `JSON.stringify` would drop or coerce (`undefined`,
|
|
63
|
+
* non-finite numbers, `bigint`, functions, symbols, `Invalid Date`) and on
|
|
64
|
+
* circular references. Use this whenever the string feeds a hash or integrity
|
|
65
|
+
* check, so distinct payloads cannot collide.
|
|
66
|
+
*
|
|
67
|
+
* @throws {TypeError} On lossy values or circular references.
|
|
68
|
+
*/
|
|
69
|
+
function canonicalStringify(value) {
|
|
70
|
+
assertHashable(value, "$", /* @__PURE__ */ new WeakSet());
|
|
71
|
+
return stableStringify(value);
|
|
72
|
+
}
|
|
73
|
+
//#endregion
|
|
74
|
+
export { stableStringify as n, canonicalStringify as t };
|
|
75
|
+
|
|
76
|
+
//# sourceMappingURL=stable-stringify.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stable-stringify.js","names":[],"sources":["../src/core/shared/stable-stringify.ts"],"sourcesContent":["import { isValidDate } from \"./temporal-guards.js\";\n\nconst CIRCULAR_STRUCTURE_MESSAGE =\n \"Cannot stably stringify a circular structure\";\n\n// `seen` holds the current traversal branch so true cycles are rejected while\n// repeated (acyclic) references are still serialized — matching JSON.stringify.\nfunction sortKeysDeep(value: unknown, seen: WeakSet<object>): unknown {\n if (Array.isArray(value)) {\n if (seen.has(value)) {\n throw new TypeError(CIRCULAR_STRUCTURE_MESSAGE);\n }\n seen.add(value);\n const mapped = value.map((item) => sortKeysDeep(item, seen));\n seen.delete(value);\n return mapped;\n }\n\n if (value instanceof Date) {\n return value;\n }\n\n if (value && typeof value === \"object\") {\n if (seen.has(value)) {\n throw new TypeError(CIRCULAR_STRUCTURE_MESSAGE);\n }\n seen.add(value);\n const record = value as Record<string, unknown>;\n const ordered = Object.create(null) as Record<string, unknown>;\n\n for (const key of Object.keys(record).sort()) {\n ordered[key] = sortKeysDeep(record[key], seen);\n }\n seen.delete(value);\n\n return ordered;\n }\n\n return value;\n}\n\n/**\n * Deterministic JSON string with object keys sorted recursively, so key order\n * does not affect the output.\n *\n * Follows `JSON.stringify` semantics for the values it serializes: `undefined`,\n * functions and symbols are dropped, non-finite numbers become `null`, `bigint`\n * throws, and `Map`/`Set` serialize as `{}`. Distinct payloads can therefore\n * collapse to the same string — this is fine for the structural-equality use it\n * serves (comparing two value objects), but **not** for hashing. For a\n * collision-resistant canonical form use {@link canonicalStringify}, which\n * rejects those lossy values up front.\n *\n * @throws {TypeError} On circular references (mirroring `JSON.stringify`).\n */\nfunction stableStringify(value: unknown): string {\n return JSON.stringify(sortKeysDeep(value, new WeakSet<object>()));\n}\n\n// Rejects every value `JSON.stringify` would silently drop or coerce, so\n// semantically different payloads can never collapse to the same canonical\n// string. Owned here (rather than in policies) so `payloadHash` and\n// `PolicyHashing` share one strict definition instead of each re-implementing\n// the deep guard.\nfunction assertHashable(\n value: unknown,\n path: string,\n seen: WeakSet<object>,\n): void {\n if (value === null) {\n return;\n }\n\n const type = typeof value;\n\n if (type === \"undefined\") {\n throw new TypeError(\n `canonicalStringify does not accept undefined values (at ${path})`,\n );\n }\n\n if (type === \"number\" && !Number.isFinite(value)) {\n throw new TypeError(\n `canonicalStringify does not accept non-finite numbers (at ${path}, value: ${String(value)})`,\n );\n }\n\n if (type === \"bigint\" || type === \"function\" || type === \"symbol\") {\n throw new TypeError(\n `canonicalStringify does not accept ${type} values (at ${path})`,\n );\n }\n\n if (type !== \"object\") {\n return;\n }\n\n if (value instanceof Date) {\n if (!isValidDate(value)) {\n throw new TypeError(\n `canonicalStringify does not accept Invalid Date (at ${path})`,\n );\n }\n return;\n }\n\n if (seen.has(value as object)) {\n throw new TypeError(\n `canonicalStringify does not accept circular references (at ${path})`,\n );\n }\n seen.add(value as object);\n\n if (Array.isArray(value)) {\n value.forEach((item, index) => {\n assertHashable(item, `${path}[${index}]`, seen);\n });\n } else {\n for (const [key, nested] of Object.entries(\n value as Record<string, unknown>,\n )) {\n assertHashable(nested, `${path}.${key}`, seen);\n }\n }\n\n seen.delete(value as object);\n}\n\n/**\n * Strict counterpart to {@link stableStringify}: same deterministic output, but\n * throws on any value `JSON.stringify` would drop or coerce (`undefined`,\n * non-finite numbers, `bigint`, functions, symbols, `Invalid Date`) and on\n * circular references. Use this whenever the string feeds a hash or integrity\n * check, so distinct payloads cannot collide.\n *\n * @throws {TypeError} On lossy values or circular references.\n */\nfunction canonicalStringify(value: unknown): string {\n assertHashable(value, \"$\", new WeakSet<object>());\n\n return stableStringify(value);\n}\n\nexport { assertHashable, canonicalStringify, stableStringify };\n"],"mappings":";;AAEA,MAAM,6BACF;AAIJ,SAAS,aAAa,OAAgB,MAAgC;CAClE,IAAI,MAAM,QAAQ,KAAK,GAAG;EACtB,IAAI,KAAK,IAAI,KAAK,GACd,MAAM,IAAI,UAAU,0BAA0B;EAElD,KAAK,IAAI,KAAK;EACd,MAAM,SAAS,MAAM,KAAK,SAAS,aAAa,MAAM,IAAI,CAAC;EAC3D,KAAK,OAAO,KAAK;EACjB,OAAO;CACX;CAEA,IAAI,iBAAiB,MACjB,OAAO;CAGX,IAAI,SAAS,OAAO,UAAU,UAAU;EACpC,IAAI,KAAK,IAAI,KAAK,GACd,MAAM,IAAI,UAAU,0BAA0B;EAElD,KAAK,IAAI,KAAK;EACd,MAAM,SAAS;EACf,MAAM,UAAU,OAAO,OAAO,IAAI;EAElC,KAAK,MAAM,OAAO,OAAO,KAAK,MAAM,EAAE,KAAK,GACvC,QAAQ,OAAO,aAAa,OAAO,MAAM,IAAI;EAEjD,KAAK,OAAO,KAAK;EAEjB,OAAO;CACX;CAEA,OAAO;AACX;;;;;;;;;;;;;;;AAgBA,SAAS,gBAAgB,OAAwB;CAC7C,OAAO,KAAK,UAAU,aAAa,uBAAO,IAAI,QAAgB,CAAC,CAAC;AACpE;AAOA,SAAS,eACL,OACA,MACA,MACI;CACJ,IAAI,UAAU,MACV;CAGJ,MAAM,OAAO,OAAO;CAEpB,IAAI,SAAS,aACT,MAAM,IAAI,UACN,2DAA2D,KAAK,EACpE;CAGJ,IAAI,SAAS,YAAY,CAAC,OAAO,SAAS,KAAK,GAC3C,MAAM,IAAI,UACN,6DAA6D,KAAK,WAAW,OAAO,KAAK,EAAE,EAC/F;CAGJ,IAAI,SAAS,YAAY,SAAS,cAAc,SAAS,UACrD,MAAM,IAAI,UACN,sCAAsC,KAAK,cAAc,KAAK,EAClE;CAGJ,IAAI,SAAS,UACT;CAGJ,IAAI,iBAAiB,MAAM;EACvB,IAAI,CAAC,YAAY,KAAK,GAClB,MAAM,IAAI,UACN,uDAAuD,KAAK,EAChE;EAEJ;CACJ;CAEA,IAAI,KAAK,IAAI,KAAe,GACxB,MAAM,IAAI,UACN,8DAA8D,KAAK,EACvE;CAEJ,KAAK,IAAI,KAAe;CAExB,IAAI,MAAM,QAAQ,KAAK,GACnB,MAAM,SAAS,MAAM,UAAU;EAC3B,eAAe,MAAM,GAAG,KAAK,GAAG,MAAM,IAAI,IAAI;CAClD,CAAC;MAED,KAAK,MAAM,CAAC,KAAK,WAAW,OAAO,QAC/B,KACJ,GACI,eAAe,QAAQ,GAAG,KAAK,GAAG,OAAO,IAAI;CAIrD,KAAK,OAAO,KAAe;AAC/B;;;;;;;;;;AAWA,SAAS,mBAAmB,OAAwB;CAChD,eAAe,OAAO,qBAAK,IAAI,QAAgB,CAAC;CAEhD,OAAO,gBAAgB,KAAK;AAChC"}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { t as DeepReadonly } from "./immutable.cjs";
|
|
2
|
+
|
|
3
|
+
//#region src/core/domain/temporal/transaction-time.d.ts
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Represents transaction time (Transaction Time).
|
|
7
|
+
* Defines when the system learned about or recorded a piece of information.
|
|
8
|
+
* Useful for auditing and reconstructing system state at a specific past moment.
|
|
9
|
+
*
|
|
10
|
+
* The window is half-open — `recordedAt` inclusive, `supersededAt` exclusive —
|
|
11
|
+
* and strictly non-empty: `supersededAt` must be later than `recordedAt`, so a
|
|
12
|
+
* record superseded in the same instant it was written is not representable.
|
|
13
|
+
*
|
|
14
|
+
* One caveat mirrors {@link ValueObject}: `createTransactionTime` clones the
|
|
15
|
+
* input dates (no aliasing) but the inner `Date`s are NOT frozen —
|
|
16
|
+
* `Object.freeze` cannot block `setTime`/`setFullYear`, so a caller can still
|
|
17
|
+
* mutate `recordedAt` in place and corrupt the audit record after creation.
|
|
18
|
+
* `readonly` only prevents reassignment. Treat the dates as read-only, or keep
|
|
19
|
+
* instants as ISO/epoch in your own state when that guarantee matters.
|
|
20
|
+
*/
|
|
21
|
+
interface TransactionTime {
|
|
22
|
+
/** When the information was first recorded in the system. */
|
|
23
|
+
readonly recordedAt: Date;
|
|
24
|
+
/** When this information was superseded or invalidated by a newer version. */
|
|
25
|
+
readonly supersededAt?: Date;
|
|
26
|
+
}
|
|
27
|
+
type CreateTransactionTimeInput = TransactionTime;
|
|
28
|
+
declare function assertTransactionTime(txTime: TransactionTime, fieldName?: string): void;
|
|
29
|
+
declare function createTransactionTime(input: CreateTransactionTimeInput): TransactionTime;
|
|
30
|
+
//#endregion
|
|
31
|
+
//#region src/core/domain/temporal/valid-time.d.ts
|
|
32
|
+
/**
|
|
33
|
+
* Represents business time (Valid Time).
|
|
34
|
+
* Defines when a piece of information is considered true or in effect in the real world.
|
|
35
|
+
*
|
|
36
|
+
* The window is half-open — `from` inclusive, `to` exclusive — and strictly
|
|
37
|
+
* non-empty: `to` must be later than `from`, so a zero-duration range `[t, t)`
|
|
38
|
+
* is deliberately not representable. Model an instantaneous event as an open
|
|
39
|
+
* range closed by the next fact, not as an empty window.
|
|
40
|
+
*
|
|
41
|
+
* One caveat mirrors {@link ValueObject}: `createValidTime` clones the input
|
|
42
|
+
* dates (no aliasing) but the inner `Date`s are NOT frozen — `Object.freeze`
|
|
43
|
+
* cannot block `setTime`/`setFullYear`, so a caller can still mutate `from`
|
|
44
|
+
* in place and corrupt the range after creation. `readonly` only prevents
|
|
45
|
+
* reassignment. Treat the dates as read-only, or keep instants as ISO/epoch in
|
|
46
|
+
* your own state when that guarantee matters.
|
|
47
|
+
*/
|
|
48
|
+
interface ValidTime {
|
|
49
|
+
/** Start of the validity window (inclusive). */
|
|
50
|
+
readonly from: Date;
|
|
51
|
+
/** End of the validity window (exclusive). When absent, the information is still in effect. */
|
|
52
|
+
readonly to?: Date;
|
|
53
|
+
}
|
|
54
|
+
type CreateValidTimeInput = ValidTime;
|
|
55
|
+
declare function assertValidTime(validTime: ValidTime, fieldName?: string): void;
|
|
56
|
+
declare function createValidTime(input: CreateValidTimeInput): ValidTime;
|
|
57
|
+
//#endregion
|
|
58
|
+
//#region src/core/domain/temporal/temporal-snapshot.d.ts
|
|
59
|
+
/**
|
|
60
|
+
* Pairs a domain datum with its time dimensions (Business and Transaction).
|
|
61
|
+
* It is the representation of an entry in the append-only (historical) table.
|
|
62
|
+
*
|
|
63
|
+
* `data` must be plain, structured-cloneable state — the primitive shape of an
|
|
64
|
+
* aggregate, not the aggregate itself. `createTemporalSnapshot` deep-clones it
|
|
65
|
+
* via `structuredClone`: functions and symbols inside `data` throw an
|
|
66
|
+
* `InvariantViolationException`, and class instances (a `ValueObject`, an
|
|
67
|
+
* `Entity`) are NOT rejected — they are silently flattened to plain objects,
|
|
68
|
+
* losing their prototype (and with it `equals`/`toPrimitive`). Project rich
|
|
69
|
+
* objects down first, e.g. with `toPrimitive()`.
|
|
70
|
+
*/
|
|
71
|
+
interface TemporalSnapshot<T> {
|
|
72
|
+
/** The data or state captured by the snapshot. */
|
|
73
|
+
readonly data: DeepReadonly<T>;
|
|
74
|
+
/** Period during which this datum was valid in the real world. */
|
|
75
|
+
readonly validTime: ValidTime;
|
|
76
|
+
/** Period during which this record was the state known to the system. */
|
|
77
|
+
readonly txTime: TransactionTime;
|
|
78
|
+
}
|
|
79
|
+
interface CreateTemporalSnapshotInput<T> {
|
|
80
|
+
readonly data: T;
|
|
81
|
+
readonly validTime: CreateValidTimeInput;
|
|
82
|
+
readonly txTime: CreateTransactionTimeInput;
|
|
83
|
+
}
|
|
84
|
+
declare function createTemporalSnapshot<T>(input: CreateTemporalSnapshotInput<T>): TemporalSnapshot<T>;
|
|
85
|
+
//#endregion
|
|
86
|
+
export { ValidTime as a, CreateTransactionTimeInput as c, createTransactionTime as d, CreateValidTimeInput as i, TransactionTime as l, TemporalSnapshot as n, assertValidTime as o, createTemporalSnapshot as r, createValidTime as s, CreateTemporalSnapshotInput as t, assertTransactionTime as u };
|
|
87
|
+
//# sourceMappingURL=temporal-snapshot.d.cts.map
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { t as DeepReadonly } from "./immutable.js";
|
|
2
|
+
|
|
3
|
+
//#region src/core/domain/temporal/transaction-time.d.ts
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Represents transaction time (Transaction Time).
|
|
7
|
+
* Defines when the system learned about or recorded a piece of information.
|
|
8
|
+
* Useful for auditing and reconstructing system state at a specific past moment.
|
|
9
|
+
*
|
|
10
|
+
* The window is half-open — `recordedAt` inclusive, `supersededAt` exclusive —
|
|
11
|
+
* and strictly non-empty: `supersededAt` must be later than `recordedAt`, so a
|
|
12
|
+
* record superseded in the same instant it was written is not representable.
|
|
13
|
+
*
|
|
14
|
+
* One caveat mirrors {@link ValueObject}: `createTransactionTime` clones the
|
|
15
|
+
* input dates (no aliasing) but the inner `Date`s are NOT frozen —
|
|
16
|
+
* `Object.freeze` cannot block `setTime`/`setFullYear`, so a caller can still
|
|
17
|
+
* mutate `recordedAt` in place and corrupt the audit record after creation.
|
|
18
|
+
* `readonly` only prevents reassignment. Treat the dates as read-only, or keep
|
|
19
|
+
* instants as ISO/epoch in your own state when that guarantee matters.
|
|
20
|
+
*/
|
|
21
|
+
interface TransactionTime {
|
|
22
|
+
/** When the information was first recorded in the system. */
|
|
23
|
+
readonly recordedAt: Date;
|
|
24
|
+
/** When this information was superseded or invalidated by a newer version. */
|
|
25
|
+
readonly supersededAt?: Date;
|
|
26
|
+
}
|
|
27
|
+
type CreateTransactionTimeInput = TransactionTime;
|
|
28
|
+
declare function assertTransactionTime(txTime: TransactionTime, fieldName?: string): void;
|
|
29
|
+
declare function createTransactionTime(input: CreateTransactionTimeInput): TransactionTime;
|
|
30
|
+
//#endregion
|
|
31
|
+
//#region src/core/domain/temporal/valid-time.d.ts
|
|
32
|
+
/**
|
|
33
|
+
* Represents business time (Valid Time).
|
|
34
|
+
* Defines when a piece of information is considered true or in effect in the real world.
|
|
35
|
+
*
|
|
36
|
+
* The window is half-open — `from` inclusive, `to` exclusive — and strictly
|
|
37
|
+
* non-empty: `to` must be later than `from`, so a zero-duration range `[t, t)`
|
|
38
|
+
* is deliberately not representable. Model an instantaneous event as an open
|
|
39
|
+
* range closed by the next fact, not as an empty window.
|
|
40
|
+
*
|
|
41
|
+
* One caveat mirrors {@link ValueObject}: `createValidTime` clones the input
|
|
42
|
+
* dates (no aliasing) but the inner `Date`s are NOT frozen — `Object.freeze`
|
|
43
|
+
* cannot block `setTime`/`setFullYear`, so a caller can still mutate `from`
|
|
44
|
+
* in place and corrupt the range after creation. `readonly` only prevents
|
|
45
|
+
* reassignment. Treat the dates as read-only, or keep instants as ISO/epoch in
|
|
46
|
+
* your own state when that guarantee matters.
|
|
47
|
+
*/
|
|
48
|
+
interface ValidTime {
|
|
49
|
+
/** Start of the validity window (inclusive). */
|
|
50
|
+
readonly from: Date;
|
|
51
|
+
/** End of the validity window (exclusive). When absent, the information is still in effect. */
|
|
52
|
+
readonly to?: Date;
|
|
53
|
+
}
|
|
54
|
+
type CreateValidTimeInput = ValidTime;
|
|
55
|
+
declare function assertValidTime(validTime: ValidTime, fieldName?: string): void;
|
|
56
|
+
declare function createValidTime(input: CreateValidTimeInput): ValidTime;
|
|
57
|
+
//#endregion
|
|
58
|
+
//#region src/core/domain/temporal/temporal-snapshot.d.ts
|
|
59
|
+
/**
|
|
60
|
+
* Pairs a domain datum with its time dimensions (Business and Transaction).
|
|
61
|
+
* It is the representation of an entry in the append-only (historical) table.
|
|
62
|
+
*
|
|
63
|
+
* `data` must be plain, structured-cloneable state — the primitive shape of an
|
|
64
|
+
* aggregate, not the aggregate itself. `createTemporalSnapshot` deep-clones it
|
|
65
|
+
* via `structuredClone`: functions and symbols inside `data` throw an
|
|
66
|
+
* `InvariantViolationException`, and class instances (a `ValueObject`, an
|
|
67
|
+
* `Entity`) are NOT rejected — they are silently flattened to plain objects,
|
|
68
|
+
* losing their prototype (and with it `equals`/`toPrimitive`). Project rich
|
|
69
|
+
* objects down first, e.g. with `toPrimitive()`.
|
|
70
|
+
*/
|
|
71
|
+
interface TemporalSnapshot<T> {
|
|
72
|
+
/** The data or state captured by the snapshot. */
|
|
73
|
+
readonly data: DeepReadonly<T>;
|
|
74
|
+
/** Period during which this datum was valid in the real world. */
|
|
75
|
+
readonly validTime: ValidTime;
|
|
76
|
+
/** Period during which this record was the state known to the system. */
|
|
77
|
+
readonly txTime: TransactionTime;
|
|
78
|
+
}
|
|
79
|
+
interface CreateTemporalSnapshotInput<T> {
|
|
80
|
+
readonly data: T;
|
|
81
|
+
readonly validTime: CreateValidTimeInput;
|
|
82
|
+
readonly txTime: CreateTransactionTimeInput;
|
|
83
|
+
}
|
|
84
|
+
declare function createTemporalSnapshot<T>(input: CreateTemporalSnapshotInput<T>): TemporalSnapshot<T>;
|
|
85
|
+
//#endregion
|
|
86
|
+
export { ValidTime as a, CreateTransactionTimeInput as c, createTransactionTime as d, CreateValidTimeInput as i, TransactionTime as l, TemporalSnapshot as n, assertValidTime as o, createTemporalSnapshot as r, createValidTime as s, CreateTemporalSnapshotInput as t, assertTransactionTime as u };
|
|
87
|
+
//# sourceMappingURL=temporal-snapshot.d.ts.map
|
|
@@ -1,8 +1,128 @@
|
|
|
1
|
+
const require_app_error = require("./app-error.cjs");
|
|
1
2
|
const require_not_found_error = require("./not-found-error.cjs");
|
|
3
|
+
const require_temporal_guards = require("./temporal-guards.cjs");
|
|
2
4
|
const require_unexpected_error = require("./unexpected-error.cjs");
|
|
3
5
|
const require_immutable = require("./immutable.cjs");
|
|
4
|
-
const
|
|
5
|
-
|
|
6
|
+
const require_decorate = require("./decorate.cjs");
|
|
7
|
+
//#region src/core/application/use-case.ts
|
|
8
|
+
const EXECUTION_COUNTER = "use_case.executions";
|
|
9
|
+
const DURATION_HISTOGRAM = "use_case.duration_ms";
|
|
10
|
+
let UseCase = class UseCase {
|
|
11
|
+
get contractVersion() {
|
|
12
|
+
return this.constructor.CONTRACT_VERSION;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Runs the use case.
|
|
16
|
+
*
|
|
17
|
+
* `run()` is the single seam every use case crosses, so it is where
|
|
18
|
+
* cross-cutting instrumentation lives. When {@link observability} exposes
|
|
19
|
+
* any adapter, the call to `execute()` is wrapped with tracing, metrics and
|
|
20
|
+
* failure logging; otherwise it delegates directly with no overhead.
|
|
21
|
+
*
|
|
22
|
+
* Observability is strictly side-effecting: a misbehaving adapter never
|
|
23
|
+
* changes the business result nor masks a thrown error — its own failures
|
|
24
|
+
* are swallowed.
|
|
25
|
+
*/
|
|
26
|
+
async run(input) {
|
|
27
|
+
const observability = this.observability();
|
|
28
|
+
if (!observability.logger && !observability.metrics && !observability.tracer) return await this.execute(input);
|
|
29
|
+
return await this.runInstrumented(input, observability);
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Adapters used to instrument {@link run}. Override to opt a use case into
|
|
33
|
+
* tracing/metrics/logging. Returns an empty object by default, which keeps
|
|
34
|
+
* the plain delegating behavior.
|
|
35
|
+
*/
|
|
36
|
+
observability() {
|
|
37
|
+
return {};
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Stable identity used for span names and metric labels. Defaults to the
|
|
41
|
+
* runtime class name; override when bundling/minification would otherwise
|
|
42
|
+
* erase a meaningful name.
|
|
43
|
+
*/
|
|
44
|
+
get useCaseName() {
|
|
45
|
+
return this.constructor.name;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Extra attributes stamped onto the span and every metric emitted for this
|
|
49
|
+
* execution. Override to attach stable, **low-cardinality** dimensions
|
|
50
|
+
* (e.g. `Command` adds `requested_by.kind`). Keep values bounded — they
|
|
51
|
+
* become metric label values, so a user id or free-form string here would
|
|
52
|
+
* explode the metric's cardinality. Defaults to none.
|
|
53
|
+
*/
|
|
54
|
+
spanAttributes(_input) {
|
|
55
|
+
return {};
|
|
56
|
+
}
|
|
57
|
+
async runInstrumented(input, observability) {
|
|
58
|
+
const { logger, metrics, tracer } = observability;
|
|
59
|
+
const name = this.useCaseName;
|
|
60
|
+
const attributes = safely(() => this.spanAttributes(input)) ?? {};
|
|
61
|
+
const span = safely(() => tracer?.startSpan(name, {
|
|
62
|
+
"use_case.name": name,
|
|
63
|
+
...attributes
|
|
64
|
+
}));
|
|
65
|
+
const startedAt = performance.now();
|
|
66
|
+
let outcome = "exception";
|
|
67
|
+
try {
|
|
68
|
+
const output = await this.execute(input);
|
|
69
|
+
outcome = output.isOk() ? "ok" : "error";
|
|
70
|
+
if (outcome === "error") {
|
|
71
|
+
const code = businessErrorCode(output);
|
|
72
|
+
safely(() => logger?.warn(`${name} returned a business error`, {
|
|
73
|
+
useCase: name,
|
|
74
|
+
...code ? { code } : {}
|
|
75
|
+
}));
|
|
76
|
+
}
|
|
77
|
+
safely(() => span?.setAttribute("use_case.outcome", outcome));
|
|
78
|
+
safely(() => metrics?.counter(EXECUTION_COUNTER, 1, {
|
|
79
|
+
useCase: name,
|
|
80
|
+
outcome,
|
|
81
|
+
...attributes
|
|
82
|
+
}));
|
|
83
|
+
return output;
|
|
84
|
+
} catch (error) {
|
|
85
|
+
safely(() => logger?.error(`${name} threw an unexpected error`, { useCase: name }));
|
|
86
|
+
safely(() => span?.setAttribute("use_case.outcome", "exception"));
|
|
87
|
+
safely(() => span?.recordException(error));
|
|
88
|
+
safely(() => metrics?.counter(EXECUTION_COUNTER, 1, {
|
|
89
|
+
useCase: name,
|
|
90
|
+
outcome: "exception",
|
|
91
|
+
...attributes
|
|
92
|
+
}));
|
|
93
|
+
throw error;
|
|
94
|
+
} finally {
|
|
95
|
+
safely(() => metrics?.histogram(DURATION_HISTOGRAM, performance.now() - startedAt, {
|
|
96
|
+
useCase: name,
|
|
97
|
+
outcome,
|
|
98
|
+
...attributes
|
|
99
|
+
}));
|
|
100
|
+
safely(() => span?.end());
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
};
|
|
104
|
+
UseCase = require_decorate.__decorate([require_immutable.version("1.0")], UseCase);
|
|
105
|
+
/**
|
|
106
|
+
* Pulls the stable `code` off a business-error `Result` for logging. Returns
|
|
107
|
+
* `undefined` when the error is not an {@link AppError} — the code is safe to
|
|
108
|
+
* log (it's the public contract), unlike the message which may carry PII.
|
|
109
|
+
*/
|
|
110
|
+
function businessErrorCode(output) {
|
|
111
|
+
const error = output.errorOrNull();
|
|
112
|
+
return error instanceof require_app_error.AppError ? error.code : void 0;
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Invokes an observability side effect, isolating any adapter failure so it
|
|
116
|
+
* cannot alter the use case outcome.
|
|
117
|
+
*/
|
|
118
|
+
function safely(effect) {
|
|
119
|
+
try {
|
|
120
|
+
return effect();
|
|
121
|
+
} catch {
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
//#endregion
|
|
6
126
|
//#region src/core/application/commands/command.ts
|
|
7
127
|
/**
|
|
8
128
|
* Base for use cases that **mutate state** (writes), following CQS.
|
|
@@ -14,8 +134,18 @@ const require_temporal_guards = require("./temporal-guards.cjs");
|
|
|
14
134
|
* The Command/Query distinction is semantic: the type declares the intent
|
|
15
135
|
* before any implementation exists, guiding code review and API contracts.
|
|
16
136
|
*/
|
|
17
|
-
let Command = class Command extends
|
|
18
|
-
|
|
137
|
+
let Command = class Command extends UseCase {
|
|
138
|
+
/**
|
|
139
|
+
* Surfaces the actor's *kind* (`"user"` / `"system"`) on the span and
|
|
140
|
+
* metrics so a write can be sliced by origin. Only the low-cardinality
|
|
141
|
+
* `kind` is emitted — never the raw id, which would blow up label
|
|
142
|
+
* cardinality and could leak a user identifier into metrics.
|
|
143
|
+
*/
|
|
144
|
+
spanAttributes(input) {
|
|
145
|
+
return { "requested_by.kind": input.requestedBy.kind };
|
|
146
|
+
}
|
|
147
|
+
};
|
|
148
|
+
Command = require_decorate.__decorate([require_immutable.version("1.0")], Command);
|
|
19
149
|
//#endregion
|
|
20
150
|
//#region src/core/application/policy-error-mapper.ts
|
|
21
151
|
function mapPolicyEvaluationError(err) {
|
|
@@ -36,11 +166,14 @@ function mapPolicyEvaluationError(err) {
|
|
|
36
166
|
policyKind: err.policyKind,
|
|
37
167
|
payloadSchemaVersion: err.payloadSchemaVersion
|
|
38
168
|
}, { cause: err.cause });
|
|
39
|
-
case "ENGINE_FAILURE": return new require_unexpected_error.UnexpectedError(err.message, err.cause, {
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
169
|
+
case "ENGINE_FAILURE": return new require_unexpected_error.UnexpectedError(err.message, err.cause, {
|
|
170
|
+
publicMessage: "Policy evaluation failed unexpectedly.",
|
|
171
|
+
metadata: {
|
|
172
|
+
policyKey: err.policyKey,
|
|
173
|
+
engine: err.engine,
|
|
174
|
+
engineVersion: err.engineVersion
|
|
175
|
+
}
|
|
176
|
+
});
|
|
44
177
|
}
|
|
45
178
|
}
|
|
46
179
|
//#endregion
|
|
@@ -58,12 +191,17 @@ function mapPolicyEvaluationError(err) {
|
|
|
58
191
|
* A Query must never mutate persisted data — its execution must be idempotent
|
|
59
192
|
* and safe to call multiple times with the same input.
|
|
60
193
|
*/
|
|
61
|
-
let Query = class Query extends
|
|
194
|
+
let Query = class Query extends UseCase {
|
|
195
|
+
/**
|
|
196
|
+
* Declares how infrastructure should cache this query's result. Public so
|
|
197
|
+
* a caching adapter can actually read it off the instance; overrides must
|
|
198
|
+
* stay public.
|
|
199
|
+
*/
|
|
62
200
|
cacheStrategy() {
|
|
63
201
|
return { kind: "NO_CACHE" };
|
|
64
202
|
}
|
|
65
203
|
};
|
|
66
|
-
Query =
|
|
204
|
+
Query = require_decorate.__decorate([require_immutable.version("1.0")], Query);
|
|
67
205
|
//#endregion
|
|
68
206
|
//#region src/core/application/temporal/temporal-context.ts
|
|
69
207
|
function assertTemporalContext(temporalContext, fieldName = "temporalContext") {
|
|
@@ -84,20 +222,34 @@ function createTemporalContext(input = {}) {
|
|
|
84
222
|
}
|
|
85
223
|
//#endregion
|
|
86
224
|
//#region src/core/application/temporal/temporal-use-case.ts
|
|
87
|
-
|
|
225
|
+
let TemporalUseCase = class TemporalUseCase extends UseCase {
|
|
88
226
|
resolveTemporalContext(input) {
|
|
89
227
|
return createTemporalContext(input.temporalContext);
|
|
90
228
|
}
|
|
229
|
+
/**
|
|
230
|
+
* Enriches a policy `ContextSeed` with `fields.now`, taken from the
|
|
231
|
+
* context's `requestedAt` (wall-clock time of the request).
|
|
232
|
+
*
|
|
233
|
+
* NOTE: this injects **only** `now` (from `requestedAt`) — it does *not*
|
|
234
|
+
* propagate `temporalContext.asOf`. Policy evaluation derives its own
|
|
235
|
+
* `asOf` from whichever seed field the policy's `asOfSource` points at
|
|
236
|
+
* (via `fields.now` by default). So a retroactive use case (an `asOf` in
|
|
237
|
+
* the past) will still evaluate policies at the *current* time unless the
|
|
238
|
+
* author maps `temporalContext.asOf` onto that seed field explicitly. Do
|
|
239
|
+
* that mapping in the use case when back-dated evaluation is intended.
|
|
240
|
+
*/
|
|
91
241
|
buildPolicySeed(seed, temporalContext) {
|
|
242
|
+
const fields = Object.freeze({
|
|
243
|
+
...seed.fields,
|
|
244
|
+
now: new Date(temporalContext.requestedAt.getTime())
|
|
245
|
+
});
|
|
92
246
|
return Object.freeze({
|
|
93
247
|
...seed,
|
|
94
|
-
fields
|
|
95
|
-
...seed.fields,
|
|
96
|
-
now: new Date(temporalContext.requestedAt.getTime())
|
|
97
|
-
}
|
|
248
|
+
fields
|
|
98
249
|
});
|
|
99
250
|
}
|
|
100
251
|
};
|
|
252
|
+
TemporalUseCase = require_decorate.__decorate([require_immutable.version("1.0")], TemporalUseCase);
|
|
101
253
|
//#endregion
|
|
102
254
|
Object.defineProperty(exports, "Command", {
|
|
103
255
|
enumerable: true,
|
|
@@ -117,6 +269,12 @@ Object.defineProperty(exports, "TemporalUseCase", {
|
|
|
117
269
|
return TemporalUseCase;
|
|
118
270
|
}
|
|
119
271
|
});
|
|
272
|
+
Object.defineProperty(exports, "UseCase", {
|
|
273
|
+
enumerable: true,
|
|
274
|
+
get: function() {
|
|
275
|
+
return UseCase;
|
|
276
|
+
}
|
|
277
|
+
});
|
|
120
278
|
Object.defineProperty(exports, "assertTemporalContext", {
|
|
121
279
|
enumerable: true,
|
|
122
280
|
get: function() {
|