@cullet/erp-core 1.0.2 → 1.0.4
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/README.md +24 -24
- package/dist/gate-engine-registry.js.map +1 -1
- package/dist/gate-v1-payload.schema.js +5 -0
- package/dist/gate-v1-payload.schema.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/outcome.js.map +1 -1
- package/dist/policies/engines/v1/gate/index.d.ts +1 -0
- package/dist/policies/engines/v1/gate/index.js.map +1 -1
- package/dist/policy-service.js.map +1 -1
- package/dist/validation-code.js.map +1 -1
- package/dist/validation-error.js.map +1 -1
- package/dist/validation-field.js.map +1 -1
- package/meta.json +67 -67
- package/package.json +56 -56
- package/src/core/application/commands/command.ts +3 -3
- package/src/core/application/commands/requested-by.ts +60 -60
- package/src/core/application/index.ts +21 -21
- package/src/core/application/policy-error-mapper.ts +54 -54
- package/src/core/application/ports/index.ts +6 -6
- package/src/core/application/ports/logger.port.ts +4 -4
- package/src/core/application/ports/metrics.port.ts +3 -3
- package/src/core/application/ports/policy-port.ts +7 -7
- package/src/core/application/ports/repository.port.ts +3 -3
- package/src/core/application/ports/temporal-repository.port.ts +4 -4
- package/src/core/application/ports/tracer.port.ts +7 -7
- package/src/core/application/queries/query.ts +13 -13
- package/src/core/application/temporal/index.ts +7 -7
- package/src/core/application/temporal/temporal-context.ts +20 -20
- package/src/core/application/temporal/temporal-use-case.ts +22 -22
- package/src/core/application/use-case.ts +10 -10
- package/src/core/config/core-config.instance.ts +1 -1
- package/src/core/config/core-config.ts +59 -59
- package/src/core/config/index.ts +6 -6
- package/src/core/config/policy-reporter.ts +31 -31
- package/src/core/config/silent-policy-reporter.ts +1 -1
- package/src/core/domain/entity.ts +44 -44
- package/src/core/domain/rulesets/entity-ruleset.contracts.ts +6 -6
- package/src/core/domain/rulesets/ruleset-registry.ts +49 -49
- package/src/core/domain/rulesets/ruleset.contracts.ts +6 -6
- package/src/core/domain/rulesets/value-object-ruleset.contracts.ts +5 -5
- package/src/core/domain/temporal/index.ts +11 -11
- package/src/core/domain/temporal/temporal-range.ts +16 -16
- package/src/core/domain/temporal/temporal-snapshot.ts +24 -24
- package/src/core/domain/temporal/transaction-time.ts +30 -30
- package/src/core/domain/temporal/valid-time.ts +29 -29
- package/src/core/domain/value-object.ts +16 -16
- package/src/core/errors/app-error.ts +64 -64
- package/src/core/errors/authentication-error.ts +134 -134
- package/src/core/errors/authorization-error.ts +154 -154
- package/src/core/errors/business-rule-violation-error.ts +15 -15
- package/src/core/errors/conflict-error.ts +202 -201
- package/src/core/errors/error-codes.ts +48 -48
- package/src/core/errors/idempotency-error.ts +324 -320
- package/src/core/errors/index.ts +50 -50
- package/src/core/errors/integration-error.ts +120 -120
- package/src/core/errors/legacy-incompatible-error.ts +13 -13
- package/src/core/errors/not-found-error.ts +14 -14
- package/src/core/errors/serialization-error.ts +167 -167
- package/src/core/errors/temporal-error.ts +109 -107
- package/src/core/errors/types.ts +35 -35
- package/src/core/errors/unexpected-error.ts +10 -10
- package/src/core/errors/utils/index.ts +2 -2
- package/src/core/errors/utils/json-safe.ts +53 -53
- package/src/core/errors/validation-error.ts +18 -18
- package/src/core/exceptions/business-rule-violation-exception.ts +6 -6
- package/src/core/exceptions/domain-exception.ts +5 -5
- package/src/core/exceptions/entity-not-found-exception.ts +6 -6
- package/src/core/exceptions/invalid-state-transition-exception.ts +8 -8
- package/src/core/exceptions/invariant-violation-exception.ts +3 -3
- package/src/core/exceptions/validation-code.ts +35 -29
- package/src/core/exceptions/validation-exception.ts +19 -19
- package/src/core/exceptions/validation-field.ts +20 -19
- package/src/core/index.ts +7 -7
- package/src/core/policies/asof/asof.ts +47 -47
- package/src/core/policies/catalog/catalog.instance.ts +8 -6
- package/src/core/policies/catalog/index.ts +4 -4
- package/src/core/policies/catalog/policy-catalog-entry.ts +199 -197
- package/src/core/policies/catalog/policy-catalog.ts +101 -101
- package/src/core/policies/catalog/policy-key.ts +51 -51
- package/src/core/policies/context/context-builder.ts +335 -313
- package/src/core/policies/context/context-registry.instance.ts +9 -9
- package/src/core/policies/context/context-registry.ts +61 -61
- package/src/core/policies/context/context-resolver.ts +12 -12
- package/src/core/policies/context/context-seed.ts +19 -15
- package/src/core/policies/context/index.ts +9 -9
- package/src/core/policies/context/path.ts +135 -128
- package/src/core/policies/defs/in-memory-policy-definition-repo.ts +56 -55
- package/src/core/policies/defs/index.ts +10 -10
- package/src/core/policies/defs/policy-definition-repository.ts +3 -3
- package/src/core/policies/defs/policy-definition.ts +143 -142
- package/src/core/policies/defs/policy-scope.ts +21 -19
- package/src/core/policies/defs/policy-semver.ts +51 -51
- package/src/core/policies/engines/compute-engine-registry.ts +43 -43
- package/src/core/policies/engines/compute-evaluator-registry.ts +20 -20
- package/src/core/policies/engines/compute-registry.ts +45 -45
- package/src/core/policies/engines/compute-types.ts +15 -15
- package/src/core/policies/engines/condition-evaluator-reporter.ts +17 -17
- package/src/core/policies/engines/gate-engine-registry.ts +27 -25
- package/src/core/policies/engines/gate-types.ts +28 -28
- package/src/core/policies/engines/index.ts +28 -28
- package/src/core/policies/engines/parse-compute-payload.ts +51 -45
- package/src/core/policies/engines/parse-gate-payload.ts +42 -42
- package/src/core/policies/engines/v1/compute/compute-engine-v1.ts +39 -37
- package/src/core/policies/engines/v1/compute/compute-payload.schema.ts +43 -41
- package/src/core/policies/engines/v1/compute/index.ts +6 -6
- package/src/core/policies/engines/v1/compute/resolve-decision-table-v1.ts +41 -39
- package/src/core/policies/engines/v1/condition-evaluator.ts +557 -504
- package/src/core/policies/engines/v1/condition-schema.ts +35 -35
- package/src/core/policies/engines/v1/condition-types.ts +21 -21
- package/src/core/policies/engines/v1/gate/gate-engine-v1.ts +134 -130
- package/src/core/policies/engines/v1/gate/gate-types-v1.ts +5 -5
- package/src/core/policies/engines/v1/gate/gate-v1-payload.schema.ts +32 -30
- package/src/core/policies/engines/v1/gate/index.ts +4 -4
- package/src/core/policies/engines/v1/index.ts +8 -8
- package/src/core/policies/index.ts +72 -72
- package/src/core/policies/package/policy-package.ts +3 -3
- package/src/core/policies/policy-ids.ts +25 -25
- package/src/core/policies/resolver/policy-resolver.ts +34 -34
- package/src/core/policies/service/index.ts +6 -6
- package/src/core/policies/service/policy-evaluation-error.ts +141 -133
- package/src/core/policies/service/policy-service.ts +432 -419
- package/src/core/policies/utils/date.ts +3 -3
- package/src/core/policies/utils/hash.ts +61 -61
- package/src/core/result/outcome.ts +131 -126
- package/src/core/result/result.ts +130 -127
- package/src/core/shared/aggregate-version.ts +5 -5
- package/src/core/shared/hashing.ts +18 -18
- package/src/core/shared/immutable.ts +29 -29
- package/src/core/shared/temporal-guards.ts +9 -9
- package/src/core/versioning/domain-event-contracts.ts +40 -40
- package/src/core/versioning/version.ts +19 -19
- package/src/examples/rulesets/entity/order-creation-rules-v1.ts +18 -18
- package/src/examples/rulesets/entity/order-invariants-v1.ts +31 -29
- package/src/examples/rulesets/entity/order-invariants-v2.ts +12 -12
- package/src/examples/rulesets/entity/order.ts +127 -127
- package/src/examples/rulesets/value-object/cpf-rules-v1.ts +41 -39
- package/src/examples/rulesets/value-object/cpf-rules-v2.ts +14 -12
- package/src/examples/rulesets/value-object/cpf.ts +24 -24
- package/src/examples/rulesets/value-object/customer.ts +42 -39
- package/src/examples/rulesets/value-object/person-name-rules-v1.ts +27 -27
- package/src/examples/rulesets/value-object/person-name-rules-v2.ts +13 -13
- package/src/examples/rulesets/value-object/person-name.ts +29 -26
- package/src/index.ts +2 -2
- package/src/version.ts +1 -1
|
@@ -1,60 +1,60 @@
|
|
|
1
1
|
// Semver without build metadata: comparisons are defined only over
|
|
2
2
|
// major.minor.patch and optional pre-release identifiers (semver §11).
|
|
3
3
|
export const POLICY_SEMVER_PATTERN =
|
|
4
|
-
|
|
4
|
+
/^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[A-Za-z-][0-9A-Za-z-]*)(?:\.(?:0|[1-9]\d*|\d*[A-Za-z-][0-9A-Za-z-]*))*))?(?:\+([0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*))?$/;
|
|
5
5
|
|
|
6
6
|
interface ParsedSemver {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
7
|
+
major: number;
|
|
8
|
+
minor: number;
|
|
9
|
+
patch: number;
|
|
10
|
+
preRelease: string | null;
|
|
11
11
|
}
|
|
12
12
|
|
|
13
13
|
function parseSemver(version: string): ParsedSemver {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
14
|
+
const withoutBuild = version.split("+")[0];
|
|
15
|
+
const [core, preRelease = null] = withoutBuild.split("-") as [
|
|
16
|
+
string,
|
|
17
|
+
string | undefined,
|
|
18
|
+
];
|
|
19
|
+
const [major, minor, patch] = core.split(".").map(Number) as [
|
|
20
|
+
number,
|
|
21
|
+
number,
|
|
22
|
+
number,
|
|
23
|
+
];
|
|
24
|
+
return { major, minor, patch, preRelease: preRelease ?? null };
|
|
25
25
|
}
|
|
26
26
|
|
|
27
27
|
function comparePreRelease(a: string, b: string): number {
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
28
|
+
const aIds = a.split(".");
|
|
29
|
+
const bIds = b.split(".");
|
|
30
|
+
const len = Math.max(aIds.length, bIds.length);
|
|
31
31
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
32
|
+
for (let i = 0; i < len; i++) {
|
|
33
|
+
const ai = aIds[i];
|
|
34
|
+
const bi = bIds[i];
|
|
35
35
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
36
|
+
// Shorter pre-release has lower precedence when prefix matches (semver §11.4.4)
|
|
37
|
+
if (ai === undefined) return -1;
|
|
38
|
+
if (bi === undefined) return 1;
|
|
39
39
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
40
|
+
const aNum = Number(ai);
|
|
41
|
+
const bNum = Number(bi);
|
|
42
|
+
const aIsNum = !Number.isNaN(aNum);
|
|
43
|
+
const bIsNum = !Number.isNaN(bNum);
|
|
44
44
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
45
|
+
// Numeric identifiers always have lower precedence than alphanumeric (semver §11.4.1)
|
|
46
|
+
if (aIsNum && !bIsNum) return -1;
|
|
47
|
+
if (!aIsNum && bIsNum) return 1;
|
|
48
48
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
49
|
+
if (aIsNum && bIsNum) {
|
|
50
|
+
if (aNum !== bNum) return aNum - bNum;
|
|
51
|
+
} else {
|
|
52
|
+
if (ai < bi) return -1;
|
|
53
|
+
if (ai > bi) return 1;
|
|
54
|
+
}
|
|
54
55
|
}
|
|
55
|
-
}
|
|
56
56
|
|
|
57
|
-
|
|
57
|
+
return 0;
|
|
58
58
|
}
|
|
59
59
|
|
|
60
60
|
/**
|
|
@@ -65,19 +65,19 @@ function comparePreRelease(a: string, b: string): number {
|
|
|
65
65
|
* `POLICY_SEMVER_PATTERN` validation (i.e. values stored in PolicyDefinition).
|
|
66
66
|
*/
|
|
67
67
|
export function comparePolicySemver(a: string, b: string): number {
|
|
68
|
-
|
|
69
|
-
|
|
68
|
+
const av = parseSemver(a);
|
|
69
|
+
const bv = parseSemver(b);
|
|
70
70
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
71
|
+
if (av.major !== bv.major) return av.major - bv.major;
|
|
72
|
+
if (av.minor !== bv.minor) return av.minor - bv.minor;
|
|
73
|
+
if (av.patch !== bv.patch) return av.patch - bv.patch;
|
|
74
74
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
75
|
+
// Release > pre-release (semver §11.3)
|
|
76
|
+
if (av.preRelease === null && bv.preRelease !== null) return 1;
|
|
77
|
+
if (av.preRelease !== null && bv.preRelease === null) return -1;
|
|
78
|
+
if (av.preRelease !== null && bv.preRelease !== null) {
|
|
79
|
+
return comparePreRelease(av.preRelease, bv.preRelease);
|
|
80
|
+
}
|
|
81
81
|
|
|
82
|
-
|
|
82
|
+
return 0;
|
|
83
83
|
}
|
|
@@ -6,52 +6,52 @@ import type { ComputeEvaluatorRegistry } from "./compute-evaluator-registry";
|
|
|
6
6
|
import type { ComputeOutcome, VersionedComputeEngine } from "./compute-types";
|
|
7
7
|
|
|
8
8
|
export class ComputeEngineRegistry {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
register(engine: VersionedComputeEngine): void {
|
|
12
|
-
this.engines.set(engine.version, engine);
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
get(version: number): VersionedComputeEngine | undefined {
|
|
16
|
-
return this.engines.get(version);
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
evaluate(
|
|
20
|
-
version: number,
|
|
21
|
-
params: {
|
|
22
|
-
readonly policyKey: string;
|
|
23
|
-
readonly payloadSchemaVersion: number;
|
|
24
|
-
readonly payload: unknown;
|
|
25
|
-
readonly context: PolicyContext;
|
|
26
|
-
readonly evaluators: ComputeEvaluatorRegistry;
|
|
27
|
-
},
|
|
28
|
-
): Result<ComputeOutcome, string> {
|
|
29
|
-
const engine = this.get(version);
|
|
30
|
-
if (engine === undefined) {
|
|
31
|
-
return Result.err(
|
|
32
|
-
`No compute engine registered for version "${version}"`,
|
|
33
|
-
);
|
|
34
|
-
}
|
|
9
|
+
private readonly engines = new Map<number, VersionedComputeEngine>();
|
|
35
10
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
return Result.err(
|
|
39
|
-
`No compute evaluator registered for policy key "${params.policyKey}" and version "${version}"`,
|
|
40
|
-
);
|
|
11
|
+
register(engine: VersionedComputeEngine): void {
|
|
12
|
+
this.engines.set(engine.version, engine);
|
|
41
13
|
}
|
|
42
14
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
params.payload,
|
|
46
|
-
);
|
|
47
|
-
if (parsedPayloadResult.isErr()) {
|
|
48
|
-
return Result.err(parsedPayloadResult.errorOrNull()!);
|
|
15
|
+
get(version: number): VersionedComputeEngine | undefined {
|
|
16
|
+
return this.engines.get(version);
|
|
49
17
|
}
|
|
50
18
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
19
|
+
evaluate(
|
|
20
|
+
version: number,
|
|
21
|
+
params: {
|
|
22
|
+
readonly policyKey: string;
|
|
23
|
+
readonly payloadSchemaVersion: number;
|
|
24
|
+
readonly payload: unknown;
|
|
25
|
+
readonly context: PolicyContext;
|
|
26
|
+
readonly evaluators: ComputeEvaluatorRegistry;
|
|
27
|
+
},
|
|
28
|
+
): Result<ComputeOutcome, string> {
|
|
29
|
+
const engine = this.get(version);
|
|
30
|
+
if (engine === undefined) {
|
|
31
|
+
return Result.err(
|
|
32
|
+
`No compute engine registered for version "${version}"`,
|
|
33
|
+
);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const evaluator = params.evaluators.get(params.policyKey, version);
|
|
37
|
+
if (evaluator === undefined) {
|
|
38
|
+
return Result.err(
|
|
39
|
+
`No compute evaluator registered for policy key "${params.policyKey}" and version "${version}"`,
|
|
40
|
+
);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const parsedPayloadResult = ComputePayloadParsers.parse(
|
|
44
|
+
params.payloadSchemaVersion,
|
|
45
|
+
params.payload,
|
|
46
|
+
);
|
|
47
|
+
if (parsedPayloadResult.isErr()) {
|
|
48
|
+
return Result.err(parsedPayloadResult.errorOrNull()!);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
return engine.evaluate(
|
|
52
|
+
parsedPayloadResult.getOrNull()!,
|
|
53
|
+
params.context,
|
|
54
|
+
evaluator,
|
|
55
|
+
);
|
|
56
|
+
}
|
|
57
57
|
}
|
|
@@ -1,28 +1,28 @@
|
|
|
1
1
|
import type {
|
|
2
|
-
|
|
3
|
-
|
|
2
|
+
ComputeEvaluator,
|
|
3
|
+
ComputeEvaluatorRegistration,
|
|
4
4
|
} from "./compute-types";
|
|
5
5
|
|
|
6
6
|
export class ComputeEvaluatorRegistry {
|
|
7
|
-
|
|
7
|
+
private readonly evaluators = new Map<string, ComputeEvaluator>();
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
9
|
+
private static toRegistryKey(policyKey: string, version: number): string {
|
|
10
|
+
return `${policyKey}::${version}`;
|
|
11
|
+
}
|
|
12
12
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
13
|
+
register(registration: ComputeEvaluatorRegistration): void {
|
|
14
|
+
this.evaluators.set(
|
|
15
|
+
ComputeEvaluatorRegistry.toRegistryKey(
|
|
16
|
+
registration.policyKey,
|
|
17
|
+
registration.version,
|
|
18
|
+
),
|
|
19
|
+
registration.evaluator,
|
|
20
|
+
);
|
|
21
|
+
}
|
|
22
22
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
23
|
+
get(policyKey: string, version: number): ComputeEvaluator | undefined {
|
|
24
|
+
return this.evaluators.get(
|
|
25
|
+
ComputeEvaluatorRegistry.toRegistryKey(policyKey, version),
|
|
26
|
+
);
|
|
27
|
+
}
|
|
28
28
|
}
|
|
@@ -5,63 +5,63 @@ import { ComputeEngineRegistry } from "./compute-engine-registry";
|
|
|
5
5
|
import { ComputeEvaluatorRegistry } from "./compute-evaluator-registry";
|
|
6
6
|
import type { PolicyContext } from "./gate-types";
|
|
7
7
|
import type {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
8
|
+
ComputeEvaluator,
|
|
9
|
+
ComputeEvaluatorRegistration,
|
|
10
|
+
ComputeOutcome,
|
|
11
|
+
VersionedComputeEngine,
|
|
12
12
|
} from "./compute-types";
|
|
13
13
|
import { ComputeEngineV1 } from "./v1/compute";
|
|
14
14
|
|
|
15
15
|
export interface ComputeRegistryOptions {
|
|
16
|
-
|
|
16
|
+
readonly coreConfig?: CoreConfig;
|
|
17
17
|
}
|
|
18
18
|
|
|
19
19
|
export class ComputeRegistry {
|
|
20
|
-
|
|
21
|
-
|
|
20
|
+
private readonly engines = new ComputeEngineRegistry();
|
|
21
|
+
private readonly evaluators = new ComputeEvaluatorRegistry();
|
|
22
22
|
|
|
23
|
-
|
|
24
|
-
|
|
23
|
+
constructor(params: ComputeRegistryOptions = {}) {
|
|
24
|
+
const resolvedCoreConfig = params.coreConfig ?? coreConfig;
|
|
25
25
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
26
|
+
this.engines.register(
|
|
27
|
+
new ComputeEngineV1({
|
|
28
|
+
coreConfig: resolvedCoreConfig,
|
|
29
|
+
}),
|
|
30
|
+
);
|
|
31
|
+
}
|
|
32
32
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
33
|
+
register(registration: ComputeEvaluatorRegistration): void {
|
|
34
|
+
this.evaluators.register(registration);
|
|
35
|
+
}
|
|
36
36
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
37
|
+
registerEngine(engine: VersionedComputeEngine): void {
|
|
38
|
+
this.engines.register(engine);
|
|
39
|
+
}
|
|
40
40
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
41
|
+
getEngine(version: number): VersionedComputeEngine | undefined {
|
|
42
|
+
return this.engines.get(version);
|
|
43
|
+
}
|
|
44
44
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
45
|
+
getEvaluator(
|
|
46
|
+
policyKey: string,
|
|
47
|
+
version: number,
|
|
48
|
+
): ComputeEvaluator | undefined {
|
|
49
|
+
return this.evaluators.get(policyKey, version);
|
|
50
|
+
}
|
|
51
51
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
52
|
+
evaluate(
|
|
53
|
+
policyKey: string,
|
|
54
|
+
computeEngineVersion: number,
|
|
55
|
+
payloadSchemaVersion: number,
|
|
56
|
+
payload: unknown,
|
|
57
|
+
context: PolicyContext,
|
|
58
|
+
): Result<ComputeOutcome, string> {
|
|
59
|
+
return this.engines.evaluate(computeEngineVersion, {
|
|
60
|
+
policyKey,
|
|
61
|
+
payloadSchemaVersion,
|
|
62
|
+
payload,
|
|
63
|
+
context,
|
|
64
|
+
evaluators: this.evaluators,
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
67
|
}
|
|
@@ -9,8 +9,8 @@ import type { PolicyContext, PolicyViolation } from "./gate-types";
|
|
|
9
9
|
export type ComputeStatus = "OK" | "NOT_APPLICABLE" | "ERROR";
|
|
10
10
|
|
|
11
11
|
export interface ComputeOutcomeData {
|
|
12
|
-
|
|
13
|
-
|
|
12
|
+
readonly values: unknown | null;
|
|
13
|
+
readonly violations: readonly PolicyViolation[];
|
|
14
14
|
}
|
|
15
15
|
|
|
16
16
|
/** Semantic outcome of a COMPUTE policy evaluation. */
|
|
@@ -29,23 +29,23 @@ export type ComputeOutcome = Outcome<ComputeStatus, ComputeOutcomeData>;
|
|
|
29
29
|
* Outcome carries the semantic business decision.
|
|
30
30
|
*/
|
|
31
31
|
export interface ComputeEvaluator {
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
32
|
+
evaluate(
|
|
33
|
+
resolvedValue: unknown,
|
|
34
|
+
context: Record<string, unknown>,
|
|
35
|
+
): Result<ComputeOutcome, string>;
|
|
36
36
|
}
|
|
37
37
|
|
|
38
38
|
export interface ComputeEvaluatorRegistration {
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
39
|
+
readonly policyKey: string;
|
|
40
|
+
readonly version: number;
|
|
41
|
+
readonly evaluator: ComputeEvaluator;
|
|
42
42
|
}
|
|
43
43
|
|
|
44
44
|
export interface VersionedComputeEngine {
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
45
|
+
readonly version: number;
|
|
46
|
+
evaluate(
|
|
47
|
+
payload: ComputePayload,
|
|
48
|
+
context: PolicyContext,
|
|
49
|
+
evaluator: ComputeEvaluator,
|
|
50
|
+
): Result<ComputeOutcome, string>;
|
|
51
51
|
}
|
|
@@ -1,34 +1,34 @@
|
|
|
1
1
|
export interface ConditionEvaluationCause {
|
|
2
|
-
|
|
3
|
-
|
|
2
|
+
readonly name: string;
|
|
3
|
+
readonly message: string;
|
|
4
4
|
}
|
|
5
5
|
|
|
6
6
|
export type ConditionEvaluationReportLevel = "warn" | "error";
|
|
7
7
|
|
|
8
8
|
export type ConditionEvaluationReportTag =
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
9
|
+
| "MISSING_CONTEXT_FIELD"
|
|
10
|
+
| "NULLISH_NUMERIC_OPERAND_NOT_ALLOWED"
|
|
11
|
+
| "NULLISH_DATE_OPERAND_NOT_ALLOWED"
|
|
12
|
+
| "INVALID_DATE_OPERAND"
|
|
13
|
+
| "CONDITION_EVAL_THREW";
|
|
14
14
|
|
|
15
15
|
export interface ConditionEvaluationReport {
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
16
|
+
readonly occurredAt: Date;
|
|
17
|
+
readonly level: ConditionEvaluationReportLevel;
|
|
18
|
+
readonly tag: ConditionEvaluationReportTag;
|
|
19
|
+
readonly message: string;
|
|
20
|
+
readonly engineVersion: number;
|
|
21
|
+
readonly details: Readonly<Record<string, unknown>>;
|
|
22
22
|
}
|
|
23
23
|
|
|
24
24
|
export interface ConditionEvaluatorReporter {
|
|
25
|
-
|
|
26
|
-
|
|
25
|
+
warn(report: ConditionEvaluationReport): void;
|
|
26
|
+
error(report: ConditionEvaluationReport): void;
|
|
27
27
|
}
|
|
28
28
|
|
|
29
29
|
// Either fully silent (no reporter, no version stamp) or fully observable
|
|
30
30
|
// (reporter paired with engineVersion so every report carries its origin).
|
|
31
31
|
export type ConditionEvaluationOptions = {
|
|
32
|
-
|
|
33
|
-
|
|
32
|
+
readonly reporter: ConditionEvaluatorReporter;
|
|
33
|
+
readonly engineVersion: number;
|
|
34
34
|
};
|
|
@@ -1,38 +1,40 @@
|
|
|
1
1
|
import { Result } from "../../result/result";
|
|
2
2
|
|
|
3
3
|
import type {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
4
|
+
GateOutcome,
|
|
5
|
+
PolicyContext,
|
|
6
|
+
VersionedGateEngine,
|
|
7
7
|
} from "./gate-types";
|
|
8
8
|
import { GatePayloadParsers } from "./parse-gate-payload";
|
|
9
9
|
|
|
10
10
|
export class GateEngineRegistry {
|
|
11
|
-
|
|
11
|
+
private readonly engines = new Map<number, VersionedGateEngine>();
|
|
12
12
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
get(version: number): VersionedGateEngine | undefined {
|
|
18
|
-
return this.engines.get(version);
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
evaluate(
|
|
22
|
-
version: number,
|
|
23
|
-
payload: unknown,
|
|
24
|
-
context: PolicyContext,
|
|
25
|
-
): Result<GateOutcome, string> {
|
|
26
|
-
const engine = this.engines.get(version);
|
|
27
|
-
if (!engine) {
|
|
28
|
-
return Result.err(`No gate engine registered for version "${version}"`);
|
|
13
|
+
register(engine: VersionedGateEngine): void {
|
|
14
|
+
this.engines.set(engine.version, engine);
|
|
29
15
|
}
|
|
30
16
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
return Result.err(parsedPayload.errorOrNull()!);
|
|
17
|
+
get(version: number): VersionedGateEngine | undefined {
|
|
18
|
+
return this.engines.get(version);
|
|
34
19
|
}
|
|
35
20
|
|
|
36
|
-
|
|
37
|
-
|
|
21
|
+
evaluate(
|
|
22
|
+
version: number,
|
|
23
|
+
payload: unknown,
|
|
24
|
+
context: PolicyContext,
|
|
25
|
+
): Result<GateOutcome, string> {
|
|
26
|
+
const engine = this.engines.get(version);
|
|
27
|
+
if (!engine) {
|
|
28
|
+
return Result.err(
|
|
29
|
+
`No gate engine registered for version "${version}"`,
|
|
30
|
+
);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const parsedPayload = GatePayloadParsers.parse(version, payload);
|
|
34
|
+
if (parsedPayload.isErr()) {
|
|
35
|
+
return Result.err(parsedPayload.errorOrNull()!);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
return engine.evaluate(parsedPayload.getOrNull()!, context);
|
|
39
|
+
}
|
|
38
40
|
}
|
|
@@ -8,42 +8,42 @@ import type { GatePayload } from "./gate-payload";
|
|
|
8
8
|
export type PolicyContext = Record<string, unknown>;
|
|
9
9
|
|
|
10
10
|
export interface GateTraceLeafSnapshot {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
11
|
+
readonly kind: "LEAF";
|
|
12
|
+
readonly field: string;
|
|
13
|
+
readonly op: string;
|
|
14
|
+
readonly allowNull?: true;
|
|
15
15
|
}
|
|
16
16
|
|
|
17
17
|
export type GateTraceNodeSnapshot =
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
18
|
+
| GateTraceLeafSnapshot
|
|
19
|
+
| {
|
|
20
|
+
readonly kind: "AND" | "OR";
|
|
21
|
+
readonly childCount: number;
|
|
22
|
+
}
|
|
23
|
+
| {
|
|
24
|
+
readonly kind: "NOT";
|
|
25
|
+
};
|
|
26
26
|
|
|
27
27
|
export interface GateViolationTrace {
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
28
|
+
readonly conditionPath: string;
|
|
29
|
+
readonly decisiveNode: GateTraceNodeSnapshot;
|
|
30
|
+
readonly lastEvaluatedLeaf: GateTraceLeafSnapshot;
|
|
31
|
+
readonly lastEvaluatedLeafPath: string;
|
|
32
|
+
readonly lastEvaluatedLeafResult: boolean;
|
|
33
|
+
readonly field: string;
|
|
34
|
+
readonly op: string;
|
|
35
35
|
}
|
|
36
36
|
|
|
37
37
|
export interface PolicyViolation {
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
38
|
+
readonly code: string;
|
|
39
|
+
readonly message: string;
|
|
40
|
+
readonly trace?: GateViolationTrace;
|
|
41
41
|
}
|
|
42
42
|
|
|
43
43
|
export type GateStatus = "ALLOW" | "DENY";
|
|
44
44
|
|
|
45
45
|
export interface GateOutcomeData {
|
|
46
|
-
|
|
46
|
+
readonly violations: readonly PolicyViolation[];
|
|
47
47
|
}
|
|
48
48
|
|
|
49
49
|
/** Semantic outcome of a GATE policy evaluation. */
|
|
@@ -55,9 +55,9 @@ export type GateOutcome = Outcome<GateStatus, GateOutcomeData>;
|
|
|
55
55
|
* the registry's responsibility via `parseGatePayload`.
|
|
56
56
|
*/
|
|
57
57
|
export interface VersionedGateEngine {
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
58
|
+
readonly version: number;
|
|
59
|
+
evaluate(
|
|
60
|
+
payload: GatePayload,
|
|
61
|
+
context: PolicyContext,
|
|
62
|
+
): Result<GateOutcome, string>;
|
|
63
63
|
}
|