@cullet/erp-core 1.5.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 +2 -2
- 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 +8 -1
- package/dist/app-error.cjs.map +1 -1
- package/dist/app-error.js +8 -1
- 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 +115 -184
- package/dist/condition-evaluator.cjs.map +1 -1
- package/dist/condition-evaluator.js +116 -179
- 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 +1 -1
- package/dist/errors/index.d.cts +1 -1
- package/dist/errors/index.d.ts +1 -1
- package/dist/errors/index.js +2 -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 +1 -1
- package/dist/hashing.cjs.map +1 -1
- package/dist/hashing.js +2 -2
- 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 +13 -10
- 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 +7 -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 -15
- package/dist/policies/engines/v1/gate/index.d.ts +3 -15
- 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 +40 -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 +42 -48
- package/dist/policy-service.js.map +1 -1
- package/dist/requested-by.cjs +1 -1
- package/dist/requested-by.cjs.map +1 -1
- package/dist/requested-by.js +1 -1
- 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 +43 -3
- package/dist/stable-stringify.cjs.map +1 -1
- package/dist/stable-stringify.js +38 -4
- package/dist/stable-stringify.js.map +1 -1
- package/dist/temporal-snapshot.d.cts +87 -0
- package/dist/temporal-snapshot.d.ts +87 -0
- package/dist/temporal-use-case.cjs +169 -16
- package/dist/temporal-use-case.cjs.map +1 -1
- package/dist/temporal-use-case.d.cts +76 -43
- package/dist/temporal-use-case.d.ts +76 -43
- package/dist/temporal-use-case.js +162 -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 +140 -2
- 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 -3
- package/dist/uuid-identifier.js.map +1 -1
- package/dist/uuid.cjs +11 -6
- package/dist/uuid.cjs.map +1 -1
- package/dist/uuid.js +11 -6
- package/dist/uuid.js.map +1 -1
- 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 +24 -8
- package/dist/validation-error.d.ts +24 -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 +12 -2
- package/dist/value-object.cjs.map +1 -1
- package/dist/value-object.d.cts +16 -0
- package/dist/value-object.d.ts +16 -0
- package/dist/value-object.js +13 -3
- 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 +3 -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 +12 -3
- 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 +19 -2
- package/src/core/application/temporal/temporal-use-case.ts +31 -4
- package/src/core/application/use-case.ts +44 -7
- 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 +50 -7
- 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 +5 -3
- package/src/core/domain/value-object.ts +17 -0
- 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 +15 -1
- 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 +99 -325
- 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 -3
- package/src/core/shared/immutable.ts +22 -4
- package/src/core/shared/stable-stringify.ts +91 -4
- package/src/core/shared/uuid.ts +11 -6
- 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 -122
- package/dist/entity.cjs.map +0 -1
- package/dist/entity.js +0 -111
- package/dist/entity.js.map +0 -1
- package/dist/use-case.cjs +0 -92
- package/dist/use-case.cjs.map +0 -1
- package/dist/use-case.js +0 -87
- package/dist/use-case.js.map +0 -1
package/dist/requested-by.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"requested-by.js","names":[],"sources":["../src/core/application/commands/requested-by.ts"],"sourcesContent":["import { ValidationCode } from \"../../exceptions/validation-code.js\";\nimport { InvalidValueException } from \"../../exceptions/validation-exception.js\";\nimport { ValidationField } from \"../../exceptions/validation-field.js\";\nimport { UUID_PATTERN } from \"../../shared/uuid.js\";\n\n// Identifies who triggered a Command.\n// Two valid formats:\n// - Human user: canonical RFC-4122 UUID, versions 1–
|
|
1
|
+
{"version":3,"file":"requested-by.js","names":[],"sources":["../src/core/application/commands/requested-by.ts"],"sourcesContent":["import { ValidationCode } from \"../../exceptions/validation-code.js\";\nimport { InvalidValueException } from \"../../exceptions/validation-exception.js\";\nimport { ValidationField } from \"../../exceptions/validation-field.js\";\nimport { UUID_PATTERN } from \"../../shared/uuid.js\";\n\n// Identifies who triggered a Command.\n// Two valid formats:\n// - Human user: canonical RFC-4122 UUID, versions 1–8 (including the\n// time-ordered v7) — the same pattern `UuidIdentifier` enforces\n// (e.g.: \"550e8400-e29b-41d4-a716-446655440000\"). The constraint is\n// single-sourced in `shared/uuid.ts`, so `UuidIdentifier` and this value\n// always accept the same set of versions.\n// - System identity: \"system:<job>\" where <job> is [a-z][a-z0-9]*(-[a-z0-9]+)*\n// (e.g.: \"system:late-fee-job\", \"system:email-sender\")\n//\n// The split between `fromUser` / `fromSystem` / `parse` exists to make intent\n// explicit at the call-site: whoever builds the value knows where it came from.\n\ntype RequestedByKind = \"user\" | \"system\";\n\nconst REQUESTED_BY_FIELD = ValidationField.of(\"requestedBy\");\n\n// system:<job> where <job> starts with a lowercase letter and may have hyphens between segments\nconst SYSTEM_IDENTITY_PATTERN = /^system:[a-z][a-z0-9]*(?:-[a-z0-9]+)*$/;\n\nclass RequestedBy {\n readonly kind: RequestedByKind;\n readonly raw: string;\n\n private constructor(kind: RequestedByKind, raw: string) {\n this.kind = kind;\n // UUIDs are matched case-insensitively (see `shared/uuid.ts`), so the\n // same user id can arrive lowercased from Postgres and uppercased from a\n // JWT. Canonicalize user ids to lowercase here — the single choke point\n // every factory passes through — so exact-string comparators downstream\n // (`Grant.appliesTo`, `rbacContextFields`) never miss the right identity\n // on case alone. System identities are already lowercase by pattern.\n this.raw = kind === \"user\" ? raw.toLowerCase() : raw;\n Object.freeze(this);\n }\n\n /** Builds from a human user ID (must be a UUID). */\n static fromUser(userId: string): RequestedBy {\n if (!UUID_PATTERN.test(userId)) {\n throw new InvalidValueException(\n REQUESTED_BY_FIELD,\n ValidationCode.INVALID_FORMAT,\n `requestedBy: user identity must be a UUID, got \"${userId}\"`,\n );\n }\n\n return new RequestedBy(\"user\", userId);\n }\n\n /**\n * Builds from a system identity.\n * Only accepts the format \"system:<job>\", where <job> matches [a-z][a-z0-9]*(-[a-z0-9]+)*.\n */\n static fromSystem(systemIdentity: string): RequestedBy {\n if (!SYSTEM_IDENTITY_PATTERN.test(systemIdentity)) {\n throw new InvalidValueException(\n REQUESTED_BY_FIELD,\n ValidationCode.INVALID_FORMAT,\n `requestedBy: system identity must match \"system:<job>\" (e.g. \"system:late-fee-job\"), got \"${systemIdentity}\"`,\n );\n }\n\n return new RequestedBy(\"system\", systemIdentity);\n }\n\n /**\n * Infers the kind from the raw value.\n * Use when the origin (user vs system) is not known at the call-site.\n */\n static parse(raw: string): RequestedBy {\n if (UUID_PATTERN.test(raw)) {\n return new RequestedBy(\"user\", raw);\n }\n\n if (SYSTEM_IDENTITY_PATTERN.test(raw)) {\n return new RequestedBy(\"system\", raw);\n }\n\n throw new InvalidValueException(\n REQUESTED_BY_FIELD,\n ValidationCode.INVALID_FORMAT,\n `requestedBy must be a UUID (user) or \"system:<job>\" (system), got \"${raw}\"`,\n );\n }\n\n get isUser(): boolean {\n return this.kind === \"user\";\n }\n\n get isSystem(): boolean {\n return this.kind === \"system\";\n }\n\n toString(): string {\n return this.raw;\n }\n}\n\nexport type { RequestedByKind };\nexport { RequestedBy };\n"],"mappings":";;;;;AAoBA,MAAM,qBAAqB,gBAAgB,GAAG,aAAa;AAG3D,MAAM,0BAA0B;AAEhC,IAAM,cAAN,MAAM,YAAY;CAId,YAAoB,MAAuB,KAAa;EACpD,KAAK,OAAO;EAOZ,KAAK,MAAM,SAAS,SAAS,IAAI,YAAY,IAAI;EACjD,OAAO,OAAO,IAAI;CACtB;;CAGA,OAAO,SAAS,QAA6B;EACzC,IAAI,CAAC,aAAa,KAAK,MAAM,GACzB,MAAM,IAAI,sBACN,oBACA,eAAe,gBACf,mDAAmD,OAAO,EAC9D;EAGJ,OAAO,IAAI,YAAY,QAAQ,MAAM;CACzC;;;;;CAMA,OAAO,WAAW,gBAAqC;EACnD,IAAI,CAAC,wBAAwB,KAAK,cAAc,GAC5C,MAAM,IAAI,sBACN,oBACA,eAAe,gBACf,6FAA6F,eAAe,EAChH;EAGJ,OAAO,IAAI,YAAY,UAAU,cAAc;CACnD;;;;;CAMA,OAAO,MAAM,KAA0B;EACnC,IAAI,aAAa,KAAK,GAAG,GACrB,OAAO,IAAI,YAAY,QAAQ,GAAG;EAGtC,IAAI,wBAAwB,KAAK,GAAG,GAChC,OAAO,IAAI,YAAY,UAAU,GAAG;EAGxC,MAAM,IAAI,sBACN,oBACA,eAAe,gBACf,sEAAsE,IAAI,EAC9E;CACJ;CAEA,IAAI,SAAkB;EAClB,OAAO,KAAK,SAAS;CACzB;CAEA,IAAI,WAAoB;EACpB,OAAO,KAAK,SAAS;CACzB;CAEA,WAAmB;EACf,OAAO,KAAK;CAChB;AACJ"}
|
package/dist/result.cjs
CHANGED
|
@@ -19,6 +19,9 @@ var Result = class Result {
|
|
|
19
19
|
}
|
|
20
20
|
/**
|
|
21
21
|
* Returns the value when this is success, or null when this is an error.
|
|
22
|
+
*
|
|
23
|
+
* Note: an `Ok(null)` also returns `null`, indistinguishable from an `Err`.
|
|
24
|
+
* Use `match`/`isOk` when that distinction matters.
|
|
22
25
|
*/
|
|
23
26
|
getOrNull() {
|
|
24
27
|
return this.match({
|
|
@@ -42,12 +45,14 @@ var Result = class Result {
|
|
|
42
45
|
return this.match({
|
|
43
46
|
ok: (value) => value,
|
|
44
47
|
err: (error) => {
|
|
45
|
-
throw
|
|
48
|
+
throw toError(error);
|
|
46
49
|
}
|
|
47
50
|
});
|
|
48
51
|
}
|
|
49
52
|
/**
|
|
50
53
|
* Transforms the success value using the provided function. Keeps the error when this is a failure.
|
|
54
|
+
*
|
|
55
|
+
* Exceptions thrown by `transform` propagate; they are not captured into an `Err`.
|
|
51
56
|
*/
|
|
52
57
|
map(transform) {
|
|
53
58
|
return this.match({
|
|
@@ -57,6 +62,8 @@ var Result = class Result {
|
|
|
57
62
|
}
|
|
58
63
|
/**
|
|
59
64
|
* Transforms the error using the provided function. Keeps the value when this is a success.
|
|
65
|
+
*
|
|
66
|
+
* Exceptions thrown by `transform` propagate; they are not captured into an `Err`.
|
|
60
67
|
*/
|
|
61
68
|
mapError(transform) {
|
|
62
69
|
return this.match({
|
|
@@ -66,6 +73,8 @@ var Result = class Result {
|
|
|
66
73
|
}
|
|
67
74
|
/**
|
|
68
75
|
* Chains another operation that returns a Result when this result is a success.
|
|
76
|
+
*
|
|
77
|
+
* Exceptions thrown by `transform` propagate; they are not captured into an `Err`.
|
|
69
78
|
*/
|
|
70
79
|
flatMap(transform) {
|
|
71
80
|
return this.match({
|
|
@@ -76,6 +85,9 @@ var Result = class Result {
|
|
|
76
85
|
};
|
|
77
86
|
/**
|
|
78
87
|
* Success variant of Result.
|
|
88
|
+
*
|
|
89
|
+
* The instance is frozen, but the freeze is shallow: a contained mutable
|
|
90
|
+
* object is not frozen.
|
|
79
91
|
*/
|
|
80
92
|
var Ok = class extends Result {
|
|
81
93
|
constructor(value) {
|
|
@@ -112,6 +124,22 @@ var Err = class extends Result {
|
|
|
112
124
|
return handlers.err(this.error);
|
|
113
125
|
}
|
|
114
126
|
};
|
|
127
|
+
/**
|
|
128
|
+
* Coerces an arbitrary error value into an Error for `getOrThrow`, preserving
|
|
129
|
+
* the original structured payload as `cause` so nothing is lost when `E` is a
|
|
130
|
+
* plain object/DTO rather than an Error.
|
|
131
|
+
*/
|
|
132
|
+
function toError(error) {
|
|
133
|
+
if (error instanceof Error) return error;
|
|
134
|
+
if (typeof error === "string") return new Error(error);
|
|
135
|
+
let message;
|
|
136
|
+
try {
|
|
137
|
+
message = JSON.stringify(error);
|
|
138
|
+
} catch {}
|
|
139
|
+
const wrapped = new Error(message ?? String(error));
|
|
140
|
+
wrapped.cause = error;
|
|
141
|
+
return wrapped;
|
|
142
|
+
}
|
|
115
143
|
//#endregion
|
|
116
144
|
Object.defineProperty(exports, "Err", {
|
|
117
145
|
enumerable: true,
|
package/dist/result.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"result.cjs","names":[],"sources":["../src/core/result/result.ts"],"sourcesContent":["/**\n * Represents the outcome of an operation that may succeed or fail.\n * Useful for avoiding excessive exceptions and making error flows explicit.\n */\nexport abstract class Result<T, E> {\n protected constructor() {\n // prevent direct instantiation\n }\n\n /**\n * Creates a success result containing a value.\n */\n static ok<T>(value: T): Result<T, never> {\n return new Ok(value);\n }\n\n /**\n * Creates an error result containing an error object.\n */\n static err<E>(error: E): Result<never, E> {\n return new Err(error);\n }\n\n /**\n * Returns true when this is a success result.\n */\n abstract isOk(): this is Ok<T>;\n\n /**\n * Returns true when this is an error result.\n */\n abstract isErr(): this is Err<E>;\n\n /**\n * Runs a handler based on the result state (similar to pattern matching).\n */\n abstract match<R>(handlers: {\n ok: (value: T) => R;\n err: (error: E) => R;\n }): R;\n\n /**\n * Returns the value when this is success, or null when this is an error.\n */\n getOrNull(): T | null {\n return this.match({\n ok: (value) => value,\n err: () => null,\n });\n }\n\n /**\n * Returns the error when this is a failure, or null when this is a success.\n */\n errorOrNull(): E | null {\n return this.match({\n ok: () => null,\n err: (error) => error,\n });\n }\n\n /**\n * Returns the value when this is a success, or throws the error when this is a failure.\n */\n getOrThrow(): T {\n return this.match({\n ok: (value) => value,\n err: (error) => {\n throw
|
|
1
|
+
{"version":3,"file":"result.cjs","names":[],"sources":["../src/core/result/result.ts"],"sourcesContent":["/**\n * Represents the outcome of an operation that may succeed or fail.\n * Useful for avoiding excessive exceptions and making error flows explicit.\n */\nexport abstract class Result<T, E> {\n protected constructor() {\n // prevent direct instantiation\n }\n\n /**\n * Creates a success result containing a value.\n */\n static ok<T>(value: T): Result<T, never> {\n return new Ok(value);\n }\n\n /**\n * Creates an error result containing an error object.\n */\n static err<E>(error: E): Result<never, E> {\n return new Err(error);\n }\n\n /**\n * Returns true when this is a success result.\n */\n abstract isOk(): this is Ok<T>;\n\n /**\n * Returns true when this is an error result.\n */\n abstract isErr(): this is Err<E>;\n\n /**\n * Runs a handler based on the result state (similar to pattern matching).\n */\n abstract match<R>(handlers: {\n ok: (value: T) => R;\n err: (error: E) => R;\n }): R;\n\n /**\n * Returns the value when this is success, or null when this is an error.\n *\n * Note: an `Ok(null)` also returns `null`, indistinguishable from an `Err`.\n * Use `match`/`isOk` when that distinction matters.\n */\n getOrNull(): T | null {\n return this.match({\n ok: (value) => value,\n err: () => null,\n });\n }\n\n /**\n * Returns the error when this is a failure, or null when this is a success.\n */\n errorOrNull(): E | null {\n return this.match({\n ok: () => null,\n err: (error) => error,\n });\n }\n\n /**\n * Returns the value when this is a success, or throws the error when this is a failure.\n */\n getOrThrow(): T {\n return this.match({\n ok: (value) => value,\n err: (error) => {\n throw toError(error);\n },\n });\n }\n\n /**\n * Transforms the success value using the provided function. Keeps the error when this is a failure.\n *\n * Exceptions thrown by `transform` propagate; they are not captured into an `Err`.\n */\n map<R>(transform: (value: T) => R): Result<R, E> {\n return this.match<Result<R, E>>({\n ok: (value) => Result.ok(transform(value)),\n // TS does not \"see\" variance here; the cast is intentional and safe:\n // Err carries no T, so it can be widened to Result<R, E> safely.\n err: (error) => Result.err(error) as unknown as Result<R, E>,\n });\n }\n\n /**\n * Transforms the error using the provided function. Keeps the value when this is a success.\n *\n * Exceptions thrown by `transform` propagate; they are not captured into an `Err`.\n */\n mapError<F>(transform: (error: E) => F): Result<T, F> {\n return this.match<Result<T, F>>({\n // Ok carries no E, so it can be widened to Result<T, F>.\n ok: (value) => Result.ok(value) as unknown as Result<T, F>,\n err: (error) => Result.err(transform(error)),\n });\n }\n\n /**\n * Chains another operation that returns a Result when this result is a success.\n *\n * Exceptions thrown by `transform` propagate; they are not captured into an `Err`.\n */\n flatMap<R>(transform: (value: T) => Result<R, E>): Result<R, E> {\n return this.match<Result<R, E>>({\n ok: (value) => transform(value),\n err: (error) => Result.err(error) as unknown as Result<R, E>,\n });\n }\n}\n\n/**\n * Success variant of Result.\n *\n * The instance is frozen, but the freeze is shallow: a contained mutable\n * object is not frozen.\n */\nexport class Ok<T> extends Result<T, never> {\n public readonly value: T;\n\n constructor(value: T) {\n super();\n this.value = value;\n Object.freeze(this);\n }\n\n isOk(): this is Ok<T> {\n return true;\n }\n\n isErr(): this is Err<never> {\n return false;\n }\n\n match<R>(handlers: { ok: (value: T) => R; err: (error: never) => R }): R {\n return handlers.ok(this.value);\n }\n}\n\n/**\n * Error variant of Result.\n */\nexport class Err<E> extends Result<never, E> {\n public readonly error: E;\n\n constructor(error: E) {\n super();\n this.error = error;\n Object.freeze(this);\n }\n\n isOk(): this is Ok<never> {\n return false;\n }\n\n isErr(): this is Err<E> {\n return true;\n }\n\n match<R>(handlers: { ok: (value: never) => R; err: (error: E) => R }): R {\n return handlers.err(this.error);\n }\n}\n\n/**\n * Coerces an arbitrary error value into an Error for `getOrThrow`, preserving\n * the original structured payload as `cause` so nothing is lost when `E` is a\n * plain object/DTO rather than an Error.\n */\nfunction toError(error: unknown): Error {\n if (error instanceof Error) return error;\n if (typeof error === \"string\") return new Error(error);\n let message: string | undefined;\n try {\n message = JSON.stringify(error);\n } catch {\n // circular / BigInt / etc.\n }\n const wrapped = new Error(message ?? String(error));\n // Attach the original payload as `cause` (assigned, not passed to the\n // ES2022 constructor option, to stay within the ES2020 lib target).\n (wrapped as Error & { cause?: unknown }).cause = error;\n return wrapped;\n}\n"],"mappings":";;;;;AAIA,IAAsB,SAAtB,MAAsB,OAAa;CAC/B,cAAwB,CAExB;;;;CAKA,OAAO,GAAM,OAA4B;EACrC,OAAO,IAAI,GAAG,KAAK;CACvB;;;;CAKA,OAAO,IAAO,OAA4B;EACtC,OAAO,IAAI,IAAI,KAAK;CACxB;;;;;;;CA0BA,YAAsB;EAClB,OAAO,KAAK,MAAM;GACd,KAAK,UAAU;GACf,WAAW;EACf,CAAC;CACL;;;;CAKA,cAAwB;EACpB,OAAO,KAAK,MAAM;GACd,UAAU;GACV,MAAM,UAAU;EACpB,CAAC;CACL;;;;CAKA,aAAgB;EACZ,OAAO,KAAK,MAAM;GACd,KAAK,UAAU;GACf,MAAM,UAAU;IACZ,MAAM,QAAQ,KAAK;GACvB;EACJ,CAAC;CACL;;;;;;CAOA,IAAO,WAA0C;EAC7C,OAAO,KAAK,MAAoB;GAC5B,KAAK,UAAU,OAAO,GAAG,UAAU,KAAK,CAAC;GAGzC,MAAM,UAAU,OAAO,IAAI,KAAK;EACpC,CAAC;CACL;;;;;;CAOA,SAAY,WAA0C;EAClD,OAAO,KAAK,MAAoB;GAE5B,KAAK,UAAU,OAAO,GAAG,KAAK;GAC9B,MAAM,UAAU,OAAO,IAAI,UAAU,KAAK,CAAC;EAC/C,CAAC;CACL;;;;;;CAOA,QAAW,WAAqD;EAC5D,OAAO,KAAK,MAAoB;GAC5B,KAAK,UAAU,UAAU,KAAK;GAC9B,MAAM,UAAU,OAAO,IAAI,KAAK;EACpC,CAAC;CACL;AACJ;;;;;;;AAQA,IAAa,KAAb,cAA2B,OAAiB;CAGxC,YAAY,OAAU;EAClB,MAAM;EACN,KAAK,QAAQ;EACb,OAAO,OAAO,IAAI;CACtB;CAEA,OAAsB;EAClB,OAAO;CACX;CAEA,QAA4B;EACxB,OAAO;CACX;CAEA,MAAS,UAAgE;EACrE,OAAO,SAAS,GAAG,KAAK,KAAK;CACjC;AACJ;;;;AAKA,IAAa,MAAb,cAA4B,OAAiB;CAGzC,YAAY,OAAU;EAClB,MAAM;EACN,KAAK,QAAQ;EACb,OAAO,OAAO,IAAI;CACtB;CAEA,OAA0B;EACtB,OAAO;CACX;CAEA,QAAwB;EACpB,OAAO;CACX;CAEA,MAAS,UAAgE;EACrE,OAAO,SAAS,IAAI,KAAK,KAAK;CAClC;AACJ;;;;;;AAOA,SAAS,QAAQ,OAAuB;CACpC,IAAI,iBAAiB,OAAO,OAAO;CACnC,IAAI,OAAO,UAAU,UAAU,OAAO,IAAI,MAAM,KAAK;CACrD,IAAI;CACJ,IAAI;EACA,UAAU,KAAK,UAAU,KAAK;CAClC,QAAQ,CAER;CACA,MAAM,UAAU,IAAI,MAAM,WAAW,OAAO,KAAK,CAAC;CAGlD,QAAyC,QAAQ;CACjD,OAAO;AACX"}
|
package/dist/result.d.cts
CHANGED
|
@@ -30,6 +30,9 @@ declare abstract class Result<T, E> {
|
|
|
30
30
|
}): R;
|
|
31
31
|
/**
|
|
32
32
|
* Returns the value when this is success, or null when this is an error.
|
|
33
|
+
*
|
|
34
|
+
* Note: an `Ok(null)` also returns `null`, indistinguishable from an `Err`.
|
|
35
|
+
* Use `match`/`isOk` when that distinction matters.
|
|
33
36
|
*/
|
|
34
37
|
getOrNull(): T | null;
|
|
35
38
|
/**
|
|
@@ -42,19 +45,28 @@ declare abstract class Result<T, E> {
|
|
|
42
45
|
getOrThrow(): T;
|
|
43
46
|
/**
|
|
44
47
|
* Transforms the success value using the provided function. Keeps the error when this is a failure.
|
|
48
|
+
*
|
|
49
|
+
* Exceptions thrown by `transform` propagate; they are not captured into an `Err`.
|
|
45
50
|
*/
|
|
46
51
|
map<R>(transform: (value: T) => R): Result<R, E>;
|
|
47
52
|
/**
|
|
48
53
|
* Transforms the error using the provided function. Keeps the value when this is a success.
|
|
54
|
+
*
|
|
55
|
+
* Exceptions thrown by `transform` propagate; they are not captured into an `Err`.
|
|
49
56
|
*/
|
|
50
57
|
mapError<F>(transform: (error: E) => F): Result<T, F>;
|
|
51
58
|
/**
|
|
52
59
|
* Chains another operation that returns a Result when this result is a success.
|
|
60
|
+
*
|
|
61
|
+
* Exceptions thrown by `transform` propagate; they are not captured into an `Err`.
|
|
53
62
|
*/
|
|
54
63
|
flatMap<R>(transform: (value: T) => Result<R, E>): Result<R, E>;
|
|
55
64
|
}
|
|
56
65
|
/**
|
|
57
66
|
* Success variant of Result.
|
|
67
|
+
*
|
|
68
|
+
* The instance is frozen, but the freeze is shallow: a contained mutable
|
|
69
|
+
* object is not frozen.
|
|
58
70
|
*/
|
|
59
71
|
declare class Ok<T> extends Result<T, never> {
|
|
60
72
|
readonly value: T;
|
package/dist/result.d.ts
CHANGED
|
@@ -30,6 +30,9 @@ declare abstract class Result<T, E> {
|
|
|
30
30
|
}): R;
|
|
31
31
|
/**
|
|
32
32
|
* Returns the value when this is success, or null when this is an error.
|
|
33
|
+
*
|
|
34
|
+
* Note: an `Ok(null)` also returns `null`, indistinguishable from an `Err`.
|
|
35
|
+
* Use `match`/`isOk` when that distinction matters.
|
|
33
36
|
*/
|
|
34
37
|
getOrNull(): T | null;
|
|
35
38
|
/**
|
|
@@ -42,19 +45,28 @@ declare abstract class Result<T, E> {
|
|
|
42
45
|
getOrThrow(): T;
|
|
43
46
|
/**
|
|
44
47
|
* Transforms the success value using the provided function. Keeps the error when this is a failure.
|
|
48
|
+
*
|
|
49
|
+
* Exceptions thrown by `transform` propagate; they are not captured into an `Err`.
|
|
45
50
|
*/
|
|
46
51
|
map<R>(transform: (value: T) => R): Result<R, E>;
|
|
47
52
|
/**
|
|
48
53
|
* Transforms the error using the provided function. Keeps the value when this is a success.
|
|
54
|
+
*
|
|
55
|
+
* Exceptions thrown by `transform` propagate; they are not captured into an `Err`.
|
|
49
56
|
*/
|
|
50
57
|
mapError<F>(transform: (error: E) => F): Result<T, F>;
|
|
51
58
|
/**
|
|
52
59
|
* Chains another operation that returns a Result when this result is a success.
|
|
60
|
+
*
|
|
61
|
+
* Exceptions thrown by `transform` propagate; they are not captured into an `Err`.
|
|
53
62
|
*/
|
|
54
63
|
flatMap<R>(transform: (value: T) => Result<R, E>): Result<R, E>;
|
|
55
64
|
}
|
|
56
65
|
/**
|
|
57
66
|
* Success variant of Result.
|
|
67
|
+
*
|
|
68
|
+
* The instance is frozen, but the freeze is shallow: a contained mutable
|
|
69
|
+
* object is not frozen.
|
|
58
70
|
*/
|
|
59
71
|
declare class Ok<T> extends Result<T, never> {
|
|
60
72
|
readonly value: T;
|
package/dist/result.js
CHANGED
|
@@ -19,6 +19,9 @@ var Result = class Result {
|
|
|
19
19
|
}
|
|
20
20
|
/**
|
|
21
21
|
* Returns the value when this is success, or null when this is an error.
|
|
22
|
+
*
|
|
23
|
+
* Note: an `Ok(null)` also returns `null`, indistinguishable from an `Err`.
|
|
24
|
+
* Use `match`/`isOk` when that distinction matters.
|
|
22
25
|
*/
|
|
23
26
|
getOrNull() {
|
|
24
27
|
return this.match({
|
|
@@ -42,12 +45,14 @@ var Result = class Result {
|
|
|
42
45
|
return this.match({
|
|
43
46
|
ok: (value) => value,
|
|
44
47
|
err: (error) => {
|
|
45
|
-
throw
|
|
48
|
+
throw toError(error);
|
|
46
49
|
}
|
|
47
50
|
});
|
|
48
51
|
}
|
|
49
52
|
/**
|
|
50
53
|
* Transforms the success value using the provided function. Keeps the error when this is a failure.
|
|
54
|
+
*
|
|
55
|
+
* Exceptions thrown by `transform` propagate; they are not captured into an `Err`.
|
|
51
56
|
*/
|
|
52
57
|
map(transform) {
|
|
53
58
|
return this.match({
|
|
@@ -57,6 +62,8 @@ var Result = class Result {
|
|
|
57
62
|
}
|
|
58
63
|
/**
|
|
59
64
|
* Transforms the error using the provided function. Keeps the value when this is a success.
|
|
65
|
+
*
|
|
66
|
+
* Exceptions thrown by `transform` propagate; they are not captured into an `Err`.
|
|
60
67
|
*/
|
|
61
68
|
mapError(transform) {
|
|
62
69
|
return this.match({
|
|
@@ -66,6 +73,8 @@ var Result = class Result {
|
|
|
66
73
|
}
|
|
67
74
|
/**
|
|
68
75
|
* Chains another operation that returns a Result when this result is a success.
|
|
76
|
+
*
|
|
77
|
+
* Exceptions thrown by `transform` propagate; they are not captured into an `Err`.
|
|
69
78
|
*/
|
|
70
79
|
flatMap(transform) {
|
|
71
80
|
return this.match({
|
|
@@ -76,6 +85,9 @@ var Result = class Result {
|
|
|
76
85
|
};
|
|
77
86
|
/**
|
|
78
87
|
* Success variant of Result.
|
|
88
|
+
*
|
|
89
|
+
* The instance is frozen, but the freeze is shallow: a contained mutable
|
|
90
|
+
* object is not frozen.
|
|
79
91
|
*/
|
|
80
92
|
var Ok = class extends Result {
|
|
81
93
|
constructor(value) {
|
|
@@ -112,6 +124,22 @@ var Err = class extends Result {
|
|
|
112
124
|
return handlers.err(this.error);
|
|
113
125
|
}
|
|
114
126
|
};
|
|
127
|
+
/**
|
|
128
|
+
* Coerces an arbitrary error value into an Error for `getOrThrow`, preserving
|
|
129
|
+
* the original structured payload as `cause` so nothing is lost when `E` is a
|
|
130
|
+
* plain object/DTO rather than an Error.
|
|
131
|
+
*/
|
|
132
|
+
function toError(error) {
|
|
133
|
+
if (error instanceof Error) return error;
|
|
134
|
+
if (typeof error === "string") return new Error(error);
|
|
135
|
+
let message;
|
|
136
|
+
try {
|
|
137
|
+
message = JSON.stringify(error);
|
|
138
|
+
} catch {}
|
|
139
|
+
const wrapped = new Error(message ?? String(error));
|
|
140
|
+
wrapped.cause = error;
|
|
141
|
+
return wrapped;
|
|
142
|
+
}
|
|
115
143
|
//#endregion
|
|
116
144
|
export { Ok as n, Result as r, Err as t };
|
|
117
145
|
|
package/dist/result.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"result.js","names":[],"sources":["../src/core/result/result.ts"],"sourcesContent":["/**\n * Represents the outcome of an operation that may succeed or fail.\n * Useful for avoiding excessive exceptions and making error flows explicit.\n */\nexport abstract class Result<T, E> {\n protected constructor() {\n // prevent direct instantiation\n }\n\n /**\n * Creates a success result containing a value.\n */\n static ok<T>(value: T): Result<T, never> {\n return new Ok(value);\n }\n\n /**\n * Creates an error result containing an error object.\n */\n static err<E>(error: E): Result<never, E> {\n return new Err(error);\n }\n\n /**\n * Returns true when this is a success result.\n */\n abstract isOk(): this is Ok<T>;\n\n /**\n * Returns true when this is an error result.\n */\n abstract isErr(): this is Err<E>;\n\n /**\n * Runs a handler based on the result state (similar to pattern matching).\n */\n abstract match<R>(handlers: {\n ok: (value: T) => R;\n err: (error: E) => R;\n }): R;\n\n /**\n * Returns the value when this is success, or null when this is an error.\n */\n getOrNull(): T | null {\n return this.match({\n ok: (value) => value,\n err: () => null,\n });\n }\n\n /**\n * Returns the error when this is a failure, or null when this is a success.\n */\n errorOrNull(): E | null {\n return this.match({\n ok: () => null,\n err: (error) => error,\n });\n }\n\n /**\n * Returns the value when this is a success, or throws the error when this is a failure.\n */\n getOrThrow(): T {\n return this.match({\n ok: (value) => value,\n err: (error) => {\n throw
|
|
1
|
+
{"version":3,"file":"result.js","names":[],"sources":["../src/core/result/result.ts"],"sourcesContent":["/**\n * Represents the outcome of an operation that may succeed or fail.\n * Useful for avoiding excessive exceptions and making error flows explicit.\n */\nexport abstract class Result<T, E> {\n protected constructor() {\n // prevent direct instantiation\n }\n\n /**\n * Creates a success result containing a value.\n */\n static ok<T>(value: T): Result<T, never> {\n return new Ok(value);\n }\n\n /**\n * Creates an error result containing an error object.\n */\n static err<E>(error: E): Result<never, E> {\n return new Err(error);\n }\n\n /**\n * Returns true when this is a success result.\n */\n abstract isOk(): this is Ok<T>;\n\n /**\n * Returns true when this is an error result.\n */\n abstract isErr(): this is Err<E>;\n\n /**\n * Runs a handler based on the result state (similar to pattern matching).\n */\n abstract match<R>(handlers: {\n ok: (value: T) => R;\n err: (error: E) => R;\n }): R;\n\n /**\n * Returns the value when this is success, or null when this is an error.\n *\n * Note: an `Ok(null)` also returns `null`, indistinguishable from an `Err`.\n * Use `match`/`isOk` when that distinction matters.\n */\n getOrNull(): T | null {\n return this.match({\n ok: (value) => value,\n err: () => null,\n });\n }\n\n /**\n * Returns the error when this is a failure, or null when this is a success.\n */\n errorOrNull(): E | null {\n return this.match({\n ok: () => null,\n err: (error) => error,\n });\n }\n\n /**\n * Returns the value when this is a success, or throws the error when this is a failure.\n */\n getOrThrow(): T {\n return this.match({\n ok: (value) => value,\n err: (error) => {\n throw toError(error);\n },\n });\n }\n\n /**\n * Transforms the success value using the provided function. Keeps the error when this is a failure.\n *\n * Exceptions thrown by `transform` propagate; they are not captured into an `Err`.\n */\n map<R>(transform: (value: T) => R): Result<R, E> {\n return this.match<Result<R, E>>({\n ok: (value) => Result.ok(transform(value)),\n // TS does not \"see\" variance here; the cast is intentional and safe:\n // Err carries no T, so it can be widened to Result<R, E> safely.\n err: (error) => Result.err(error) as unknown as Result<R, E>,\n });\n }\n\n /**\n * Transforms the error using the provided function. Keeps the value when this is a success.\n *\n * Exceptions thrown by `transform` propagate; they are not captured into an `Err`.\n */\n mapError<F>(transform: (error: E) => F): Result<T, F> {\n return this.match<Result<T, F>>({\n // Ok carries no E, so it can be widened to Result<T, F>.\n ok: (value) => Result.ok(value) as unknown as Result<T, F>,\n err: (error) => Result.err(transform(error)),\n });\n }\n\n /**\n * Chains another operation that returns a Result when this result is a success.\n *\n * Exceptions thrown by `transform` propagate; they are not captured into an `Err`.\n */\n flatMap<R>(transform: (value: T) => Result<R, E>): Result<R, E> {\n return this.match<Result<R, E>>({\n ok: (value) => transform(value),\n err: (error) => Result.err(error) as unknown as Result<R, E>,\n });\n }\n}\n\n/**\n * Success variant of Result.\n *\n * The instance is frozen, but the freeze is shallow: a contained mutable\n * object is not frozen.\n */\nexport class Ok<T> extends Result<T, never> {\n public readonly value: T;\n\n constructor(value: T) {\n super();\n this.value = value;\n Object.freeze(this);\n }\n\n isOk(): this is Ok<T> {\n return true;\n }\n\n isErr(): this is Err<never> {\n return false;\n }\n\n match<R>(handlers: { ok: (value: T) => R; err: (error: never) => R }): R {\n return handlers.ok(this.value);\n }\n}\n\n/**\n * Error variant of Result.\n */\nexport class Err<E> extends Result<never, E> {\n public readonly error: E;\n\n constructor(error: E) {\n super();\n this.error = error;\n Object.freeze(this);\n }\n\n isOk(): this is Ok<never> {\n return false;\n }\n\n isErr(): this is Err<E> {\n return true;\n }\n\n match<R>(handlers: { ok: (value: never) => R; err: (error: E) => R }): R {\n return handlers.err(this.error);\n }\n}\n\n/**\n * Coerces an arbitrary error value into an Error for `getOrThrow`, preserving\n * the original structured payload as `cause` so nothing is lost when `E` is a\n * plain object/DTO rather than an Error.\n */\nfunction toError(error: unknown): Error {\n if (error instanceof Error) return error;\n if (typeof error === \"string\") return new Error(error);\n let message: string | undefined;\n try {\n message = JSON.stringify(error);\n } catch {\n // circular / BigInt / etc.\n }\n const wrapped = new Error(message ?? String(error));\n // Attach the original payload as `cause` (assigned, not passed to the\n // ES2022 constructor option, to stay within the ES2020 lib target).\n (wrapped as Error & { cause?: unknown }).cause = error;\n return wrapped;\n}\n"],"mappings":";;;;;AAIA,IAAsB,SAAtB,MAAsB,OAAa;CAC/B,cAAwB,CAExB;;;;CAKA,OAAO,GAAM,OAA4B;EACrC,OAAO,IAAI,GAAG,KAAK;CACvB;;;;CAKA,OAAO,IAAO,OAA4B;EACtC,OAAO,IAAI,IAAI,KAAK;CACxB;;;;;;;CA0BA,YAAsB;EAClB,OAAO,KAAK,MAAM;GACd,KAAK,UAAU;GACf,WAAW;EACf,CAAC;CACL;;;;CAKA,cAAwB;EACpB,OAAO,KAAK,MAAM;GACd,UAAU;GACV,MAAM,UAAU;EACpB,CAAC;CACL;;;;CAKA,aAAgB;EACZ,OAAO,KAAK,MAAM;GACd,KAAK,UAAU;GACf,MAAM,UAAU;IACZ,MAAM,QAAQ,KAAK;GACvB;EACJ,CAAC;CACL;;;;;;CAOA,IAAO,WAA0C;EAC7C,OAAO,KAAK,MAAoB;GAC5B,KAAK,UAAU,OAAO,GAAG,UAAU,KAAK,CAAC;GAGzC,MAAM,UAAU,OAAO,IAAI,KAAK;EACpC,CAAC;CACL;;;;;;CAOA,SAAY,WAA0C;EAClD,OAAO,KAAK,MAAoB;GAE5B,KAAK,UAAU,OAAO,GAAG,KAAK;GAC9B,MAAM,UAAU,OAAO,IAAI,UAAU,KAAK,CAAC;EAC/C,CAAC;CACL;;;;;;CAOA,QAAW,WAAqD;EAC5D,OAAO,KAAK,MAAoB;GAC5B,KAAK,UAAU,UAAU,KAAK;GAC9B,MAAM,UAAU,OAAO,IAAI,KAAK;EACpC,CAAC;CACL;AACJ;;;;;;;AAQA,IAAa,KAAb,cAA2B,OAAiB;CAGxC,YAAY,OAAU;EAClB,MAAM;EACN,KAAK,QAAQ;EACb,OAAO,OAAO,IAAI;CACtB;CAEA,OAAsB;EAClB,OAAO;CACX;CAEA,QAA4B;EACxB,OAAO;CACX;CAEA,MAAS,UAAgE;EACrE,OAAO,SAAS,GAAG,KAAK,KAAK;CACjC;AACJ;;;;AAKA,IAAa,MAAb,cAA4B,OAAiB;CAGzC,YAAY,OAAU;EAClB,MAAM;EACN,KAAK,QAAQ;EACb,OAAO,OAAO,IAAI;CACtB;CAEA,OAA0B;EACtB,OAAO;CACX;CAEA,QAAwB;EACpB,OAAO;CACX;CAEA,MAAS,UAAgE;EACrE,OAAO,SAAS,IAAI,KAAK,KAAK;CAClC;AACJ;;;;;;AAOA,SAAS,QAAQ,OAAuB;CACpC,IAAI,iBAAiB,OAAO,OAAO;CACnC,IAAI,OAAO,UAAU,UAAU,OAAO,IAAI,MAAM,KAAK;CACrD,IAAI;CACJ,IAAI;EACA,UAAU,KAAK,UAAU,KAAK;CAClC,QAAQ,CAER;CACA,MAAM,UAAU,IAAI,MAAM,WAAW,OAAO,KAAK,CAAC;CAGlD,QAAyC,QAAQ;CACjD,OAAO;AACX"}
|
package/dist/rule.cjs
CHANGED
|
@@ -97,46 +97,73 @@ const ENGINE_VERSION = 1;
|
|
|
97
97
|
* "errors as values" contract of `RbacAuthorizer`/`GateEngineV1`.
|
|
98
98
|
*
|
|
99
99
|
* A denial maps to {@link AuthorizationError.policyDenied}, attributed to the
|
|
100
|
-
* deciding rule's `id`/`version
|
|
100
|
+
* deciding rule's `id`/`version`. A condition-evaluation failure (a missing
|
|
101
101
|
* attribute, a wrong-typed operand) fails closed as
|
|
102
|
-
* {@link AuthorizationError.forbidden}
|
|
103
|
-
*
|
|
102
|
+
* {@link AuthorizationError.forbidden} — also attributed to the culprit rule's
|
|
103
|
+
* `id`/`version` — unless the set opts into `onEvaluationError: "skip-rule"`, in
|
|
104
|
+
* which case the unevaluable rule is skipped and the rest decide. Every resolved
|
|
105
|
+
* decision (PERMIT and DENY) is emitted best-effort through the configured
|
|
106
|
+
* reporter as an `abac-decision` event (silent by default). Timestamps come from
|
|
107
|
+
* an injectable `clock` (default `() => new Date()`), so the decisor is pure
|
|
108
|
+
* given one. Loading the dynamic attributes is the consumer's job (behind an
|
|
109
|
+
* `AbacAuthorizerPort` adapter); this class only decides.
|
|
104
110
|
*/
|
|
105
111
|
var AbacAuthorizer = class {
|
|
106
112
|
constructor(params = {}) {
|
|
107
113
|
this.coreConfig = params.coreConfig ?? require_condition_evaluator.coreConfig;
|
|
114
|
+
this.clock = params.clock ?? (() => /* @__PURE__ */ new Date());
|
|
108
115
|
}
|
|
109
116
|
authorize(request, policies) {
|
|
110
117
|
const evaluator = new require_condition_evaluator.ConditionEvaluatorV1(abacContext(request), this.coreConfig.getConditionEvaluationOptions(ENGINE_VERSION));
|
|
111
118
|
const applicable = [];
|
|
112
119
|
for (const rule of policies.rules) {
|
|
113
120
|
const matched = evaluator.evaluate(rule.condition);
|
|
114
|
-
if (matched.isErr())
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
121
|
+
if (matched.isErr()) {
|
|
122
|
+
if (policies.onEvaluationError === "skip-rule") continue;
|
|
123
|
+
return require_result.Result.err(require_authorization_error.AuthorizationError.forbidden({
|
|
124
|
+
action: request.action,
|
|
125
|
+
resource: request.resource,
|
|
126
|
+
actor: { actorId: request.actor.raw },
|
|
127
|
+
policyId: rule.id,
|
|
128
|
+
policyVersion: rule.version,
|
|
129
|
+
details: matched.errorOrNull() ?? void 0
|
|
130
|
+
}));
|
|
131
|
+
}
|
|
120
132
|
if (matched.getOrThrow()) applicable.push(rule);
|
|
121
133
|
}
|
|
122
134
|
const { effect, decidingRule } = combine(policies.algorithm, applicable, policies.defaultEffect);
|
|
135
|
+
this.reportDecision(request, policies, effect, decidingRule);
|
|
123
136
|
if (effect === "PERMIT") return require_result.Result.ok({
|
|
124
137
|
action: request.action,
|
|
125
138
|
effect: "PERMIT",
|
|
126
139
|
matchedRule: decidingRule?.id ?? "<default>",
|
|
127
140
|
algorithm: policies.algorithm,
|
|
128
|
-
grantedAtIso: (
|
|
141
|
+
grantedAtIso: this.clock().toISOString()
|
|
129
142
|
});
|
|
130
143
|
return require_result.Result.err(require_authorization_error.AuthorizationError.policyDenied({
|
|
131
144
|
policyId: decidingRule?.id ?? "<default-deny>",
|
|
132
145
|
policyVersion: decidingRule?.version ?? 0,
|
|
133
|
-
evaluatedAtIso: (
|
|
146
|
+
evaluatedAtIso: this.clock().toISOString(),
|
|
134
147
|
action: request.action,
|
|
135
148
|
resource: request.resource,
|
|
136
|
-
actor: {
|
|
149
|
+
actor: { actorId: request.actor.raw },
|
|
137
150
|
reasonCode: decidingRule?.id
|
|
138
151
|
}));
|
|
139
152
|
}
|
|
153
|
+
reportDecision(request, policies, effect, decidingRule) {
|
|
154
|
+
this.coreConfig.reportSafely({
|
|
155
|
+
kind: "abac-decision",
|
|
156
|
+
level: "info",
|
|
157
|
+
action: request.action,
|
|
158
|
+
resource: request.resource,
|
|
159
|
+
actorId: request.actor.raw,
|
|
160
|
+
effect,
|
|
161
|
+
decidingRuleId: decidingRule?.id ?? (effect === "PERMIT" ? "<default>" : "<default-deny>"),
|
|
162
|
+
decidingRuleVersion: decidingRule?.version,
|
|
163
|
+
algorithm: policies.algorithm,
|
|
164
|
+
occurredAt: this.clock()
|
|
165
|
+
});
|
|
166
|
+
}
|
|
140
167
|
};
|
|
141
168
|
//#endregion
|
|
142
169
|
//#region src/core/abac/composite-authorizer.ts
|
|
@@ -163,26 +190,43 @@ var CompositeAuthorizer = class {
|
|
|
163
190
|
};
|
|
164
191
|
//#endregion
|
|
165
192
|
//#region src/core/abac/domain/policy-set.ts
|
|
193
|
+
const POLICY_SET_FIELD = require_validation_field.ValidationField.of("abacPolicySet");
|
|
166
194
|
/**
|
|
167
195
|
* An ordered collection of {@link AbacRule}s plus how to combine them
|
|
168
|
-
* ({@link CombiningAlgorithm})
|
|
169
|
-
* ({@link defaultEffect})
|
|
196
|
+
* ({@link CombiningAlgorithm}), what to decide when no rule applies
|
|
197
|
+
* ({@link defaultEffect}), and how to react to an unevaluable rule
|
|
198
|
+
* ({@link onEvaluationError}). This is where ABAC goes beyond a single gate
|
|
170
199
|
* condition: several PERMIT/DENY rules resolved by an explicit algorithm.
|
|
171
200
|
*
|
|
172
|
-
* Closed by default — the combining algorithm defaults to `"deny-overrides"
|
|
173
|
-
* the default effect to `"DENY"`,
|
|
174
|
-
*
|
|
201
|
+
* Closed by default — the combining algorithm defaults to `"deny-overrides"`,
|
|
202
|
+
* the default effect to `"DENY"`, and evaluation errors to `"fail-closed"`, so a
|
|
203
|
+
* request matching nothing (or hitting a technical error) is denied. A plain
|
|
204
|
+
* holder (not a value object); build through {@link of}.
|
|
175
205
|
*/
|
|
176
206
|
var AbacPolicySet = class AbacPolicySet {
|
|
177
|
-
constructor(_rules, _algorithm, _defaultEffect) {
|
|
207
|
+
constructor(_rules, _algorithm, _defaultEffect, _onEvaluationError) {
|
|
178
208
|
this._rules = _rules;
|
|
179
209
|
this._algorithm = _algorithm;
|
|
180
210
|
this._defaultEffect = _defaultEffect;
|
|
211
|
+
this._onEvaluationError = _onEvaluationError;
|
|
181
212
|
Object.freeze(this._rules);
|
|
182
213
|
Object.freeze(this);
|
|
183
214
|
}
|
|
215
|
+
/**
|
|
216
|
+
* Builds a policy set. Rejects duplicate rule ids with
|
|
217
|
+
* {@link InvalidValueException} so a denial's `policyId` attribution is
|
|
218
|
+
* unambiguous.
|
|
219
|
+
*/
|
|
184
220
|
static of(rules, options = {}) {
|
|
185
|
-
|
|
221
|
+
AbacPolicySet.assertUniqueIds(rules);
|
|
222
|
+
return new AbacPolicySet([...rules], options.algorithm ?? "deny-overrides", options.defaultEffect ?? "DENY", options.onEvaluationError ?? "fail-closed");
|
|
223
|
+
}
|
|
224
|
+
static assertUniqueIds(rules) {
|
|
225
|
+
const seen = /* @__PURE__ */ new Set();
|
|
226
|
+
for (const rule of rules) {
|
|
227
|
+
if (seen.has(rule.id)) throw new require_validation_exception.InvalidValueException(POLICY_SET_FIELD.nested("rules"), require_validation_code.ValidationCode.ALREADY_EXISTS, `abac policy set has duplicate rule id "${rule.id}"; ids must be unique for unambiguous audit attribution`);
|
|
228
|
+
seen.add(rule.id);
|
|
229
|
+
}
|
|
186
230
|
}
|
|
187
231
|
get rules() {
|
|
188
232
|
return this._rules;
|
|
@@ -193,11 +237,13 @@ var AbacPolicySet = class AbacPolicySet {
|
|
|
193
237
|
get defaultEffect() {
|
|
194
238
|
return this._defaultEffect;
|
|
195
239
|
}
|
|
240
|
+
get onEvaluationError() {
|
|
241
|
+
return this._onEvaluationError;
|
|
242
|
+
}
|
|
196
243
|
};
|
|
197
244
|
//#endregion
|
|
198
|
-
//#region src/core/
|
|
199
|
-
const
|
|
200
|
-
const CONDITION_OPS = new Set([
|
|
245
|
+
//#region src/core/policies/engines/v1/condition-types.ts
|
|
246
|
+
const CONDITION_OPS = [
|
|
201
247
|
"eq",
|
|
202
248
|
"neq",
|
|
203
249
|
"gt",
|
|
@@ -208,7 +254,11 @@ const CONDITION_OPS = new Set([
|
|
|
208
254
|
"notIn",
|
|
209
255
|
"isNull",
|
|
210
256
|
"isNotNull"
|
|
211
|
-
]
|
|
257
|
+
];
|
|
258
|
+
//#endregion
|
|
259
|
+
//#region src/core/abac/domain/rule.ts
|
|
260
|
+
const RULE_FIELD = require_validation_field.ValidationField.of("abacRule");
|
|
261
|
+
const SUPPORTED_OPS = new Set(CONDITION_OPS);
|
|
212
262
|
const RULE_EFFECTS = new Set(["PERMIT", "DENY"]);
|
|
213
263
|
function isPlainObject(value) {
|
|
214
264
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
@@ -225,6 +275,26 @@ function isPlainObject(value) {
|
|
|
225
275
|
* authored in TypeScript — trusted data, not untrusted JSON — the structural
|
|
226
276
|
* check is a hand-rolled walk of the condition tree rather than a schema parse.
|
|
227
277
|
* Build one through {@link of}; the constructor is private.
|
|
278
|
+
*
|
|
279
|
+
* **Attribute semantics a rule author must know (the condition is evaluated by
|
|
280
|
+
* the shared gate/compute engine, whose runtime rules apply here too):**
|
|
281
|
+
* - **A referenced attribute that is *absent* from the request is a technical
|
|
282
|
+
* evaluation error, not a non-match.** By default (`onEvaluationError:
|
|
283
|
+
* "fail-closed"` on the {@link AbacPolicySet}) that error fails the *entire*
|
|
284
|
+
* decision closed — so adding a rule that reads an attribute a given call site
|
|
285
|
+
* does not yet populate makes that call site start returning `forbidden`,
|
|
286
|
+
* *even for requests the older rules used to permit*. Choose
|
|
287
|
+
* `onEvaluationError: "skip-rule"` on the set to skip an unevaluable rule
|
|
288
|
+
* instead of failing the whole set.
|
|
289
|
+
* - **`isNull` / `isNotNull` test for an explicit `null`/`undefined` *value*, not
|
|
290
|
+
* for a missing key.** A key that is absent from the bag never reaches the
|
|
291
|
+
* operator — it short-circuits to the missing-attribute error above. To match
|
|
292
|
+
* "no deletedAt", the consumer must put `deletedAt: null` in the bag, not omit
|
|
293
|
+
* it. Pass `allowNull: true` on a relational leaf to treat a present `null` as
|
|
294
|
+
* a non-match instead of an error.
|
|
295
|
+
* - **Date comparison operands must be strict ISO-8601 UTC**
|
|
296
|
+
* (`YYYY-MM-DDTHH:mm:ss.sssZ`); a bare `"2026-07-09"` or an offset like
|
|
297
|
+
* `+00:00` is rejected as an invalid operand and fails closed.
|
|
228
298
|
*/
|
|
229
299
|
var AbacRule = class AbacRule extends require_value_object.ValueObject {
|
|
230
300
|
constructor(props) {
|
|
@@ -258,7 +328,7 @@ var AbacRule = class AbacRule extends require_value_object.ValueObject {
|
|
|
258
328
|
}
|
|
259
329
|
if ("field" in node && "op" in node) {
|
|
260
330
|
if (typeof node.field !== "string" || node.field.trim().length === 0) AbacRule.rejectCondition(`abac rule condition leaf "field" must be a non-empty string, got "${String(node.field)}"`);
|
|
261
|
-
if (typeof node.op !== "string" || !
|
|
331
|
+
if (typeof node.op !== "string" || !SUPPORTED_OPS.has(node.op)) AbacRule.rejectCondition(`abac rule condition leaf "op" is not a supported operator, got "${String(node.op)}"`);
|
|
262
332
|
return;
|
|
263
333
|
}
|
|
264
334
|
AbacRule.rejectCondition("abac rule condition must be a leaf { field, op } or an { and | or | not } node");
|