@cullet/erp-core 1.2.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.
- package/KIT_CONTEXT.md +5 -3
- package/README.md +160 -0
- package/dist/abac/index.cjs +7 -0
- package/dist/abac/index.d.cts +2 -0
- package/dist/abac/index.d.ts +2 -0
- package/dist/abac/index.js +2 -0
- package/dist/app-error.cjs +193 -0
- package/dist/app-error.cjs.map +1 -0
- package/dist/app-error.d.cts +108 -0
- package/dist/app-error.js +170 -0
- package/dist/app-error.js.map +1 -0
- package/dist/application/index.cjs +27 -0
- package/dist/application/index.d.cts +3 -0
- package/dist/application/index.d.ts +2 -1
- package/dist/application/index.js +2 -1
- package/dist/authorization-error.cjs +157 -0
- package/dist/authorization-error.cjs.map +1 -0
- package/dist/authorization-error.d.cts +113 -0
- package/dist/authorization-error.d.ts +113 -0
- package/dist/authorization-error.js +152 -0
- package/dist/authorization-error.js.map +1 -0
- package/dist/authorizer.port.d.cts +130 -0
- package/dist/authorizer.port.d.ts +130 -0
- package/dist/composite-authorizer.d.cts +216 -0
- package/dist/composite-authorizer.d.ts +216 -0
- package/dist/condition-evaluator.cjs +565 -0
- package/dist/condition-evaluator.cjs.map +1 -0
- package/dist/condition-evaluator.js +536 -0
- package/dist/condition-evaluator.js.map +1 -0
- package/dist/core-config.d.cts +172 -0
- package/dist/{gate-types.d.ts → core-config.d.ts} +78 -77
- package/dist/domain/index.cjs +17 -0
- package/dist/domain/index.d.cts +4 -0
- package/dist/domain/index.d.ts +2 -1
- package/dist/domain/index.js +2 -1
- package/dist/domain-event-contracts.cjs +34 -0
- package/dist/domain-event-contracts.cjs.map +1 -0
- package/dist/domain-event-contracts.d.cts +27 -0
- package/dist/domain-event-contracts.js +2 -1
- package/dist/domain-event-contracts.js.map +1 -1
- package/dist/domain-exception.cjs +17 -0
- package/dist/domain-exception.cjs.map +1 -0
- package/dist/domain-exception.d.cts +7 -0
- package/dist/entity.cjs +126 -0
- package/dist/entity.cjs.map +1 -0
- package/dist/entity.js +115 -0
- package/dist/entity.js.map +1 -0
- package/dist/errors/index.cjs +43 -0
- package/dist/errors/index.d.cts +4 -0
- package/dist/errors/index.d.ts +2 -1
- package/dist/errors/index.js +4 -2
- package/dist/exceptions/index.cjs +17 -0
- package/dist/exceptions/index.d.cts +5 -0
- package/dist/exceptions/validation-field.cjs +3 -0
- package/dist/exceptions/validation-field.d.cts +2 -0
- package/dist/gate-engine-registry.cjs +309 -0
- package/dist/gate-engine-registry.cjs.map +1 -0
- package/dist/gate-engine-registry.d.cts +82 -0
- package/dist/gate-engine-registry.d.ts +3 -2
- package/dist/gate-engine-registry.js +2 -1
- package/dist/gate-engine-registry.js.map +1 -1
- package/dist/gate-v1-payload.schema.cjs +76 -0
- package/dist/gate-v1-payload.schema.cjs.map +1 -0
- package/dist/gate-v1-payload.schema.js +1 -533
- package/dist/gate-v1-payload.schema.js.map +1 -1
- package/dist/hashing.cjs +66 -0
- package/dist/hashing.cjs.map +1 -0
- package/dist/immutable.cjs +77 -0
- package/dist/immutable.cjs.map +1 -0
- package/dist/immutable.d.cts +6 -0
- package/dist/index.cjs +181 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +37 -0
- package/dist/index.d.ts +19 -12
- package/dist/index.js +15 -9
- package/dist/index.js.map +1 -1
- package/dist/invalid-state-transition-exception.cjs +47 -0
- package/dist/invalid-state-transition-exception.cjs.map +1 -0
- package/dist/invariant-violation-exception.cjs +16 -0
- package/dist/invariant-violation-exception.cjs.map +1 -0
- package/dist/not-found-error.cjs +65 -0
- package/dist/not-found-error.cjs.map +1 -0
- package/dist/not-found-error.js +1 -1
- package/dist/outcome.cjs +97 -0
- package/dist/outcome.cjs.map +1 -0
- package/dist/outcome.d.cts +58 -0
- package/dist/outcome.d.ts +1 -83
- package/dist/parse-gate-payload.d.cts +62 -0
- package/dist/parse-gate-payload.d.ts +2 -2
- package/dist/path.d.cts +90 -0
- package/dist/path.d.ts +2 -2
- package/dist/plugin.cjs +79 -0
- package/dist/plugin.cjs.map +1 -0
- package/dist/plugin.d.cts +85 -0
- package/dist/plugins/index.cjs +3 -0
- package/dist/plugins/index.d.cts +2 -0
- package/dist/policies/engines/index.cjs +10 -0
- package/dist/policies/engines/index.d.cts +4 -0
- package/dist/policies/engines/index.d.ts +1 -1
- package/dist/policies/engines/v1/gate/index.cjs +92 -0
- package/dist/policies/engines/v1/gate/index.cjs.map +1 -0
- package/dist/policies/engines/v1/gate/index.d.cts +121 -0
- package/dist/policies/engines/v1/gate/index.d.ts +2 -2
- package/dist/policies/engines/v1/gate/index.js +2 -1
- package/dist/policies/engines/v1/gate/index.js.map +1 -1
- package/dist/policies/index.cjs +41 -0
- package/dist/policies/index.d.cts +8 -0
- package/dist/policies/index.d.ts +3 -2
- package/dist/policies/index.js +2 -2
- package/dist/policy-bridge.cjs +437 -0
- package/dist/policy-bridge.cjs.map +1 -0
- package/dist/policy-bridge.d.cts +185 -0
- package/dist/policy-bridge.d.ts +185 -0
- package/dist/policy-bridge.js +396 -0
- package/dist/policy-bridge.js.map +1 -0
- package/dist/policy-service.cjs +1190 -0
- package/dist/policy-service.cjs.map +1 -0
- package/dist/policy-service.d.cts +559 -0
- package/dist/policy-service.d.ts +2 -2
- package/dist/policy-service.js +239 -239
- package/dist/policy-service.js.map +1 -1
- package/dist/rbac/index.cjs +9 -0
- package/dist/rbac/index.d.cts +3 -0
- package/dist/rbac/index.d.ts +3 -0
- package/dist/rbac/index.js +2 -0
- package/dist/requested-by.cjs +54 -0
- package/dist/requested-by.cjs.map +1 -0
- package/dist/requested-by.d.cts +25 -0
- package/dist/requested-by.d.ts +25 -0
- package/dist/requested-by.js +49 -0
- package/dist/requested-by.js.map +1 -0
- package/dist/result/index.cjs +7 -0
- package/dist/result/index.d.cts +3 -0
- package/dist/result/index.d.ts +2 -1
- package/dist/result.cjs +135 -0
- package/dist/result.cjs.map +1 -0
- package/dist/result.d.cts +84 -0
- package/dist/result.d.ts +84 -0
- package/dist/rule.cjs +327 -0
- package/dist/rule.cjs.map +1 -0
- package/dist/rule.js +298 -0
- package/dist/rule.js.map +1 -0
- package/dist/ruleset-registry.cjs +47 -0
- package/dist/ruleset-registry.cjs.map +1 -0
- package/dist/rulesets/index.cjs +3 -0
- package/dist/rulesets/index.d.cts +2 -0
- package/dist/temporal-guards.cjs +32 -0
- package/dist/temporal-guards.cjs.map +1 -0
- package/dist/temporal-use-case.cjs +139 -0
- package/dist/temporal-use-case.cjs.map +1 -0
- package/dist/temporal-use-case.d.cts +282 -0
- package/dist/temporal-use-case.d.ts +5 -27
- package/dist/temporal-use-case.js +2 -48
- package/dist/temporal-use-case.js.map +1 -1
- package/dist/unexpected-error.cjs +19 -0
- package/dist/unexpected-error.cjs.map +1 -0
- package/dist/unexpected-error.js +2 -167
- package/dist/unexpected-error.js.map +1 -1
- package/dist/use-case.cjs +96 -0
- package/dist/use-case.cjs.map +1 -0
- package/dist/uuid-identifier.cjs +65 -0
- package/dist/uuid-identifier.cjs.map +1 -0
- package/dist/uuid-identifier.d.cts +147 -0
- package/dist/uuid-identifier.d.ts +2 -85
- package/dist/validation-code.cjs +60 -0
- package/dist/validation-code.cjs.map +1 -0
- package/dist/validation-code.d.cts +23 -0
- package/dist/validation-error.cjs +887 -0
- package/dist/validation-error.cjs.map +1 -0
- package/dist/validation-error.d.cts +681 -0
- package/dist/validation-error.d.ts +2 -98
- package/dist/validation-error.js +7 -134
- package/dist/validation-error.js.map +1 -1
- package/dist/validation-exception.cjs +41 -0
- package/dist/validation-exception.cjs.map +1 -0
- package/dist/validation-exception.d.cts +50 -0
- package/dist/validation-field.cjs +28 -0
- package/dist/validation-field.cjs.map +1 -0
- package/dist/validation-field.d.cts +12 -0
- package/dist/value-object-ruleset.contracts.d.cts +36 -0
- package/dist/value-object.cjs +85 -0
- package/dist/value-object.cjs.map +1 -0
- package/dist/value-object.d.cts +89 -0
- package/dist/value-object.d.ts +89 -0
- package/dist/value-object.js +1 -112
- package/dist/value-object.js.map +1 -1
- package/dist/version.d.cts +10 -0
- package/dist/versioning/index.cjs +7 -0
- package/dist/versioning/index.d.cts +3 -0
- package/meta.json +19 -4
- package/package.json +173 -28
- package/src/abac/index.ts +1 -0
- package/src/core/abac/abac-request.ts +29 -0
- package/src/core/abac/attributes.ts +39 -0
- package/src/core/abac/authorizer.ts +108 -0
- package/src/core/abac/combining.ts +63 -0
- package/src/core/abac/composite-authorizer.ts +45 -0
- package/src/core/abac/domain/policy-set.ts +52 -0
- package/src/core/abac/domain/rule.ts +197 -0
- package/src/core/abac/index.ts +21 -0
- package/src/core/application/ports/abac-authorizer.port.ts +20 -0
- package/src/core/application/ports/authorizer.port.ts +22 -0
- package/src/core/errors/authorization-error.ts +26 -0
- package/src/core/errors/error-codes.ts +1 -0
- package/src/core/index.ts +7 -0
- package/src/core/rbac/access-request.ts +32 -0
- package/src/core/rbac/authorizer.ts +91 -0
- package/src/core/rbac/domain/grant.ts +96 -0
- package/src/core/rbac/domain/permission-set.ts +67 -0
- package/src/core/rbac/domain/permission.ts +119 -0
- package/src/core/rbac/domain/role.ts +101 -0
- package/src/core/rbac/domain/scope.ts +84 -0
- package/src/core/rbac/index.ts +15 -0
- package/src/core/rbac/policy-bridge.ts +44 -0
- package/src/examples/application/authorize-cancel-order-abac.example.ts +107 -0
- package/src/examples/application/authorize-cancel-order.example.ts +101 -0
- package/src/rbac/index.ts +1 -0
- package/src/version.ts +1 -1
|
@@ -0,0 +1,559 @@
|
|
|
1
|
+
import { r as Result } from "./result.cjs";
|
|
2
|
+
import { O as GatePayload, a as PolicyDecisionId, c as TenantId, i as PolicyReporter, o as PolicyDefinitionId, s as SchoolId, y as GateOutcome } from "./core-config.cjs";
|
|
3
|
+
import { a as ComputeOutcome, l as ComputePayload, n as ComputeRegistry, t as GateEngineRegistry } from "./gate-engine-registry.cjs";
|
|
4
|
+
|
|
5
|
+
//#region src/core/policies/catalog/catalog-types.d.ts
|
|
6
|
+
type PolicyKind = "GATE" | "COMPUTE";
|
|
7
|
+
type PolicyOwner = "PLATFORM_OWNER" | "TENANT_ADMIN" | "SCHOOL_ADMIN";
|
|
8
|
+
type PolicyScopeLevel = "GLOBAL" | "TENANT" | "SCHOOL";
|
|
9
|
+
/**
|
|
10
|
+
* Defines where the "as of" date comes from for a given policy.
|
|
11
|
+
*/
|
|
12
|
+
type AsOfSource = "NOW" | "CALLER_PROVIDED";
|
|
13
|
+
//#endregion
|
|
14
|
+
//#region src/core/policies/catalog/policy-key.d.ts
|
|
15
|
+
declare class PolicyKey {
|
|
16
|
+
readonly module: string;
|
|
17
|
+
readonly area: string;
|
|
18
|
+
readonly name: string;
|
|
19
|
+
private constructor();
|
|
20
|
+
static parse(raw: string): Result<PolicyKey, string>;
|
|
21
|
+
toString(): string;
|
|
22
|
+
equals(other: PolicyKey): boolean;
|
|
23
|
+
}
|
|
24
|
+
//#endregion
|
|
25
|
+
//#region src/core/policies/catalog/policy-catalog-entry.d.ts
|
|
26
|
+
/**
|
|
27
|
+
* Properties for creating a PolicyCatalogEntry.
|
|
28
|
+
*/
|
|
29
|
+
interface PolicyCatalogEntryProps {
|
|
30
|
+
readonly key: PolicyKey;
|
|
31
|
+
readonly kind: PolicyKind;
|
|
32
|
+
readonly gateEngineVersion?: number;
|
|
33
|
+
readonly computeEngineVersion?: number;
|
|
34
|
+
readonly payloadSchemaVersion?: number;
|
|
35
|
+
readonly owner: PolicyOwner;
|
|
36
|
+
readonly allowedScopes: readonly PolicyScopeLevel[];
|
|
37
|
+
readonly asOfSource: AsOfSource;
|
|
38
|
+
/** Paths the context must contain for this policy to be evaluable. */
|
|
39
|
+
readonly contextRequirements: readonly string[];
|
|
40
|
+
readonly tags?: readonly string[];
|
|
41
|
+
readonly description: string;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Represents a single entry in the Policy Catalog.
|
|
45
|
+
* Encapsulates metadata and validation logic for a policy.
|
|
46
|
+
*/
|
|
47
|
+
declare class PolicyCatalogEntry {
|
|
48
|
+
readonly key: PolicyKey;
|
|
49
|
+
readonly kind: PolicyKind;
|
|
50
|
+
readonly gateEngineVersion?: number;
|
|
51
|
+
readonly computeEngineVersion?: number;
|
|
52
|
+
readonly payloadSchemaVersion?: number;
|
|
53
|
+
readonly owner: PolicyOwner;
|
|
54
|
+
readonly allowedScopes: readonly PolicyScopeLevel[];
|
|
55
|
+
readonly asOfSource: AsOfSource;
|
|
56
|
+
readonly contextRequirements: readonly string[];
|
|
57
|
+
readonly tags: readonly string[];
|
|
58
|
+
readonly description: string;
|
|
59
|
+
constructor(props: PolicyCatalogEntryProps);
|
|
60
|
+
static assertFamilyConsistency(entries: readonly PolicyCatalogEntry[]): void;
|
|
61
|
+
static from(entry: PolicyCatalogEntry | PolicyCatalogEntryProps): PolicyCatalogEntry;
|
|
62
|
+
isGate(): boolean;
|
|
63
|
+
isCompute(): boolean;
|
|
64
|
+
allowsScope(scope: PolicyScopeLevel): boolean;
|
|
65
|
+
hasExplicitVersionSelector(): boolean;
|
|
66
|
+
matchesVersion(params: {
|
|
67
|
+
readonly kind: PolicyKind;
|
|
68
|
+
readonly gateEngineVersion?: number;
|
|
69
|
+
readonly computeEngineVersion?: number;
|
|
70
|
+
readonly payloadSchemaVersion?: number;
|
|
71
|
+
}): boolean;
|
|
72
|
+
toVariantKey(): string;
|
|
73
|
+
usesNowAsOf(): boolean;
|
|
74
|
+
usesCallerProvidedAsOf(): boolean;
|
|
75
|
+
requiresContext(path: string): boolean;
|
|
76
|
+
requiresAllContexts(paths: readonly string[]): boolean;
|
|
77
|
+
hasTag(tag: string): boolean;
|
|
78
|
+
equals(other: PolicyCatalogEntry): boolean;
|
|
79
|
+
toJSON(): PolicyCatalogEntryProps;
|
|
80
|
+
}
|
|
81
|
+
//#endregion
|
|
82
|
+
//#region src/core/policies/catalog/policy-catalog.d.ts
|
|
83
|
+
/**
|
|
84
|
+
* In-code catalog of all known policies.
|
|
85
|
+
* The catalog defines the universe of policies; the database cannot invent new ones.
|
|
86
|
+
*/
|
|
87
|
+
declare class PolicyCatalog {
|
|
88
|
+
private readonly versionedEntries;
|
|
89
|
+
private readonly entriesByKey;
|
|
90
|
+
/**
|
|
91
|
+
* Indexes the given entries up front into two lookups — one per exact
|
|
92
|
+
* variant, one per policy key (the "family") — and validates them eagerly:
|
|
93
|
+
* a duplicate variant or an internally inconsistent family throws here, at
|
|
94
|
+
* wiring time, so a malformed catalog fails fast at startup rather than on
|
|
95
|
+
* the first evaluation.
|
|
96
|
+
*
|
|
97
|
+
* @throws {UnexpectedError} When two entries resolve to the same variant key.
|
|
98
|
+
*/
|
|
99
|
+
constructor(entries: readonly PolicyCatalogEntry[]);
|
|
100
|
+
/**
|
|
101
|
+
* Returns the single entry for a key. Deliberately strict: if the key has
|
|
102
|
+
* more than one variant it errs rather than guessing, steering the caller to
|
|
103
|
+
* {@link getVersioned} or {@link getFamily}. Use this only when a key is
|
|
104
|
+
* known to be unambiguous.
|
|
105
|
+
*
|
|
106
|
+
* @returns `ok` with the entry, or `err` when the key is unknown or ambiguous.
|
|
107
|
+
*/
|
|
108
|
+
get(key: string): Result<PolicyCatalogEntry, string>;
|
|
109
|
+
/**
|
|
110
|
+
* Returns every variant registered under a key — the whole "family". This is
|
|
111
|
+
* the lookup the evaluation pipeline uses, since a single key may host
|
|
112
|
+
* several engine/schema variants that the resolver then chooses between.
|
|
113
|
+
*
|
|
114
|
+
* @returns `ok` with the family (always non-empty), or `err` when the key is unknown.
|
|
115
|
+
*/
|
|
116
|
+
getFamily(key: string): Result<readonly PolicyCatalogEntry[], string>;
|
|
117
|
+
/**
|
|
118
|
+
* Resolves the one entry matching an exact variant — kind plus engine and
|
|
119
|
+
* payload-schema versions. Falls back to the sole family member when a key
|
|
120
|
+
* has exactly one entry and that entry declares no explicit version
|
|
121
|
+
* selector, so unversioned single-variant policies "just work" without the
|
|
122
|
+
* caller spelling out versions.
|
|
123
|
+
*
|
|
124
|
+
* @param params - The key and the variant coordinates to match on.
|
|
125
|
+
* @returns `ok` with the matching entry, or `err` when no variant matches.
|
|
126
|
+
*/
|
|
127
|
+
getVersioned(params: {
|
|
128
|
+
readonly key: string;
|
|
129
|
+
readonly kind: "GATE" | "COMPUTE";
|
|
130
|
+
readonly gateEngineVersion?: number;
|
|
131
|
+
readonly computeEngineVersion?: number;
|
|
132
|
+
readonly payloadSchemaVersion?: number;
|
|
133
|
+
}): Result<PolicyCatalogEntry, string>;
|
|
134
|
+
/** Lists every registered variant across all keys — useful for introspection and diagnostics. */
|
|
135
|
+
list(): readonly PolicyCatalogEntry[];
|
|
136
|
+
/** Returns whether any variant is registered under the given key. */
|
|
137
|
+
has(key: string): boolean;
|
|
138
|
+
}
|
|
139
|
+
//#endregion
|
|
140
|
+
//#region src/core/policies/defs/policy-payload.contracts.d.ts
|
|
141
|
+
type AnyPolicyPayload = GatePayload | ComputePayload;
|
|
142
|
+
/**
|
|
143
|
+
* Payload type associated with a policy key.
|
|
144
|
+
*
|
|
145
|
+
* Intentionally NOT backed by global module augmentation: two modules registering
|
|
146
|
+
* different shapes for the same literal key would silently override each other and
|
|
147
|
+
* the last `declare module` wins. Per-key payload typing must be expressed
|
|
148
|
+
* explicitly at the definition site (see {@link PolicyDefinition} type params).
|
|
149
|
+
*/
|
|
150
|
+
//#endregion
|
|
151
|
+
//#region src/core/policies/defs/policy-scope.d.ts
|
|
152
|
+
/**
|
|
153
|
+
* Scope attached to a PolicyDefinition — defines where it applies.
|
|
154
|
+
*/
|
|
155
|
+
interface PolicyScope {
|
|
156
|
+
readonly level: PolicyScopeLevel;
|
|
157
|
+
readonly tenantId: TenantId | null;
|
|
158
|
+
readonly schoolId: SchoolId | null;
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* Ordered list of scopes from most-specific to least-specific.
|
|
162
|
+
* Used by the resolver to match definitions.
|
|
163
|
+
*
|
|
164
|
+
* Example:
|
|
165
|
+
* ```
|
|
166
|
+
* [
|
|
167
|
+
* { level: 'SCHOOL', tenantId: 't1', schoolId: 's1' },
|
|
168
|
+
* { level: 'TENANT', tenantId: 't1', schoolId: null },
|
|
169
|
+
* { level: 'GLOBAL', tenantId: null, schoolId: null },
|
|
170
|
+
* ]
|
|
171
|
+
* ```
|
|
172
|
+
*/
|
|
173
|
+
type ScopeChain = readonly PolicyScope[];
|
|
174
|
+
declare class PolicyScopeMatcher {
|
|
175
|
+
static weight(level: PolicyScopeLevel): number;
|
|
176
|
+
static matchesChain(defScope: PolicyScope, chain: ScopeChain): boolean;
|
|
177
|
+
static equals(a: PolicyScope, b: PolicyScope): boolean;
|
|
178
|
+
}
|
|
179
|
+
//#endregion
|
|
180
|
+
//#region src/core/policies/defs/policy-definition.d.ts
|
|
181
|
+
type PolicyDefinitionStatus = "DRAFT" | "PUBLISHED" | "RETIRED";
|
|
182
|
+
interface BasePolicyDefinitionProps<K extends string = string, P extends AnyPolicyPayload = AnyPolicyPayload> {
|
|
183
|
+
readonly id: PolicyDefinitionId;
|
|
184
|
+
readonly policyKey: K;
|
|
185
|
+
readonly policyVersion: string;
|
|
186
|
+
readonly payloadSchemaVersion: number;
|
|
187
|
+
readonly contextVersionMin: number;
|
|
188
|
+
readonly contextVersionMax: number;
|
|
189
|
+
readonly enabled: boolean;
|
|
190
|
+
readonly status: PolicyDefinitionStatus;
|
|
191
|
+
readonly scope: PolicyScope;
|
|
192
|
+
readonly effectiveFrom: Date;
|
|
193
|
+
readonly effectiveTo: Date | null;
|
|
194
|
+
readonly priority: number;
|
|
195
|
+
readonly payloadJson: P;
|
|
196
|
+
readonly payloadHash: string;
|
|
197
|
+
readonly createdAt: Date;
|
|
198
|
+
readonly publishedAt: Date | null;
|
|
199
|
+
}
|
|
200
|
+
interface GatePolicyDefinitionProps<K extends string = string, P extends AnyPolicyPayload = AnyPolicyPayload> extends BasePolicyDefinitionProps<K, P> {
|
|
201
|
+
readonly kind: "GATE";
|
|
202
|
+
readonly gateEngineVersion: number;
|
|
203
|
+
}
|
|
204
|
+
interface ComputePolicyDefinitionProps<K extends string = string, P extends AnyPolicyPayload = AnyPolicyPayload> extends BasePolicyDefinitionProps<K, P> {
|
|
205
|
+
readonly kind: "COMPUTE";
|
|
206
|
+
readonly computeEngineVersion: number;
|
|
207
|
+
}
|
|
208
|
+
type GatePolicyDefinitionInput<K extends string = string, P extends AnyPolicyPayload = AnyPolicyPayload> = Omit<GatePolicyDefinitionProps<K, P>, "kind" | "enabled"> & {
|
|
209
|
+
readonly enabled?: boolean;
|
|
210
|
+
};
|
|
211
|
+
type ComputePolicyDefinitionInput<K extends string = string, P extends AnyPolicyPayload = AnyPolicyPayload> = Omit<ComputePolicyDefinitionProps<K, P>, "kind" | "enabled"> & {
|
|
212
|
+
readonly enabled?: boolean;
|
|
213
|
+
};
|
|
214
|
+
type PolicyDefinitionProps<K extends string = string, P extends AnyPolicyPayload = AnyPolicyPayload> = GatePolicyDefinitionProps<K, P> | ComputePolicyDefinitionProps<K, P>;
|
|
215
|
+
declare class PolicyDefinition<K extends string = string, P extends AnyPolicyPayload = AnyPolicyPayload> {
|
|
216
|
+
private static assertPolicyVersionIsSemver;
|
|
217
|
+
private static assertCoherentBounds;
|
|
218
|
+
readonly id: PolicyDefinitionId;
|
|
219
|
+
readonly policyKey: K;
|
|
220
|
+
readonly policyVersion: string;
|
|
221
|
+
readonly payloadSchemaVersion: number;
|
|
222
|
+
readonly contextVersionMin: number;
|
|
223
|
+
readonly contextVersionMax: number;
|
|
224
|
+
readonly gateEngineVersion?: number;
|
|
225
|
+
readonly computeEngineVersion?: number;
|
|
226
|
+
readonly enabled: boolean;
|
|
227
|
+
readonly status: PolicyDefinitionStatus;
|
|
228
|
+
readonly scope: PolicyScope;
|
|
229
|
+
readonly effectiveFrom: Date;
|
|
230
|
+
readonly effectiveTo: Date | null;
|
|
231
|
+
readonly priority: number;
|
|
232
|
+
readonly payloadJson: P;
|
|
233
|
+
readonly payloadHash: string;
|
|
234
|
+
readonly createdAt: Date;
|
|
235
|
+
readonly publishedAt: Date | null;
|
|
236
|
+
readonly kind: PolicyKind;
|
|
237
|
+
private constructor();
|
|
238
|
+
static gate<K extends string, P extends AnyPolicyPayload = AnyPolicyPayload>(props: GatePolicyDefinitionInput<K, P>): PolicyDefinition<K, P>;
|
|
239
|
+
static compute<K extends string, P extends AnyPolicyPayload = AnyPolicyPayload>(props: ComputePolicyDefinitionInput<K, P>): PolicyDefinition<K, P>;
|
|
240
|
+
isGate(): this is PolicyDefinition<K, P> & {
|
|
241
|
+
readonly kind: "GATE";
|
|
242
|
+
readonly gateEngineVersion: number;
|
|
243
|
+
};
|
|
244
|
+
isCompute(): this is PolicyDefinition<K, P> & {
|
|
245
|
+
readonly kind: "COMPUTE";
|
|
246
|
+
readonly computeEngineVersion: number;
|
|
247
|
+
};
|
|
248
|
+
}
|
|
249
|
+
interface FindCandidatesParams {
|
|
250
|
+
readonly policyKey: string;
|
|
251
|
+
readonly kind: PolicyKind;
|
|
252
|
+
readonly payloadSchemaVersion?: number;
|
|
253
|
+
readonly asOf: Date;
|
|
254
|
+
readonly contextVersion: number;
|
|
255
|
+
readonly scopeChain: readonly PolicyScope[];
|
|
256
|
+
}
|
|
257
|
+
//#endregion
|
|
258
|
+
//#region src/core/policies/defs/policy-definition-repository.d.ts
|
|
259
|
+
interface PolicyDefinitionRepository {
|
|
260
|
+
findCandidates(params: FindCandidatesParams): PolicyDefinition[];
|
|
261
|
+
}
|
|
262
|
+
//#endregion
|
|
263
|
+
//#region src/core/policies/context/context-seed.d.ts
|
|
264
|
+
/**
|
|
265
|
+
* Seed data provided by the use case / caller.
|
|
266
|
+
* Contains raw IDs and inputs that the context system will resolve into paths.
|
|
267
|
+
* Each resolver is responsible for validating and extracting what it needs from fields.
|
|
268
|
+
*/
|
|
269
|
+
interface ContextSeed {
|
|
270
|
+
readonly tenantId: TenantId;
|
|
271
|
+
readonly schoolId: SchoolId;
|
|
272
|
+
readonly fields: Record<string, unknown>;
|
|
273
|
+
}
|
|
274
|
+
declare class ContextSeedValidator {
|
|
275
|
+
private static isBlankSeedId;
|
|
276
|
+
static validate(seed: ContextSeed): Result<ContextSeed, string>;
|
|
277
|
+
}
|
|
278
|
+
//#endregion
|
|
279
|
+
//#region src/core/policies/context/context-resolver.d.ts
|
|
280
|
+
interface ContextResolverRetryOptions {
|
|
281
|
+
readonly maxAttempts?: number;
|
|
282
|
+
readonly initialDelayMs?: number;
|
|
283
|
+
readonly maxDelayMs?: number;
|
|
284
|
+
readonly backoffMultiplier?: number;
|
|
285
|
+
}
|
|
286
|
+
interface ContextResolverCircuitBreakerOptions {
|
|
287
|
+
readonly failureThreshold?: number;
|
|
288
|
+
readonly cooldownMs?: number;
|
|
289
|
+
}
|
|
290
|
+
interface ContextResolverResilienceOptions {
|
|
291
|
+
readonly timeoutMs?: number;
|
|
292
|
+
readonly retry?: ContextResolverRetryOptions | false;
|
|
293
|
+
readonly circuitBreaker?: ContextResolverCircuitBreakerOptions | false;
|
|
294
|
+
}
|
|
295
|
+
/**
|
|
296
|
+
* A resolver that knows how to produce a value for a specific context path.
|
|
297
|
+
* Designed as async to allow future DB/API lookups without breaking the interface.
|
|
298
|
+
*/
|
|
299
|
+
interface ContextValueResolver {
|
|
300
|
+
readonly path: string;
|
|
301
|
+
readonly resilience?: ContextResolverResilienceOptions;
|
|
302
|
+
resolve(seed: ContextSeed): Promise<Result<unknown, string>>;
|
|
303
|
+
}
|
|
304
|
+
//#endregion
|
|
305
|
+
//#region src/core/policies/context/context-registry.d.ts
|
|
306
|
+
interface ContextResolverRegistrationOptions {
|
|
307
|
+
readonly namespace?: string;
|
|
308
|
+
}
|
|
309
|
+
/**
|
|
310
|
+
* Registry of context value resolvers, keyed by path.
|
|
311
|
+
*/
|
|
312
|
+
declare class ContextResolverRegistry {
|
|
313
|
+
private readonly resolvers;
|
|
314
|
+
register(resolver: ContextValueResolver, options?: ContextResolverRegistrationOptions): Result<void, string>;
|
|
315
|
+
registerAll(resolvers: readonly ContextValueResolver[], options?: ContextResolverRegistrationOptions): Result<void, string>;
|
|
316
|
+
get(path: string): ContextValueResolver | undefined;
|
|
317
|
+
has(path: string): boolean;
|
|
318
|
+
getNamespace(path: string): string | undefined;
|
|
319
|
+
}
|
|
320
|
+
declare function registerNamespacedContextResolversIn(registry: ContextResolverRegistry, namespace: string, resolvers: readonly ContextValueResolver[]): Result<ContextResolverRegistry, string>;
|
|
321
|
+
//#endregion
|
|
322
|
+
//#region src/core/policies/context/context-builder.d.ts
|
|
323
|
+
interface PolicyContextBuilderOptions {
|
|
324
|
+
readonly defaultResolverResilience?: ContextResolverResilienceOptions;
|
|
325
|
+
readonly now?: () => number;
|
|
326
|
+
readonly sleep?: (delayMs: number) => Promise<void>;
|
|
327
|
+
}
|
|
328
|
+
/**
|
|
329
|
+
* Builds a context object by resolving required paths from a seed.
|
|
330
|
+
*/
|
|
331
|
+
declare class PolicyContextBuilder {
|
|
332
|
+
private readonly registry;
|
|
333
|
+
private readonly circuitStates;
|
|
334
|
+
private readonly defaultResolverResilience?;
|
|
335
|
+
private readonly now;
|
|
336
|
+
private readonly sleep;
|
|
337
|
+
constructor(registry: ContextResolverRegistry, options?: PolicyContextBuilderOptions);
|
|
338
|
+
private static clampPositiveInteger;
|
|
339
|
+
private static clampNonNegativeInteger;
|
|
340
|
+
private static normalizeTimeoutMs;
|
|
341
|
+
private static mergeRetryOptions;
|
|
342
|
+
private static mergeCircuitBreakerOptions;
|
|
343
|
+
private resolveResilienceOptions;
|
|
344
|
+
private getRetryDelayMs;
|
|
345
|
+
private resetCircuit;
|
|
346
|
+
private isCircuitOpen;
|
|
347
|
+
private recordResolverFailure;
|
|
348
|
+
private static describeThrownResolverError;
|
|
349
|
+
private resolveOnce;
|
|
350
|
+
private resolveWithResilience;
|
|
351
|
+
/**
|
|
352
|
+
* Resolves all required context paths and returns a flat context object.
|
|
353
|
+
* If any required path cannot be resolved, returns an error.
|
|
354
|
+
*/
|
|
355
|
+
build(requirements: readonly string[], seed: ContextSeed): Promise<Result<Record<string, unknown>, string>>;
|
|
356
|
+
}
|
|
357
|
+
//#endregion
|
|
358
|
+
//#region src/core/policies/asof/asof.d.ts
|
|
359
|
+
interface DeriveAsOfOptions {
|
|
360
|
+
readonly maxFutureYears?: number;
|
|
361
|
+
}
|
|
362
|
+
declare class PolicyAsOfResolver {
|
|
363
|
+
private static resolveMaxFutureYears;
|
|
364
|
+
static derive(source: AsOfSource, seed: ContextSeed, now: Date, options?: DeriveAsOfOptions): Result<Date, string>;
|
|
365
|
+
}
|
|
366
|
+
//#endregion
|
|
367
|
+
//#region src/core/policies/resolver/policy-resolver.d.ts
|
|
368
|
+
/**
|
|
369
|
+
* Selects the best policy definition from a list of already-filtered candidates.
|
|
370
|
+
*
|
|
371
|
+
* Ordering criteria (highest priority first):
|
|
372
|
+
* 1. Scope specificity: SCHOOL (3) > TENANT (2) > GLOBAL (1)
|
|
373
|
+
* 2. Priority: higher number wins
|
|
374
|
+
* 3. publishedAt descending (fallback to createdAt)
|
|
375
|
+
* 4. Engine version descending (prefer-latest-engine-version — deterministic tiebreaker)
|
|
376
|
+
* 5. policyVersion descending (semver — final deterministic tiebreaker)
|
|
377
|
+
*/
|
|
378
|
+
declare class PolicyResolver {
|
|
379
|
+
/**
|
|
380
|
+
* Picks the single winning definition from an already-filtered candidate
|
|
381
|
+
* list by applying the class's ordering criteria in priority order. The sort
|
|
382
|
+
* runs over a copy, so the caller's array is left untouched, and the final
|
|
383
|
+
* semver tiebreaker guarantees the winner is fully deterministic — it never
|
|
384
|
+
* depends on the order the repository returned the candidates in.
|
|
385
|
+
*
|
|
386
|
+
* @param candidates - Definitions already filtered to match key, scope, and as-of.
|
|
387
|
+
* @returns `ok` with the highest-ranked definition, or `err` when the list is empty.
|
|
388
|
+
*/
|
|
389
|
+
resolveBest(candidates: readonly PolicyDefinition[]): Result<PolicyDefinition, string>;
|
|
390
|
+
}
|
|
391
|
+
//#endregion
|
|
392
|
+
//#region src/core/policies/service/policy-evaluation-error.d.ts
|
|
393
|
+
type PolicyEvaluationError = {
|
|
394
|
+
readonly kind: "INVALID_CONTEXT";
|
|
395
|
+
readonly stage: "SEED_VALIDATION" | "AS_OF_DERIVATION" | "CONTEXT_BUILD";
|
|
396
|
+
readonly policyKey: string;
|
|
397
|
+
readonly message: string;
|
|
398
|
+
readonly cause: string;
|
|
399
|
+
} | {
|
|
400
|
+
readonly kind: "INVALID_POLICY_KEY";
|
|
401
|
+
readonly rawPolicyKey: string;
|
|
402
|
+
readonly message: string;
|
|
403
|
+
readonly cause: string;
|
|
404
|
+
} | {
|
|
405
|
+
readonly kind: "POLICY_NOT_FOUND";
|
|
406
|
+
readonly policyKey: string;
|
|
407
|
+
readonly message: string;
|
|
408
|
+
readonly cause: string;
|
|
409
|
+
} | {
|
|
410
|
+
readonly kind: "POLICY_DEFINITION_NOT_FOUND";
|
|
411
|
+
readonly policyKey: string;
|
|
412
|
+
readonly contextVersion: number;
|
|
413
|
+
readonly asOf: Date;
|
|
414
|
+
readonly message: string;
|
|
415
|
+
readonly cause: string;
|
|
416
|
+
} | {
|
|
417
|
+
readonly kind: "POLICY_VARIANT_NOT_FOUND";
|
|
418
|
+
readonly policyKey: string;
|
|
419
|
+
readonly policyKind: PolicyKind;
|
|
420
|
+
readonly payloadSchemaVersion: number;
|
|
421
|
+
readonly gateEngineVersion?: number;
|
|
422
|
+
readonly computeEngineVersion?: number;
|
|
423
|
+
readonly message: string;
|
|
424
|
+
readonly cause: string;
|
|
425
|
+
} | {
|
|
426
|
+
readonly kind: "ENGINE_FAILURE";
|
|
427
|
+
readonly policyKey: string;
|
|
428
|
+
readonly engine: PolicyKind;
|
|
429
|
+
readonly engineVersion: number;
|
|
430
|
+
readonly message: string;
|
|
431
|
+
readonly cause: string;
|
|
432
|
+
};
|
|
433
|
+
declare class PolicyEvaluationErrors {
|
|
434
|
+
static invalidContext(stage: Extract<PolicyEvaluationError, {
|
|
435
|
+
kind: "INVALID_CONTEXT";
|
|
436
|
+
}>["stage"], policyKey: string, cause: string): PolicyEvaluationError;
|
|
437
|
+
static invalidPolicyKey(rawPolicyKey: string, cause: string): PolicyEvaluationError;
|
|
438
|
+
static policyNotFound(policyKey: string, cause: string): PolicyEvaluationError;
|
|
439
|
+
static policyDefinitionNotFound(policyKey: string, asOf: Date, contextVersion: number, cause: string): PolicyEvaluationError;
|
|
440
|
+
static policyVariantNotFound(params: {
|
|
441
|
+
readonly policyKey: string;
|
|
442
|
+
readonly policyKind: PolicyKind;
|
|
443
|
+
readonly payloadSchemaVersion: number;
|
|
444
|
+
readonly gateEngineVersion?: number;
|
|
445
|
+
readonly computeEngineVersion?: number;
|
|
446
|
+
readonly cause: string;
|
|
447
|
+
}): PolicyEvaluationError;
|
|
448
|
+
static engineFailure(policyKey: string, engine: PolicyKind, engineVersion: number, cause: string): PolicyEvaluationError;
|
|
449
|
+
}
|
|
450
|
+
//#endregion
|
|
451
|
+
//#region src/core/policies/service/policy-service.d.ts
|
|
452
|
+
/**
|
|
453
|
+
* Everything a single policy evaluation needs. `policyKey` names the policy,
|
|
454
|
+
* `scopeChain` narrows the search from most-specific to least-specific scope,
|
|
455
|
+
* `contextVersion` pins which context schema applies, and `seed` carries the
|
|
456
|
+
* raw facts the context builder expands. `decisionId` is the caller's
|
|
457
|
+
* idempotency/audit handle, echoed back on the result.
|
|
458
|
+
*/
|
|
459
|
+
interface EvaluateInput {
|
|
460
|
+
readonly decisionId: PolicyDecisionId;
|
|
461
|
+
readonly policyKey: string;
|
|
462
|
+
readonly scopeChain: readonly PolicyScope[];
|
|
463
|
+
readonly contextVersion: number;
|
|
464
|
+
readonly seed: ContextSeed;
|
|
465
|
+
}
|
|
466
|
+
/**
|
|
467
|
+
* Cross-cutting knobs for a {@link PolicyService}: how the as-of instant is
|
|
468
|
+
* derived ({@link asOf}) and where evaluation telemetry is sent
|
|
469
|
+
* ({@link reporter}). Both are optional; the service defaults to a silent
|
|
470
|
+
* reporter so telemetry is opt-in.
|
|
471
|
+
*/
|
|
472
|
+
interface PolicyServiceOptions {
|
|
473
|
+
readonly asOf?: DeriveAsOfOptions;
|
|
474
|
+
readonly reporter?: PolicyReporter;
|
|
475
|
+
}
|
|
476
|
+
/**
|
|
477
|
+
* The collaborators a {@link PolicyService} is wired with. Each is an injected
|
|
478
|
+
* port so the service stays storage- and engine-agnostic: the catalog defines
|
|
479
|
+
* the universe of policies, `defRepo` supplies their versioned definitions, the
|
|
480
|
+
* `resolver` picks the winning definition, and the engine registries execute it.
|
|
481
|
+
*/
|
|
482
|
+
interface PolicyServiceParams {
|
|
483
|
+
readonly catalog: PolicyCatalog;
|
|
484
|
+
readonly contextBuilder: PolicyContextBuilder;
|
|
485
|
+
readonly defRepo: PolicyDefinitionRepository;
|
|
486
|
+
readonly resolver: PolicyResolver;
|
|
487
|
+
readonly gateEngines: GateEngineRegistry;
|
|
488
|
+
readonly computeRegistry: ComputeRegistry;
|
|
489
|
+
readonly options?: PolicyServiceOptions;
|
|
490
|
+
}
|
|
491
|
+
/** Union of all possible policy decision Outcomes. */
|
|
492
|
+
type PolicyDecision = GateOutcome | ComputeOutcome;
|
|
493
|
+
/**
|
|
494
|
+
* The full record of one successful evaluation. Beyond the business
|
|
495
|
+
* {@link decision}, it pins the exact definition that produced it — id, key,
|
|
496
|
+
* version, payload hash, and engine version — together with the `asOf` instant
|
|
497
|
+
* the policy was selected for and the wall-clock `evaluatedAt`. That provenance
|
|
498
|
+
* is what makes a decision reproducible and auditable after the fact.
|
|
499
|
+
*/
|
|
500
|
+
interface PolicyEvaluationResult {
|
|
501
|
+
readonly decisionId: PolicyDecisionId;
|
|
502
|
+
readonly ref: {
|
|
503
|
+
readonly definitionId: PolicyDefinitionId;
|
|
504
|
+
readonly policyKey: string;
|
|
505
|
+
readonly policyVersion: string;
|
|
506
|
+
readonly payloadHash: string;
|
|
507
|
+
readonly gateEngineVersion?: number;
|
|
508
|
+
readonly computeEngineVersion?: number;
|
|
509
|
+
};
|
|
510
|
+
readonly kind: "GATE" | "COMPUTE";
|
|
511
|
+
readonly asOf: Date;
|
|
512
|
+
readonly evaluatedAt: Date;
|
|
513
|
+
readonly contextVersion: number;
|
|
514
|
+
/** Business decision expressed as an Outcome — not a technical Result. */
|
|
515
|
+
readonly decision: PolicyDecision;
|
|
516
|
+
}
|
|
517
|
+
/**
|
|
518
|
+
* Orchestrates the end-to-end evaluation of a policy: it parses and validates
|
|
519
|
+
* the context seed, derives the as-of instant, resolves the best matching
|
|
520
|
+
* definition for the requested key/scope/version, builds the context the policy
|
|
521
|
+
* needs, and runs it through the right engine (gate or compute).
|
|
522
|
+
*
|
|
523
|
+
* The service never throws for an expected failure: every step returns a
|
|
524
|
+
* {@link Result}, and the failures are folded into a typed
|
|
525
|
+
* {@link PolicyEvaluationError} so callers branch on `isErr()` rather than
|
|
526
|
+
* catching. Telemetry is best-effort — a throwing reporter can never abort an
|
|
527
|
+
* evaluation — which keeps observability strictly side-band.
|
|
528
|
+
*/
|
|
529
|
+
declare class PolicyService {
|
|
530
|
+
#private;
|
|
531
|
+
private readonly catalog;
|
|
532
|
+
private readonly contextBuilder;
|
|
533
|
+
private readonly defRepo;
|
|
534
|
+
private readonly resolver;
|
|
535
|
+
private readonly gateEngines;
|
|
536
|
+
private readonly computeRegistry;
|
|
537
|
+
private readonly options;
|
|
538
|
+
constructor(params: PolicyServiceParams);
|
|
539
|
+
private resolveEvaluationNow;
|
|
540
|
+
/**
|
|
541
|
+
* Evaluates a policy and returns its decision.
|
|
542
|
+
*
|
|
543
|
+
* This is the single public entry point. It delegates the actual work to the
|
|
544
|
+
* private pipeline and wraps it with telemetry: a completed or failed event
|
|
545
|
+
* is reported either way, but reporting is guarded so it can never change the
|
|
546
|
+
* outcome. The returned {@link Result} is `ok` with a
|
|
547
|
+
* {@link PolicyEvaluationResult} on success, or `err` with a typed
|
|
548
|
+
* {@link PolicyEvaluationError} describing which stage rejected the input —
|
|
549
|
+
* the method itself does not throw for expected failures.
|
|
550
|
+
*
|
|
551
|
+
* @param input - The policy key, scope chain, context version, and seed to
|
|
552
|
+
* evaluate, plus the caller's `decisionId`.
|
|
553
|
+
* @returns A `Result` carrying the decision or the evaluation error.
|
|
554
|
+
*/
|
|
555
|
+
evaluate(input: EvaluateInput): Promise<Result<PolicyEvaluationResult, PolicyEvaluationError>>;
|
|
556
|
+
}
|
|
557
|
+
//#endregion
|
|
558
|
+
export { PolicyScope as A, BasePolicyDefinitionProps as C, PolicyDefinition as D, GatePolicyDefinitionProps as E, PolicyKey as F, AsOfSource as I, PolicyKind as L, ScopeChain as M, PolicyCatalog as N, PolicyDefinitionProps as O, PolicyCatalogEntryProps as P, PolicyOwner as R, PolicyDefinitionRepository as S, FindCandidatesParams as T, ContextResolverResilienceOptions as _, PolicyServiceOptions as a, ContextSeed as b, PolicyEvaluationErrors as c, PolicyAsOfResolver as d, PolicyContextBuilder as f, ContextResolverCircuitBreakerOptions as g, registerNamespacedContextResolversIn as h, PolicyService as i, PolicyScopeMatcher as j, PolicyDefinitionStatus as k, PolicyResolver as l, ContextResolverRegistry as m, PolicyDecision as n, PolicyServiceParams as o, PolicyContextBuilderOptions as p, PolicyEvaluationResult as r, PolicyEvaluationError as s, EvaluateInput as t, DeriveAsOfOptions as u, ContextResolverRetryOptions as v, ComputePolicyDefinitionProps as w, ContextSeedValidator as x, ContextValueResolver as y, PolicyScopeLevel as z };
|
|
559
|
+
//# sourceMappingURL=policy-service.d.cts.map
|
package/dist/policy-service.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { r as Result } from "./result.js";
|
|
2
|
+
import { O as GatePayload, a as PolicyDecisionId, c as TenantId, i as PolicyReporter, o as PolicyDefinitionId, s as SchoolId, y as GateOutcome } from "./core-config.js";
|
|
3
3
|
import { a as ComputeOutcome, l as ComputePayload, n as ComputeRegistry, t as GateEngineRegistry } from "./gate-engine-registry.js";
|
|
4
4
|
|
|
5
5
|
//#region src/core/policies/catalog/catalog-types.d.ts
|