@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.
Files changed (146) hide show
  1. package/README.md +24 -24
  2. package/dist/gate-engine-registry.js.map +1 -1
  3. package/dist/gate-v1-payload.schema.js +5 -0
  4. package/dist/gate-v1-payload.schema.js.map +1 -1
  5. package/dist/index.d.ts +2 -2
  6. package/dist/index.js +1 -1
  7. package/dist/index.js.map +1 -1
  8. package/dist/outcome.js.map +1 -1
  9. package/dist/policies/engines/v1/gate/index.d.ts +1 -0
  10. package/dist/policies/engines/v1/gate/index.js.map +1 -1
  11. package/dist/policy-service.js.map +1 -1
  12. package/dist/validation-code.js.map +1 -1
  13. package/dist/validation-error.js.map +1 -1
  14. package/dist/validation-field.js.map +1 -1
  15. package/meta.json +67 -67
  16. package/package.json +56 -56
  17. package/src/core/application/commands/command.ts +3 -3
  18. package/src/core/application/commands/requested-by.ts +60 -60
  19. package/src/core/application/index.ts +21 -21
  20. package/src/core/application/policy-error-mapper.ts +54 -54
  21. package/src/core/application/ports/index.ts +6 -6
  22. package/src/core/application/ports/logger.port.ts +4 -4
  23. package/src/core/application/ports/metrics.port.ts +3 -3
  24. package/src/core/application/ports/policy-port.ts +7 -7
  25. package/src/core/application/ports/repository.port.ts +3 -3
  26. package/src/core/application/ports/temporal-repository.port.ts +4 -4
  27. package/src/core/application/ports/tracer.port.ts +7 -7
  28. package/src/core/application/queries/query.ts +13 -13
  29. package/src/core/application/temporal/index.ts +7 -7
  30. package/src/core/application/temporal/temporal-context.ts +20 -20
  31. package/src/core/application/temporal/temporal-use-case.ts +22 -22
  32. package/src/core/application/use-case.ts +10 -10
  33. package/src/core/config/core-config.instance.ts +1 -1
  34. package/src/core/config/core-config.ts +59 -59
  35. package/src/core/config/index.ts +6 -6
  36. package/src/core/config/policy-reporter.ts +31 -31
  37. package/src/core/config/silent-policy-reporter.ts +1 -1
  38. package/src/core/domain/entity.ts +44 -44
  39. package/src/core/domain/rulesets/entity-ruleset.contracts.ts +6 -6
  40. package/src/core/domain/rulesets/ruleset-registry.ts +49 -49
  41. package/src/core/domain/rulesets/ruleset.contracts.ts +6 -6
  42. package/src/core/domain/rulesets/value-object-ruleset.contracts.ts +5 -5
  43. package/src/core/domain/temporal/index.ts +11 -11
  44. package/src/core/domain/temporal/temporal-range.ts +16 -16
  45. package/src/core/domain/temporal/temporal-snapshot.ts +24 -24
  46. package/src/core/domain/temporal/transaction-time.ts +30 -30
  47. package/src/core/domain/temporal/valid-time.ts +29 -29
  48. package/src/core/domain/value-object.ts +16 -16
  49. package/src/core/errors/app-error.ts +64 -64
  50. package/src/core/errors/authentication-error.ts +134 -134
  51. package/src/core/errors/authorization-error.ts +154 -154
  52. package/src/core/errors/business-rule-violation-error.ts +15 -15
  53. package/src/core/errors/conflict-error.ts +202 -201
  54. package/src/core/errors/error-codes.ts +48 -48
  55. package/src/core/errors/idempotency-error.ts +324 -320
  56. package/src/core/errors/index.ts +50 -50
  57. package/src/core/errors/integration-error.ts +120 -120
  58. package/src/core/errors/legacy-incompatible-error.ts +13 -13
  59. package/src/core/errors/not-found-error.ts +14 -14
  60. package/src/core/errors/serialization-error.ts +167 -167
  61. package/src/core/errors/temporal-error.ts +109 -107
  62. package/src/core/errors/types.ts +35 -35
  63. package/src/core/errors/unexpected-error.ts +10 -10
  64. package/src/core/errors/utils/index.ts +2 -2
  65. package/src/core/errors/utils/json-safe.ts +53 -53
  66. package/src/core/errors/validation-error.ts +18 -18
  67. package/src/core/exceptions/business-rule-violation-exception.ts +6 -6
  68. package/src/core/exceptions/domain-exception.ts +5 -5
  69. package/src/core/exceptions/entity-not-found-exception.ts +6 -6
  70. package/src/core/exceptions/invalid-state-transition-exception.ts +8 -8
  71. package/src/core/exceptions/invariant-violation-exception.ts +3 -3
  72. package/src/core/exceptions/validation-code.ts +35 -29
  73. package/src/core/exceptions/validation-exception.ts +19 -19
  74. package/src/core/exceptions/validation-field.ts +20 -19
  75. package/src/core/index.ts +7 -7
  76. package/src/core/policies/asof/asof.ts +47 -47
  77. package/src/core/policies/catalog/catalog.instance.ts +8 -6
  78. package/src/core/policies/catalog/index.ts +4 -4
  79. package/src/core/policies/catalog/policy-catalog-entry.ts +199 -197
  80. package/src/core/policies/catalog/policy-catalog.ts +101 -101
  81. package/src/core/policies/catalog/policy-key.ts +51 -51
  82. package/src/core/policies/context/context-builder.ts +335 -313
  83. package/src/core/policies/context/context-registry.instance.ts +9 -9
  84. package/src/core/policies/context/context-registry.ts +61 -61
  85. package/src/core/policies/context/context-resolver.ts +12 -12
  86. package/src/core/policies/context/context-seed.ts +19 -15
  87. package/src/core/policies/context/index.ts +9 -9
  88. package/src/core/policies/context/path.ts +135 -128
  89. package/src/core/policies/defs/in-memory-policy-definition-repo.ts +56 -55
  90. package/src/core/policies/defs/index.ts +10 -10
  91. package/src/core/policies/defs/policy-definition-repository.ts +3 -3
  92. package/src/core/policies/defs/policy-definition.ts +143 -142
  93. package/src/core/policies/defs/policy-scope.ts +21 -19
  94. package/src/core/policies/defs/policy-semver.ts +51 -51
  95. package/src/core/policies/engines/compute-engine-registry.ts +43 -43
  96. package/src/core/policies/engines/compute-evaluator-registry.ts +20 -20
  97. package/src/core/policies/engines/compute-registry.ts +45 -45
  98. package/src/core/policies/engines/compute-types.ts +15 -15
  99. package/src/core/policies/engines/condition-evaluator-reporter.ts +17 -17
  100. package/src/core/policies/engines/gate-engine-registry.ts +27 -25
  101. package/src/core/policies/engines/gate-types.ts +28 -28
  102. package/src/core/policies/engines/index.ts +28 -28
  103. package/src/core/policies/engines/parse-compute-payload.ts +51 -45
  104. package/src/core/policies/engines/parse-gate-payload.ts +42 -42
  105. package/src/core/policies/engines/v1/compute/compute-engine-v1.ts +39 -37
  106. package/src/core/policies/engines/v1/compute/compute-payload.schema.ts +43 -41
  107. package/src/core/policies/engines/v1/compute/index.ts +6 -6
  108. package/src/core/policies/engines/v1/compute/resolve-decision-table-v1.ts +41 -39
  109. package/src/core/policies/engines/v1/condition-evaluator.ts +557 -504
  110. package/src/core/policies/engines/v1/condition-schema.ts +35 -35
  111. package/src/core/policies/engines/v1/condition-types.ts +21 -21
  112. package/src/core/policies/engines/v1/gate/gate-engine-v1.ts +134 -130
  113. package/src/core/policies/engines/v1/gate/gate-types-v1.ts +5 -5
  114. package/src/core/policies/engines/v1/gate/gate-v1-payload.schema.ts +32 -30
  115. package/src/core/policies/engines/v1/gate/index.ts +4 -4
  116. package/src/core/policies/engines/v1/index.ts +8 -8
  117. package/src/core/policies/index.ts +72 -72
  118. package/src/core/policies/package/policy-package.ts +3 -3
  119. package/src/core/policies/policy-ids.ts +25 -25
  120. package/src/core/policies/resolver/policy-resolver.ts +34 -34
  121. package/src/core/policies/service/index.ts +6 -6
  122. package/src/core/policies/service/policy-evaluation-error.ts +141 -133
  123. package/src/core/policies/service/policy-service.ts +432 -419
  124. package/src/core/policies/utils/date.ts +3 -3
  125. package/src/core/policies/utils/hash.ts +61 -61
  126. package/src/core/result/outcome.ts +131 -126
  127. package/src/core/result/result.ts +130 -127
  128. package/src/core/shared/aggregate-version.ts +5 -5
  129. package/src/core/shared/hashing.ts +18 -18
  130. package/src/core/shared/immutable.ts +29 -29
  131. package/src/core/shared/temporal-guards.ts +9 -9
  132. package/src/core/versioning/domain-event-contracts.ts +40 -40
  133. package/src/core/versioning/version.ts +19 -19
  134. package/src/examples/rulesets/entity/order-creation-rules-v1.ts +18 -18
  135. package/src/examples/rulesets/entity/order-invariants-v1.ts +31 -29
  136. package/src/examples/rulesets/entity/order-invariants-v2.ts +12 -12
  137. package/src/examples/rulesets/entity/order.ts +127 -127
  138. package/src/examples/rulesets/value-object/cpf-rules-v1.ts +41 -39
  139. package/src/examples/rulesets/value-object/cpf-rules-v2.ts +14 -12
  140. package/src/examples/rulesets/value-object/cpf.ts +24 -24
  141. package/src/examples/rulesets/value-object/customer.ts +42 -39
  142. package/src/examples/rulesets/value-object/person-name-rules-v1.ts +27 -27
  143. package/src/examples/rulesets/value-object/person-name-rules-v2.ts +13 -13
  144. package/src/examples/rulesets/value-object/person-name.ts +29 -26
  145. package/src/index.ts +2 -2
  146. package/src/version.ts +1 -1
@@ -1,73 +1,74 @@
1
1
  import type {
2
- FindCandidatesParams,
3
- PolicyDefinition,
2
+ FindCandidatesParams,
3
+ PolicyDefinition,
4
4
  } from "./policy-definition";
5
5
  import type { PolicyDefinitionRepository } from "./policy-definition-repository";
6
6
  import { PolicyScopeMatcher } from "./policy-scope";
7
7
 
8
8
  export class InMemoryPolicyDefinitionRepository implements PolicyDefinitionRepository {
9
- private readonly definitions: readonly PolicyDefinition[];
10
- private readonly definitionsByPolicyKey: ReadonlyMap<
11
- string,
12
- readonly PolicyDefinition[]
13
- >;
9
+ private readonly definitions: readonly PolicyDefinition[];
10
+ private readonly definitionsByPolicyKey: ReadonlyMap<
11
+ string,
12
+ readonly PolicyDefinition[]
13
+ >;
14
14
 
15
- constructor(definitions: readonly PolicyDefinition[]) {
16
- this.definitions = definitions;
17
- this.definitionsByPolicyKey = definitions.reduce<
18
- Map<string, PolicyDefinition[]>
19
- >((index, definition) => {
20
- const existingDefinitions = index.get(definition.policyKey) ?? [];
21
- existingDefinitions.push(definition);
22
- index.set(definition.policyKey, existingDefinitions);
23
- return index;
24
- }, new Map<string, PolicyDefinition[]>());
25
- }
15
+ constructor(definitions: readonly PolicyDefinition[]) {
16
+ this.definitions = definitions;
17
+ this.definitionsByPolicyKey = definitions.reduce<
18
+ Map<string, PolicyDefinition[]>
19
+ >((index, definition) => {
20
+ const existingDefinitions = index.get(definition.policyKey) ?? [];
21
+ existingDefinitions.push(definition);
22
+ index.set(definition.policyKey, existingDefinitions);
23
+ return index;
24
+ }, new Map<string, PolicyDefinition[]>());
25
+ }
26
26
 
27
- findCandidates(params: FindCandidatesParams): PolicyDefinition[] {
28
- const {
29
- policyKey,
30
- kind,
31
- payloadSchemaVersion,
32
- asOf,
33
- contextVersion,
34
- scopeChain,
35
- } = params;
27
+ findCandidates(params: FindCandidatesParams): PolicyDefinition[] {
28
+ const {
29
+ policyKey,
30
+ kind,
31
+ payloadSchemaVersion,
32
+ asOf,
33
+ contextVersion,
34
+ scopeChain,
35
+ } = params;
36
36
 
37
- const candidatesForPolicyKey =
38
- this.definitionsByPolicyKey.get(policyKey) ?? [];
37
+ const candidatesForPolicyKey =
38
+ this.definitionsByPolicyKey.get(policyKey) ?? [];
39
39
 
40
- const candidates = candidatesForPolicyKey.filter((def) => {
41
- // Match kind
42
- if (def.kind !== kind) return false;
43
- if (
44
- payloadSchemaVersion !== undefined &&
45
- def.payloadSchemaVersion !== payloadSchemaVersion
46
- ) {
47
- return false;
48
- }
40
+ const candidates = candidatesForPolicyKey.filter((def) => {
41
+ // Match kind
42
+ if (def.kind !== kind) return false;
43
+ if (
44
+ payloadSchemaVersion !== undefined &&
45
+ def.payloadSchemaVersion !== payloadSchemaVersion
46
+ ) {
47
+ return false;
48
+ }
49
49
 
50
- if (!def.enabled) return false;
50
+ if (!def.enabled) return false;
51
51
 
52
- // Only PUBLISHED
53
- if (def.status !== "PUBLISHED") return false;
52
+ // Only PUBLISHED
53
+ if (def.status !== "PUBLISHED") return false;
54
54
 
55
- // Effective date range (effectiveFrom <= asOf, and effectiveTo is null or > asOf)
56
- if (def.effectiveFrom > asOf) return false;
57
- if (def.effectiveTo !== null && def.effectiveTo <= asOf) return false;
55
+ // Effective date range (effectiveFrom <= asOf, and effectiveTo is null or > asOf)
56
+ if (def.effectiveFrom > asOf) return false;
57
+ if (def.effectiveTo !== null && def.effectiveTo <= asOf)
58
+ return false;
58
59
 
59
- // Context version compatibility
60
- if (contextVersion < def.contextVersionMin) return false;
61
- if (contextVersion > def.contextVersionMax) return false;
60
+ // Context version compatibility
61
+ if (contextVersion < def.contextVersionMin) return false;
62
+ if (contextVersion > def.contextVersionMax) return false;
62
63
 
63
- // Scope must match one of the chain entries
64
- if (!PolicyScopeMatcher.matchesChain(def.scope, scopeChain)) {
65
- return false;
66
- }
64
+ // Scope must match one of the chain entries
65
+ if (!PolicyScopeMatcher.matchesChain(def.scope, scopeChain)) {
66
+ return false;
67
+ }
67
68
 
68
- return true;
69
- });
69
+ return true;
70
+ });
70
71
 
71
- return candidates;
72
- }
72
+ return candidates;
73
+ }
73
74
  }
@@ -1,19 +1,19 @@
1
1
  export { InMemoryPolicyDefinitionRepository } from "./in-memory-policy-definition-repo";
2
2
  export type {
3
- BasePolicyDefinitionProps,
4
- ComputePolicyDefinitionInput,
5
- ComputePolicyDefinitionProps,
6
- FindCandidatesParams,
7
- GatePolicyDefinitionInput,
8
- GatePolicyDefinitionProps,
9
- PolicyDefinitionProps,
10
- PolicyDefinitionStatus,
3
+ BasePolicyDefinitionProps,
4
+ ComputePolicyDefinitionInput,
5
+ ComputePolicyDefinitionProps,
6
+ FindCandidatesParams,
7
+ GatePolicyDefinitionInput,
8
+ GatePolicyDefinitionProps,
9
+ PolicyDefinitionProps,
10
+ PolicyDefinitionStatus,
11
11
  } from "./policy-definition";
12
12
  export { PolicyDefinition } from "./policy-definition";
13
13
  export type { PolicyDefinitionRepository } from "./policy-definition-repository";
14
14
  export type {
15
- AnyPolicyPayload,
16
- PayloadForKey,
15
+ AnyPolicyPayload,
16
+ PayloadForKey,
17
17
  } from "./policy-payload.contracts";
18
18
  export { comparePolicySemver, POLICY_SEMVER_PATTERN } from "./policy-semver";
19
19
  export type { PolicyScope, ScopeChain } from "./policy-scope";
@@ -1,8 +1,8 @@
1
1
  import type {
2
- FindCandidatesParams,
3
- PolicyDefinition,
2
+ FindCandidatesParams,
3
+ PolicyDefinition,
4
4
  } from "./policy-definition";
5
5
 
6
6
  export interface PolicyDefinitionRepository {
7
- findCandidates(params: FindCandidatesParams): PolicyDefinition[];
7
+ findCandidates(params: FindCandidatesParams): PolicyDefinition[];
8
8
  }
@@ -13,180 +13,181 @@ export type PolicyDefinitionStatus = "DRAFT" | "PUBLISHED" | "RETIRED";
13
13
  // ─── Definition props ───────────────────────────────────────────────────────
14
14
 
15
15
  export interface BasePolicyDefinitionProps<
16
- K extends string = string,
17
- P extends AnyPolicyPayload = AnyPolicyPayload,
16
+ K extends string = string,
17
+ P extends AnyPolicyPayload = AnyPolicyPayload,
18
18
  > {
19
- readonly id: PolicyDefinitionId;
20
- readonly policyKey: K;
21
- readonly policyVersion: string; // semver
22
- readonly payloadSchemaVersion: number;
23
- readonly contextVersionMin: number;
24
- readonly contextVersionMax: number;
25
- readonly enabled: boolean;
26
- readonly status: PolicyDefinitionStatus;
27
- readonly scope: PolicyScope;
28
- readonly effectiveFrom: Date;
29
- readonly effectiveTo: Date | null;
30
- readonly priority: number;
31
- readonly payloadJson: P;
32
- readonly payloadHash: string;
33
- readonly createdAt: Date;
34
- readonly publishedAt: Date | null;
19
+ readonly id: PolicyDefinitionId;
20
+ readonly policyKey: K;
21
+ readonly policyVersion: string; // semver
22
+ readonly payloadSchemaVersion: number;
23
+ readonly contextVersionMin: number;
24
+ readonly contextVersionMax: number;
25
+ readonly enabled: boolean;
26
+ readonly status: PolicyDefinitionStatus;
27
+ readonly scope: PolicyScope;
28
+ readonly effectiveFrom: Date;
29
+ readonly effectiveTo: Date | null;
30
+ readonly priority: number;
31
+ readonly payloadJson: P;
32
+ readonly payloadHash: string;
33
+ readonly createdAt: Date;
34
+ readonly publishedAt: Date | null;
35
35
  }
36
36
 
37
37
  export interface GatePolicyDefinitionProps<
38
- K extends string = string,
39
- P extends AnyPolicyPayload = AnyPolicyPayload,
38
+ K extends string = string,
39
+ P extends AnyPolicyPayload = AnyPolicyPayload,
40
40
  > extends BasePolicyDefinitionProps<K, P> {
41
- readonly kind: "GATE";
42
- readonly gateEngineVersion: number;
41
+ readonly kind: "GATE";
42
+ readonly gateEngineVersion: number;
43
43
  }
44
44
 
45
45
  export interface ComputePolicyDefinitionProps<
46
- K extends string = string,
47
- P extends AnyPolicyPayload = AnyPolicyPayload,
46
+ K extends string = string,
47
+ P extends AnyPolicyPayload = AnyPolicyPayload,
48
48
  > extends BasePolicyDefinitionProps<K, P> {
49
- readonly kind: "COMPUTE";
50
- readonly computeEngineVersion: number;
49
+ readonly kind: "COMPUTE";
50
+ readonly computeEngineVersion: number;
51
51
  }
52
52
 
53
53
  export type GatePolicyDefinitionInput<
54
- K extends string = string,
55
- P extends AnyPolicyPayload = AnyPolicyPayload,
54
+ K extends string = string,
55
+ P extends AnyPolicyPayload = AnyPolicyPayload,
56
56
  > = Omit<GatePolicyDefinitionProps<K, P>, "kind" | "enabled"> & {
57
- readonly enabled?: boolean;
57
+ readonly enabled?: boolean;
58
58
  };
59
59
 
60
60
  export type ComputePolicyDefinitionInput<
61
- K extends string = string,
62
- P extends AnyPolicyPayload = AnyPolicyPayload,
61
+ K extends string = string,
62
+ P extends AnyPolicyPayload = AnyPolicyPayload,
63
63
  > = Omit<ComputePolicyDefinitionProps<K, P>, "kind" | "enabled"> & {
64
- readonly enabled?: boolean;
64
+ readonly enabled?: boolean;
65
65
  };
66
66
 
67
67
  export type PolicyDefinitionProps<
68
- K extends string = string,
69
- P extends AnyPolicyPayload = AnyPolicyPayload,
68
+ K extends string = string,
69
+ P extends AnyPolicyPayload = AnyPolicyPayload,
70
70
  > = GatePolicyDefinitionProps<K, P> | ComputePolicyDefinitionProps<K, P>;
71
71
 
72
72
  // ─── Definition row (as if loaded from database) ────────────────────────────
73
73
 
74
74
  export class PolicyDefinition<
75
- K extends string = string,
76
- P extends AnyPolicyPayload = AnyPolicyPayload,
75
+ K extends string = string,
76
+ P extends AnyPolicyPayload = AnyPolicyPayload,
77
77
  > {
78
- private static assertPolicyVersionIsSemver(policyVersion: string): void {
79
- if (!POLICY_SEMVER_PATTERN.test(policyVersion)) {
80
- throw new InvariantViolationException(
81
- `PolicyDefinition.policyVersion must be a valid semver. Received: "${policyVersion}"`,
82
- );
78
+ private static assertPolicyVersionIsSemver(policyVersion: string): void {
79
+ if (!POLICY_SEMVER_PATTERN.test(policyVersion)) {
80
+ throw new InvariantViolationException(
81
+ `PolicyDefinition.policyVersion must be a valid semver. Received: "${policyVersion}"`,
82
+ );
83
+ }
84
+ }
85
+
86
+ public readonly id: PolicyDefinitionId;
87
+ public readonly policyKey: K;
88
+
89
+ public readonly policyVersion: string;
90
+ public readonly payloadSchemaVersion: number;
91
+ public readonly contextVersionMin: number;
92
+ public readonly contextVersionMax: number;
93
+ public readonly gateEngineVersion?: number;
94
+ public readonly computeEngineVersion?: number;
95
+
96
+ public readonly enabled: boolean;
97
+ public readonly status: PolicyDefinitionStatus;
98
+ public readonly scope: PolicyScope;
99
+ public readonly effectiveFrom: Date;
100
+ public readonly effectiveTo: Date | null;
101
+ public readonly priority: number;
102
+ public readonly payloadJson: P;
103
+ public readonly payloadHash: string;
104
+ public readonly createdAt: Date;
105
+ public readonly publishedAt: Date | null;
106
+ public readonly kind: PolicyKind;
107
+
108
+ private constructor(props: PolicyDefinitionProps<K, P>) {
109
+ // The static factories are the supported API. This guard stays here because
110
+ // TypeScript privacy does not protect runtime-only construction paths.
111
+ this.id = props.id;
112
+ this.policyKey = props.policyKey;
113
+ PolicyDefinition.assertPolicyVersionIsSemver(props.policyVersion);
114
+ this.policyVersion = props.policyVersion;
115
+ this.payloadSchemaVersion = props.payloadSchemaVersion;
116
+ this.contextVersionMin = props.contextVersionMin;
117
+ this.contextVersionMax = props.contextVersionMax;
118
+ this.enabled = props.enabled;
119
+ this.status = props.status;
120
+ this.scope = props.scope;
121
+ this.effectiveFrom = props.effectiveFrom;
122
+ this.effectiveTo = props.effectiveTo;
123
+ this.priority = props.priority;
124
+ this.payloadJson = props.payloadJson;
125
+ this.payloadHash = props.payloadHash;
126
+ this.createdAt = props.createdAt;
127
+ this.publishedAt = props.publishedAt;
128
+ this.kind = props.kind;
129
+
130
+ if (props.kind === "GATE") {
131
+ if (props.gateEngineVersion === undefined) {
132
+ throw new InvariantViolationException(
133
+ `PolicyDefinition of kind 'GATE' must have a 'gateEngineVersion'. Policy key: ${props.policyKey}`,
134
+ );
135
+ }
136
+ this.gateEngineVersion = props.gateEngineVersion;
137
+ } else {
138
+ if (props.computeEngineVersion === undefined) {
139
+ throw new InvariantViolationException(
140
+ `PolicyDefinition of kind 'COMPUTE' must have a 'computeEngineVersion'. Policy key: ${props.policyKey}`,
141
+ );
142
+ }
143
+ this.computeEngineVersion = props.computeEngineVersion;
144
+ }
83
145
  }
84
- }
85
-
86
- public readonly id: PolicyDefinitionId;
87
- public readonly policyKey: K;
88
-
89
- public readonly policyVersion: string;
90
- public readonly payloadSchemaVersion: number;
91
- public readonly contextVersionMin: number;
92
- public readonly contextVersionMax: number;
93
- public readonly gateEngineVersion?: number;
94
- public readonly computeEngineVersion?: number;
95
-
96
- public readonly enabled: boolean;
97
- public readonly status: PolicyDefinitionStatus;
98
- public readonly scope: PolicyScope;
99
- public readonly effectiveFrom: Date;
100
- public readonly effectiveTo: Date | null;
101
- public readonly priority: number;
102
- public readonly payloadJson: P;
103
- public readonly payloadHash: string;
104
- public readonly createdAt: Date;
105
- public readonly publishedAt: Date | null;
106
- public readonly kind: PolicyKind;
107
-
108
- private constructor(props: PolicyDefinitionProps<K, P>) {
109
- // The static factories are the supported API. This guard stays here because
110
- // TypeScript privacy does not protect runtime-only construction paths.
111
- this.id = props.id;
112
- this.policyKey = props.policyKey;
113
- PolicyDefinition.assertPolicyVersionIsSemver(props.policyVersion);
114
- this.policyVersion = props.policyVersion;
115
- this.payloadSchemaVersion = props.payloadSchemaVersion;
116
- this.contextVersionMin = props.contextVersionMin;
117
- this.contextVersionMax = props.contextVersionMax;
118
- this.enabled = props.enabled;
119
- this.status = props.status;
120
- this.scope = props.scope;
121
- this.effectiveFrom = props.effectiveFrom;
122
- this.effectiveTo = props.effectiveTo;
123
- this.priority = props.priority;
124
- this.payloadJson = props.payloadJson;
125
- this.payloadHash = props.payloadHash;
126
- this.createdAt = props.createdAt;
127
- this.publishedAt = props.publishedAt;
128
- this.kind = props.kind;
129
-
130
- if (props.kind === "GATE") {
131
- if (props.gateEngineVersion === undefined) {
132
- throw new InvariantViolationException(
133
- `PolicyDefinition of kind 'GATE' must have a 'gateEngineVersion'. Policy key: ${props.policyKey}`,
134
- );
135
- }
136
- this.gateEngineVersion = props.gateEngineVersion;
137
- } else {
138
- if (props.computeEngineVersion === undefined) {
139
- throw new InvariantViolationException(
140
- `PolicyDefinition of kind 'COMPUTE' must have a 'computeEngineVersion'. Policy key: ${props.policyKey}`,
141
- );
142
- }
143
- this.computeEngineVersion = props.computeEngineVersion;
146
+
147
+ static gate<
148
+ K extends string,
149
+ P extends AnyPolicyPayload = AnyPolicyPayload,
150
+ >(props: GatePolicyDefinitionInput<K, P>): PolicyDefinition<K, P> {
151
+ return new PolicyDefinition<K, P>({
152
+ ...props,
153
+ enabled: props.enabled ?? true,
154
+ kind: "GATE",
155
+ });
144
156
  }
145
- }
146
-
147
- static gate<K extends string, P extends AnyPolicyPayload = AnyPolicyPayload>(
148
- props: GatePolicyDefinitionInput<K, P>,
149
- ): PolicyDefinition<K, P> {
150
- return new PolicyDefinition<K, P>({
151
- ...props,
152
- enabled: props.enabled ?? true,
153
- kind: "GATE",
154
- });
155
- }
156
-
157
- static compute<
158
- K extends string,
159
- P extends AnyPolicyPayload = AnyPolicyPayload,
160
- >(props: ComputePolicyDefinitionInput<K, P>): PolicyDefinition<K, P> {
161
- return new PolicyDefinition<K, P>({
162
- ...props,
163
- enabled: props.enabled ?? true,
164
- kind: "COMPUTE",
165
- });
166
- }
167
-
168
- isGate(): this is PolicyDefinition<K, P> & {
169
- readonly kind: "GATE";
170
- readonly gateEngineVersion: number;
171
- } {
172
- return this.kind === "GATE";
173
- }
174
157
 
175
- isCompute(): this is PolicyDefinition<K, P> & {
176
- readonly kind: "COMPUTE";
177
- readonly computeEngineVersion: number;
178
- } {
179
- return this.kind === "COMPUTE";
180
- }
158
+ static compute<
159
+ K extends string,
160
+ P extends AnyPolicyPayload = AnyPolicyPayload,
161
+ >(props: ComputePolicyDefinitionInput<K, P>): PolicyDefinition<K, P> {
162
+ return new PolicyDefinition<K, P>({
163
+ ...props,
164
+ enabled: props.enabled ?? true,
165
+ kind: "COMPUTE",
166
+ });
167
+ }
168
+
169
+ isGate(): this is PolicyDefinition<K, P> & {
170
+ readonly kind: "GATE";
171
+ readonly gateEngineVersion: number;
172
+ } {
173
+ return this.kind === "GATE";
174
+ }
175
+
176
+ isCompute(): this is PolicyDefinition<K, P> & {
177
+ readonly kind: "COMPUTE";
178
+ readonly computeEngineVersion: number;
179
+ } {
180
+ return this.kind === "COMPUTE";
181
+ }
181
182
  }
182
183
 
183
184
  // ─── Query parameters ───────────────────────────────────────────────────────
184
185
 
185
186
  export interface FindCandidatesParams {
186
- readonly policyKey: string;
187
- readonly kind: PolicyKind;
188
- readonly payloadSchemaVersion?: number;
189
- readonly asOf: Date;
190
- readonly contextVersion: number;
191
- readonly scopeChain: readonly PolicyScope[];
187
+ readonly policyKey: string;
188
+ readonly kind: PolicyKind;
189
+ readonly payloadSchemaVersion?: number;
190
+ readonly asOf: Date;
191
+ readonly contextVersion: number;
192
+ readonly scopeChain: readonly PolicyScope[];
192
193
  }
@@ -5,9 +5,9 @@ import type { SchoolId, TenantId } from "../policy-ids";
5
5
  * Scope attached to a PolicyDefinition — defines where it applies.
6
6
  */
7
7
  export interface PolicyScope {
8
- readonly level: PolicyScopeLevel;
9
- readonly tenantId: TenantId | null;
10
- readonly schoolId: SchoolId | null;
8
+ readonly level: PolicyScopeLevel;
9
+ readonly tenantId: TenantId | null;
10
+ readonly schoolId: SchoolId | null;
11
11
  }
12
12
 
13
13
  /**
@@ -29,25 +29,27 @@ export type ScopeChain = readonly PolicyScope[];
29
29
  * Numeric weight for scope specificity (higher = more specific).
30
30
  */
31
31
  const SCOPE_WEIGHT: Record<PolicyScopeLevel, number> = {
32
- SCHOOL: 3,
33
- TENANT: 2,
34
- GLOBAL: 1,
32
+ SCHOOL: 3,
33
+ TENANT: 2,
34
+ GLOBAL: 1,
35
35
  };
36
36
 
37
37
  export class PolicyScopeMatcher {
38
- static weight(level: PolicyScopeLevel): number {
39
- return SCOPE_WEIGHT[level];
40
- }
38
+ static weight(level: PolicyScopeLevel): number {
39
+ return SCOPE_WEIGHT[level];
40
+ }
41
41
 
42
- static matchesChain(defScope: PolicyScope, chain: ScopeChain): boolean {
43
- return chain.some((scope) => PolicyScopeMatcher.equals(defScope, scope));
44
- }
42
+ static matchesChain(defScope: PolicyScope, chain: ScopeChain): boolean {
43
+ return chain.some((scope) =>
44
+ PolicyScopeMatcher.equals(defScope, scope),
45
+ );
46
+ }
45
47
 
46
- static equals(a: PolicyScope, b: PolicyScope): boolean {
47
- return (
48
- a.level === b.level &&
49
- a.tenantId === b.tenantId &&
50
- a.schoolId === b.schoolId
51
- );
52
- }
48
+ static equals(a: PolicyScope, b: PolicyScope): boolean {
49
+ return (
50
+ a.level === b.level &&
51
+ a.tenantId === b.tenantId &&
52
+ a.schoolId === b.schoolId
53
+ );
54
+ }
53
55
  }