@contractspec/lib.contracts 0.0.0-canary-20260119225944 → 0.0.0-canary-20260202053150
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/dist/app-config/{contracts.d.ts → app-config.contracts.d.ts} +52 -52
- package/dist/app-config/{contracts.js → app-config.contracts.js} +1 -1
- package/dist/app-config/lifecycle-contracts.d.ts +55 -55
- package/dist/app-config/runtime.d.ts +4 -4
- package/dist/app-config/spec.d.ts +2 -2
- package/dist/capabilities/capabilities.d.ts +64 -5
- package/dist/capabilities/capabilities.js +125 -0
- package/dist/capabilities/context.d.ts +88 -0
- package/dist/capabilities/context.js +87 -0
- package/dist/capabilities/docs/capabilities.docblock.js +191 -2
- package/dist/capabilities/guards.d.ts +110 -0
- package/dist/capabilities/guards.js +146 -0
- package/dist/capabilities/index.d.ts +4 -1
- package/dist/capabilities/index.js +4 -1
- package/dist/capabilities/validation.d.ts +76 -0
- package/dist/capabilities/validation.js +141 -0
- package/dist/client/react/feature-render.d.ts +2 -2
- package/dist/data-views/data-views.d.ts +2 -1
- package/dist/data-views/index.d.ts +2 -1
- package/dist/data-views/registry.d.ts +1 -1
- package/dist/data-views/report/contractVerificationTable.d.ts +10 -0
- package/dist/data-views/report/contractVerificationTable.js +95 -0
- package/dist/data-views/runtime.d.ts +2 -2
- package/dist/data-views/spec.d.ts +2 -8
- package/dist/data-views/types.d.ts +1 -1
- package/dist/docs/capabilities/documentationSystem.capability.d.ts +7 -0
- package/dist/docs/capabilities/documentationSystem.capability.js +71 -0
- package/dist/docs/capabilities/index.d.ts +2 -0
- package/dist/docs/capabilities/index.js +3 -0
- package/dist/docs/commands/docsGenerate.command.d.ts +95 -0
- package/dist/docs/commands/docsGenerate.command.js +142 -0
- package/dist/docs/commands/docsPublish.command.d.ts +64 -0
- package/dist/docs/commands/docsPublish.command.js +107 -0
- package/dist/docs/commands/index.d.ts +3 -0
- package/dist/docs/commands/index.js +4 -0
- package/dist/docs/constants.d.ts +7 -0
- package/dist/docs/constants.js +10 -0
- package/dist/docs/contracts.d.ts +442 -0
- package/dist/docs/contracts.js +58 -0
- package/dist/docs/ensure-docblocks.d.ts +1 -0
- package/dist/docs/ensure-docblocks.js +1 -0
- package/dist/docs/events/docsGenerated.event.d.ts +62 -0
- package/dist/docs/events/docsGenerated.event.js +53 -0
- package/dist/docs/events/docsPublished.event.d.ts +70 -0
- package/dist/docs/events/docsPublished.event.js +57 -0
- package/dist/docs/events/index.d.ts +3 -0
- package/dist/docs/events/index.js +4 -0
- package/dist/docs/forms/docsSearch.form.d.ts +22 -0
- package/dist/docs/forms/docsSearch.form.js +113 -0
- package/dist/docs/forms/index.d.ts +2 -0
- package/dist/docs/forms/index.js +3 -0
- package/dist/docs/index.d.ts +23 -2
- package/dist/docs/index.js +25 -1
- package/dist/docs/presentations/docsLayout.presentation.d.ts +7 -0
- package/dist/docs/presentations/docsLayout.presentation.js +35 -0
- package/dist/docs/presentations/docsReferencePage.presentation.d.ts +7 -0
- package/dist/docs/presentations/docsReferencePage.presentation.js +35 -0
- package/dist/docs/presentations/index.d.ts +3 -0
- package/dist/docs/presentations/index.js +4 -0
- package/dist/docs/queries/contractReference.query.d.ts +217 -0
- package/dist/docs/queries/contractReference.query.js +125 -0
- package/dist/docs/queries/docsIndex.query.d.ts +272 -0
- package/dist/docs/queries/docsIndex.query.js +133 -0
- package/dist/docs/queries/index.d.ts +3 -0
- package/dist/docs/queries/index.js +4 -0
- package/dist/docs/tech/cli.docblock.js +10 -0
- package/dist/docs/tech/docs-system.docblock.d.ts +1 -0
- package/dist/docs/tech/docs-system.docblock.js +128 -0
- package/dist/docs/tech/report-verification-table.docblock.d.ts +1 -0
- package/dist/docs/tech/report-verification-table.docblock.js +50 -0
- package/dist/docs/views/contractReference.dataView.d.ts +7 -0
- package/dist/docs/views/contractReference.dataView.js +80 -0
- package/dist/docs/views/docsIndex.dataView.d.ts +7 -0
- package/dist/docs/views/docsIndex.dataView.js +136 -0
- package/dist/docs/views/exampleCatalog.dataView.d.ts +7 -0
- package/dist/docs/views/exampleCatalog.dataView.js +91 -0
- package/dist/docs/views/index.d.ts +4 -0
- package/dist/docs/views/index.js +5 -0
- package/dist/events.d.ts +80 -14
- package/dist/events.js +33 -3
- package/dist/examples/schema.d.ts +12 -12
- package/dist/examples/types.d.ts +1 -1
- package/dist/experiments/spec.d.ts +9 -6
- package/dist/features/index.d.ts +2 -2
- package/dist/features/install.d.ts +4 -4
- package/dist/features/types.d.ts +28 -32
- package/dist/forms/forms.d.ts +1 -1
- package/dist/index.d.ts +60 -31
- package/dist/index.js +32 -4
- package/dist/install.d.ts +1 -1
- package/dist/integrations/openbanking/contracts/accounts.d.ts +67 -67
- package/dist/integrations/openbanking/contracts/balances.d.ts +35 -35
- package/dist/integrations/openbanking/contracts/transactions.d.ts +49 -49
- package/dist/integrations/openbanking/models.d.ts +55 -55
- package/dist/integrations/operations.d.ts +103 -103
- package/dist/integrations/spec.d.ts +2 -2
- package/dist/jsonschema.d.ts +1 -1
- package/dist/knowledge/operations.d.ts +67 -67
- package/dist/knowledge/spec.d.ts +1 -1
- package/dist/llm/exporters.d.ts +4 -4
- package/dist/llm/types.d.ts +1 -1
- package/dist/markdown.d.ts +2 -2
- package/dist/operations/index.d.ts +4 -1
- package/dist/operations/index.js +4 -1
- package/dist/operations/operation.d.ts +8 -2
- package/dist/operations/registry.d.ts +1 -1
- package/dist/operations/report/getContractVerificationStatus.d.ts +75 -0
- package/dist/operations/report/getContractVerificationStatus.js +96 -0
- package/dist/operations/report/index.d.ts +13 -0
- package/dist/operations/report/index.js +45 -0
- package/dist/ownership.d.ts +133 -8
- package/dist/ownership.js +25 -0
- package/dist/policy/context.d.ts +237 -0
- package/dist/policy/context.js +227 -0
- package/dist/policy/guards.d.ts +145 -0
- package/dist/policy/guards.js +254 -0
- package/dist/policy/index.d.ts +12 -1
- package/dist/policy/index.js +11 -1
- package/dist/policy/spec.d.ts +7 -4
- package/dist/policy/validation.d.ts +67 -0
- package/dist/policy/validation.js +307 -0
- package/dist/presentations/presentations.d.ts +6 -0
- package/dist/presentations/registry.d.ts +1 -1
- package/dist/registry.d.ts +1 -1
- package/dist/serialization/index.d.ts +3 -0
- package/dist/serialization/index.js +3 -0
- package/dist/serialization/serializers.d.ts +40 -0
- package/dist/serialization/serializers.js +148 -0
- package/dist/serialization/types.d.ts +103 -0
- package/dist/serialization/types.js +0 -0
- package/dist/server/rest-elysia.d.ts +1 -1
- package/dist/server/rest-express.d.ts +1 -1
- package/dist/server/rest-generic.d.ts +1 -1
- package/dist/server/rest-next-app.d.ts +1 -1
- package/dist/server/rest-next-mcp.d.ts +1 -1
- package/dist/server/rest-next-pages.d.ts +1 -1
- package/dist/telemetry/spec.d.ts +1 -1
- package/dist/telemetry/tracker.d.ts +3 -2
- package/dist/tests/runner.d.ts +1 -1
- package/dist/tests/spec.d.ts +17 -12
- package/dist/themes.d.ts +8 -5
- package/dist/translations/index.d.ts +6 -0
- package/dist/translations/index.js +5 -0
- package/dist/translations/registry.d.ts +144 -0
- package/dist/translations/registry.js +223 -0
- package/dist/translations/spec.d.ts +126 -0
- package/dist/translations/spec.js +31 -0
- package/dist/translations/validation.d.ts +85 -0
- package/dist/translations/validation.js +328 -0
- package/dist/types.d.ts +142 -16
- package/dist/versioning/index.d.ts +2 -1
- package/dist/versioning/index.js +2 -1
- package/dist/versioning/refs.d.ts +179 -0
- package/dist/versioning/refs.js +161 -0
- package/dist/workflow/context.d.ts +191 -0
- package/dist/workflow/context.js +227 -0
- package/dist/workflow/index.d.ts +6 -3
- package/dist/workflow/index.js +4 -2
- package/dist/workflow/spec.d.ts +4 -11
- package/dist/workflow/validation.d.ts +64 -2
- package/dist/workflow/validation.js +194 -1
- package/dist/workspace-config/contractsrc-schema.d.ts +3 -3
- package/dist/workspace-config/contractsrc-schema.js +1 -0
- package/dist/workspace-config/contractsrc-types.d.ts +7 -8
- package/dist/workspace-config/index.d.ts +2 -2
- package/package.json +52 -9
|
@@ -1,39 +1,98 @@
|
|
|
1
|
+
import { VersionedSpecRef } from "../versioning/refs.js";
|
|
1
2
|
import { OwnerShipMeta } from "../ownership.js";
|
|
2
3
|
|
|
3
4
|
//#region src/capabilities/capabilities.d.ts
|
|
5
|
+
/** Classification of capability types. */
|
|
4
6
|
type CapabilityKind = 'api' | 'event' | 'data' | 'ui' | 'integration';
|
|
7
|
+
/** Surfaces where capabilities can be exposed or consumed. */
|
|
5
8
|
type CapabilitySurface = 'operation' | 'event' | 'workflow' | 'presentation' | 'resource';
|
|
9
|
+
/**
|
|
10
|
+
* Reference to a capability on a specific surface.
|
|
11
|
+
* Extends VersionedSpecRef with surface and description context.
|
|
12
|
+
*/
|
|
6
13
|
interface CapabilitySurfaceRef {
|
|
14
|
+
/** The surface type where this capability is exposed. */
|
|
7
15
|
surface: CapabilitySurface;
|
|
16
|
+
/** Unique key identifying the spec on that surface (e.g., operation key). */
|
|
8
17
|
key: string;
|
|
9
|
-
version
|
|
18
|
+
/** Semantic version of the spec on that surface. */
|
|
19
|
+
version?: string;
|
|
20
|
+
/** Optional description of what this capability provides. */
|
|
10
21
|
description?: string;
|
|
11
22
|
}
|
|
23
|
+
/** Metadata for a capability spec, extending ownership with kind. */
|
|
12
24
|
interface CapabilityMeta extends OwnerShipMeta {
|
|
25
|
+
/** The kind/category of this capability. */
|
|
13
26
|
kind: CapabilityKind;
|
|
14
27
|
}
|
|
28
|
+
/**
|
|
29
|
+
* Requirement for a capability dependency.
|
|
30
|
+
* Used to declare what capabilities a spec needs.
|
|
31
|
+
*/
|
|
15
32
|
interface CapabilityRequirement {
|
|
33
|
+
/** Unique key of the required capability. */
|
|
16
34
|
key: string;
|
|
35
|
+
/** Optional specific version required. */
|
|
17
36
|
version?: string;
|
|
37
|
+
/** Optional kind filter for the requirement. */
|
|
18
38
|
kind?: CapabilityKind;
|
|
39
|
+
/** If true, the requirement is optional and won't block if missing. */
|
|
19
40
|
optional?: boolean;
|
|
41
|
+
/** Human-readable reason why this capability is required. */
|
|
20
42
|
reason?: string;
|
|
21
43
|
}
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
44
|
+
/**
|
|
45
|
+
* Reference to a capability spec.
|
|
46
|
+
* Uses key and version to identify a specific capability.
|
|
47
|
+
*/
|
|
48
|
+
type CapabilityRef = VersionedSpecRef;
|
|
26
49
|
interface CapabilitySpec {
|
|
27
50
|
meta: CapabilityMeta;
|
|
51
|
+
/** Capabilities this capability extends (inherits requirements from). */
|
|
52
|
+
extends?: CapabilityRef;
|
|
53
|
+
/** Surfaces (operations, events, presentations, etc.) this capability provides. */
|
|
28
54
|
provides?: CapabilitySurfaceRef[];
|
|
55
|
+
/** Capabilities that must be present for this capability to function. */
|
|
29
56
|
requires?: CapabilityRequirement[];
|
|
30
57
|
}
|
|
31
58
|
declare class CapabilityRegistry {
|
|
32
59
|
private readonly items;
|
|
60
|
+
/** Reverse index: surface key -> capability key (built lazily) */
|
|
61
|
+
private surfaceIndex;
|
|
33
62
|
register(spec: CapabilitySpec): this;
|
|
34
63
|
list(): CapabilitySpec[];
|
|
35
64
|
get(key: string, version?: string): CapabilitySpec | undefined;
|
|
36
65
|
satisfies(requirement: CapabilityRequirement, additional?: CapabilityRef[] | undefined): boolean;
|
|
66
|
+
/** Build reverse index from surface specs to capabilities. */
|
|
67
|
+
private buildSurfaceIndex;
|
|
68
|
+
/** Get all operation keys provided by a capability. */
|
|
69
|
+
getOperationsFor(capabilityKey: string, version?: string): string[];
|
|
70
|
+
/** Get all event keys provided by a capability. */
|
|
71
|
+
getEventsFor(capabilityKey: string, version?: string): string[];
|
|
72
|
+
/** Get all presentation keys provided by a capability. */
|
|
73
|
+
getPresentationsFor(capabilityKey: string, version?: string): string[];
|
|
74
|
+
/** Get all workflow keys provided by a capability. */
|
|
75
|
+
getWorkflowsFor(capabilityKey: string, version?: string): string[];
|
|
76
|
+
/** Get all resource keys provided by a capability. */
|
|
77
|
+
getResourcesFor(capabilityKey: string, version?: string): string[];
|
|
78
|
+
/** Get capability refs that provide a specific operation. */
|
|
79
|
+
getCapabilitiesForOperation(operationKey: string): CapabilityRef[];
|
|
80
|
+
/** Get capability refs that provide a specific event. */
|
|
81
|
+
getCapabilitiesForEvent(eventKey: string): CapabilityRef[];
|
|
82
|
+
/** Get capability refs that provide a specific presentation. */
|
|
83
|
+
getCapabilitiesForPresentation(presentationKey: string): CapabilityRef[];
|
|
84
|
+
/** Get the ancestor chain for a capability (from immediate parent to root). */
|
|
85
|
+
getAncestors(capabilityKey: string, version?: string): CapabilitySpec[];
|
|
86
|
+
/**
|
|
87
|
+
* Get effective requirements for a capability, including inherited ones.
|
|
88
|
+
* Requirements from ancestors are included, with child requirements taking
|
|
89
|
+
* precedence over parent requirements for the same key.
|
|
90
|
+
*/
|
|
91
|
+
getEffectiveRequirements(capabilityKey: string, version?: string): CapabilityRequirement[];
|
|
92
|
+
/**
|
|
93
|
+
* Get all surfaces provided by a capability, including inherited ones.
|
|
94
|
+
*/
|
|
95
|
+
getEffectiveSurfaces(capabilityKey: string, version?: string): CapabilitySurfaceRef[];
|
|
37
96
|
}
|
|
38
97
|
declare function capabilityKey(spec: CapabilitySpec): string;
|
|
39
98
|
declare function defineCapability(spec: CapabilitySpec): CapabilitySpec;
|
|
@@ -4,10 +4,13 @@ import { compareVersions } from "compare-versions";
|
|
|
4
4
|
const capKey = (key, version) => `${key}.v${version}`;
|
|
5
5
|
var CapabilityRegistry = class {
|
|
6
6
|
items = /* @__PURE__ */ new Map();
|
|
7
|
+
/** Reverse index: surface key -> capability key (built lazily) */
|
|
8
|
+
surfaceIndex = null;
|
|
7
9
|
register(spec) {
|
|
8
10
|
const key = capKey(spec.meta.key, spec.meta.version);
|
|
9
11
|
if (this.items.has(key)) throw new Error(`Duplicate capability ${key}`);
|
|
10
12
|
this.items.set(key, spec);
|
|
13
|
+
this.surfaceIndex = null;
|
|
11
14
|
return this;
|
|
12
15
|
}
|
|
13
16
|
list() {
|
|
@@ -31,6 +34,128 @@ var CapabilityRegistry = class {
|
|
|
31
34
|
if (requirement.version != null && spec.meta.version !== requirement.version) return false;
|
|
32
35
|
return true;
|
|
33
36
|
}
|
|
37
|
+
/** Build reverse index from surface specs to capabilities. */
|
|
38
|
+
buildSurfaceIndex() {
|
|
39
|
+
if (this.surfaceIndex) return this.surfaceIndex;
|
|
40
|
+
this.surfaceIndex = /* @__PURE__ */ new Map();
|
|
41
|
+
for (const spec of this.items.values()) {
|
|
42
|
+
const capabilityKey$1 = capKey(spec.meta.key, spec.meta.version);
|
|
43
|
+
for (const surface of spec.provides ?? []) {
|
|
44
|
+
const surfaceKey = `${surface.surface}:${surface.key}`;
|
|
45
|
+
if (!this.surfaceIndex.has(surfaceKey)) this.surfaceIndex.set(surfaceKey, /* @__PURE__ */ new Set());
|
|
46
|
+
this.surfaceIndex.get(surfaceKey)?.add(capabilityKey$1);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
return this.surfaceIndex;
|
|
50
|
+
}
|
|
51
|
+
/** Get all operation keys provided by a capability. */
|
|
52
|
+
getOperationsFor(capabilityKey$1, version) {
|
|
53
|
+
const spec = this.get(capabilityKey$1, version);
|
|
54
|
+
if (!spec) return [];
|
|
55
|
+
return spec.provides?.filter((s) => s.surface === "operation").map((s) => s.key) ?? [];
|
|
56
|
+
}
|
|
57
|
+
/** Get all event keys provided by a capability. */
|
|
58
|
+
getEventsFor(capabilityKey$1, version) {
|
|
59
|
+
const spec = this.get(capabilityKey$1, version);
|
|
60
|
+
if (!spec) return [];
|
|
61
|
+
return spec.provides?.filter((s) => s.surface === "event").map((s) => s.key) ?? [];
|
|
62
|
+
}
|
|
63
|
+
/** Get all presentation keys provided by a capability. */
|
|
64
|
+
getPresentationsFor(capabilityKey$1, version) {
|
|
65
|
+
const spec = this.get(capabilityKey$1, version);
|
|
66
|
+
if (!spec) return [];
|
|
67
|
+
return spec.provides?.filter((s) => s.surface === "presentation").map((s) => s.key) ?? [];
|
|
68
|
+
}
|
|
69
|
+
/** Get all workflow keys provided by a capability. */
|
|
70
|
+
getWorkflowsFor(capabilityKey$1, version) {
|
|
71
|
+
const spec = this.get(capabilityKey$1, version);
|
|
72
|
+
if (!spec) return [];
|
|
73
|
+
return spec.provides?.filter((s) => s.surface === "workflow").map((s) => s.key) ?? [];
|
|
74
|
+
}
|
|
75
|
+
/** Get all resource keys provided by a capability. */
|
|
76
|
+
getResourcesFor(capabilityKey$1, version) {
|
|
77
|
+
const spec = this.get(capabilityKey$1, version);
|
|
78
|
+
if (!spec) return [];
|
|
79
|
+
return spec.provides?.filter((s) => s.surface === "resource").map((s) => s.key) ?? [];
|
|
80
|
+
}
|
|
81
|
+
/** Get capability refs that provide a specific operation. */
|
|
82
|
+
getCapabilitiesForOperation(operationKey) {
|
|
83
|
+
const capKeys = this.buildSurfaceIndex().get(`operation:${operationKey}`);
|
|
84
|
+
if (!capKeys) return [];
|
|
85
|
+
return [...capKeys].map((k) => {
|
|
86
|
+
const spec = this.items.get(k);
|
|
87
|
+
return {
|
|
88
|
+
key: spec?.meta.key ?? "",
|
|
89
|
+
version: spec?.meta.version ?? ""
|
|
90
|
+
};
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
/** Get capability refs that provide a specific event. */
|
|
94
|
+
getCapabilitiesForEvent(eventKey) {
|
|
95
|
+
const capKeys = this.buildSurfaceIndex().get(`event:${eventKey}`);
|
|
96
|
+
if (!capKeys) return [];
|
|
97
|
+
return [...capKeys].map((k) => {
|
|
98
|
+
const spec = this.items.get(k);
|
|
99
|
+
return {
|
|
100
|
+
key: spec?.meta.key ?? "",
|
|
101
|
+
version: spec?.meta.version ?? ""
|
|
102
|
+
};
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
/** Get capability refs that provide a specific presentation. */
|
|
106
|
+
getCapabilitiesForPresentation(presentationKey) {
|
|
107
|
+
const capKeys = this.buildSurfaceIndex().get(`presentation:${presentationKey}`);
|
|
108
|
+
if (!capKeys) return [];
|
|
109
|
+
return [...capKeys].map((k) => {
|
|
110
|
+
const spec = this.items.get(k);
|
|
111
|
+
return {
|
|
112
|
+
key: spec?.meta.key ?? "",
|
|
113
|
+
version: spec?.meta.version ?? ""
|
|
114
|
+
};
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
/** Get the ancestor chain for a capability (from immediate parent to root). */
|
|
118
|
+
getAncestors(capabilityKey$1, version) {
|
|
119
|
+
const ancestors = [];
|
|
120
|
+
const visited = /* @__PURE__ */ new Set();
|
|
121
|
+
let current = this.get(capabilityKey$1, version);
|
|
122
|
+
while (current?.extends) {
|
|
123
|
+
const parentKey = capKey(current.extends.key, current.extends.version);
|
|
124
|
+
if (visited.has(parentKey)) break;
|
|
125
|
+
visited.add(parentKey);
|
|
126
|
+
const parent = this.get(current.extends.key, current.extends.version);
|
|
127
|
+
if (!parent) break;
|
|
128
|
+
ancestors.push(parent);
|
|
129
|
+
current = parent;
|
|
130
|
+
}
|
|
131
|
+
return ancestors;
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* Get effective requirements for a capability, including inherited ones.
|
|
135
|
+
* Requirements from ancestors are included, with child requirements taking
|
|
136
|
+
* precedence over parent requirements for the same key.
|
|
137
|
+
*/
|
|
138
|
+
getEffectiveRequirements(capabilityKey$1, version) {
|
|
139
|
+
const spec = this.get(capabilityKey$1, version);
|
|
140
|
+
if (!spec) return [];
|
|
141
|
+
const ancestors = this.getAncestors(capabilityKey$1, version);
|
|
142
|
+
const requirementMap = /* @__PURE__ */ new Map();
|
|
143
|
+
for (const ancestor of [...ancestors].reverse()) for (const req of ancestor.requires ?? []) requirementMap.set(req.key, req);
|
|
144
|
+
for (const req of spec.requires ?? []) requirementMap.set(req.key, req);
|
|
145
|
+
return [...requirementMap.values()];
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* Get all surfaces provided by a capability, including inherited ones.
|
|
149
|
+
*/
|
|
150
|
+
getEffectiveSurfaces(capabilityKey$1, version) {
|
|
151
|
+
const spec = this.get(capabilityKey$1, version);
|
|
152
|
+
if (!spec) return [];
|
|
153
|
+
const ancestors = this.getAncestors(capabilityKey$1, version);
|
|
154
|
+
const surfaces = [];
|
|
155
|
+
for (const ancestor of [...ancestors].reverse()) surfaces.push(...ancestor.provides ?? []);
|
|
156
|
+
surfaces.push(...spec.provides ?? []);
|
|
157
|
+
return surfaces;
|
|
158
|
+
}
|
|
34
159
|
};
|
|
35
160
|
function matchesRequirement(ref, requirement) {
|
|
36
161
|
if (ref.key !== requirement.key) return false;
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { CapabilityRef } from "./capabilities.js";
|
|
2
|
+
|
|
3
|
+
//#region src/capabilities/context.d.ts
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Error thrown when a required capability is missing.
|
|
7
|
+
*/
|
|
8
|
+
declare class CapabilityMissingError extends Error {
|
|
9
|
+
readonly capabilityKey: string;
|
|
10
|
+
readonly requiredVersion?: string;
|
|
11
|
+
constructor(capabilityKey: string, requiredVersion?: string);
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Runtime context for checking capability access.
|
|
15
|
+
*
|
|
16
|
+
* Created from a list of enabled capabilities (e.g., from user subscription,
|
|
17
|
+
* tenant config, or feature flags). Provides methods to check and require
|
|
18
|
+
* capabilities at runtime.
|
|
19
|
+
*/
|
|
20
|
+
interface CapabilityContext {
|
|
21
|
+
/** Set of enabled capability keys (without version). */
|
|
22
|
+
readonly capabilities: ReadonlySet<string>;
|
|
23
|
+
/** Map of capability key to version for version-specific checks. */
|
|
24
|
+
readonly capabilityVersions: ReadonlyMap<string, string>;
|
|
25
|
+
/**
|
|
26
|
+
* Check if a capability is enabled.
|
|
27
|
+
* @param key - Capability key to check
|
|
28
|
+
* @param version - Optional specific version to require
|
|
29
|
+
* @returns True if capability is enabled
|
|
30
|
+
*/
|
|
31
|
+
hasCapability(key: string, version?: string): boolean;
|
|
32
|
+
/**
|
|
33
|
+
* Require a capability, throwing if not enabled.
|
|
34
|
+
* @param key - Capability key to require
|
|
35
|
+
* @param version - Optional specific version to require
|
|
36
|
+
* @throws {CapabilityMissingError} If capability is not enabled
|
|
37
|
+
*/
|
|
38
|
+
requireCapability(key: string, version?: string): void;
|
|
39
|
+
/**
|
|
40
|
+
* Check if all specified capabilities are enabled.
|
|
41
|
+
* @param keys - Array of capability keys to check
|
|
42
|
+
* @returns True if all capabilities are enabled
|
|
43
|
+
*/
|
|
44
|
+
hasAllCapabilities(keys: string[]): boolean;
|
|
45
|
+
/**
|
|
46
|
+
* Check if any of the specified capabilities are enabled.
|
|
47
|
+
* @param keys - Array of capability keys to check
|
|
48
|
+
* @returns True if at least one capability is enabled
|
|
49
|
+
*/
|
|
50
|
+
hasAnyCapability(keys: string[]): boolean;
|
|
51
|
+
/**
|
|
52
|
+
* Get enabled capabilities matching a pattern.
|
|
53
|
+
* @param pattern - Glob-like pattern (supports * wildcard)
|
|
54
|
+
* @returns Array of matching capability keys
|
|
55
|
+
*/
|
|
56
|
+
getMatchingCapabilities(pattern: string): string[];
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Creates a capability context from enabled capabilities.
|
|
60
|
+
*
|
|
61
|
+
* @param enabledCapabilities - Array of capability refs that are enabled
|
|
62
|
+
* @returns CapabilityContext for checking/requiring capabilities
|
|
63
|
+
*
|
|
64
|
+
* @example
|
|
65
|
+
* ```typescript
|
|
66
|
+
* // From user subscription capabilities
|
|
67
|
+
* const userCaps = await getUserCapabilities(userId);
|
|
68
|
+
* const ctx = createCapabilityContext(userCaps);
|
|
69
|
+
*
|
|
70
|
+
* // In handler
|
|
71
|
+
* ctx.requireCapability('premium-features');
|
|
72
|
+
* ```
|
|
73
|
+
*/
|
|
74
|
+
declare function createCapabilityContext(enabledCapabilities: CapabilityRef[]): CapabilityContext;
|
|
75
|
+
/**
|
|
76
|
+
* Creates an empty capability context (no capabilities enabled).
|
|
77
|
+
* Useful for anonymous users or testing.
|
|
78
|
+
*/
|
|
79
|
+
declare function createEmptyCapabilityContext(): CapabilityContext;
|
|
80
|
+
/**
|
|
81
|
+
* Creates a capability context with all capabilities enabled (bypass).
|
|
82
|
+
* Useful for admin users or internal services.
|
|
83
|
+
*
|
|
84
|
+
* @param allCapabilities - Array of all capability refs to enable
|
|
85
|
+
*/
|
|
86
|
+
declare function createBypassCapabilityContext(allCapabilities: CapabilityRef[]): CapabilityContext;
|
|
87
|
+
//#endregion
|
|
88
|
+
export { CapabilityContext, CapabilityMissingError, createBypassCapabilityContext, createCapabilityContext, createEmptyCapabilityContext };
|
|
@@ -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
|
|
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:
|
|
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
|
|