@cullet/erp-core 1.3.0 → 1.4.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.
Files changed (166) hide show
  1. package/KIT_CONTEXT.md +5 -3
  2. package/README.md +160 -0
  3. package/dist/abac/index.cjs +7 -0
  4. package/dist/abac/index.d.cts +2 -0
  5. package/dist/abac/index.d.ts +2 -0
  6. package/dist/abac/index.js +2 -0
  7. package/dist/app-error.cjs +193 -0
  8. package/dist/app-error.cjs.map +1 -0
  9. package/dist/app-error.js +170 -0
  10. package/dist/app-error.js.map +1 -0
  11. package/dist/application/index.cjs +2 -1
  12. package/dist/application/index.d.cts +2 -1
  13. package/dist/application/index.d.ts +2 -1
  14. package/dist/application/index.js +2 -1
  15. package/dist/authorization-error.cjs +157 -0
  16. package/dist/authorization-error.cjs.map +1 -0
  17. package/dist/authorization-error.d.cts +113 -0
  18. package/dist/authorization-error.d.ts +113 -0
  19. package/dist/authorization-error.js +152 -0
  20. package/dist/authorization-error.js.map +1 -0
  21. package/dist/authorizer.port.d.cts +130 -0
  22. package/dist/authorizer.port.d.ts +130 -0
  23. package/dist/composite-authorizer.d.cts +216 -0
  24. package/dist/composite-authorizer.d.ts +216 -0
  25. package/dist/condition-evaluator.cjs +565 -0
  26. package/dist/condition-evaluator.cjs.map +1 -0
  27. package/dist/condition-evaluator.js +536 -0
  28. package/dist/condition-evaluator.js.map +1 -0
  29. package/dist/{gate-types.d.ts → core-config.d.cts} +78 -77
  30. package/dist/{gate-types.d.cts → core-config.d.ts} +78 -77
  31. package/dist/domain/index.cjs +2 -1
  32. package/dist/domain/index.d.cts +2 -1
  33. package/dist/domain/index.d.ts +2 -1
  34. package/dist/domain/index.js +2 -1
  35. package/dist/domain-event-contracts.cjs +3 -2
  36. package/dist/domain-event-contracts.cjs.map +1 -1
  37. package/dist/domain-event-contracts.js +2 -1
  38. package/dist/domain-event-contracts.js.map +1 -1
  39. package/dist/entity.cjs +126 -0
  40. package/dist/entity.cjs.map +1 -0
  41. package/dist/entity.js +115 -0
  42. package/dist/entity.js.map +1 -0
  43. package/dist/errors/index.cjs +8 -6
  44. package/dist/errors/index.d.cts +2 -1
  45. package/dist/errors/index.d.ts +2 -1
  46. package/dist/errors/index.js +4 -2
  47. package/dist/gate-engine-registry.cjs +5 -4
  48. package/dist/gate-engine-registry.cjs.map +1 -1
  49. package/dist/gate-engine-registry.d.cts +3 -2
  50. package/dist/gate-engine-registry.d.ts +3 -2
  51. package/dist/gate-engine-registry.js +2 -1
  52. package/dist/gate-engine-registry.js.map +1 -1
  53. package/dist/gate-v1-payload.schema.cjs +0 -562
  54. package/dist/gate-v1-payload.schema.cjs.map +1 -1
  55. package/dist/gate-v1-payload.schema.js +1 -533
  56. package/dist/gate-v1-payload.schema.js.map +1 -1
  57. package/dist/index.cjs +34 -16
  58. package/dist/index.cjs.map +1 -1
  59. package/dist/index.d.cts +19 -12
  60. package/dist/index.d.ts +19 -12
  61. package/dist/index.js +15 -9
  62. package/dist/index.js.map +1 -1
  63. package/dist/not-found-error.cjs +5 -5
  64. package/dist/not-found-error.cjs.map +1 -1
  65. package/dist/not-found-error.js +1 -1
  66. package/dist/outcome.d.cts +1 -83
  67. package/dist/outcome.d.ts +1 -83
  68. package/dist/parse-gate-payload.d.cts +2 -2
  69. package/dist/parse-gate-payload.d.ts +2 -2
  70. package/dist/path.d.cts +2 -2
  71. package/dist/path.d.ts +2 -2
  72. package/dist/policies/engines/index.d.cts +1 -1
  73. package/dist/policies/engines/index.d.ts +1 -1
  74. package/dist/policies/engines/v1/gate/index.cjs +5 -4
  75. package/dist/policies/engines/v1/gate/index.cjs.map +1 -1
  76. package/dist/policies/engines/v1/gate/index.d.cts +2 -2
  77. package/dist/policies/engines/v1/gate/index.d.ts +2 -2
  78. package/dist/policies/engines/v1/gate/index.js +2 -1
  79. package/dist/policies/engines/v1/gate/index.js.map +1 -1
  80. package/dist/policies/index.cjs +5 -5
  81. package/dist/policies/index.d.cts +3 -2
  82. package/dist/policies/index.d.ts +3 -2
  83. package/dist/policies/index.js +2 -2
  84. package/dist/policy-bridge.cjs +437 -0
  85. package/dist/policy-bridge.cjs.map +1 -0
  86. package/dist/policy-bridge.d.cts +185 -0
  87. package/dist/policy-bridge.d.ts +185 -0
  88. package/dist/policy-bridge.js +396 -0
  89. package/dist/policy-bridge.js.map +1 -0
  90. package/dist/policy-service.cjs +239 -239
  91. package/dist/policy-service.cjs.map +1 -1
  92. package/dist/policy-service.d.cts +2 -2
  93. package/dist/policy-service.d.ts +2 -2
  94. package/dist/policy-service.js +239 -239
  95. package/dist/policy-service.js.map +1 -1
  96. package/dist/rbac/index.cjs +9 -0
  97. package/dist/rbac/index.d.cts +3 -0
  98. package/dist/rbac/index.d.ts +3 -0
  99. package/dist/rbac/index.js +2 -0
  100. package/dist/requested-by.cjs +54 -0
  101. package/dist/requested-by.cjs.map +1 -0
  102. package/dist/requested-by.d.cts +25 -0
  103. package/dist/requested-by.d.ts +25 -0
  104. package/dist/requested-by.js +49 -0
  105. package/dist/requested-by.js.map +1 -0
  106. package/dist/result/index.d.cts +2 -1
  107. package/dist/result/index.d.ts +2 -1
  108. package/dist/result.d.cts +84 -0
  109. package/dist/result.d.ts +84 -0
  110. package/dist/rule.cjs +327 -0
  111. package/dist/rule.cjs.map +1 -0
  112. package/dist/rule.js +298 -0
  113. package/dist/rule.js.map +1 -0
  114. package/dist/temporal-use-case.cjs +1 -53
  115. package/dist/temporal-use-case.cjs.map +1 -1
  116. package/dist/temporal-use-case.d.cts +5 -27
  117. package/dist/temporal-use-case.d.ts +5 -27
  118. package/dist/temporal-use-case.js +2 -48
  119. package/dist/temporal-use-case.js.map +1 -1
  120. package/dist/unexpected-error.cjs +3 -192
  121. package/dist/unexpected-error.cjs.map +1 -1
  122. package/dist/unexpected-error.js +2 -167
  123. package/dist/unexpected-error.js.map +1 -1
  124. package/dist/uuid-identifier.d.cts +2 -85
  125. package/dist/uuid-identifier.d.ts +2 -85
  126. package/dist/validation-error.cjs +33 -166
  127. package/dist/validation-error.cjs.map +1 -1
  128. package/dist/validation-error.d.cts +2 -98
  129. package/dist/validation-error.d.ts +2 -98
  130. package/dist/validation-error.js +7 -134
  131. package/dist/validation-error.js.map +1 -1
  132. package/dist/value-object.cjs +0 -123
  133. package/dist/value-object.cjs.map +1 -1
  134. package/dist/value-object.d.cts +89 -0
  135. package/dist/value-object.d.ts +89 -0
  136. package/dist/value-object.js +1 -112
  137. package/dist/value-object.js.map +1 -1
  138. package/meta.json +18 -4
  139. package/package.json +27 -1
  140. package/src/abac/index.ts +1 -0
  141. package/src/core/abac/abac-request.ts +29 -0
  142. package/src/core/abac/attributes.ts +39 -0
  143. package/src/core/abac/authorizer.ts +108 -0
  144. package/src/core/abac/combining.ts +63 -0
  145. package/src/core/abac/composite-authorizer.ts +45 -0
  146. package/src/core/abac/domain/policy-set.ts +52 -0
  147. package/src/core/abac/domain/rule.ts +197 -0
  148. package/src/core/abac/index.ts +21 -0
  149. package/src/core/application/ports/abac-authorizer.port.ts +20 -0
  150. package/src/core/application/ports/authorizer.port.ts +22 -0
  151. package/src/core/errors/authorization-error.ts +26 -0
  152. package/src/core/errors/error-codes.ts +1 -0
  153. package/src/core/index.ts +7 -0
  154. package/src/core/rbac/access-request.ts +32 -0
  155. package/src/core/rbac/authorizer.ts +91 -0
  156. package/src/core/rbac/domain/grant.ts +96 -0
  157. package/src/core/rbac/domain/permission-set.ts +67 -0
  158. package/src/core/rbac/domain/permission.ts +119 -0
  159. package/src/core/rbac/domain/role.ts +101 -0
  160. package/src/core/rbac/domain/scope.ts +84 -0
  161. package/src/core/rbac/index.ts +15 -0
  162. package/src/core/rbac/policy-bridge.ts +44 -0
  163. package/src/examples/application/authorize-cancel-order-abac.example.ts +107 -0
  164. package/src/examples/application/authorize-cancel-order.example.ts +101 -0
  165. package/src/rbac/index.ts +1 -0
  166. package/src/version.ts +1 -1
@@ -1,12 +1,248 @@
1
- const require_unexpected_error = require("./unexpected-error.cjs");
2
1
  const require_hashing = require("./hashing.cjs");
2
+ const require_unexpected_error = require("./unexpected-error.cjs");
3
3
  const require_validation_code = require("./validation-code.cjs");
4
4
  const require_validation_field = require("./validation-field.cjs");
5
5
  const require_invariant_violation_exception = require("./invariant-violation-exception.cjs");
6
6
  const require_validation_exception = require("./validation-exception.cjs");
7
7
  const require_temporal_guards = require("./temporal-guards.cjs");
8
- const require_gate_v1_payload_schema = require("./gate-v1-payload.schema.cjs");
9
8
  const require_result = require("./result.cjs");
9
+ const require_condition_evaluator = require("./condition-evaluator.cjs");
10
+ //#region src/core/policies/context/context-builder.ts
11
+ const DEFAULT_RESOLVER_RETRY_OPTIONS = {
12
+ maxAttempts: 1,
13
+ initialDelayMs: 25,
14
+ maxDelayMs: 1e3,
15
+ backoffMultiplier: 2
16
+ };
17
+ /**
18
+ * Builds a context object by resolving required paths from a seed.
19
+ */
20
+ var PolicyContextBuilder = class PolicyContextBuilder {
21
+ constructor(registry, options = {}) {
22
+ this.registry = registry;
23
+ this.circuitStates = /* @__PURE__ */ new Map();
24
+ this.defaultResolverResilience = options.defaultResolverResilience;
25
+ this.now = options.now ?? Date.now;
26
+ this.sleep = options.sleep ?? (async (delayMs) => {
27
+ if (delayMs <= 0) return;
28
+ await new Promise((resolve) => {
29
+ setTimeout(resolve, delayMs);
30
+ });
31
+ });
32
+ }
33
+ static clampPositiveInteger(value, fallback) {
34
+ if (value === void 0 || !Number.isFinite(value)) return fallback;
35
+ return Math.max(1, Math.trunc(value));
36
+ }
37
+ static clampNonNegativeInteger(value, fallback) {
38
+ if (value === void 0 || !Number.isFinite(value)) return fallback;
39
+ return Math.max(0, Math.trunc(value));
40
+ }
41
+ static normalizeTimeoutMs(value) {
42
+ if (value === void 0 || !Number.isFinite(value) || value <= 0) return null;
43
+ return Math.trunc(value);
44
+ }
45
+ static mergeRetryOptions(defaults, overrides) {
46
+ if (overrides === false) return false;
47
+ if (overrides !== void 0) return {
48
+ ...defaults === false || defaults === void 0 ? {} : defaults,
49
+ ...overrides
50
+ };
51
+ return defaults;
52
+ }
53
+ static mergeCircuitBreakerOptions(defaults, overrides) {
54
+ if (overrides === false) return false;
55
+ if (overrides !== void 0) return {
56
+ ...defaults === false || defaults === void 0 ? {} : defaults,
57
+ ...overrides
58
+ };
59
+ return defaults;
60
+ }
61
+ resolveResilienceOptions(resolver) {
62
+ const defaults = this.defaultResolverResilience;
63
+ const override = resolver.resilience;
64
+ const retry = PolicyContextBuilder.mergeRetryOptions(defaults?.retry, override?.retry);
65
+ const circuitBreaker = PolicyContextBuilder.mergeCircuitBreakerOptions(defaults?.circuitBreaker, override?.circuitBreaker);
66
+ const retryOptions = retry === false ? void 0 : retry;
67
+ const circuitBreakerOptions = circuitBreaker === false ? void 0 : circuitBreaker;
68
+ return {
69
+ timeoutMs: PolicyContextBuilder.normalizeTimeoutMs(override?.timeoutMs ?? defaults?.timeoutMs),
70
+ retry: {
71
+ maxAttempts: PolicyContextBuilder.clampPositiveInteger(retryOptions?.maxAttempts, DEFAULT_RESOLVER_RETRY_OPTIONS.maxAttempts),
72
+ initialDelayMs: PolicyContextBuilder.clampNonNegativeInteger(retryOptions?.initialDelayMs, DEFAULT_RESOLVER_RETRY_OPTIONS.initialDelayMs),
73
+ maxDelayMs: PolicyContextBuilder.clampNonNegativeInteger(retryOptions?.maxDelayMs, DEFAULT_RESOLVER_RETRY_OPTIONS.maxDelayMs),
74
+ backoffMultiplier: PolicyContextBuilder.clampPositiveInteger(retryOptions?.backoffMultiplier, DEFAULT_RESOLVER_RETRY_OPTIONS.backoffMultiplier)
75
+ },
76
+ circuitBreaker: circuitBreakerOptions === void 0 ? null : {
77
+ failureThreshold: PolicyContextBuilder.clampPositiveInteger(circuitBreakerOptions.failureThreshold, 5),
78
+ cooldownMs: PolicyContextBuilder.clampPositiveInteger(circuitBreakerOptions.cooldownMs, 3e4)
79
+ }
80
+ };
81
+ }
82
+ getRetryDelayMs(attempt, retry) {
83
+ return Math.min(retry.initialDelayMs * retry.backoffMultiplier ** (attempt - 1), retry.maxDelayMs);
84
+ }
85
+ resetCircuit(path) {
86
+ this.circuitStates.delete(path);
87
+ }
88
+ isCircuitOpen(path, circuitBreaker) {
89
+ if (circuitBreaker === null) return false;
90
+ const state = this.circuitStates.get(path);
91
+ if (state === void 0 || state.openUntilMs === null) return false;
92
+ if (state.openUntilMs <= this.now()) {
93
+ this.resetCircuit(path);
94
+ return false;
95
+ }
96
+ return true;
97
+ }
98
+ recordResolverFailure(path, circuitBreaker) {
99
+ if (circuitBreaker === null) return;
100
+ const nextFailureCount = (this.circuitStates.get(path)?.failureCount ?? 0) + 1;
101
+ if (nextFailureCount >= circuitBreaker.failureThreshold) {
102
+ this.circuitStates.set(path, {
103
+ failureCount: 0,
104
+ openUntilMs: this.now() + circuitBreaker.cooldownMs
105
+ });
106
+ return;
107
+ }
108
+ this.circuitStates.set(path, {
109
+ failureCount: nextFailureCount,
110
+ openUntilMs: null
111
+ });
112
+ }
113
+ static describeThrownResolverError(error) {
114
+ if (error instanceof Error) return `threw ${error.name}: ${error.message}`;
115
+ return `threw ${String(error)}`;
116
+ }
117
+ async resolveOnce(resolver, seed, timeoutMs) {
118
+ const execute = async () => {
119
+ try {
120
+ return await resolver.resolve(seed);
121
+ } catch (error) {
122
+ return require_result.Result.err(PolicyContextBuilder.describeThrownResolverError(error));
123
+ }
124
+ };
125
+ if (timeoutMs === null) return execute();
126
+ let timeoutId;
127
+ try {
128
+ return await Promise.race([execute(), new Promise((resolve) => {
129
+ timeoutId = setTimeout(() => {
130
+ resolve(require_result.Result.err(`timed out after ${timeoutMs}ms`));
131
+ }, timeoutMs);
132
+ })]);
133
+ } finally {
134
+ if (timeoutId !== void 0) clearTimeout(timeoutId);
135
+ }
136
+ }
137
+ async resolveWithResilience(path, resolver, seed) {
138
+ const resilience = this.resolveResilienceOptions(resolver);
139
+ if (this.isCircuitOpen(path, resilience.circuitBreaker)) return require_result.Result.err("circuit breaker is open");
140
+ let lastError = "resolver failed";
141
+ for (let attempt = 1; attempt <= resilience.retry.maxAttempts; attempt++) {
142
+ const result = await this.resolveOnce(resolver, seed, resilience.timeoutMs);
143
+ if (result.isOk()) {
144
+ this.resetCircuit(path);
145
+ return result;
146
+ }
147
+ lastError = result.errorOrNull() ?? lastError;
148
+ if (attempt < resilience.retry.maxAttempts) await this.sleep(this.getRetryDelayMs(attempt, resilience.retry));
149
+ }
150
+ this.recordResolverFailure(path, resilience.circuitBreaker);
151
+ return require_result.Result.err(lastError);
152
+ }
153
+ /**
154
+ * Resolves all required context paths and returns a flat context object.
155
+ * If any required path cannot be resolved, returns an error.
156
+ */
157
+ async build(requirements, seed) {
158
+ const context = {};
159
+ for (const path of requirements) {
160
+ const resolver = this.registry.get(path);
161
+ if (!resolver) return require_result.Result.err(`Missing resolver for path "${path}"`);
162
+ const result = await this.resolveWithResilience(path, resolver, seed);
163
+ if (result.isErr()) return require_result.Result.err(`Resolver error for path "${path}": ${result.errorOrNull()}`);
164
+ const setPathResult = require_condition_evaluator.PolicyContextPath.set(context, path, result.getOrNull());
165
+ if (setPathResult.isErr()) return require_result.Result.err(setPathResult.errorOrNull());
166
+ }
167
+ const missing = requirements.filter((path) => {
168
+ const result = require_condition_evaluator.PolicyContextPath.getOrAbsent(context, path);
169
+ return result.isErr() || result.getOrNull() === void 0;
170
+ });
171
+ if (missing.length > 0) return require_result.Result.err(`Context missing required paths after resolution: ${missing.join(", ")}`);
172
+ return require_result.Result.ok(context);
173
+ }
174
+ };
175
+ //#endregion
176
+ //#region src/core/policies/context/context-registry.ts
177
+ const DEFAULT_CONTEXT_RESOLVER_NAMESPACE = "default";
178
+ function normalizeNamespace(namespace) {
179
+ const normalized = namespace?.trim();
180
+ if (normalized === void 0 || normalized.length === 0) return DEFAULT_CONTEXT_RESOLVER_NAMESPACE;
181
+ return normalized;
182
+ }
183
+ /**
184
+ * Registry of context value resolvers, keyed by path.
185
+ */
186
+ var ContextResolverRegistry = class {
187
+ constructor() {
188
+ this.resolvers = /* @__PURE__ */ new Map();
189
+ }
190
+ register(resolver, options = {}) {
191
+ const namespace = normalizeNamespace(options.namespace);
192
+ const existing = this.resolvers.get(resolver.path);
193
+ if (existing !== void 0 && existing.namespace !== namespace) return require_result.Result.err(`Resolver path "${resolver.path}" is already registered by namespace "${existing.namespace}" and cannot be replaced by "${namespace}".`);
194
+ this.resolvers.set(resolver.path, {
195
+ namespace,
196
+ resolver
197
+ });
198
+ return require_result.Result.ok(void 0);
199
+ }
200
+ registerAll(resolvers, options = {}) {
201
+ for (const r of resolvers) {
202
+ const registrationResult = this.register(r, options);
203
+ if (registrationResult.isErr()) return registrationResult;
204
+ }
205
+ return require_result.Result.ok(void 0);
206
+ }
207
+ get(path) {
208
+ return this.resolvers.get(path)?.resolver;
209
+ }
210
+ has(path) {
211
+ return this.resolvers.has(path);
212
+ }
213
+ getNamespace(path) {
214
+ return this.resolvers.get(path)?.namespace;
215
+ }
216
+ };
217
+ function registerNamespacedContextResolversIn(registry, namespace, resolvers) {
218
+ const registrationResult = registry.registerAll(resolvers, { namespace });
219
+ if (registrationResult.isErr()) return require_result.Result.err(registrationResult.errorOrNull());
220
+ return require_result.Result.ok(registry);
221
+ }
222
+ //#endregion
223
+ //#region src/core/policies/context/context-registry.instance.ts
224
+ /**
225
+ * Official instance of the ContextResolverRegistry.
226
+ * Use `new ContextResolverRegistry()` plus `registerNamespacedContextResolversIn`
227
+ * when each runtime/composition root needs its own isolated resolver graph.
228
+ */
229
+ const contextResolverRegistry = new ContextResolverRegistry();
230
+ function registerNamespacedContextResolvers(namespace, resolvers) {
231
+ return registerNamespacedContextResolversIn(contextResolverRegistry, namespace, resolvers);
232
+ }
233
+ //#endregion
234
+ //#region src/core/policies/context/context-seed.ts
235
+ var ContextSeedValidator = class ContextSeedValidator {
236
+ static isBlankSeedId(value) {
237
+ return value.trim().length === 0;
238
+ }
239
+ static validate(seed) {
240
+ if (ContextSeedValidator.isBlankSeedId(seed.tenantId)) return require_result.Result.err("ContextSeed tenantId must be a non-empty string");
241
+ if (ContextSeedValidator.isBlankSeedId(seed.schoolId)) return require_result.Result.err("ContextSeed schoolId must be a non-empty string");
242
+ return require_result.Result.ok(seed);
243
+ }
244
+ };
245
+ //#endregion
10
246
  //#region src/core/policies/utils/hash.ts
11
247
  /**
12
248
  * Produces a deterministic SHA-256 hex digest of a canonical JSON representation.
@@ -487,242 +723,6 @@ var PolicyDefinition = class PolicyDefinition {
487
723
  }
488
724
  };
489
725
  //#endregion
490
- //#region src/core/policies/context/context-builder.ts
491
- const DEFAULT_RESOLVER_RETRY_OPTIONS = {
492
- maxAttempts: 1,
493
- initialDelayMs: 25,
494
- maxDelayMs: 1e3,
495
- backoffMultiplier: 2
496
- };
497
- /**
498
- * Builds a context object by resolving required paths from a seed.
499
- */
500
- var PolicyContextBuilder = class PolicyContextBuilder {
501
- constructor(registry, options = {}) {
502
- this.registry = registry;
503
- this.circuitStates = /* @__PURE__ */ new Map();
504
- this.defaultResolverResilience = options.defaultResolverResilience;
505
- this.now = options.now ?? Date.now;
506
- this.sleep = options.sleep ?? (async (delayMs) => {
507
- if (delayMs <= 0) return;
508
- await new Promise((resolve) => {
509
- setTimeout(resolve, delayMs);
510
- });
511
- });
512
- }
513
- static clampPositiveInteger(value, fallback) {
514
- if (value === void 0 || !Number.isFinite(value)) return fallback;
515
- return Math.max(1, Math.trunc(value));
516
- }
517
- static clampNonNegativeInteger(value, fallback) {
518
- if (value === void 0 || !Number.isFinite(value)) return fallback;
519
- return Math.max(0, Math.trunc(value));
520
- }
521
- static normalizeTimeoutMs(value) {
522
- if (value === void 0 || !Number.isFinite(value) || value <= 0) return null;
523
- return Math.trunc(value);
524
- }
525
- static mergeRetryOptions(defaults, overrides) {
526
- if (overrides === false) return false;
527
- if (overrides !== void 0) return {
528
- ...defaults === false || defaults === void 0 ? {} : defaults,
529
- ...overrides
530
- };
531
- return defaults;
532
- }
533
- static mergeCircuitBreakerOptions(defaults, overrides) {
534
- if (overrides === false) return false;
535
- if (overrides !== void 0) return {
536
- ...defaults === false || defaults === void 0 ? {} : defaults,
537
- ...overrides
538
- };
539
- return defaults;
540
- }
541
- resolveResilienceOptions(resolver) {
542
- const defaults = this.defaultResolverResilience;
543
- const override = resolver.resilience;
544
- const retry = PolicyContextBuilder.mergeRetryOptions(defaults?.retry, override?.retry);
545
- const circuitBreaker = PolicyContextBuilder.mergeCircuitBreakerOptions(defaults?.circuitBreaker, override?.circuitBreaker);
546
- const retryOptions = retry === false ? void 0 : retry;
547
- const circuitBreakerOptions = circuitBreaker === false ? void 0 : circuitBreaker;
548
- return {
549
- timeoutMs: PolicyContextBuilder.normalizeTimeoutMs(override?.timeoutMs ?? defaults?.timeoutMs),
550
- retry: {
551
- maxAttempts: PolicyContextBuilder.clampPositiveInteger(retryOptions?.maxAttempts, DEFAULT_RESOLVER_RETRY_OPTIONS.maxAttempts),
552
- initialDelayMs: PolicyContextBuilder.clampNonNegativeInteger(retryOptions?.initialDelayMs, DEFAULT_RESOLVER_RETRY_OPTIONS.initialDelayMs),
553
- maxDelayMs: PolicyContextBuilder.clampNonNegativeInteger(retryOptions?.maxDelayMs, DEFAULT_RESOLVER_RETRY_OPTIONS.maxDelayMs),
554
- backoffMultiplier: PolicyContextBuilder.clampPositiveInteger(retryOptions?.backoffMultiplier, DEFAULT_RESOLVER_RETRY_OPTIONS.backoffMultiplier)
555
- },
556
- circuitBreaker: circuitBreakerOptions === void 0 ? null : {
557
- failureThreshold: PolicyContextBuilder.clampPositiveInteger(circuitBreakerOptions.failureThreshold, 5),
558
- cooldownMs: PolicyContextBuilder.clampPositiveInteger(circuitBreakerOptions.cooldownMs, 3e4)
559
- }
560
- };
561
- }
562
- getRetryDelayMs(attempt, retry) {
563
- return Math.min(retry.initialDelayMs * retry.backoffMultiplier ** (attempt - 1), retry.maxDelayMs);
564
- }
565
- resetCircuit(path) {
566
- this.circuitStates.delete(path);
567
- }
568
- isCircuitOpen(path, circuitBreaker) {
569
- if (circuitBreaker === null) return false;
570
- const state = this.circuitStates.get(path);
571
- if (state === void 0 || state.openUntilMs === null) return false;
572
- if (state.openUntilMs <= this.now()) {
573
- this.resetCircuit(path);
574
- return false;
575
- }
576
- return true;
577
- }
578
- recordResolverFailure(path, circuitBreaker) {
579
- if (circuitBreaker === null) return;
580
- const nextFailureCount = (this.circuitStates.get(path)?.failureCount ?? 0) + 1;
581
- if (nextFailureCount >= circuitBreaker.failureThreshold) {
582
- this.circuitStates.set(path, {
583
- failureCount: 0,
584
- openUntilMs: this.now() + circuitBreaker.cooldownMs
585
- });
586
- return;
587
- }
588
- this.circuitStates.set(path, {
589
- failureCount: nextFailureCount,
590
- openUntilMs: null
591
- });
592
- }
593
- static describeThrownResolverError(error) {
594
- if (error instanceof Error) return `threw ${error.name}: ${error.message}`;
595
- return `threw ${String(error)}`;
596
- }
597
- async resolveOnce(resolver, seed, timeoutMs) {
598
- const execute = async () => {
599
- try {
600
- return await resolver.resolve(seed);
601
- } catch (error) {
602
- return require_result.Result.err(PolicyContextBuilder.describeThrownResolverError(error));
603
- }
604
- };
605
- if (timeoutMs === null) return execute();
606
- let timeoutId;
607
- try {
608
- return await Promise.race([execute(), new Promise((resolve) => {
609
- timeoutId = setTimeout(() => {
610
- resolve(require_result.Result.err(`timed out after ${timeoutMs}ms`));
611
- }, timeoutMs);
612
- })]);
613
- } finally {
614
- if (timeoutId !== void 0) clearTimeout(timeoutId);
615
- }
616
- }
617
- async resolveWithResilience(path, resolver, seed) {
618
- const resilience = this.resolveResilienceOptions(resolver);
619
- if (this.isCircuitOpen(path, resilience.circuitBreaker)) return require_result.Result.err("circuit breaker is open");
620
- let lastError = "resolver failed";
621
- for (let attempt = 1; attempt <= resilience.retry.maxAttempts; attempt++) {
622
- const result = await this.resolveOnce(resolver, seed, resilience.timeoutMs);
623
- if (result.isOk()) {
624
- this.resetCircuit(path);
625
- return result;
626
- }
627
- lastError = result.errorOrNull() ?? lastError;
628
- if (attempt < resilience.retry.maxAttempts) await this.sleep(this.getRetryDelayMs(attempt, resilience.retry));
629
- }
630
- this.recordResolverFailure(path, resilience.circuitBreaker);
631
- return require_result.Result.err(lastError);
632
- }
633
- /**
634
- * Resolves all required context paths and returns a flat context object.
635
- * If any required path cannot be resolved, returns an error.
636
- */
637
- async build(requirements, seed) {
638
- const context = {};
639
- for (const path of requirements) {
640
- const resolver = this.registry.get(path);
641
- if (!resolver) return require_result.Result.err(`Missing resolver for path "${path}"`);
642
- const result = await this.resolveWithResilience(path, resolver, seed);
643
- if (result.isErr()) return require_result.Result.err(`Resolver error for path "${path}": ${result.errorOrNull()}`);
644
- const setPathResult = require_gate_v1_payload_schema.PolicyContextPath.set(context, path, result.getOrNull());
645
- if (setPathResult.isErr()) return require_result.Result.err(setPathResult.errorOrNull());
646
- }
647
- const missing = requirements.filter((path) => {
648
- const result = require_gate_v1_payload_schema.PolicyContextPath.getOrAbsent(context, path);
649
- return result.isErr() || result.getOrNull() === void 0;
650
- });
651
- if (missing.length > 0) return require_result.Result.err(`Context missing required paths after resolution: ${missing.join(", ")}`);
652
- return require_result.Result.ok(context);
653
- }
654
- };
655
- //#endregion
656
- //#region src/core/policies/context/context-registry.ts
657
- const DEFAULT_CONTEXT_RESOLVER_NAMESPACE = "default";
658
- function normalizeNamespace(namespace) {
659
- const normalized = namespace?.trim();
660
- if (normalized === void 0 || normalized.length === 0) return DEFAULT_CONTEXT_RESOLVER_NAMESPACE;
661
- return normalized;
662
- }
663
- /**
664
- * Registry of context value resolvers, keyed by path.
665
- */
666
- var ContextResolverRegistry = class {
667
- constructor() {
668
- this.resolvers = /* @__PURE__ */ new Map();
669
- }
670
- register(resolver, options = {}) {
671
- const namespace = normalizeNamespace(options.namespace);
672
- const existing = this.resolvers.get(resolver.path);
673
- if (existing !== void 0 && existing.namespace !== namespace) return require_result.Result.err(`Resolver path "${resolver.path}" is already registered by namespace "${existing.namespace}" and cannot be replaced by "${namespace}".`);
674
- this.resolvers.set(resolver.path, {
675
- namespace,
676
- resolver
677
- });
678
- return require_result.Result.ok(void 0);
679
- }
680
- registerAll(resolvers, options = {}) {
681
- for (const r of resolvers) {
682
- const registrationResult = this.register(r, options);
683
- if (registrationResult.isErr()) return registrationResult;
684
- }
685
- return require_result.Result.ok(void 0);
686
- }
687
- get(path) {
688
- return this.resolvers.get(path)?.resolver;
689
- }
690
- has(path) {
691
- return this.resolvers.has(path);
692
- }
693
- getNamespace(path) {
694
- return this.resolvers.get(path)?.namespace;
695
- }
696
- };
697
- function registerNamespacedContextResolversIn(registry, namespace, resolvers) {
698
- const registrationResult = registry.registerAll(resolvers, { namespace });
699
- if (registrationResult.isErr()) return require_result.Result.err(registrationResult.errorOrNull());
700
- return require_result.Result.ok(registry);
701
- }
702
- //#endregion
703
- //#region src/core/policies/context/context-registry.instance.ts
704
- /**
705
- * Official instance of the ContextResolverRegistry.
706
- * Use `new ContextResolverRegistry()` plus `registerNamespacedContextResolversIn`
707
- * when each runtime/composition root needs its own isolated resolver graph.
708
- */
709
- const contextResolverRegistry = new ContextResolverRegistry();
710
- function registerNamespacedContextResolvers(namespace, resolvers) {
711
- return registerNamespacedContextResolversIn(contextResolverRegistry, namespace, resolvers);
712
- }
713
- //#endregion
714
- //#region src/core/policies/context/context-seed.ts
715
- var ContextSeedValidator = class ContextSeedValidator {
716
- static isBlankSeedId(value) {
717
- return value.trim().length === 0;
718
- }
719
- static validate(seed) {
720
- if (ContextSeedValidator.isBlankSeedId(seed.tenantId)) return require_result.Result.err("ContextSeed tenantId must be a non-empty string");
721
- if (ContextSeedValidator.isBlankSeedId(seed.schoolId)) return require_result.Result.err("ContextSeed schoolId must be a non-empty string");
722
- return require_result.Result.ok(seed);
723
- }
724
- };
725
- //#endregion
726
726
  //#region src/core/policies/asof/asof.ts
727
727
  const DEFAULT_MAX_AS_OF_FUTURE_YEARS = 10;
728
728
  var PolicyAsOfResolver = class PolicyAsOfResolver {
@@ -875,7 +875,7 @@ var PolicyService = class {
875
875
  this.gateEngines = params.gateEngines;
876
876
  this.computeRegistry = params.computeRegistry;
877
877
  this.options = params.options ?? {};
878
- this.#reporter = this.options.reporter ?? new require_gate_v1_payload_schema.SilentPolicyReporter();
878
+ this.#reporter = this.options.reporter ?? new require_condition_evaluator.SilentPolicyReporter();
879
879
  }
880
880
  #reportSafely(event) {
881
881
  try {