@cullet/erp-core 1.0.2 → 1.0.3

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 (144) 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.map +1 -1
  4. package/dist/index.d.ts +2 -2
  5. package/dist/index.js +1 -1
  6. package/dist/index.js.map +1 -1
  7. package/dist/outcome.js.map +1 -1
  8. package/dist/policies/engines/v1/gate/index.js.map +1 -1
  9. package/dist/policy-service.js.map +1 -1
  10. package/dist/validation-code.js.map +1 -1
  11. package/dist/validation-error.js.map +1 -1
  12. package/dist/validation-field.js.map +1 -1
  13. package/meta.json +67 -67
  14. package/package.json +56 -56
  15. package/src/core/application/commands/command.ts +3 -3
  16. package/src/core/application/commands/requested-by.ts +60 -60
  17. package/src/core/application/index.ts +21 -21
  18. package/src/core/application/policy-error-mapper.ts +54 -54
  19. package/src/core/application/ports/index.ts +6 -6
  20. package/src/core/application/ports/logger.port.ts +4 -4
  21. package/src/core/application/ports/metrics.port.ts +3 -3
  22. package/src/core/application/ports/policy-port.ts +7 -7
  23. package/src/core/application/ports/repository.port.ts +3 -3
  24. package/src/core/application/ports/temporal-repository.port.ts +4 -4
  25. package/src/core/application/ports/tracer.port.ts +7 -7
  26. package/src/core/application/queries/query.ts +13 -13
  27. package/src/core/application/temporal/index.ts +7 -7
  28. package/src/core/application/temporal/temporal-context.ts +20 -20
  29. package/src/core/application/temporal/temporal-use-case.ts +22 -22
  30. package/src/core/application/use-case.ts +10 -10
  31. package/src/core/config/core-config.instance.ts +1 -1
  32. package/src/core/config/core-config.ts +59 -59
  33. package/src/core/config/index.ts +6 -6
  34. package/src/core/config/policy-reporter.ts +31 -31
  35. package/src/core/config/silent-policy-reporter.ts +1 -1
  36. package/src/core/domain/entity.ts +44 -44
  37. package/src/core/domain/rulesets/entity-ruleset.contracts.ts +6 -6
  38. package/src/core/domain/rulesets/ruleset-registry.ts +49 -49
  39. package/src/core/domain/rulesets/ruleset.contracts.ts +6 -6
  40. package/src/core/domain/rulesets/value-object-ruleset.contracts.ts +5 -5
  41. package/src/core/domain/temporal/index.ts +11 -11
  42. package/src/core/domain/temporal/temporal-range.ts +16 -16
  43. package/src/core/domain/temporal/temporal-snapshot.ts +24 -24
  44. package/src/core/domain/temporal/transaction-time.ts +30 -30
  45. package/src/core/domain/temporal/valid-time.ts +29 -29
  46. package/src/core/domain/value-object.ts +16 -16
  47. package/src/core/errors/app-error.ts +64 -64
  48. package/src/core/errors/authentication-error.ts +134 -134
  49. package/src/core/errors/authorization-error.ts +154 -154
  50. package/src/core/errors/business-rule-violation-error.ts +15 -15
  51. package/src/core/errors/conflict-error.ts +202 -201
  52. package/src/core/errors/error-codes.ts +48 -48
  53. package/src/core/errors/idempotency-error.ts +324 -320
  54. package/src/core/errors/index.ts +50 -50
  55. package/src/core/errors/integration-error.ts +120 -120
  56. package/src/core/errors/legacy-incompatible-error.ts +13 -13
  57. package/src/core/errors/not-found-error.ts +14 -14
  58. package/src/core/errors/serialization-error.ts +167 -167
  59. package/src/core/errors/temporal-error.ts +109 -107
  60. package/src/core/errors/types.ts +35 -35
  61. package/src/core/errors/unexpected-error.ts +10 -10
  62. package/src/core/errors/utils/index.ts +2 -2
  63. package/src/core/errors/utils/json-safe.ts +53 -53
  64. package/src/core/errors/validation-error.ts +18 -18
  65. package/src/core/exceptions/business-rule-violation-exception.ts +6 -6
  66. package/src/core/exceptions/domain-exception.ts +5 -5
  67. package/src/core/exceptions/entity-not-found-exception.ts +6 -6
  68. package/src/core/exceptions/invalid-state-transition-exception.ts +8 -8
  69. package/src/core/exceptions/invariant-violation-exception.ts +3 -3
  70. package/src/core/exceptions/validation-code.ts +35 -29
  71. package/src/core/exceptions/validation-exception.ts +19 -19
  72. package/src/core/exceptions/validation-field.ts +20 -19
  73. package/src/core/index.ts +7 -7
  74. package/src/core/policies/asof/asof.ts +47 -47
  75. package/src/core/policies/catalog/catalog.instance.ts +8 -6
  76. package/src/core/policies/catalog/index.ts +4 -4
  77. package/src/core/policies/catalog/policy-catalog-entry.ts +199 -197
  78. package/src/core/policies/catalog/policy-catalog.ts +101 -101
  79. package/src/core/policies/catalog/policy-key.ts +51 -51
  80. package/src/core/policies/context/context-builder.ts +335 -313
  81. package/src/core/policies/context/context-registry.instance.ts +9 -9
  82. package/src/core/policies/context/context-registry.ts +61 -61
  83. package/src/core/policies/context/context-resolver.ts +12 -12
  84. package/src/core/policies/context/context-seed.ts +19 -15
  85. package/src/core/policies/context/index.ts +9 -9
  86. package/src/core/policies/context/path.ts +135 -128
  87. package/src/core/policies/defs/in-memory-policy-definition-repo.ts +56 -55
  88. package/src/core/policies/defs/index.ts +10 -10
  89. package/src/core/policies/defs/policy-definition-repository.ts +3 -3
  90. package/src/core/policies/defs/policy-definition.ts +143 -142
  91. package/src/core/policies/defs/policy-scope.ts +21 -19
  92. package/src/core/policies/defs/policy-semver.ts +51 -51
  93. package/src/core/policies/engines/compute-engine-registry.ts +43 -43
  94. package/src/core/policies/engines/compute-evaluator-registry.ts +20 -20
  95. package/src/core/policies/engines/compute-registry.ts +45 -45
  96. package/src/core/policies/engines/compute-types.ts +15 -15
  97. package/src/core/policies/engines/condition-evaluator-reporter.ts +17 -17
  98. package/src/core/policies/engines/gate-engine-registry.ts +27 -25
  99. package/src/core/policies/engines/gate-types.ts +28 -28
  100. package/src/core/policies/engines/index.ts +28 -28
  101. package/src/core/policies/engines/parse-compute-payload.ts +51 -45
  102. package/src/core/policies/engines/parse-gate-payload.ts +42 -42
  103. package/src/core/policies/engines/v1/compute/compute-engine-v1.ts +39 -37
  104. package/src/core/policies/engines/v1/compute/compute-payload.schema.ts +43 -41
  105. package/src/core/policies/engines/v1/compute/index.ts +6 -6
  106. package/src/core/policies/engines/v1/compute/resolve-decision-table-v1.ts +41 -39
  107. package/src/core/policies/engines/v1/condition-evaluator.ts +547 -505
  108. package/src/core/policies/engines/v1/condition-schema.ts +35 -35
  109. package/src/core/policies/engines/v1/condition-types.ts +21 -21
  110. package/src/core/policies/engines/v1/gate/gate-engine-v1.ts +134 -130
  111. package/src/core/policies/engines/v1/gate/gate-types-v1.ts +5 -5
  112. package/src/core/policies/engines/v1/gate/gate-v1-payload.schema.ts +32 -30
  113. package/src/core/policies/engines/v1/gate/index.ts +4 -4
  114. package/src/core/policies/engines/v1/index.ts +8 -8
  115. package/src/core/policies/index.ts +72 -72
  116. package/src/core/policies/package/policy-package.ts +3 -3
  117. package/src/core/policies/policy-ids.ts +25 -25
  118. package/src/core/policies/resolver/policy-resolver.ts +34 -34
  119. package/src/core/policies/service/index.ts +6 -6
  120. package/src/core/policies/service/policy-evaluation-error.ts +141 -133
  121. package/src/core/policies/service/policy-service.ts +432 -419
  122. package/src/core/policies/utils/date.ts +3 -3
  123. package/src/core/policies/utils/hash.ts +61 -61
  124. package/src/core/result/outcome.ts +131 -126
  125. package/src/core/result/result.ts +130 -127
  126. package/src/core/shared/aggregate-version.ts +5 -5
  127. package/src/core/shared/hashing.ts +18 -18
  128. package/src/core/shared/immutable.ts +29 -29
  129. package/src/core/shared/temporal-guards.ts +9 -9
  130. package/src/core/versioning/domain-event-contracts.ts +40 -40
  131. package/src/core/versioning/version.ts +19 -19
  132. package/src/examples/rulesets/entity/order-creation-rules-v1.ts +18 -18
  133. package/src/examples/rulesets/entity/order-invariants-v1.ts +31 -29
  134. package/src/examples/rulesets/entity/order-invariants-v2.ts +12 -12
  135. package/src/examples/rulesets/entity/order.ts +127 -127
  136. package/src/examples/rulesets/value-object/cpf-rules-v1.ts +41 -39
  137. package/src/examples/rulesets/value-object/cpf-rules-v2.ts +14 -12
  138. package/src/examples/rulesets/value-object/cpf.ts +24 -24
  139. package/src/examples/rulesets/value-object/customer.ts +42 -39
  140. package/src/examples/rulesets/value-object/person-name-rules-v1.ts +27 -27
  141. package/src/examples/rulesets/value-object/person-name-rules-v2.ts +13 -13
  142. package/src/examples/rulesets/value-object/person-name.ts +29 -26
  143. package/src/index.ts +2 -2
  144. 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
  }