@contractspec/lib.contracts 1.49.0 → 1.51.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 (73) hide show
  1. package/dist/app-config/contracts.d.ts +51 -51
  2. package/dist/app-config/events.d.ts +27 -27
  3. package/dist/app-config/lifecycle-contracts.d.ts +55 -55
  4. package/dist/app-config/runtime.d.ts +1 -1
  5. package/dist/app-config/spec.d.ts +1 -1
  6. package/dist/capabilities/capabilities.d.ts +64 -5
  7. package/dist/capabilities/capabilities.js +125 -0
  8. package/dist/capabilities/context.d.ts +88 -0
  9. package/dist/capabilities/context.js +87 -0
  10. package/dist/capabilities/docs/capabilities.docblock.js +191 -2
  11. package/dist/capabilities/guards.d.ts +110 -0
  12. package/dist/capabilities/guards.js +146 -0
  13. package/dist/capabilities/index.d.ts +4 -1
  14. package/dist/capabilities/index.js +4 -1
  15. package/dist/capabilities/validation.d.ts +76 -0
  16. package/dist/capabilities/validation.js +141 -0
  17. package/dist/client/react/feature-render.d.ts +2 -2
  18. package/dist/data-views/runtime.d.ts +1 -1
  19. package/dist/events.d.ts +79 -13
  20. package/dist/events.js +33 -3
  21. package/dist/examples/schema.d.ts +10 -10
  22. package/dist/experiments/spec.d.ts +7 -4
  23. package/dist/features/install.d.ts +4 -4
  24. package/dist/features/types.d.ts +28 -32
  25. package/dist/index.d.ts +21 -12
  26. package/dist/index.js +12 -3
  27. package/dist/install.d.ts +1 -1
  28. package/dist/integrations/openbanking/contracts/accounts.d.ts +67 -67
  29. package/dist/integrations/openbanking/contracts/balances.d.ts +35 -35
  30. package/dist/integrations/openbanking/contracts/transactions.d.ts +49 -49
  31. package/dist/integrations/openbanking/models.d.ts +55 -55
  32. package/dist/integrations/operations.d.ts +103 -103
  33. package/dist/integrations/spec.d.ts +1 -1
  34. package/dist/knowledge/operations.d.ts +67 -67
  35. package/dist/llm/exporters.d.ts +2 -2
  36. package/dist/markdown.d.ts +1 -1
  37. package/dist/onboarding-base.d.ts +29 -29
  38. package/dist/operations/operation.d.ts +6 -0
  39. package/dist/ownership.d.ts +133 -8
  40. package/dist/ownership.js +25 -0
  41. package/dist/policy/context.d.ts +237 -0
  42. package/dist/policy/context.js +227 -0
  43. package/dist/policy/guards.d.ts +145 -0
  44. package/dist/policy/guards.js +254 -0
  45. package/dist/policy/index.d.ts +12 -1
  46. package/dist/policy/index.js +11 -1
  47. package/dist/policy/spec.d.ts +7 -4
  48. package/dist/policy/validation.d.ts +67 -0
  49. package/dist/policy/validation.js +307 -0
  50. package/dist/presentations/presentations.d.ts +6 -0
  51. package/dist/tests/spec.d.ts +17 -12
  52. package/dist/themes.d.ts +7 -4
  53. package/dist/translations/index.d.ts +6 -0
  54. package/dist/translations/index.js +5 -0
  55. package/dist/translations/registry.d.ts +144 -0
  56. package/dist/translations/registry.js +223 -0
  57. package/dist/translations/spec.d.ts +126 -0
  58. package/dist/translations/spec.js +31 -0
  59. package/dist/translations/validation.d.ts +85 -0
  60. package/dist/translations/validation.js +328 -0
  61. package/dist/types.d.ts +140 -14
  62. package/dist/versioning/index.d.ts +2 -1
  63. package/dist/versioning/index.js +2 -1
  64. package/dist/versioning/refs.d.ts +179 -0
  65. package/dist/versioning/refs.js +161 -0
  66. package/dist/workflow/context.d.ts +191 -0
  67. package/dist/workflow/context.js +227 -0
  68. package/dist/workflow/index.d.ts +4 -2
  69. package/dist/workflow/index.js +4 -2
  70. package/dist/workflow/spec.d.ts +1 -1
  71. package/dist/workflow/validation.d.ts +64 -2
  72. package/dist/workflow/validation.js +194 -1
  73. package/package.json +19 -6
@@ -0,0 +1,227 @@
1
+ //#region src/policy/context.ts
2
+ /**
3
+ * Runtime policy context for opt-in policy enforcement.
4
+ *
5
+ * Provides a context object that can be used to check if a user/tenant
6
+ * has specific roles, permissions, and attributes at runtime.
7
+ *
8
+ * @module policy/context
9
+ *
10
+ * @example
11
+ * ```typescript
12
+ * import { createPolicyContext } from '@contractspec/lib.contracts';
13
+ *
14
+ * // Create context from user's roles and permissions
15
+ * const ctx = createPolicyContext({
16
+ * id: 'user-123',
17
+ * tenantId: 'tenant-456',
18
+ * roles: ['admin', 'editor'],
19
+ * permissions: ['read:articles', 'write:articles'],
20
+ * attributes: { department: 'engineering' },
21
+ * });
22
+ *
23
+ * // Check roles and permissions
24
+ * if (ctx.hasRole('admin')) {
25
+ * // User has admin role
26
+ * }
27
+ *
28
+ * // Audit access attempts
29
+ * ctx.auditAccess('article.update', 'allowed');
30
+ * ```
31
+ */
32
+ /**
33
+ * Error thrown when a policy violation occurs.
34
+ */
35
+ var PolicyViolationError = class extends Error {
36
+ violationType;
37
+ details;
38
+ constructor(type, details) {
39
+ const message = formatPolicyViolationMessage(type, details);
40
+ super(message);
41
+ this.name = "PolicyViolationError";
42
+ this.violationType = type;
43
+ this.details = details;
44
+ }
45
+ };
46
+ function formatPolicyViolationMessage(type, details) {
47
+ const prefix = details.operation ? `Policy violation for "${details.operation}": ` : "Policy violation: ";
48
+ switch (type) {
49
+ case "missing_role": return `${prefix}Missing required role "${details.requiredRole}"`;
50
+ case "missing_permission": return `${prefix}Missing required permission "${details.requiredPermission}"`;
51
+ case "missing_attribute": return `${prefix}Missing or invalid attribute "${details.requiredAttribute?.key}"`;
52
+ case "rate_limit_exceeded": return `${prefix}Rate limit exceeded for key "${details.rateLimitKey}"`;
53
+ case "consent_required": return `${prefix}Consent required: ${details.consentIds?.join(", ")}`;
54
+ case "access_denied": return `${prefix}${details.reason ?? "Access denied"}`;
55
+ }
56
+ }
57
+ var PolicyContextImpl = class {
58
+ user;
59
+ roles;
60
+ permissions;
61
+ rateLimitStates = /* @__PURE__ */ new Map();
62
+ rateLimitConfigs;
63
+ auditHandler;
64
+ constructor(user, options = {}) {
65
+ this.user = user;
66
+ this.roles = new Set(user.roles);
67
+ this.permissions = new Set(user.permissions);
68
+ this.rateLimitConfigs = options.rateLimits ?? {};
69
+ this.auditHandler = options.auditHandler;
70
+ }
71
+ hasRole(role) {
72
+ return this.roles.has(role);
73
+ }
74
+ hasAnyRole(roles) {
75
+ return roles.some((r) => this.roles.has(r));
76
+ }
77
+ hasAllRoles(roles) {
78
+ return roles.every((r) => this.roles.has(r));
79
+ }
80
+ requireRole(role) {
81
+ if (!this.hasRole(role)) throw new PolicyViolationError("missing_role", { requiredRole: role });
82
+ }
83
+ hasPermission(permission) {
84
+ return this.permissions.has(permission);
85
+ }
86
+ hasAnyPermission(permissions) {
87
+ return permissions.some((p) => this.permissions.has(p));
88
+ }
89
+ hasAllPermissions(permissions) {
90
+ return permissions.every((p) => this.permissions.has(p));
91
+ }
92
+ requirePermission(permission) {
93
+ if (!this.hasPermission(permission)) throw new PolicyViolationError("missing_permission", { requiredPermission: permission });
94
+ }
95
+ getAttribute(key) {
96
+ return this.user.attributes[key];
97
+ }
98
+ checkAttribute(key, expected) {
99
+ return this.user.attributes[key] === expected;
100
+ }
101
+ checkAttributeOneOf(key, allowedValues) {
102
+ return allowedValues.includes(this.user.attributes[key]);
103
+ }
104
+ checkRateLimit(key) {
105
+ const config = this.rateLimitConfigs[key];
106
+ if (!config) return {
107
+ allowed: true,
108
+ remaining: Infinity
109
+ };
110
+ const now = Date.now();
111
+ const state = this.rateLimitStates.get(key);
112
+ if (!state || now - state.windowStart >= config.windowMs) return {
113
+ allowed: true,
114
+ remaining: config.limit
115
+ };
116
+ const remaining = Math.max(0, config.limit - state.count);
117
+ if (remaining <= 0) return {
118
+ allowed: false,
119
+ remaining: 0,
120
+ resetAt: new Date(state.windowStart + config.windowMs),
121
+ retryAfterMs: state.windowStart + config.windowMs - now
122
+ };
123
+ return {
124
+ allowed: true,
125
+ remaining
126
+ };
127
+ }
128
+ consumeRateLimit(key, cost = 1) {
129
+ const config = this.rateLimitConfigs[key];
130
+ if (!config) return {
131
+ allowed: true,
132
+ remaining: Infinity
133
+ };
134
+ const now = Date.now();
135
+ let state = this.rateLimitStates.get(key);
136
+ if (!state || now - state.windowStart >= config.windowMs) state = {
137
+ key,
138
+ count: 0,
139
+ windowStart: now,
140
+ windowMs: config.windowMs,
141
+ limit: config.limit
142
+ };
143
+ if (state.count + cost > config.limit) {
144
+ const resetAt = new Date(state.windowStart + config.windowMs);
145
+ const retryAfterMs = state.windowStart + config.windowMs - now;
146
+ this.rateLimitStates.set(key, state);
147
+ return {
148
+ allowed: false,
149
+ remaining: Math.max(0, config.limit - state.count),
150
+ resetAt,
151
+ retryAfterMs
152
+ };
153
+ }
154
+ state.count += cost;
155
+ this.rateLimitStates.set(key, state);
156
+ return {
157
+ allowed: true,
158
+ remaining: Math.max(0, config.limit - state.count)
159
+ };
160
+ }
161
+ auditAccess(operation, result, reason) {
162
+ if (!this.auditHandler) return;
163
+ const entry = {
164
+ timestamp: /* @__PURE__ */ new Date(),
165
+ operation,
166
+ result,
167
+ reason,
168
+ userId: this.user.id,
169
+ tenantId: this.user.tenantId,
170
+ attributes: this.user.attributes
171
+ };
172
+ Promise.resolve(this.auditHandler(entry)).catch(() => {});
173
+ }
174
+ };
175
+ /**
176
+ * Creates a policy context from user information.
177
+ *
178
+ * @param user - User information for policy evaluation
179
+ * @param options - Additional context options
180
+ * @returns PolicyContext for checking/requiring policy compliance
181
+ *
182
+ * @example
183
+ * ```typescript
184
+ * const ctx = createPolicyContext({
185
+ * id: 'user-123',
186
+ * roles: ['editor'],
187
+ * permissions: ['read:articles'],
188
+ * attributes: { department: 'marketing' },
189
+ * });
190
+ *
191
+ * ctx.requireRole('editor');
192
+ * ctx.requirePermission('read:articles');
193
+ * ```
194
+ */
195
+ function createPolicyContext(user, options) {
196
+ return new PolicyContextImpl(user, options);
197
+ }
198
+ /**
199
+ * Creates an empty policy context (no roles/permissions).
200
+ * Useful for anonymous users or testing.
201
+ */
202
+ function createAnonymousPolicyContext(options) {
203
+ return new PolicyContextImpl({
204
+ id: "anonymous",
205
+ roles: [],
206
+ permissions: [],
207
+ attributes: {}
208
+ }, options);
209
+ }
210
+ /**
211
+ * Creates a bypass policy context with all roles/permissions.
212
+ * Useful for admin users or internal services.
213
+ *
214
+ * @param allRoles - All roles to grant
215
+ * @param allPermissions - All permissions to grant
216
+ */
217
+ function createBypassPolicyContext(allRoles, allPermissions, options) {
218
+ return new PolicyContextImpl({
219
+ id: "system",
220
+ roles: allRoles,
221
+ permissions: allPermissions,
222
+ attributes: { bypass: true }
223
+ }, options);
224
+ }
225
+
226
+ //#endregion
227
+ export { PolicyViolationError, createAnonymousPolicyContext, createBypassPolicyContext, createPolicyContext };
@@ -0,0 +1,145 @@
1
+ import { AnyOperationSpec } from "../operations/operation.js";
2
+ import { PolicyContext } from "./context.js";
3
+
4
+ //#region src/policy/guards.d.ts
5
+
6
+ /** Result of a policy guard check. */
7
+ interface PolicyGuardResult {
8
+ /** Whether the guard passed. */
9
+ allowed: boolean;
10
+ /** Reason for denial if guard failed. */
11
+ reason?: string;
12
+ /** Missing requirements if guard failed. */
13
+ missing?: {
14
+ roles?: string[];
15
+ permissions?: string[];
16
+ flags?: string[];
17
+ };
18
+ }
19
+ /**
20
+ * Check if an operation's policy constraints are satisfied.
21
+ *
22
+ * @param ctx - Policy context to check against
23
+ * @param operation - Operation spec to check
24
+ * @param flags - Available feature flags
25
+ * @returns Guard result indicating if operation is allowed
26
+ *
27
+ * @example
28
+ * ```typescript
29
+ * const result = checkPolicyForOperation(ctx, myOperation, ['feature-x']);
30
+ * if (!result.allowed) {
31
+ * console.log('Denied:', result.reason);
32
+ * }
33
+ * ```
34
+ */
35
+ declare function checkPolicyForOperation(ctx: PolicyContext, operation: AnyOperationSpec, flags?: string[]): PolicyGuardResult;
36
+ /**
37
+ * Assert that an operation's policy constraints are satisfied.
38
+ *
39
+ * @param ctx - Policy context to check against
40
+ * @param operation - Operation spec to check
41
+ * @param flags - Available feature flags
42
+ * @throws {PolicyViolationError} If policy is not satisfied
43
+ *
44
+ * @example
45
+ * ```typescript
46
+ * // Throws if policy not satisfied
47
+ * assertPolicyForOperation(ctx, myOperation);
48
+ *
49
+ * // Safe to proceed with operation
50
+ * await handler(input);
51
+ * ```
52
+ */
53
+ declare function assertPolicyForOperation(ctx: PolicyContext, operation: AnyOperationSpec, flags?: string[]): void;
54
+ /**
55
+ * Filter operations to only those with satisfied policy constraints.
56
+ *
57
+ * @param ctx - Policy context to check against
58
+ * @param operations - Operations to filter
59
+ * @param flags - Available feature flags
60
+ * @returns Operations that have their policies satisfied
61
+ */
62
+ declare function filterOperationsByPolicy(ctx: PolicyContext, operations: AnyOperationSpec[], flags?: string[]): AnyOperationSpec[];
63
+ /**
64
+ * Check if user has the required role for an operation.
65
+ *
66
+ * @param ctx - Policy context to check against
67
+ * @param requiredRole - Role required for the operation
68
+ * @param operation - Optional operation name for error messages
69
+ * @returns Guard result
70
+ */
71
+ declare function checkRole(ctx: PolicyContext, requiredRole: string, operation?: string): PolicyGuardResult;
72
+ /**
73
+ * Assert user has the required role.
74
+ *
75
+ * @param ctx - Policy context to check against
76
+ * @param requiredRole - Role required
77
+ * @param operation - Optional operation name for error messages
78
+ * @throws {PolicyViolationError} If role is missing
79
+ */
80
+ declare function assertRole(ctx: PolicyContext, requiredRole: string, operation?: string): void;
81
+ /**
82
+ * Check if user has any of the required roles.
83
+ *
84
+ * @param ctx - Policy context to check against
85
+ * @param requiredRoles - Any of these roles is sufficient
86
+ * @param operation - Optional operation name for error messages
87
+ * @returns Guard result
88
+ */
89
+ declare function checkAnyRole(ctx: PolicyContext, requiredRoles: string[], operation?: string): PolicyGuardResult;
90
+ /**
91
+ * Check if user has the required permission.
92
+ *
93
+ * @param ctx - Policy context to check against
94
+ * @param requiredPermission - Permission required
95
+ * @param operation - Optional operation name for error messages
96
+ * @returns Guard result
97
+ */
98
+ declare function checkPermission(ctx: PolicyContext, requiredPermission: string, operation?: string): PolicyGuardResult;
99
+ /**
100
+ * Assert user has the required permission.
101
+ *
102
+ * @param ctx - Policy context to check against
103
+ * @param requiredPermission - Permission required
104
+ * @param operation - Optional operation name for error messages
105
+ * @throws {PolicyViolationError} If permission is missing
106
+ */
107
+ declare function assertPermission(ctx: PolicyContext, requiredPermission: string, operation?: string): void;
108
+ /**
109
+ * Check if user has all of the required permissions.
110
+ *
111
+ * @param ctx - Policy context to check against
112
+ * @param requiredPermissions - All of these permissions are required
113
+ * @param operation - Optional operation name for error messages
114
+ * @returns Guard result
115
+ */
116
+ declare function checkAllPermissions(ctx: PolicyContext, requiredPermissions: string[], operation?: string): PolicyGuardResult;
117
+ interface CombinedPolicyRequirements {
118
+ roles?: string[];
119
+ anyRole?: string[];
120
+ permissions?: string[];
121
+ anyPermission?: string[];
122
+ flags?: string[];
123
+ }
124
+ /**
125
+ * Check multiple policy requirements at once.
126
+ *
127
+ * @param ctx - Policy context to check against
128
+ * @param requirements - Combined requirements to check
129
+ * @param flags - Available feature flags
130
+ * @param operation - Optional operation name for error messages
131
+ * @returns Guard result
132
+ */
133
+ declare function checkCombinedPolicy(ctx: PolicyContext, requirements: CombinedPolicyRequirements, flags?: string[], operation?: string): PolicyGuardResult;
134
+ /**
135
+ * Assert multiple policy requirements at once.
136
+ *
137
+ * @param ctx - Policy context to check against
138
+ * @param requirements - Combined requirements to check
139
+ * @param flags - Available feature flags
140
+ * @param operation - Optional operation name for error messages
141
+ * @throws {PolicyViolationError} If any requirement is not met
142
+ */
143
+ declare function assertCombinedPolicy(ctx: PolicyContext, requirements: CombinedPolicyRequirements, flags?: string[], operation?: string): void;
144
+ //#endregion
145
+ export { CombinedPolicyRequirements, PolicyGuardResult, assertCombinedPolicy, assertPermission, assertPolicyForOperation, assertRole, checkAllPermissions, checkAnyRole, checkCombinedPolicy, checkPermission, checkPolicyForOperation, checkRole, filterOperationsByPolicy };
@@ -0,0 +1,254 @@
1
+ import { PolicyViolationError } from "./context.js";
2
+
3
+ //#region src/policy/guards.ts
4
+ function checkAuthLevel(ctx, required) {
5
+ if (required === "anonymous") return true;
6
+ if (required === "user") return ctx.user.id !== "anonymous";
7
+ if (required === "admin") return ctx.hasRole("admin");
8
+ return false;
9
+ }
10
+ /**
11
+ * Check if an operation's policy constraints are satisfied.
12
+ *
13
+ * @param ctx - Policy context to check against
14
+ * @param operation - Operation spec to check
15
+ * @param flags - Available feature flags
16
+ * @returns Guard result indicating if operation is allowed
17
+ *
18
+ * @example
19
+ * ```typescript
20
+ * const result = checkPolicyForOperation(ctx, myOperation, ['feature-x']);
21
+ * if (!result.allowed) {
22
+ * console.log('Denied:', result.reason);
23
+ * }
24
+ * ```
25
+ */
26
+ function checkPolicyForOperation(ctx, operation, flags = []) {
27
+ const { policy } = operation;
28
+ const operationName = `${operation.meta.key}.v${operation.meta.version}`;
29
+ const missing = {};
30
+ if (!checkAuthLevel(ctx, policy.auth)) return {
31
+ allowed: false,
32
+ reason: `Operation "${operationName}" requires "${policy.auth}" auth level`,
33
+ missing: { roles: policy.auth === "admin" ? ["admin"] : void 0 }
34
+ };
35
+ if (policy.flags?.length) {
36
+ const availableFlags = new Set(flags);
37
+ const missingFlags = policy.flags.filter((f) => !availableFlags.has(f));
38
+ if (missingFlags.length > 0) {
39
+ missing.flags = missingFlags;
40
+ return {
41
+ allowed: false,
42
+ reason: `Operation "${operationName}" requires feature flags: ${missingFlags.join(", ")}`,
43
+ missing
44
+ };
45
+ }
46
+ }
47
+ return { allowed: true };
48
+ }
49
+ /**
50
+ * Assert that an operation's policy constraints are satisfied.
51
+ *
52
+ * @param ctx - Policy context to check against
53
+ * @param operation - Operation spec to check
54
+ * @param flags - Available feature flags
55
+ * @throws {PolicyViolationError} If policy is not satisfied
56
+ *
57
+ * @example
58
+ * ```typescript
59
+ * // Throws if policy not satisfied
60
+ * assertPolicyForOperation(ctx, myOperation);
61
+ *
62
+ * // Safe to proceed with operation
63
+ * await handler(input);
64
+ * ```
65
+ */
66
+ function assertPolicyForOperation(ctx, operation, flags = []) {
67
+ const result = checkPolicyForOperation(ctx, operation, flags);
68
+ if (!result.allowed) {
69
+ const operationName$1 = `${operation.meta.key}.v${operation.meta.version}`;
70
+ ctx.auditAccess(operationName$1, "denied", result.reason);
71
+ throw new PolicyViolationError("access_denied", {
72
+ operation: operationName$1,
73
+ reason: result.reason
74
+ });
75
+ }
76
+ const operationName = `${operation.meta.key}.v${operation.meta.version}`;
77
+ ctx.auditAccess(operationName, "allowed");
78
+ }
79
+ /**
80
+ * Filter operations to only those with satisfied policy constraints.
81
+ *
82
+ * @param ctx - Policy context to check against
83
+ * @param operations - Operations to filter
84
+ * @param flags - Available feature flags
85
+ * @returns Operations that have their policies satisfied
86
+ */
87
+ function filterOperationsByPolicy(ctx, operations, flags = []) {
88
+ return operations.filter((op) => checkPolicyForOperation(ctx, op, flags).allowed);
89
+ }
90
+ /**
91
+ * Check if user has the required role for an operation.
92
+ *
93
+ * @param ctx - Policy context to check against
94
+ * @param requiredRole - Role required for the operation
95
+ * @param operation - Optional operation name for error messages
96
+ * @returns Guard result
97
+ */
98
+ function checkRole(ctx, requiredRole, operation) {
99
+ if (ctx.hasRole(requiredRole)) return { allowed: true };
100
+ return {
101
+ allowed: false,
102
+ reason: operation ? `Operation "${operation}" requires role "${requiredRole}"` : `Missing required role "${requiredRole}"`,
103
+ missing: { roles: [requiredRole] }
104
+ };
105
+ }
106
+ /**
107
+ * Assert user has the required role.
108
+ *
109
+ * @param ctx - Policy context to check against
110
+ * @param requiredRole - Role required
111
+ * @param operation - Optional operation name for error messages
112
+ * @throws {PolicyViolationError} If role is missing
113
+ */
114
+ function assertRole(ctx, requiredRole, operation) {
115
+ if (!checkRole(ctx, requiredRole, operation).allowed) throw new PolicyViolationError("missing_role", {
116
+ operation,
117
+ requiredRole
118
+ });
119
+ }
120
+ /**
121
+ * Check if user has any of the required roles.
122
+ *
123
+ * @param ctx - Policy context to check against
124
+ * @param requiredRoles - Any of these roles is sufficient
125
+ * @param operation - Optional operation name for error messages
126
+ * @returns Guard result
127
+ */
128
+ function checkAnyRole(ctx, requiredRoles, operation) {
129
+ if (ctx.hasAnyRole(requiredRoles)) return { allowed: true };
130
+ return {
131
+ allowed: false,
132
+ reason: operation ? `Operation "${operation}" requires one of roles: ${requiredRoles.join(", ")}` : `Missing required role (need one of: ${requiredRoles.join(", ")})`,
133
+ missing: { roles: requiredRoles }
134
+ };
135
+ }
136
+ /**
137
+ * Check if user has the required permission.
138
+ *
139
+ * @param ctx - Policy context to check against
140
+ * @param requiredPermission - Permission required
141
+ * @param operation - Optional operation name for error messages
142
+ * @returns Guard result
143
+ */
144
+ function checkPermission(ctx, requiredPermission, operation) {
145
+ if (ctx.hasPermission(requiredPermission)) return { allowed: true };
146
+ return {
147
+ allowed: false,
148
+ reason: operation ? `Operation "${operation}" requires permission "${requiredPermission}"` : `Missing required permission "${requiredPermission}"`,
149
+ missing: { permissions: [requiredPermission] }
150
+ };
151
+ }
152
+ /**
153
+ * Assert user has the required permission.
154
+ *
155
+ * @param ctx - Policy context to check against
156
+ * @param requiredPermission - Permission required
157
+ * @param operation - Optional operation name for error messages
158
+ * @throws {PolicyViolationError} If permission is missing
159
+ */
160
+ function assertPermission(ctx, requiredPermission, operation) {
161
+ if (!checkPermission(ctx, requiredPermission, operation).allowed) throw new PolicyViolationError("missing_permission", {
162
+ operation,
163
+ requiredPermission
164
+ });
165
+ }
166
+ /**
167
+ * Check if user has all of the required permissions.
168
+ *
169
+ * @param ctx - Policy context to check against
170
+ * @param requiredPermissions - All of these permissions are required
171
+ * @param operation - Optional operation name for error messages
172
+ * @returns Guard result
173
+ */
174
+ function checkAllPermissions(ctx, requiredPermissions, operation) {
175
+ const missing = requiredPermissions.filter((p) => !ctx.hasPermission(p));
176
+ if (missing.length === 0) return { allowed: true };
177
+ return {
178
+ allowed: false,
179
+ reason: operation ? `Operation "${operation}" requires permissions: ${missing.join(", ")}` : `Missing required permissions: ${missing.join(", ")}`,
180
+ missing: { permissions: missing }
181
+ };
182
+ }
183
+ /**
184
+ * Check multiple policy requirements at once.
185
+ *
186
+ * @param ctx - Policy context to check against
187
+ * @param requirements - Combined requirements to check
188
+ * @param flags - Available feature flags
189
+ * @param operation - Optional operation name for error messages
190
+ * @returns Guard result
191
+ */
192
+ function checkCombinedPolicy(ctx, requirements, flags = [], operation) {
193
+ const missing = {};
194
+ const reasons = [];
195
+ if (requirements.roles?.length) {
196
+ const missingRoles = requirements.roles.filter((r) => !ctx.hasRole(r));
197
+ if (missingRoles.length > 0) {
198
+ missing.roles = missingRoles;
199
+ reasons.push(`Missing roles: ${missingRoles.join(", ")}`);
200
+ }
201
+ }
202
+ if (requirements.anyRole?.length) {
203
+ if (!ctx.hasAnyRole(requirements.anyRole)) {
204
+ missing.roles = [...missing.roles ?? [], ...requirements.anyRole];
205
+ reasons.push(`Need one of roles: ${requirements.anyRole.join(", ")}`);
206
+ }
207
+ }
208
+ if (requirements.permissions?.length) {
209
+ const missingPerms = requirements.permissions.filter((p) => !ctx.hasPermission(p));
210
+ if (missingPerms.length > 0) {
211
+ missing.permissions = missingPerms;
212
+ reasons.push(`Missing permissions: ${missingPerms.join(", ")}`);
213
+ }
214
+ }
215
+ if (requirements.anyPermission?.length) {
216
+ if (!ctx.hasAnyPermission(requirements.anyPermission)) {
217
+ missing.permissions = [...missing.permissions ?? [], ...requirements.anyPermission];
218
+ reasons.push(`Need one of permissions: ${requirements.anyPermission.join(", ")}`);
219
+ }
220
+ }
221
+ if (requirements.flags?.length) {
222
+ const availableFlags = new Set(flags);
223
+ const missingFlags = requirements.flags.filter((f) => !availableFlags.has(f));
224
+ if (missingFlags.length > 0) {
225
+ missing.flags = missingFlags;
226
+ reasons.push(`Missing feature flags: ${missingFlags.join(", ")}`);
227
+ }
228
+ }
229
+ if (reasons.length > 0) return {
230
+ allowed: false,
231
+ reason: (operation ? `Operation "${operation}": ` : "") + reasons.join("; "),
232
+ missing
233
+ };
234
+ return { allowed: true };
235
+ }
236
+ /**
237
+ * Assert multiple policy requirements at once.
238
+ *
239
+ * @param ctx - Policy context to check against
240
+ * @param requirements - Combined requirements to check
241
+ * @param flags - Available feature flags
242
+ * @param operation - Optional operation name for error messages
243
+ * @throws {PolicyViolationError} If any requirement is not met
244
+ */
245
+ function assertCombinedPolicy(ctx, requirements, flags = [], operation) {
246
+ const result = checkCombinedPolicy(ctx, requirements, flags, operation);
247
+ if (!result.allowed) throw new PolicyViolationError("access_denied", {
248
+ operation,
249
+ reason: result.reason
250
+ });
251
+ }
252
+
253
+ //#endregion
254
+ export { assertCombinedPolicy, assertPermission, assertPolicyForOperation, assertRole, checkAllPermissions, checkAnyRole, checkCombinedPolicy, checkPermission, checkPolicyForOperation, checkRole, filterOperationsByPolicy };
@@ -2,4 +2,15 @@ import { AttributeMatcher, ConsentDefinition, FieldPolicyRule, PIIPolicy, Policy
2
2
  import { PolicyRegistry } from "./registry.js";
3
3
  import { DecisionContext, PolicyEngine, ResourceContext, SubjectContext, SubjectRelationship } from "./engine.js";
4
4
  import { OPAAdapterOptions, OPAClient, OPAEvaluationResult, OPAPolicyAdapter, buildOPAInput } from "./opa-adapter.js";
5
- export { AttributeMatcher, ConsentDefinition, DecisionContext, FieldPolicyRule, OPAAdapterOptions, OPAClient, OPAEvaluationResult, OPAPolicyAdapter, PIIPolicy, PolicyCondition, PolicyEffect, PolicyEngine, PolicyMeta, PolicyOPAConfig, PolicyRef, PolicyRegistry, PolicyRule, PolicySpec, RateLimitDefinition, RelationshipDefinition, RelationshipMatcher, ResourceContext, ResourceMatcher, SubjectContext, SubjectMatcher, SubjectRelationship, buildOPAInput };
5
+ import { AuditEntry, AuditHandler, PolicyContext, PolicyContextOptions, PolicyUser, PolicyViolationDetails, PolicyViolationError, PolicyViolationType, RateLimitResult, RateLimitState, createAnonymousPolicyContext, createBypassPolicyContext, createPolicyContext } from "./context.js";
6
+ import { CombinedPolicyRequirements, PolicyGuardResult, assertCombinedPolicy, assertPermission, assertPolicyForOperation, assertRole, checkAllPermissions, checkAnyRole, checkCombinedPolicy, checkPermission, checkPolicyForOperation, checkRole, filterOperationsByPolicy } from "./guards.js";
7
+ import { PolicyConsistencyDeps, PolicyValidationError, PolicyValidationIssue, PolicyValidationLevel, PolicyValidationResult, assertPolicyConsistency, assertPolicySpecValid, validatePolicyConsistency, validatePolicySpec } from "./validation.js";
8
+
9
+ //#region src/policy/index.d.ts
10
+
11
+ /**
12
+ * Helper to define a Policy.
13
+ */
14
+ declare const definePolicy: (spec: PolicySpec) => PolicySpec;
15
+ //#endregion
16
+ export { AttributeMatcher, AuditEntry, AuditHandler, CombinedPolicyRequirements, ConsentDefinition, DecisionContext, FieldPolicyRule, OPAAdapterOptions, OPAClient, OPAEvaluationResult, OPAPolicyAdapter, PIIPolicy, PolicyCondition, PolicyConsistencyDeps, PolicyContext, PolicyContextOptions, PolicyEffect, PolicyEngine, PolicyGuardResult, PolicyMeta, PolicyOPAConfig, PolicyRef, PolicyRegistry, PolicyRule, PolicySpec, PolicyUser, PolicyValidationError, PolicyValidationIssue, PolicyValidationLevel, PolicyValidationResult, PolicyViolationDetails, PolicyViolationError, PolicyViolationType, RateLimitDefinition, RateLimitResult, RateLimitState, RelationshipDefinition, RelationshipMatcher, ResourceContext, ResourceMatcher, SubjectContext, SubjectMatcher, SubjectRelationship, assertCombinedPolicy, assertPermission, assertPolicyConsistency, assertPolicyForOperation, assertPolicySpecValid, assertRole, buildOPAInput, checkAllPermissions, checkAnyRole, checkCombinedPolicy, checkPermission, checkPolicyForOperation, checkRole, createAnonymousPolicyContext, createBypassPolicyContext, createPolicyContext, definePolicy, filterOperationsByPolicy, validatePolicyConsistency, validatePolicySpec };
@@ -1,5 +1,15 @@
1
1
  import { PolicyRegistry } from "./registry.js";
2
2
  import { PolicyEngine } from "./engine.js";
3
3
  import { OPAPolicyAdapter, buildOPAInput } from "./opa-adapter.js";
4
+ import { PolicyViolationError, createAnonymousPolicyContext, createBypassPolicyContext, createPolicyContext } from "./context.js";
5
+ import { assertCombinedPolicy, assertPermission, assertPolicyForOperation, assertRole, checkAllPermissions, checkAnyRole, checkCombinedPolicy, checkPermission, checkPolicyForOperation, checkRole, filterOperationsByPolicy } from "./guards.js";
6
+ import { PolicyValidationError, assertPolicyConsistency, assertPolicySpecValid, validatePolicyConsistency, validatePolicySpec } from "./validation.js";
4
7
 
5
- export { OPAPolicyAdapter, PolicyEngine, PolicyRegistry, buildOPAInput };
8
+ //#region src/policy/index.ts
9
+ /**
10
+ * Helper to define a Policy.
11
+ */
12
+ const definePolicy = (spec) => spec;
13
+
14
+ //#endregion
15
+ export { OPAPolicyAdapter, PolicyEngine, PolicyRegistry, PolicyValidationError, PolicyViolationError, assertCombinedPolicy, assertPermission, assertPolicyConsistency, assertPolicyForOperation, assertPolicySpecValid, assertRole, buildOPAInput, checkAllPermissions, checkAnyRole, checkCombinedPolicy, checkPermission, checkPolicyForOperation, checkRole, createAnonymousPolicyContext, createBypassPolicyContext, createPolicyContext, definePolicy, filterOperationsByPolicy, validatePolicyConsistency, validatePolicySpec };
@@ -1,6 +1,8 @@
1
+ import { VersionedSpecRef } from "../versioning/refs.js";
1
2
  import { OwnerShipMeta } from "../ownership.js";
2
3
 
3
4
  //#region src/policy/spec.d.ts
5
+ /** Effect of a policy rule: allow or deny access. */
4
6
  type PolicyEffect = 'allow' | 'deny';
5
7
  interface RelationshipDefinition {
6
8
  subjectType: string;
@@ -95,9 +97,10 @@ interface PolicySpec {
95
97
  rateLimits?: RateLimitDefinition[];
96
98
  opa?: PolicyOPAConfig;
97
99
  }
98
- interface PolicyRef {
99
- key: string;
100
- version: string;
101
- }
100
+ /**
101
+ * Reference to a policy spec.
102
+ * Uses key and version to identify a specific policy.
103
+ */
104
+ type PolicyRef = VersionedSpecRef;
102
105
  //#endregion
103
106
  export { AttributeMatcher, ConsentDefinition, FieldPolicyRule, PIIPolicy, PolicyCondition, PolicyEffect, PolicyMeta, PolicyOPAConfig, PolicyRef, PolicyRule, PolicySpec, RateLimitDefinition, RelationshipDefinition, RelationshipMatcher, ResourceMatcher, SubjectMatcher };