@contractspec/lib.contracts 1.50.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 (66) 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 +40 -4
  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/contract-registry/schemas.d.ts +2 -2
  19. package/dist/data-views/runtime.d.ts +1 -1
  20. package/dist/events.d.ts +6 -0
  21. package/dist/examples/schema.d.ts +11 -11
  22. package/dist/experiments/spec.d.ts +1 -1
  23. package/dist/features/install.d.ts +4 -4
  24. package/dist/features/types.d.ts +4 -4
  25. package/dist/index.d.ts +21 -13
  26. package/dist/index.js +11 -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 +1 -1
  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/policy/context.d.ts +237 -0
  40. package/dist/policy/context.js +227 -0
  41. package/dist/policy/guards.d.ts +145 -0
  42. package/dist/policy/guards.js +254 -0
  43. package/dist/policy/index.d.ts +12 -1
  44. package/dist/policy/index.js +11 -1
  45. package/dist/policy/spec.d.ts +1 -1
  46. package/dist/policy/validation.d.ts +67 -0
  47. package/dist/policy/validation.js +307 -0
  48. package/dist/presentations/presentations.d.ts +6 -0
  49. package/dist/tests/spec.d.ts +1 -1
  50. package/dist/themes.d.ts +1 -1
  51. package/dist/translations/index.d.ts +6 -0
  52. package/dist/translations/index.js +5 -0
  53. package/dist/translations/registry.d.ts +144 -0
  54. package/dist/translations/registry.js +223 -0
  55. package/dist/translations/spec.d.ts +126 -0
  56. package/dist/translations/spec.js +31 -0
  57. package/dist/translations/validation.d.ts +85 -0
  58. package/dist/translations/validation.js +328 -0
  59. package/dist/workflow/context.d.ts +191 -0
  60. package/dist/workflow/context.js +227 -0
  61. package/dist/workflow/index.d.ts +4 -2
  62. package/dist/workflow/index.js +4 -2
  63. package/dist/workflow/spec.d.ts +1 -1
  64. package/dist/workflow/validation.d.ts +64 -2
  65. package/dist/workflow/validation.js +194 -1
  66. package/package.json +18 -6
@@ -0,0 +1,87 @@
1
+ //#region src/capabilities/context.ts
2
+ /**
3
+ * Error thrown when a required capability is missing.
4
+ */
5
+ var CapabilityMissingError = class extends Error {
6
+ capabilityKey;
7
+ requiredVersion;
8
+ constructor(capabilityKey, requiredVersion) {
9
+ const versionSuffix = requiredVersion ? `.v${requiredVersion}` : "";
10
+ super(`Missing required capability: ${capabilityKey}${versionSuffix}`);
11
+ this.name = "CapabilityMissingError";
12
+ this.capabilityKey = capabilityKey;
13
+ this.requiredVersion = requiredVersion;
14
+ }
15
+ };
16
+ var CapabilityContextImpl = class {
17
+ capabilities;
18
+ capabilityVersions;
19
+ constructor(enabledCapabilities) {
20
+ const capSet = /* @__PURE__ */ new Set();
21
+ const versionMap = /* @__PURE__ */ new Map();
22
+ for (const cap of enabledCapabilities) {
23
+ capSet.add(cap.key);
24
+ versionMap.set(cap.key, cap.version);
25
+ }
26
+ this.capabilities = capSet;
27
+ this.capabilityVersions = versionMap;
28
+ }
29
+ hasCapability(key, version) {
30
+ if (!this.capabilities.has(key)) return false;
31
+ if (version != null) return this.capabilityVersions.get(key) === version;
32
+ return true;
33
+ }
34
+ requireCapability(key, version) {
35
+ if (!this.hasCapability(key, version)) throw new CapabilityMissingError(key, version);
36
+ }
37
+ hasAllCapabilities(keys) {
38
+ return keys.every((k) => this.capabilities.has(k));
39
+ }
40
+ hasAnyCapability(keys) {
41
+ return keys.some((k) => this.capabilities.has(k));
42
+ }
43
+ getMatchingCapabilities(pattern) {
44
+ if (!pattern.includes("*")) return this.capabilities.has(pattern) ? [pattern] : [];
45
+ const regexPattern = pattern.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*");
46
+ const regex = /* @__PURE__ */ new RegExp(`^${regexPattern}$`);
47
+ return [...this.capabilities].filter((key) => regex.test(key));
48
+ }
49
+ };
50
+ /**
51
+ * Creates a capability context from enabled capabilities.
52
+ *
53
+ * @param enabledCapabilities - Array of capability refs that are enabled
54
+ * @returns CapabilityContext for checking/requiring capabilities
55
+ *
56
+ * @example
57
+ * ```typescript
58
+ * // From user subscription capabilities
59
+ * const userCaps = await getUserCapabilities(userId);
60
+ * const ctx = createCapabilityContext(userCaps);
61
+ *
62
+ * // In handler
63
+ * ctx.requireCapability('premium-features');
64
+ * ```
65
+ */
66
+ function createCapabilityContext(enabledCapabilities) {
67
+ return new CapabilityContextImpl(enabledCapabilities);
68
+ }
69
+ /**
70
+ * Creates an empty capability context (no capabilities enabled).
71
+ * Useful for anonymous users or testing.
72
+ */
73
+ function createEmptyCapabilityContext() {
74
+ return new CapabilityContextImpl([]);
75
+ }
76
+ /**
77
+ * Creates a capability context with all capabilities enabled (bypass).
78
+ * Useful for admin users or internal services.
79
+ *
80
+ * @param allCapabilities - Array of all capability refs to enable
81
+ */
82
+ function createBypassCapabilityContext(allCapabilities) {
83
+ return new CapabilityContextImpl(allCapabilities);
84
+ }
85
+
86
+ //#endregion
87
+ export { CapabilityMissingError, createBypassCapabilityContext, createCapabilityContext, createEmptyCapabilityContext };
@@ -4,7 +4,7 @@ import { registerDocBlocks } from "../../docs/registry.js";
4
4
  const tech_contracts_capabilities_DocBlocks = [{
5
5
  id: "docs.tech.contracts.capabilities",
6
6
  title: "CapabilitySpec Overview",
7
- summary: "Capability specs provide a canonical, versioned contract for what a module offers (`provides`) and what it depends on (`requires`). They enable safe composition across features, automated validation during `installFeature`, and consistent documentation for shared surfaces (APIs, events, workflows, UI, resources).",
7
+ summary: "Capability specs define what a module provides (operations, events, presentations) and requires (dependencies). They enable bidirectional linking, inheritance, runtime enforcement, and automated validation.",
8
8
  kind: "reference",
9
9
  visibility: "public",
10
10
  route: "/docs/tech/contracts/capabilities",
@@ -13,7 +13,196 @@ const tech_contracts_capabilities_DocBlocks = [{
13
13
  "contracts",
14
14
  "capabilities"
15
15
  ],
16
- body: "# CapabilitySpec Overview\n\n## Purpose\n\nCapability specs provide a canonical, versioned contract for what a module offers (`provides`) and what it depends on (`requires`). They enable safe composition across features, automated validation during `installFeature`, and consistent documentation for shared surfaces (APIs, events, workflows, UI, resources).\n\n## Schema\n\nDefined in `@contractspec/lib.contracts/src/capabilities.ts`.\n\n```ts\nexport interface CapabilitySpec {\n meta: CapabilityMeta; // ownership metadata + { key, version, kind }\n provides?: CapabilitySurfaceRef[]; // what concrete surfaces this capability exposes\n requires?: CapabilityRequirement[];// capabilities that must already exist\n}\n```\n\n- **CapabilityMeta**\n - `key`: stable slug (e.g., `payments.stripe`)\n - `version`: bump on breaking changes\n - `kind`: `'api' | 'event' | 'data' | 'ui' | 'integration'`\n - ownership fields (`title`, `description`, `domain`, `owners`, `tags`, `stability`)\n- **CapabilitySurfaceRef**\n - `surface`: `'operation' | 'event' | 'workflow' | 'presentation' | 'resource'`\n - `name` / `version`: points to the declared contract (operation name, event name, etc.)\n - optional `description`\n- **CapabilityRequirement**\n - `key`: capability slug to satisfy\n - `version?`: pin to an exact version when required (defaults to highest registered)\n - `kind?`: extra guard if the same key hosts multiple kinds\n - `optional?`: skip strict enforcement (informational requirement)\n - `reason?`: why this dependency exists (docs + tooling)\n\n## Registry\n\n`CapabilityRegistry` provides:\n\n- `register(spec)`: register a capability (`key + version` must be unique)\n- `get(key, version?)`: retrieve the exact or highest version\n- `list()`: inspect all capabilities\n- `satisfies(requirement, additional?)`: check if a requirement is met (includes locally provided capabilities passed via `additional`)\n\n## Feature Integration\n\n`FeatureModuleSpec` now accepts:\n\n```ts\ncapabilities?: {\n provides?: CapabilityRef[]; // capabilities this feature exposes\n requires?: CapabilityRequirement[]; // capabilities the feature needs\n};\n```\n\nDuring `installFeature`:\n\n1. `provides` entries must exist in the `CapabilityRegistry`.\n2. `requires` entries must be satisfied either by:\n - the same feature’s `provides`,\n - or existing capabilities already registered in the global registry.\n\nErrors are thrown when dependencies cannot be satisfied, preventing unsafe module composition.\n\n## Authoring Guidelines\n\n1. **Register capability specs** in a shared package (e.g., `packages/.../capabilities`) before referencing them in features.\n2. **Version consciously**: bump capability versions when the provided surfaces or contract semantics change.\n3. **Document dependencies** via `reason` strings to help operators understand why a capability is required.\n4. **Prefer stable keys** that map to business/technical domains (`billing.invoices`, `payments.stripe`, `cms.assets`).\n5. When introducing new capability kinds, update the `CapabilityKind` union and accompanying docs/tests.\n\n## Tooling (Roadmap)\n\n- CLI validation warns when feature specs reference missing capabilities.\n- Future build steps will leverage capability data to scaffold adapters and enforce policy in generated code.\n- Capability metadata will surface in docs/LLM guides to describe module marketplaces and installation flows.\n\n"
16
+ body: `# CapabilitySpec Overview
17
+
18
+ ## Purpose
19
+
20
+ Capabilities are **module interfaces** that define:
21
+ 1. What operations, events, and presentations a module exposes (\`provides\`)
22
+ 2. What other capabilities it depends on (\`requires\`)
23
+ 3. Inheritance hierarchies via \`extends\`
24
+
25
+ They enable:
26
+ - **Bidirectional linking**: Specs reference capabilities, capabilities list their specs
27
+ - **Dependency validation**: Features can't install without satisfying requirements
28
+ - **Runtime enforcement**: Check capabilities before executing operations
29
+ - **Inheritance**: Build capability hierarchies with shared requirements
30
+
31
+ ## Schema
32
+
33
+ \`\`\`ts
34
+ export interface CapabilitySpec {
35
+ meta: CapabilityMeta; // ownership metadata + { key, version, kind }
36
+ extends?: CapabilityRef; // NEW: inherit from parent capability
37
+ provides?: CapabilitySurfaceRef[]; // surfaces this capability exposes
38
+ requires?: CapabilityRequirement[];// capabilities that must exist
39
+ }
40
+ \`\`\`
41
+
42
+ ### Bidirectional Linking
43
+
44
+ Operations, events, and presentations can now declare their capability:
45
+
46
+ \`\`\`ts
47
+ // In OperationSpec
48
+ {
49
+ meta: { key: 'payments.charge.create', ... },
50
+ capability: { key: 'payments', version: '1.0.0' }, // Links to capability
51
+ io: { ... }
52
+ }
53
+
54
+ // In CapabilitySpec
55
+ {
56
+ meta: { key: 'payments', version: '1.0.0', ... },
57
+ provides: [
58
+ { surface: 'operation', key: 'payments.charge.create' }
59
+ ]
60
+ }
61
+ \`\`\`
62
+
63
+ Validation ensures both sides match via \`validateCapabilityConsistency()\`.
64
+
65
+ ## Registry Query Methods
66
+
67
+ The \`CapabilityRegistry\` now provides rich query capabilities:
68
+
69
+ \`\`\`ts
70
+ // Forward lookups: Capability → Specs
71
+ registry.getOperationsFor('payments'); // ['payments.charge.create', ...]
72
+ registry.getEventsFor('payments'); // ['payments.charge.succeeded', ...]
73
+ registry.getPresentationsFor('payments'); // ['payments.dashboard', ...]
74
+
75
+ // Reverse lookups: Spec → Capabilities
76
+ registry.getCapabilitiesForOperation('payments.charge.create');
77
+ registry.getCapabilitiesForEvent('payments.charge.succeeded');
78
+ registry.getCapabilitiesForPresentation('payments.dashboard');
79
+
80
+ // Inheritance
81
+ registry.getAncestors('payments.stripe'); // Parent chain
82
+ registry.getEffectiveRequirements('payments.stripe'); // Includes inherited
83
+ registry.getEffectiveSurfaces('payments.stripe'); // Includes inherited
84
+ \`\`\`
85
+
86
+ ## Inheritance
87
+
88
+ Capabilities can extend other capabilities:
89
+
90
+ \`\`\`ts
91
+ // Base capability
92
+ defineCapability({
93
+ meta: { key: 'payments.base', version: '1.0.0', ... },
94
+ requires: [{ key: 'auth', version: '1.0.0' }],
95
+ provides: [{ surface: 'operation', key: 'payments.list' }]
96
+ });
97
+
98
+ // Child capability inherits requirements
99
+ defineCapability({
100
+ meta: { key: 'payments.stripe', version: '1.0.0', ... },
101
+ extends: { key: 'payments.base', version: '1.0.0' },
102
+ requires: [{ key: 'encryption', version: '1.0.0' }], // Added
103
+ provides: [{ surface: 'operation', key: 'payments.stripe.charge' }]
104
+ });
105
+
106
+ // getEffectiveRequirements('payments.stripe') returns:
107
+ // [{ key: 'auth', ... }, { key: 'encryption', ... }]
108
+ \`\`\`
109
+
110
+ ## Runtime Enforcement
111
+
112
+ Use \`CapabilityContext\` for opt-in runtime checks:
113
+
114
+ \`\`\`ts
115
+ import { createCapabilityContext, assertCapabilityForOperation } from '@contractspec/lib.contracts';
116
+
117
+ // Create context from user's enabled capabilities
118
+ const ctx = createCapabilityContext(user.capabilities);
119
+
120
+ // Check capability
121
+ if (ctx.hasCapability('payments')) {
122
+ // User can access payments features
123
+ }
124
+
125
+ // Assert capability (throws if missing)
126
+ ctx.requireCapability('payments');
127
+
128
+ // Guard an operation
129
+ assertCapabilityForOperation(ctx, paymentOperation);
130
+
131
+ // Filter operations by enabled capabilities
132
+ const allowedOps = filterOperationsByCapability(ctx, allOperations);
133
+ \`\`\`
134
+
135
+ ## Validation
136
+
137
+ Validate bidirectional consistency between capabilities and specs:
138
+
139
+ \`\`\`ts
140
+ import { validateCapabilityConsistency, findOrphanSpecs } from '@contractspec/lib.contracts';
141
+
142
+ const result = validateCapabilityConsistency({
143
+ capabilities: capabilityRegistry,
144
+ operations: operationRegistry,
145
+ events: eventRegistry,
146
+ presentations: presentationRegistry,
147
+ });
148
+
149
+ if (!result.valid) {
150
+ console.error('Validation errors:', result.errors);
151
+ }
152
+
153
+ // Find specs without capability assignment (informational)
154
+ const orphans = findOrphanSpecs({ capabilities, operations });
155
+ \`\`\`
156
+
157
+ ## Feature Integration
158
+
159
+ During \`installFeature()\`:
160
+ 1. \`provides\` capabilities must exist in the registry
161
+ 2. \`requires\` must be satisfied by registered capabilities or local \`provides\`
162
+ 3. Referenced operations/events/presentations must exist
163
+
164
+ ## Authoring Guidelines
165
+
166
+ 1. **Register capabilities first** before referencing them in features
167
+ 2. **Use bidirectional linking** - set \`capability\` on specs and list them in \`provides\`
168
+ 3. **Version consciously** - bump versions on breaking changes
169
+ 4. **Document dependencies** with \`reason\` strings
170
+ 5. **Use inheritance** for capability families with shared requirements
171
+ 6. **Validate during CI** with \`validateCapabilityConsistency()\`
172
+
173
+ ## Error Handling
174
+
175
+ \`\`\`ts
176
+ import { CapabilityMissingError } from '@contractspec/lib.contracts';
177
+
178
+ try {
179
+ ctx.requireCapability('premium-features');
180
+ } catch (err) {
181
+ if (err instanceof CapabilityMissingError) {
182
+ console.log('Missing:', err.capabilityKey);
183
+ console.log('Required version:', err.requiredVersion);
184
+ }
185
+ }
186
+ \`\`\`
187
+
188
+ ## API Reference
189
+
190
+ ### Types
191
+ - \`CapabilitySpec\` - Capability definition
192
+ - \`CapabilityRef\` - Reference to a capability (key + version)
193
+ - \`CapabilitySurfaceRef\` - Reference to a provided surface
194
+ - \`CapabilityRequirement\` - Dependency requirement
195
+ - \`CapabilityContext\` - Runtime capability context
196
+ - \`CapabilityValidationResult\` - Validation result
197
+
198
+ ### Functions
199
+ - \`defineCapability(spec)\` - Define a capability spec
200
+ - \`createCapabilityContext(caps)\` - Create runtime context
201
+ - \`validateCapabilityConsistency(deps)\` - Validate bidirectional links
202
+ - \`findOrphanSpecs(deps)\` - Find specs without capability assignment
203
+ - \`assertCapabilityForOperation/Event/Presentation(ctx, spec)\` - Guards
204
+ - \`filterOperationsByCapability(ctx, ops)\` - Filter by enabled capabilities
205
+ `
17
206
  }];
18
207
  registerDocBlocks(tech_contracts_capabilities_DocBlocks);
19
208
 
@@ -0,0 +1,110 @@
1
+ import { PresentationSpec } from "../presentations/presentations.js";
2
+ import { AnyOperationSpec } from "../operations/operation.js";
3
+ import { CapabilityContext } from "./context.js";
4
+ import { AnyEventSpec } from "../events.js";
5
+
6
+ //#region src/capabilities/guards.d.ts
7
+
8
+ /** Result of a capability guard check. */
9
+ interface CapabilityGuardResult {
10
+ /** Whether the guard passed. */
11
+ allowed: boolean;
12
+ /** Missing capability if guard failed. */
13
+ missingCapability?: {
14
+ key: string;
15
+ version: string;
16
+ };
17
+ /** Reason for denial if guard failed. */
18
+ reason?: string;
19
+ }
20
+ /**
21
+ * Check if an operation's capability is enabled in the context.
22
+ *
23
+ * @param ctx - Capability context to check against
24
+ * @param operation - Operation spec to check
25
+ * @returns Guard result indicating if operation is allowed
26
+ *
27
+ * @example
28
+ * ```typescript
29
+ * const result = checkCapabilityForOperation(ctx, myOperation);
30
+ * if (!result.allowed) {
31
+ * console.log('Denied:', result.reason);
32
+ * }
33
+ * ```
34
+ */
35
+ declare function checkCapabilityForOperation(ctx: CapabilityContext, operation: AnyOperationSpec): CapabilityGuardResult;
36
+ /**
37
+ * Assert that an operation's capability is enabled, throwing if not.
38
+ *
39
+ * @param ctx - Capability context to check against
40
+ * @param operation - Operation spec to check
41
+ * @throws {CapabilityMissingError} If capability is not enabled
42
+ *
43
+ * @example
44
+ * ```typescript
45
+ * // Throws if capability missing
46
+ * assertCapabilityForOperation(ctx, myOperation);
47
+ *
48
+ * // Safe to proceed with operation
49
+ * await handler(input);
50
+ * ```
51
+ */
52
+ declare function assertCapabilityForOperation(ctx: CapabilityContext, operation: AnyOperationSpec): void;
53
+ /**
54
+ * Check if an event's capability is enabled in the context.
55
+ *
56
+ * @param ctx - Capability context to check against
57
+ * @param event - Event spec to check
58
+ * @returns Guard result indicating if event is allowed
59
+ */
60
+ declare function checkCapabilityForEvent(ctx: CapabilityContext, event: AnyEventSpec): CapabilityGuardResult;
61
+ /**
62
+ * Assert that an event's capability is enabled, throwing if not.
63
+ *
64
+ * @param ctx - Capability context to check against
65
+ * @param event - Event spec to check
66
+ * @throws {CapabilityMissingError} If capability is not enabled
67
+ */
68
+ declare function assertCapabilityForEvent(ctx: CapabilityContext, event: AnyEventSpec): void;
69
+ /**
70
+ * Check if a presentation's capability is enabled in the context.
71
+ *
72
+ * @param ctx - Capability context to check against
73
+ * @param presentation - Presentation spec to check
74
+ * @returns Guard result indicating if presentation is allowed
75
+ */
76
+ declare function checkCapabilityForPresentation(ctx: CapabilityContext, presentation: PresentationSpec): CapabilityGuardResult;
77
+ /**
78
+ * Assert that a presentation's capability is enabled, throwing if not.
79
+ *
80
+ * @param ctx - Capability context to check against
81
+ * @param presentation - Presentation spec to check
82
+ * @throws {CapabilityMissingError} If capability is not enabled
83
+ */
84
+ declare function assertCapabilityForPresentation(ctx: CapabilityContext, presentation: PresentationSpec): void;
85
+ /**
86
+ * Filter operations to only those with enabled capabilities.
87
+ *
88
+ * @param ctx - Capability context to check against
89
+ * @param operations - Operations to filter
90
+ * @returns Operations that have their capabilities enabled (or no capability requirement)
91
+ */
92
+ declare function filterOperationsByCapability(ctx: CapabilityContext, operations: AnyOperationSpec[]): AnyOperationSpec[];
93
+ /**
94
+ * Filter events to only those with enabled capabilities.
95
+ *
96
+ * @param ctx - Capability context to check against
97
+ * @param events - Events to filter
98
+ * @returns Events that have their capabilities enabled (or no capability requirement)
99
+ */
100
+ declare function filterEventsByCapability(ctx: CapabilityContext, events: AnyEventSpec[]): AnyEventSpec[];
101
+ /**
102
+ * Filter presentations to only those with enabled capabilities.
103
+ *
104
+ * @param ctx - Capability context to check against
105
+ * @param presentations - Presentations to filter
106
+ * @returns Presentations that have their capabilities enabled (or no capability requirement)
107
+ */
108
+ declare function filterPresentationsByCapability(ctx: CapabilityContext, presentations: PresentationSpec[]): PresentationSpec[];
109
+ //#endregion
110
+ export { CapabilityGuardResult, assertCapabilityForEvent, assertCapabilityForOperation, assertCapabilityForPresentation, checkCapabilityForEvent, checkCapabilityForOperation, checkCapabilityForPresentation, filterEventsByCapability, filterOperationsByCapability, filterPresentationsByCapability };
@@ -0,0 +1,146 @@
1
+ import { CapabilityMissingError } from "./context.js";
2
+
3
+ //#region src/capabilities/guards.ts
4
+ /**
5
+ * Check if an operation's capability is enabled in the context.
6
+ *
7
+ * @param ctx - Capability context to check against
8
+ * @param operation - Operation spec to check
9
+ * @returns Guard result indicating if operation is allowed
10
+ *
11
+ * @example
12
+ * ```typescript
13
+ * const result = checkCapabilityForOperation(ctx, myOperation);
14
+ * if (!result.allowed) {
15
+ * console.log('Denied:', result.reason);
16
+ * }
17
+ * ```
18
+ */
19
+ function checkCapabilityForOperation(ctx, operation) {
20
+ if (!operation.capability) return { allowed: true };
21
+ const { key, version } = operation.capability;
22
+ if (ctx.hasCapability(key, version)) return { allowed: true };
23
+ return {
24
+ allowed: false,
25
+ missingCapability: {
26
+ key,
27
+ version
28
+ },
29
+ reason: `Operation "${operation.meta.key}" requires capability "${key}.v${version}"`
30
+ };
31
+ }
32
+ /**
33
+ * Assert that an operation's capability is enabled, throwing if not.
34
+ *
35
+ * @param ctx - Capability context to check against
36
+ * @param operation - Operation spec to check
37
+ * @throws {CapabilityMissingError} If capability is not enabled
38
+ *
39
+ * @example
40
+ * ```typescript
41
+ * // Throws if capability missing
42
+ * assertCapabilityForOperation(ctx, myOperation);
43
+ *
44
+ * // Safe to proceed with operation
45
+ * await handler(input);
46
+ * ```
47
+ */
48
+ function assertCapabilityForOperation(ctx, operation) {
49
+ const result = checkCapabilityForOperation(ctx, operation);
50
+ if (!result.allowed && result.missingCapability) throw new CapabilityMissingError(result.missingCapability.key, result.missingCapability.version);
51
+ }
52
+ /**
53
+ * Check if an event's capability is enabled in the context.
54
+ *
55
+ * @param ctx - Capability context to check against
56
+ * @param event - Event spec to check
57
+ * @returns Guard result indicating if event is allowed
58
+ */
59
+ function checkCapabilityForEvent(ctx, event) {
60
+ if (!event.capability) return { allowed: true };
61
+ const { key, version } = event.capability;
62
+ if (ctx.hasCapability(key, version)) return { allowed: true };
63
+ return {
64
+ allowed: false,
65
+ missingCapability: {
66
+ key,
67
+ version
68
+ },
69
+ reason: `Event "${event.meta.key}" requires capability "${key}.v${version}"`
70
+ };
71
+ }
72
+ /**
73
+ * Assert that an event's capability is enabled, throwing if not.
74
+ *
75
+ * @param ctx - Capability context to check against
76
+ * @param event - Event spec to check
77
+ * @throws {CapabilityMissingError} If capability is not enabled
78
+ */
79
+ function assertCapabilityForEvent(ctx, event) {
80
+ const result = checkCapabilityForEvent(ctx, event);
81
+ if (!result.allowed && result.missingCapability) throw new CapabilityMissingError(result.missingCapability.key, result.missingCapability.version);
82
+ }
83
+ /**
84
+ * Check if a presentation's capability is enabled in the context.
85
+ *
86
+ * @param ctx - Capability context to check against
87
+ * @param presentation - Presentation spec to check
88
+ * @returns Guard result indicating if presentation is allowed
89
+ */
90
+ function checkCapabilityForPresentation(ctx, presentation) {
91
+ if (!presentation.capability) return { allowed: true };
92
+ const { key, version } = presentation.capability;
93
+ if (ctx.hasCapability(key, version)) return { allowed: true };
94
+ return {
95
+ allowed: false,
96
+ missingCapability: {
97
+ key,
98
+ version
99
+ },
100
+ reason: `Presentation "${presentation.meta.key}" requires capability "${key}.v${version}"`
101
+ };
102
+ }
103
+ /**
104
+ * Assert that a presentation's capability is enabled, throwing if not.
105
+ *
106
+ * @param ctx - Capability context to check against
107
+ * @param presentation - Presentation spec to check
108
+ * @throws {CapabilityMissingError} If capability is not enabled
109
+ */
110
+ function assertCapabilityForPresentation(ctx, presentation) {
111
+ const result = checkCapabilityForPresentation(ctx, presentation);
112
+ if (!result.allowed && result.missingCapability) throw new CapabilityMissingError(result.missingCapability.key, result.missingCapability.version);
113
+ }
114
+ /**
115
+ * Filter operations to only those with enabled capabilities.
116
+ *
117
+ * @param ctx - Capability context to check against
118
+ * @param operations - Operations to filter
119
+ * @returns Operations that have their capabilities enabled (or no capability requirement)
120
+ */
121
+ function filterOperationsByCapability(ctx, operations) {
122
+ return operations.filter((op) => checkCapabilityForOperation(ctx, op).allowed);
123
+ }
124
+ /**
125
+ * Filter events to only those with enabled capabilities.
126
+ *
127
+ * @param ctx - Capability context to check against
128
+ * @param events - Events to filter
129
+ * @returns Events that have their capabilities enabled (or no capability requirement)
130
+ */
131
+ function filterEventsByCapability(ctx, events) {
132
+ return events.filter((ev) => checkCapabilityForEvent(ctx, ev).allowed);
133
+ }
134
+ /**
135
+ * Filter presentations to only those with enabled capabilities.
136
+ *
137
+ * @param ctx - Capability context to check against
138
+ * @param presentations - Presentations to filter
139
+ * @returns Presentations that have their capabilities enabled (or no capability requirement)
140
+ */
141
+ function filterPresentationsByCapability(ctx, presentations) {
142
+ return presentations.filter((pres) => checkCapabilityForPresentation(ctx, pres).allowed);
143
+ }
144
+
145
+ //#endregion
146
+ export { assertCapabilityForEvent, assertCapabilityForOperation, assertCapabilityForPresentation, checkCapabilityForEvent, checkCapabilityForOperation, checkCapabilityForPresentation, filterEventsByCapability, filterOperationsByCapability, filterPresentationsByCapability };
@@ -1,3 +1,6 @@
1
1
  import { CapabilityKind, CapabilityMeta, CapabilityRef, CapabilityRegistry, CapabilityRequirement, CapabilitySpec, CapabilitySurface, CapabilitySurfaceRef, capabilityKey, defineCapability } from "./capabilities.js";
2
+ import { CapabilityValidationDeps, CapabilityValidationError, CapabilityValidationResult, findOrphanSpecs, validateCapabilityConsistency } from "./validation.js";
3
+ import { CapabilityContext, CapabilityMissingError, createBypassCapabilityContext, createCapabilityContext, createEmptyCapabilityContext } from "./context.js";
4
+ import { CapabilityGuardResult, assertCapabilityForEvent, assertCapabilityForOperation, assertCapabilityForPresentation, checkCapabilityForEvent, checkCapabilityForOperation, checkCapabilityForPresentation, filterEventsByCapability, filterOperationsByCapability, filterPresentationsByCapability } from "./guards.js";
2
5
  import { openBankingAccountsReadCapability, openBankingBalancesReadCapability, openBankingTransactionsReadCapability, registerOpenBankingCapabilities } from "./openbanking.js";
3
- export { CapabilityKind, CapabilityMeta, CapabilityRef, CapabilityRegistry, CapabilityRequirement, CapabilitySpec, CapabilitySurface, CapabilitySurfaceRef, capabilityKey, defineCapability, openBankingAccountsReadCapability, openBankingBalancesReadCapability, openBankingTransactionsReadCapability, registerOpenBankingCapabilities };
6
+ export { CapabilityContext, CapabilityGuardResult, CapabilityKind, CapabilityMeta, CapabilityMissingError, CapabilityRef, CapabilityRegistry, CapabilityRequirement, CapabilitySpec, CapabilitySurface, CapabilitySurfaceRef, CapabilityValidationDeps, CapabilityValidationError, CapabilityValidationResult, assertCapabilityForEvent, assertCapabilityForOperation, assertCapabilityForPresentation, capabilityKey, checkCapabilityForEvent, checkCapabilityForOperation, checkCapabilityForPresentation, createBypassCapabilityContext, createCapabilityContext, createEmptyCapabilityContext, defineCapability, filterEventsByCapability, filterOperationsByCapability, filterPresentationsByCapability, findOrphanSpecs, openBankingAccountsReadCapability, openBankingBalancesReadCapability, openBankingTransactionsReadCapability, registerOpenBankingCapabilities, validateCapabilityConsistency };
@@ -1,4 +1,7 @@
1
1
  import { CapabilityRegistry, capabilityKey, defineCapability } from "./capabilities.js";
2
+ import { findOrphanSpecs, validateCapabilityConsistency } from "./validation.js";
3
+ import { CapabilityMissingError, createBypassCapabilityContext, createCapabilityContext, createEmptyCapabilityContext } from "./context.js";
4
+ import { assertCapabilityForEvent, assertCapabilityForOperation, assertCapabilityForPresentation, checkCapabilityForEvent, checkCapabilityForOperation, checkCapabilityForPresentation, filterEventsByCapability, filterOperationsByCapability, filterPresentationsByCapability } from "./guards.js";
2
5
  import { openBankingAccountsReadCapability, openBankingBalancesReadCapability, openBankingTransactionsReadCapability, registerOpenBankingCapabilities } from "./openbanking.js";
3
6
 
4
- export { CapabilityRegistry, capabilityKey, defineCapability, openBankingAccountsReadCapability, openBankingBalancesReadCapability, openBankingTransactionsReadCapability, registerOpenBankingCapabilities };
7
+ export { CapabilityMissingError, CapabilityRegistry, assertCapabilityForEvent, assertCapabilityForOperation, assertCapabilityForPresentation, capabilityKey, checkCapabilityForEvent, checkCapabilityForOperation, checkCapabilityForPresentation, createBypassCapabilityContext, createCapabilityContext, createEmptyCapabilityContext, defineCapability, filterEventsByCapability, filterOperationsByCapability, filterPresentationsByCapability, findOrphanSpecs, openBankingAccountsReadCapability, openBankingBalancesReadCapability, openBankingTransactionsReadCapability, registerOpenBankingCapabilities, validateCapabilityConsistency };
@@ -0,0 +1,76 @@
1
+ import { CapabilityRegistry, CapabilitySurface } from "./capabilities.js";
2
+ import { OperationSpecRegistry } from "../operations/registry.js";
3
+ import { PresentationRegistry } from "../presentations/registry.js";
4
+ import "../presentations/index.js";
5
+ import { EventRegistry } from "../events.js";
6
+
7
+ //#region src/capabilities/validation.d.ts
8
+
9
+ /** Single validation error describing an inconsistency. */
10
+ interface CapabilityValidationError {
11
+ /** Type of validation error. */
12
+ type: 'missing_surface_spec' | 'orphan_spec' | 'capability_not_found' | 'surface_not_in_provides';
13
+ /** Human-readable error message. */
14
+ message: string;
15
+ /** Capability key involved (if applicable). */
16
+ capabilityKey?: string;
17
+ /** Surface type involved (if applicable). */
18
+ surface?: CapabilitySurface;
19
+ /** Spec key involved (if applicable). */
20
+ specKey?: string;
21
+ }
22
+ /** Result of capability consistency validation. */
23
+ interface CapabilityValidationResult {
24
+ /** Whether validation passed with no errors. */
25
+ valid: boolean;
26
+ /** List of validation errors found. */
27
+ errors: CapabilityValidationError[];
28
+ /** List of warnings (non-blocking issues). */
29
+ warnings: CapabilityValidationError[];
30
+ }
31
+ /** Registries needed for full bidirectional validation. */
32
+ interface CapabilityValidationDeps {
33
+ capabilities: CapabilityRegistry;
34
+ operations?: OperationSpecRegistry;
35
+ events?: EventRegistry;
36
+ presentations?: PresentationRegistry;
37
+ }
38
+ /**
39
+ * Validates bidirectional consistency between capabilities and their surfaces.
40
+ *
41
+ * Checks:
42
+ * 1. Forward validation: Every surface ref in capability `provides` exists
43
+ * 2. Reverse validation: Every spec with `capability` field is in that capability's `provides`
44
+ *
45
+ * @param deps - Registries to validate against
46
+ * @returns Validation result with errors and warnings
47
+ *
48
+ * @example
49
+ * ```typescript
50
+ * const result = validateCapabilityConsistency({
51
+ * capabilities: capabilityRegistry,
52
+ * operations: operationRegistry,
53
+ * events: eventRegistry,
54
+ * });
55
+ *
56
+ * if (!result.valid) {
57
+ * console.error('Capability validation failed:', result.errors);
58
+ * }
59
+ * ```
60
+ */
61
+ declare function validateCapabilityConsistency(deps: CapabilityValidationDeps): CapabilityValidationResult;
62
+ /**
63
+ * Finds specs that have no capability assignment (orphan specs).
64
+ * This is informational - orphan specs are allowed but may indicate
65
+ * incomplete capability modeling.
66
+ *
67
+ * @param deps - Registries to check
68
+ * @returns List of spec keys without capability assignment
69
+ */
70
+ declare function findOrphanSpecs(deps: CapabilityValidationDeps): {
71
+ operations: string[];
72
+ events: string[];
73
+ presentations: string[];
74
+ };
75
+ //#endregion
76
+ export { CapabilityValidationDeps, CapabilityValidationError, CapabilityValidationResult, findOrphanSpecs, validateCapabilityConsistency };