@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,7 +1,14 @@
|
|
|
1
1
|
import { PolicyContextPath } from "../../context/index.js";
|
|
2
|
-
import { PolicyDateUtils } from "../../utils/index.js";
|
|
3
2
|
import { Result } from "../../../result/result.js";
|
|
4
3
|
|
|
4
|
+
import {
|
|
5
|
+
CANONICAL_UTC_DATE_FORMAT,
|
|
6
|
+
containsInvalidDate,
|
|
7
|
+
evaluateRelationalNumbers,
|
|
8
|
+
normalizeDateOperand,
|
|
9
|
+
parseComparableDate,
|
|
10
|
+
type RelationalOperator,
|
|
11
|
+
} from "./condition-date-operands.js";
|
|
5
12
|
import type {
|
|
6
13
|
ConditionAndNode,
|
|
7
14
|
ConditionLeafNode,
|
|
@@ -25,8 +32,6 @@ const INVALID_NUMERIC_OPERAND_TAG = "INVALID_NUMERIC_OPERAND";
|
|
|
25
32
|
const INVALID_SET_OPERAND_TAG = "INVALID_SET_OPERAND";
|
|
26
33
|
const EMPTY_OR_CONDITION_TAG = "EMPTY_OR_CONDITION";
|
|
27
34
|
const EMPTY_AND_CONDITION_TAG = "EMPTY_AND_CONDITION";
|
|
28
|
-
const ISO_8601_UTC_DATE_PATTERN =
|
|
29
|
-
/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z$/;
|
|
30
35
|
|
|
31
36
|
export interface ConditionEvaluationTrace {
|
|
32
37
|
readonly conditionPath: string;
|
|
@@ -41,8 +46,6 @@ export interface TracedConditionEvaluation {
|
|
|
41
46
|
readonly trace: ConditionEvaluationTrace;
|
|
42
47
|
}
|
|
43
48
|
|
|
44
|
-
type RelationalOperator = Extract<ConditionOp, "gt" | "gte" | "lt" | "lte">;
|
|
45
|
-
|
|
46
49
|
export class ConditionEvaluatorV1 {
|
|
47
50
|
constructor(
|
|
48
51
|
private readonly context: PolicyContext,
|
|
@@ -76,106 +79,14 @@ export class ConditionEvaluatorV1 {
|
|
|
76
79
|
message: string;
|
|
77
80
|
} {
|
|
78
81
|
if (error instanceof Error) {
|
|
79
|
-
return {
|
|
80
|
-
name: error.name,
|
|
81
|
-
message: error.message,
|
|
82
|
-
};
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
return {
|
|
86
|
-
name: "NonErrorThrown",
|
|
87
|
-
message: String(error),
|
|
88
|
-
};
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
private static buildNullishNumericOperandMessage(
|
|
92
|
-
node: ConditionLeafNode,
|
|
93
|
-
actual: null | undefined,
|
|
94
|
-
): string {
|
|
95
|
-
const resolvedValue = actual === null ? "null" : "undefined";
|
|
96
|
-
|
|
97
|
-
return `${NULLISH_NUMERIC_OPERAND_NOT_ALLOWED_TAG}: "${node.field}" resolved to ${resolvedValue} for numeric operator "${node.op}"`;
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
private static buildNullishDateOperandMessage(
|
|
101
|
-
node: ConditionLeafNode,
|
|
102
|
-
actual: null | undefined,
|
|
103
|
-
): string {
|
|
104
|
-
const resolvedValue = actual === null ? "null" : "undefined";
|
|
105
|
-
|
|
106
|
-
return `${NULLISH_DATE_OPERAND_NOT_ALLOWED_TAG}: "${node.field}" resolved to ${resolvedValue} for date comparison operator "${node.op}"`;
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
private static buildInvalidDateOperandMessage(
|
|
110
|
-
node: ConditionLeafNode,
|
|
111
|
-
): string {
|
|
112
|
-
return `${INVALID_DATE_OPERAND_TAG}: "${node.field}" with operator "${node.op}" requires Date or ISO 8601 UTC string operands`;
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
private static buildInvalidNumericOperandMessage(
|
|
116
|
-
node: ConditionLeafNode,
|
|
117
|
-
): string {
|
|
118
|
-
return `${INVALID_NUMERIC_OPERAND_TAG}: "${node.field}" with operator "${node.op}" requires numeric operands`;
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
private static buildInvalidSetOperandMessage(
|
|
122
|
-
node: ConditionLeafNode,
|
|
123
|
-
): string {
|
|
124
|
-
return `${INVALID_SET_OPERAND_TAG}: "${node.field}" with operator "${node.op}" requires an array operand`;
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
private static buildEmptyOrConditionMessage(): string {
|
|
128
|
-
return `${EMPTY_OR_CONDITION_TAG}: OR nodes must contain at least one child condition`;
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
private static buildEmptyAndConditionMessage(): string {
|
|
132
|
-
return `${EMPTY_AND_CONDITION_TAG}: AND nodes must contain at least one child condition`;
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
private static parseComparableDate(
|
|
136
|
-
value: unknown,
|
|
137
|
-
): Result<Date, string> | null {
|
|
138
|
-
if (value instanceof Date) {
|
|
139
|
-
if (!PolicyDateUtils.isValid(value)) {
|
|
140
|
-
return Result.err("Invalid Date instance");
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
return Result.ok(value);
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
if (
|
|
147
|
-
typeof value !== "string" ||
|
|
148
|
-
!ISO_8601_UTC_DATE_PATTERN.test(value)
|
|
149
|
-
) {
|
|
150
|
-
return null;
|
|
82
|
+
return { name: error.name, message: error.message };
|
|
151
83
|
}
|
|
152
84
|
|
|
153
|
-
|
|
154
|
-
if (
|
|
155
|
-
!PolicyDateUtils.isValid(parsed) ||
|
|
156
|
-
parsed.toISOString() !== value
|
|
157
|
-
) {
|
|
158
|
-
return Result.err("Invalid ISO 8601 UTC date string");
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
return Result.ok(parsed);
|
|
85
|
+
return { name: "NonErrorThrown", message: String(error) };
|
|
162
86
|
}
|
|
163
87
|
|
|
164
|
-
private static
|
|
165
|
-
|
|
166
|
-
op: RelationalOperator,
|
|
167
|
-
expected: number,
|
|
168
|
-
): boolean {
|
|
169
|
-
switch (op) {
|
|
170
|
-
case "gt":
|
|
171
|
-
return actual > expected;
|
|
172
|
-
case "gte":
|
|
173
|
-
return actual >= expected;
|
|
174
|
-
case "lt":
|
|
175
|
-
return actual < expected;
|
|
176
|
-
case "lte":
|
|
177
|
-
return actual <= expected;
|
|
178
|
-
}
|
|
88
|
+
private static invalidDateOperandMessage(node: ConditionLeafNode): string {
|
|
89
|
+
return `${INVALID_DATE_OPERAND_TAG}: "${node.field}" with operator "${node.op}" requires Date or ISO 8601 UTC (${CANONICAL_UTC_DATE_FORMAT}) string operands`;
|
|
179
90
|
}
|
|
180
91
|
|
|
181
92
|
private buildReport(params: {
|
|
@@ -194,42 +105,27 @@ export class ConditionEvaluatorV1 {
|
|
|
194
105
|
};
|
|
195
106
|
}
|
|
196
107
|
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
this.options.reporter.error(
|
|
205
|
-
this.buildReport({
|
|
206
|
-
level: "error",
|
|
207
|
-
tag: CONDITION_EVAL_THROWN_TAG,
|
|
208
|
-
message,
|
|
209
|
-
details: { node, cause },
|
|
210
|
-
}),
|
|
211
|
-
);
|
|
212
|
-
|
|
213
|
-
return Result.err(message);
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
private reportDateOperandError(
|
|
108
|
+
// Single reporting seam for every leaf-level operand error. Collapses the
|
|
109
|
+
// formerly per-tag report methods, whose only real difference was the tag,
|
|
110
|
+
// message, and (for the nullish cases) an `allowNull` detail.
|
|
111
|
+
private reportLeafError(
|
|
112
|
+
tag: ConditionEvaluationReport["tag"],
|
|
113
|
+
message: string,
|
|
217
114
|
node: ConditionLeafNode,
|
|
218
115
|
actual: unknown,
|
|
116
|
+
extra?: Readonly<Record<string, unknown>>,
|
|
219
117
|
): Result<boolean, string> {
|
|
220
|
-
const message =
|
|
221
|
-
ConditionEvaluatorV1.buildInvalidDateOperandMessage(node);
|
|
222
|
-
|
|
223
118
|
this.options.reporter.error(
|
|
224
119
|
this.buildReport({
|
|
225
120
|
level: "error",
|
|
226
|
-
tag
|
|
121
|
+
tag,
|
|
227
122
|
message,
|
|
228
123
|
details: {
|
|
229
124
|
field: node.field,
|
|
230
125
|
op: node.op,
|
|
231
126
|
actual,
|
|
232
127
|
expected: node.value,
|
|
128
|
+
...extra,
|
|
233
129
|
node,
|
|
234
130
|
},
|
|
235
131
|
}),
|
|
@@ -238,50 +134,19 @@ export class ConditionEvaluatorV1 {
|
|
|
238
134
|
return Result.err(message);
|
|
239
135
|
}
|
|
240
136
|
|
|
241
|
-
private
|
|
242
|
-
node:
|
|
243
|
-
|
|
244
|
-
): Result<
|
|
245
|
-
const
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
this.options.reporter.error(
|
|
249
|
-
this.buildReport({
|
|
250
|
-
level: "error",
|
|
251
|
-
tag: INVALID_NUMERIC_OPERAND_TAG,
|
|
252
|
-
message,
|
|
253
|
-
details: {
|
|
254
|
-
field: node.field,
|
|
255
|
-
op: node.op,
|
|
256
|
-
actual,
|
|
257
|
-
expected: node.value,
|
|
258
|
-
node,
|
|
259
|
-
},
|
|
260
|
-
}),
|
|
261
|
-
);
|
|
262
|
-
|
|
263
|
-
return Result.err(message);
|
|
264
|
-
}
|
|
265
|
-
|
|
266
|
-
private reportInvalidSetOperand(
|
|
267
|
-
node: ConditionLeafNode,
|
|
268
|
-
actual: unknown,
|
|
269
|
-
): Result<boolean, string> {
|
|
270
|
-
const message =
|
|
271
|
-
ConditionEvaluatorV1.buildInvalidSetOperandMessage(node);
|
|
137
|
+
private conditionEvalErr<TValue>(
|
|
138
|
+
node: ConditionNode,
|
|
139
|
+
error: unknown,
|
|
140
|
+
): Result<TValue, string> {
|
|
141
|
+
const cause = ConditionEvaluatorV1.describeError(error);
|
|
142
|
+
const message = `${CONDITION_EVAL_THROWN_TAG}: ${cause.name}: ${cause.message}`;
|
|
272
143
|
|
|
273
144
|
this.options.reporter.error(
|
|
274
145
|
this.buildReport({
|
|
275
146
|
level: "error",
|
|
276
|
-
tag:
|
|
147
|
+
tag: CONDITION_EVAL_THROWN_TAG,
|
|
277
148
|
message,
|
|
278
|
-
details: {
|
|
279
|
-
field: node.field,
|
|
280
|
-
op: node.op,
|
|
281
|
-
actual,
|
|
282
|
-
expected: node.value,
|
|
283
|
-
node,
|
|
284
|
-
},
|
|
149
|
+
details: { node, cause },
|
|
285
150
|
}),
|
|
286
151
|
);
|
|
287
152
|
|
|
@@ -296,84 +161,49 @@ export class ConditionEvaluatorV1 {
|
|
|
296
161
|
return null;
|
|
297
162
|
}
|
|
298
163
|
|
|
299
|
-
const
|
|
300
|
-
|
|
301
|
-
const expectedDateResult = ConditionEvaluatorV1.parseComparableDate(
|
|
302
|
-
node.value,
|
|
303
|
-
);
|
|
164
|
+
const actualDate = parseComparableDate(actual);
|
|
165
|
+
const expectedDate = parseComparableDate(node.value);
|
|
304
166
|
|
|
305
|
-
|
|
167
|
+
// Neither operand is date-shaped: let the numeric path handle it.
|
|
168
|
+
if (actualDate === null && expectedDate === null) {
|
|
306
169
|
return null;
|
|
307
170
|
}
|
|
308
171
|
|
|
309
172
|
const allowsNull = node.allowNull === true;
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
if (allowsNull) {
|
|
313
|
-
return Result.ok(false);
|
|
314
|
-
}
|
|
315
|
-
|
|
316
|
-
const message = ConditionEvaluatorV1.buildNullishDateOperandMessage(
|
|
317
|
-
node,
|
|
318
|
-
actual,
|
|
319
|
-
);
|
|
320
|
-
this.options.reporter.error(
|
|
321
|
-
this.buildReport({
|
|
322
|
-
level: "error",
|
|
323
|
-
tag: NULLISH_DATE_OPERAND_NOT_ALLOWED_TAG,
|
|
324
|
-
message,
|
|
325
|
-
details: {
|
|
326
|
-
field: node.field,
|
|
327
|
-
op: node.op,
|
|
328
|
-
actual,
|
|
329
|
-
expected: node.value,
|
|
330
|
-
allowNull: allowsNull,
|
|
331
|
-
node,
|
|
332
|
-
},
|
|
333
|
-
}),
|
|
334
|
-
);
|
|
335
|
-
|
|
336
|
-
return Result.err(message);
|
|
173
|
+
if (actual === null && allowsNull) {
|
|
174
|
+
return Result.ok(false);
|
|
337
175
|
}
|
|
338
176
|
|
|
339
|
-
if (actual === undefined) {
|
|
340
|
-
const
|
|
177
|
+
if (actual === null || actual === undefined) {
|
|
178
|
+
const resolved = actual === null ? "null" : "undefined";
|
|
179
|
+
return this.reportLeafError(
|
|
180
|
+
NULLISH_DATE_OPERAND_NOT_ALLOWED_TAG,
|
|
181
|
+
`${NULLISH_DATE_OPERAND_NOT_ALLOWED_TAG}: "${node.field}" resolved to ${resolved} for date comparison operator "${node.op}"`,
|
|
341
182
|
node,
|
|
342
183
|
actual,
|
|
184
|
+
{ allowNull: allowsNull },
|
|
343
185
|
);
|
|
344
|
-
this.options.reporter.error(
|
|
345
|
-
this.buildReport({
|
|
346
|
-
level: "error",
|
|
347
|
-
tag: NULLISH_DATE_OPERAND_NOT_ALLOWED_TAG,
|
|
348
|
-
message,
|
|
349
|
-
details: {
|
|
350
|
-
field: node.field,
|
|
351
|
-
op: node.op,
|
|
352
|
-
actual,
|
|
353
|
-
expected: node.value,
|
|
354
|
-
allowNull: allowsNull,
|
|
355
|
-
node,
|
|
356
|
-
},
|
|
357
|
-
}),
|
|
358
|
-
);
|
|
359
|
-
|
|
360
|
-
return Result.err(message);
|
|
361
186
|
}
|
|
362
187
|
|
|
363
188
|
if (
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
189
|
+
actualDate === null ||
|
|
190
|
+
actualDate.isErr() ||
|
|
191
|
+
expectedDate === null ||
|
|
192
|
+
expectedDate.isErr()
|
|
368
193
|
) {
|
|
369
|
-
return this.
|
|
194
|
+
return this.reportLeafError(
|
|
195
|
+
INVALID_DATE_OPERAND_TAG,
|
|
196
|
+
ConditionEvaluatorV1.invalidDateOperandMessage(node),
|
|
197
|
+
node,
|
|
198
|
+
actual,
|
|
199
|
+
);
|
|
370
200
|
}
|
|
371
201
|
|
|
372
202
|
return Result.ok(
|
|
373
|
-
|
|
374
|
-
|
|
203
|
+
evaluateRelationalNumbers(
|
|
204
|
+
actualDate.getOrNull()!.getTime(),
|
|
375
205
|
node.op,
|
|
376
|
-
|
|
206
|
+
expectedDate.getOrNull()!.getTime(),
|
|
377
207
|
),
|
|
378
208
|
);
|
|
379
209
|
}
|
|
@@ -389,51 +219,20 @@ export class ConditionEvaluatorV1 {
|
|
|
389
219
|
case "neq":
|
|
390
220
|
return actual !== expected;
|
|
391
221
|
case "gt":
|
|
392
|
-
return (
|
|
393
|
-
typeof actual === "number" &&
|
|
394
|
-
typeof expected === "number" &&
|
|
395
|
-
ConditionEvaluatorV1.evaluateRelationalNumbers(
|
|
396
|
-
actual,
|
|
397
|
-
op,
|
|
398
|
-
expected,
|
|
399
|
-
)
|
|
400
|
-
);
|
|
401
222
|
case "gte":
|
|
402
|
-
return (
|
|
403
|
-
typeof actual === "number" &&
|
|
404
|
-
typeof expected === "number" &&
|
|
405
|
-
ConditionEvaluatorV1.evaluateRelationalNumbers(
|
|
406
|
-
actual,
|
|
407
|
-
op,
|
|
408
|
-
expected,
|
|
409
|
-
)
|
|
410
|
-
);
|
|
411
223
|
case "lt":
|
|
412
|
-
return (
|
|
413
|
-
typeof actual === "number" &&
|
|
414
|
-
typeof expected === "number" &&
|
|
415
|
-
ConditionEvaluatorV1.evaluateRelationalNumbers(
|
|
416
|
-
actual,
|
|
417
|
-
op,
|
|
418
|
-
expected,
|
|
419
|
-
)
|
|
420
|
-
);
|
|
421
224
|
case "lte":
|
|
422
225
|
return (
|
|
423
226
|
typeof actual === "number" &&
|
|
424
227
|
typeof expected === "number" &&
|
|
425
|
-
|
|
426
|
-
actual,
|
|
427
|
-
op,
|
|
428
|
-
expected,
|
|
429
|
-
)
|
|
228
|
+
evaluateRelationalNumbers(actual, op, expected)
|
|
430
229
|
);
|
|
431
230
|
case "in":
|
|
432
231
|
return Array.isArray(expected) && expected.includes(actual);
|
|
433
232
|
case "notIn":
|
|
434
233
|
return Array.isArray(expected) && !expected.includes(actual);
|
|
435
234
|
case "isNull":
|
|
436
|
-
return actual === null;
|
|
235
|
+
return actual === null || actual === undefined;
|
|
437
236
|
case "isNotNull":
|
|
438
237
|
return actual !== null && actual !== undefined;
|
|
439
238
|
}
|
|
@@ -445,29 +244,14 @@ export class ConditionEvaluatorV1 {
|
|
|
445
244
|
): Result<boolean, string> {
|
|
446
245
|
const allowsNull = node.allowNull === true;
|
|
447
246
|
if (actual === undefined || (actual === null && !allowsNull)) {
|
|
448
|
-
const
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
this.buildReport({
|
|
456
|
-
level: "error",
|
|
457
|
-
tag: NULLISH_NUMERIC_OPERAND_NOT_ALLOWED_TAG,
|
|
458
|
-
message,
|
|
459
|
-
details: {
|
|
460
|
-
field: node.field,
|
|
461
|
-
op: node.op,
|
|
462
|
-
actual,
|
|
463
|
-
expected: node.value,
|
|
464
|
-
allowNull: allowsNull,
|
|
465
|
-
node,
|
|
466
|
-
},
|
|
467
|
-
}),
|
|
247
|
+
const resolved = actual === null ? "null" : "undefined";
|
|
248
|
+
return this.reportLeafError(
|
|
249
|
+
NULLISH_NUMERIC_OPERAND_NOT_ALLOWED_TAG,
|
|
250
|
+
`${NULLISH_NUMERIC_OPERAND_NOT_ALLOWED_TAG}: "${node.field}" resolved to ${resolved} for numeric operator "${node.op}"`,
|
|
251
|
+
node,
|
|
252
|
+
actual,
|
|
253
|
+
{ allowNull: allowsNull },
|
|
468
254
|
);
|
|
469
|
-
|
|
470
|
-
return Result.err(message);
|
|
471
255
|
}
|
|
472
256
|
|
|
473
257
|
// null + allowNull: a relational comparison against a missing value
|
|
@@ -476,11 +260,21 @@ export class ConditionEvaluatorV1 {
|
|
|
476
260
|
return Result.ok(false);
|
|
477
261
|
}
|
|
478
262
|
|
|
479
|
-
// Both operands must be numbers. A present but wrong-typed
|
|
480
|
-
// context/configuration error, surfaced like the date
|
|
481
|
-
// silently collapsing to "no match".
|
|
482
|
-
if (
|
|
483
|
-
|
|
263
|
+
// Both operands must be finite numbers. A present but wrong-typed or
|
|
264
|
+
// NaN operand is a context/configuration error, surfaced like the date
|
|
265
|
+
// path instead of silently collapsing to "no match".
|
|
266
|
+
if (
|
|
267
|
+
typeof actual !== "number" ||
|
|
268
|
+
Number.isNaN(actual) ||
|
|
269
|
+
typeof node.value !== "number" ||
|
|
270
|
+
Number.isNaN(node.value)
|
|
271
|
+
) {
|
|
272
|
+
return this.reportLeafError(
|
|
273
|
+
INVALID_NUMERIC_OPERAND_TAG,
|
|
274
|
+
`${INVALID_NUMERIC_OPERAND_TAG}: "${node.field}" with operator "${node.op}" requires numeric operands`,
|
|
275
|
+
node,
|
|
276
|
+
actual,
|
|
277
|
+
);
|
|
484
278
|
}
|
|
485
279
|
|
|
486
280
|
return Result.ok(this.evaluateOperator(actual, node.op, node.value));
|
|
@@ -492,18 +286,17 @@ export class ConditionEvaluatorV1 {
|
|
|
492
286
|
node.field,
|
|
493
287
|
);
|
|
494
288
|
if (actualResult.isErr()) {
|
|
289
|
+
const message = `MISSING_CONTEXT_FIELD: "${node.field}" not found in context`;
|
|
495
290
|
this.options.reporter.warn(
|
|
496
291
|
this.buildReport({
|
|
497
292
|
level: "warn",
|
|
498
293
|
tag: "MISSING_CONTEXT_FIELD",
|
|
499
|
-
message
|
|
294
|
+
message,
|
|
500
295
|
details: { field: node.field, node },
|
|
501
296
|
}),
|
|
502
297
|
);
|
|
503
298
|
|
|
504
|
-
return Result.err(
|
|
505
|
-
`MISSING_CONTEXT_FIELD: "${node.field}" not found in context`,
|
|
506
|
-
);
|
|
299
|
+
return Result.err(message);
|
|
507
300
|
}
|
|
508
301
|
|
|
509
302
|
try {
|
|
@@ -524,7 +317,43 @@ export class ConditionEvaluatorV1 {
|
|
|
524
317
|
(node.op === "in" || node.op === "notIn") &&
|
|
525
318
|
!Array.isArray(node.value)
|
|
526
319
|
) {
|
|
527
|
-
return this.
|
|
320
|
+
return this.reportLeafError(
|
|
321
|
+
INVALID_SET_OPERAND_TAG,
|
|
322
|
+
`${INVALID_SET_OPERAND_TAG}: "${node.field}" with operator "${node.op}" requires an array operand`,
|
|
323
|
+
node,
|
|
324
|
+
actual,
|
|
325
|
+
);
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
if (
|
|
329
|
+
node.op === "eq" ||
|
|
330
|
+
node.op === "neq" ||
|
|
331
|
+
node.op === "in" ||
|
|
332
|
+
node.op === "notIn"
|
|
333
|
+
) {
|
|
334
|
+
// A Date operand would otherwise compare by reference and
|
|
335
|
+
// silently never match. Normalize valid Dates (either side,
|
|
336
|
+
// including set items) to their ISO string; an invalid Date is
|
|
337
|
+
// a context/configuration error, like the relational path.
|
|
338
|
+
if (
|
|
339
|
+
containsInvalidDate(actual) ||
|
|
340
|
+
containsInvalidDate(node.value)
|
|
341
|
+
) {
|
|
342
|
+
return this.reportLeafError(
|
|
343
|
+
INVALID_DATE_OPERAND_TAG,
|
|
344
|
+
ConditionEvaluatorV1.invalidDateOperandMessage(node),
|
|
345
|
+
node,
|
|
346
|
+
actual,
|
|
347
|
+
);
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
return Result.ok(
|
|
351
|
+
this.evaluateOperator(
|
|
352
|
+
normalizeDateOperand(actual),
|
|
353
|
+
node.op,
|
|
354
|
+
normalizeDateOperand(node.value),
|
|
355
|
+
),
|
|
356
|
+
);
|
|
528
357
|
}
|
|
529
358
|
|
|
530
359
|
return Result.ok(
|
|
@@ -577,7 +406,7 @@ export class ConditionEvaluatorV1 {
|
|
|
577
406
|
if (ConditionEvaluatorV1.isAndNode(node)) {
|
|
578
407
|
if (node.and.length === 0) {
|
|
579
408
|
return Result.err(
|
|
580
|
-
|
|
409
|
+
`${EMPTY_AND_CONDITION_TAG}: AND nodes must contain at least one child condition`,
|
|
581
410
|
);
|
|
582
411
|
}
|
|
583
412
|
|
|
@@ -642,7 +471,7 @@ export class ConditionEvaluatorV1 {
|
|
|
642
471
|
|
|
643
472
|
if (lastTrace === null) {
|
|
644
473
|
return Result.err(
|
|
645
|
-
|
|
474
|
+
`${EMPTY_OR_CONDITION_TAG}: OR nodes must contain at least one child condition`,
|
|
646
475
|
);
|
|
647
476
|
}
|
|
648
477
|
|
|
@@ -28,23 +28,27 @@ export const conditionLeafNodeSchema: z.ZodType<ConditionLeafNode> = z
|
|
|
28
28
|
})
|
|
29
29
|
.strict() as z.ZodType<ConditionLeafNode>;
|
|
30
30
|
|
|
31
|
-
|
|
32
|
-
|
|
31
|
+
// Cap nesting depth so an adversarially deep payload fails validation instead
|
|
32
|
+
// of overflowing the stack during parse: zod recurses once per level, and the
|
|
33
|
+
// old `z.lazy` schema was unbounded. The schema is built to exactly this depth
|
|
34
|
+
// (leaf-only past it), so a too-deep tree is rejected at the boundary without
|
|
35
|
+
// deep recursion. 32 is far beyond any hand-written policy — raise it only with
|
|
36
|
+
// a matching stack-safety review.
|
|
37
|
+
export const MAX_CONDITION_DEPTH = 32;
|
|
38
|
+
|
|
39
|
+
function boundedConditionNodeSchema(depth: number): z.ZodType<ConditionNode> {
|
|
40
|
+
if (depth <= 1) {
|
|
41
|
+
return conditionLeafNodeSchema;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const child = boundedConditionNodeSchema(depth - 1);
|
|
45
|
+
return z.union([
|
|
33
46
|
conditionLeafNodeSchema,
|
|
34
|
-
z
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
})
|
|
43
|
-
.strict(),
|
|
44
|
-
z
|
|
45
|
-
.object({
|
|
46
|
-
not: conditionNodeSchema,
|
|
47
|
-
})
|
|
48
|
-
.strict(),
|
|
49
|
-
]),
|
|
50
|
-
);
|
|
47
|
+
z.object({ and: z.array(child).min(1) }).strict(),
|
|
48
|
+
z.object({ or: z.array(child).min(1) }).strict(),
|
|
49
|
+
z.object({ not: child }).strict(),
|
|
50
|
+
]) as z.ZodType<ConditionNode>;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export const conditionNodeSchema: z.ZodType<ConditionNode> =
|
|
54
|
+
boundedConditionNodeSchema(MAX_CONDITION_DEPTH);
|
|
@@ -3,17 +3,24 @@
|
|
|
3
3
|
// share the same condition-tree DSL. Keeping them here avoids making compute/v1
|
|
4
4
|
// structurally dependent on gate/v1.
|
|
5
5
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
6
|
+
// Single source of truth for the operator vocabulary: the union is derived from
|
|
7
|
+
// this array so a new operator is added in exactly one place, and consumers that
|
|
8
|
+
// need the runtime list (e.g. AbacRule's structural check) reuse it instead of
|
|
9
|
+
// hand-maintaining a parallel set that can drift out of sync.
|
|
10
|
+
export const CONDITION_OPS = [
|
|
11
|
+
"eq",
|
|
12
|
+
"neq",
|
|
13
|
+
"gt",
|
|
14
|
+
"gte",
|
|
15
|
+
"lt",
|
|
16
|
+
"lte",
|
|
17
|
+
"in",
|
|
18
|
+
"notIn",
|
|
19
|
+
"isNull",
|
|
20
|
+
"isNotNull",
|
|
21
|
+
] as const;
|
|
22
|
+
|
|
23
|
+
export type ConditionOp = (typeof CONDITION_OPS)[number];
|
|
17
24
|
|
|
18
25
|
export interface ConditionLeafNode {
|
|
19
26
|
readonly field: string;
|
|
@@ -8,12 +8,10 @@ export type {
|
|
|
8
8
|
} from "../config/index.js";
|
|
9
9
|
export { CoreConfig, coreConfig } from "../config/index.js";
|
|
10
10
|
|
|
11
|
-
// ─── Result (technical execution)
|
|
12
|
-
export { Err, Ok, Result } from "../result/
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
export type { CommonOutcomeStatus } from "../result/outcome.js";
|
|
16
|
-
export { Outcome } from "../result/outcome.js";
|
|
11
|
+
// ─── Result (technical execution) + Outcome (business decision) ─────────────
|
|
12
|
+
export { Err, Ok, Result } from "../result/index.js";
|
|
13
|
+
export type { CommonOutcomeStatus } from "../result/index.js";
|
|
14
|
+
export { Outcome } from "../result/index.js";
|
|
17
15
|
|
|
18
16
|
// ─── Utils ──────────────────────────────────────────────────────────────────
|
|
19
17
|
export { PolicyHashing } from "./utils/index.js";
|