@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.
Files changed (166) hide show
  1. package/dist/app-config/{contracts.d.ts → app-config.contracts.d.ts} +52 -52
  2. package/dist/app-config/{contracts.js → app-config.contracts.js} +1 -1
  3. package/dist/app-config/lifecycle-contracts.d.ts +55 -55
  4. package/dist/app-config/runtime.d.ts +4 -4
  5. package/dist/app-config/spec.d.ts +2 -2
  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/data-views.d.ts +2 -1
  19. package/dist/data-views/index.d.ts +2 -1
  20. package/dist/data-views/registry.d.ts +1 -1
  21. package/dist/data-views/report/contractVerificationTable.d.ts +10 -0
  22. package/dist/data-views/report/contractVerificationTable.js +95 -0
  23. package/dist/data-views/runtime.d.ts +2 -2
  24. package/dist/data-views/spec.d.ts +2 -8
  25. package/dist/data-views/types.d.ts +1 -1
  26. package/dist/docs/capabilities/documentationSystem.capability.d.ts +7 -0
  27. package/dist/docs/capabilities/documentationSystem.capability.js +71 -0
  28. package/dist/docs/capabilities/index.d.ts +2 -0
  29. package/dist/docs/capabilities/index.js +3 -0
  30. package/dist/docs/commands/docsGenerate.command.d.ts +95 -0
  31. package/dist/docs/commands/docsGenerate.command.js +142 -0
  32. package/dist/docs/commands/docsPublish.command.d.ts +64 -0
  33. package/dist/docs/commands/docsPublish.command.js +107 -0
  34. package/dist/docs/commands/index.d.ts +3 -0
  35. package/dist/docs/commands/index.js +4 -0
  36. package/dist/docs/constants.d.ts +7 -0
  37. package/dist/docs/constants.js +10 -0
  38. package/dist/docs/contracts.d.ts +442 -0
  39. package/dist/docs/contracts.js +58 -0
  40. package/dist/docs/ensure-docblocks.d.ts +1 -0
  41. package/dist/docs/ensure-docblocks.js +1 -0
  42. package/dist/docs/events/docsGenerated.event.d.ts +62 -0
  43. package/dist/docs/events/docsGenerated.event.js +53 -0
  44. package/dist/docs/events/docsPublished.event.d.ts +70 -0
  45. package/dist/docs/events/docsPublished.event.js +57 -0
  46. package/dist/docs/events/index.d.ts +3 -0
  47. package/dist/docs/events/index.js +4 -0
  48. package/dist/docs/forms/docsSearch.form.d.ts +22 -0
  49. package/dist/docs/forms/docsSearch.form.js +113 -0
  50. package/dist/docs/forms/index.d.ts +2 -0
  51. package/dist/docs/forms/index.js +3 -0
  52. package/dist/docs/index.d.ts +23 -2
  53. package/dist/docs/index.js +25 -1
  54. package/dist/docs/presentations/docsLayout.presentation.d.ts +7 -0
  55. package/dist/docs/presentations/docsLayout.presentation.js +35 -0
  56. package/dist/docs/presentations/docsReferencePage.presentation.d.ts +7 -0
  57. package/dist/docs/presentations/docsReferencePage.presentation.js +35 -0
  58. package/dist/docs/presentations/index.d.ts +3 -0
  59. package/dist/docs/presentations/index.js +4 -0
  60. package/dist/docs/queries/contractReference.query.d.ts +217 -0
  61. package/dist/docs/queries/contractReference.query.js +125 -0
  62. package/dist/docs/queries/docsIndex.query.d.ts +272 -0
  63. package/dist/docs/queries/docsIndex.query.js +133 -0
  64. package/dist/docs/queries/index.d.ts +3 -0
  65. package/dist/docs/queries/index.js +4 -0
  66. package/dist/docs/tech/cli.docblock.js +10 -0
  67. package/dist/docs/tech/docs-system.docblock.d.ts +1 -0
  68. package/dist/docs/tech/docs-system.docblock.js +128 -0
  69. package/dist/docs/tech/report-verification-table.docblock.d.ts +1 -0
  70. package/dist/docs/tech/report-verification-table.docblock.js +50 -0
  71. package/dist/docs/views/contractReference.dataView.d.ts +7 -0
  72. package/dist/docs/views/contractReference.dataView.js +80 -0
  73. package/dist/docs/views/docsIndex.dataView.d.ts +7 -0
  74. package/dist/docs/views/docsIndex.dataView.js +136 -0
  75. package/dist/docs/views/exampleCatalog.dataView.d.ts +7 -0
  76. package/dist/docs/views/exampleCatalog.dataView.js +91 -0
  77. package/dist/docs/views/index.d.ts +4 -0
  78. package/dist/docs/views/index.js +5 -0
  79. package/dist/events.d.ts +80 -14
  80. package/dist/events.js +33 -3
  81. package/dist/examples/schema.d.ts +12 -12
  82. package/dist/examples/types.d.ts +1 -1
  83. package/dist/experiments/spec.d.ts +9 -6
  84. package/dist/features/index.d.ts +2 -2
  85. package/dist/features/install.d.ts +4 -4
  86. package/dist/features/types.d.ts +28 -32
  87. package/dist/forms/forms.d.ts +1 -1
  88. package/dist/index.d.ts +60 -31
  89. package/dist/index.js +32 -4
  90. package/dist/install.d.ts +1 -1
  91. package/dist/integrations/openbanking/contracts/accounts.d.ts +67 -67
  92. package/dist/integrations/openbanking/contracts/balances.d.ts +35 -35
  93. package/dist/integrations/openbanking/contracts/transactions.d.ts +49 -49
  94. package/dist/integrations/openbanking/models.d.ts +55 -55
  95. package/dist/integrations/operations.d.ts +103 -103
  96. package/dist/integrations/spec.d.ts +2 -2
  97. package/dist/jsonschema.d.ts +1 -1
  98. package/dist/knowledge/operations.d.ts +67 -67
  99. package/dist/knowledge/spec.d.ts +1 -1
  100. package/dist/llm/exporters.d.ts +4 -4
  101. package/dist/llm/types.d.ts +1 -1
  102. package/dist/markdown.d.ts +2 -2
  103. package/dist/operations/index.d.ts +4 -1
  104. package/dist/operations/index.js +4 -1
  105. package/dist/operations/operation.d.ts +8 -2
  106. package/dist/operations/registry.d.ts +1 -1
  107. package/dist/operations/report/getContractVerificationStatus.d.ts +75 -0
  108. package/dist/operations/report/getContractVerificationStatus.js +96 -0
  109. package/dist/operations/report/index.d.ts +13 -0
  110. package/dist/operations/report/index.js +45 -0
  111. package/dist/ownership.d.ts +133 -8
  112. package/dist/ownership.js +25 -0
  113. package/dist/policy/context.d.ts +237 -0
  114. package/dist/policy/context.js +227 -0
  115. package/dist/policy/guards.d.ts +145 -0
  116. package/dist/policy/guards.js +254 -0
  117. package/dist/policy/index.d.ts +12 -1
  118. package/dist/policy/index.js +11 -1
  119. package/dist/policy/spec.d.ts +7 -4
  120. package/dist/policy/validation.d.ts +67 -0
  121. package/dist/policy/validation.js +307 -0
  122. package/dist/presentations/presentations.d.ts +6 -0
  123. package/dist/presentations/registry.d.ts +1 -1
  124. package/dist/registry.d.ts +1 -1
  125. package/dist/serialization/index.d.ts +3 -0
  126. package/dist/serialization/index.js +3 -0
  127. package/dist/serialization/serializers.d.ts +40 -0
  128. package/dist/serialization/serializers.js +148 -0
  129. package/dist/serialization/types.d.ts +103 -0
  130. package/dist/serialization/types.js +0 -0
  131. package/dist/server/rest-elysia.d.ts +1 -1
  132. package/dist/server/rest-express.d.ts +1 -1
  133. package/dist/server/rest-generic.d.ts +1 -1
  134. package/dist/server/rest-next-app.d.ts +1 -1
  135. package/dist/server/rest-next-mcp.d.ts +1 -1
  136. package/dist/server/rest-next-pages.d.ts +1 -1
  137. package/dist/telemetry/spec.d.ts +1 -1
  138. package/dist/telemetry/tracker.d.ts +3 -2
  139. package/dist/tests/runner.d.ts +1 -1
  140. package/dist/tests/spec.d.ts +17 -12
  141. package/dist/themes.d.ts +8 -5
  142. package/dist/translations/index.d.ts +6 -0
  143. package/dist/translations/index.js +5 -0
  144. package/dist/translations/registry.d.ts +144 -0
  145. package/dist/translations/registry.js +223 -0
  146. package/dist/translations/spec.d.ts +126 -0
  147. package/dist/translations/spec.js +31 -0
  148. package/dist/translations/validation.d.ts +85 -0
  149. package/dist/translations/validation.js +328 -0
  150. package/dist/types.d.ts +142 -16
  151. package/dist/versioning/index.d.ts +2 -1
  152. package/dist/versioning/index.js +2 -1
  153. package/dist/versioning/refs.d.ts +179 -0
  154. package/dist/versioning/refs.js +161 -0
  155. package/dist/workflow/context.d.ts +191 -0
  156. package/dist/workflow/context.js +227 -0
  157. package/dist/workflow/index.d.ts +6 -3
  158. package/dist/workflow/index.js +4 -2
  159. package/dist/workflow/spec.d.ts +4 -11
  160. package/dist/workflow/validation.d.ts +64 -2
  161. package/dist/workflow/validation.js +194 -1
  162. package/dist/workspace-config/contractsrc-schema.d.ts +3 -3
  163. package/dist/workspace-config/contractsrc-schema.js +1 -0
  164. package/dist/workspace-config/contractsrc-types.d.ts +7 -8
  165. package/dist/workspace-config/index.d.ts +2 -2
  166. package/package.json +52 -9
@@ -0,0 +1,67 @@
1
+ import { PolicySpec } from "./spec.js";
2
+ import { PolicyRegistry } from "./registry.js";
3
+ import { OperationSpecRegistry } from "../operations/registry.js";
4
+
5
+ //#region src/policy/validation.d.ts
6
+
7
+ type PolicyValidationLevel = 'error' | 'warning' | 'info';
8
+ interface PolicyValidationIssue {
9
+ level: PolicyValidationLevel;
10
+ message: string;
11
+ path?: string;
12
+ context?: Record<string, unknown>;
13
+ }
14
+ interface PolicyValidationResult {
15
+ valid: boolean;
16
+ issues: PolicyValidationIssue[];
17
+ }
18
+ /**
19
+ * Validate a policy spec for internal consistency.
20
+ *
21
+ * @param spec - Policy spec to validate
22
+ * @returns Validation result with any issues found
23
+ *
24
+ * @example
25
+ * ```typescript
26
+ * const result = validatePolicySpec(myPolicy);
27
+ * if (!result.valid) {
28
+ * console.log('Issues:', result.issues);
29
+ * }
30
+ * ```
31
+ */
32
+ declare function validatePolicySpec(spec: PolicySpec): PolicyValidationResult;
33
+ interface PolicyConsistencyDeps {
34
+ policies: PolicyRegistry;
35
+ operations?: OperationSpecRegistry;
36
+ }
37
+ /**
38
+ * Validate policy consistency across registries.
39
+ *
40
+ * Checks that:
41
+ * - Operations reference valid policies
42
+ * - Policy actions align with operation kinds
43
+ *
44
+ * @param deps - Registry dependencies
45
+ * @returns Validation result
46
+ */
47
+ declare function validatePolicyConsistency(deps: PolicyConsistencyDeps): PolicyValidationResult;
48
+ declare class PolicyValidationError extends Error {
49
+ readonly issues: PolicyValidationIssue[];
50
+ constructor(message: string, issues: PolicyValidationIssue[]);
51
+ }
52
+ /**
53
+ * Assert that a policy spec is valid, throwing if not.
54
+ *
55
+ * @param spec - Policy spec to validate
56
+ * @throws {PolicyValidationError} If validation fails
57
+ */
58
+ declare function assertPolicySpecValid(spec: PolicySpec): void;
59
+ /**
60
+ * Assert policy consistency across registries.
61
+ *
62
+ * @param deps - Registry dependencies
63
+ * @throws {PolicyValidationError} If validation fails
64
+ */
65
+ declare function assertPolicyConsistency(deps: PolicyConsistencyDeps): void;
66
+ //#endregion
67
+ export { PolicyConsistencyDeps, PolicyValidationError, PolicyValidationIssue, PolicyValidationLevel, PolicyValidationResult, assertPolicyConsistency, assertPolicySpecValid, validatePolicyConsistency, validatePolicySpec };
@@ -0,0 +1,307 @@
1
+ //#region src/policy/validation.ts
2
+ /**
3
+ * Validate a policy spec for internal consistency.
4
+ *
5
+ * @param spec - Policy spec to validate
6
+ * @returns Validation result with any issues found
7
+ *
8
+ * @example
9
+ * ```typescript
10
+ * const result = validatePolicySpec(myPolicy);
11
+ * if (!result.valid) {
12
+ * console.log('Issues:', result.issues);
13
+ * }
14
+ * ```
15
+ */
16
+ function validatePolicySpec(spec) {
17
+ const issues = [];
18
+ validateMeta(spec, issues);
19
+ validateRules(spec, issues);
20
+ validateFieldPolicies(spec, issues);
21
+ validateRateLimits(spec, issues);
22
+ validateConsents(spec, issues);
23
+ validateRelationships(spec, issues);
24
+ return {
25
+ valid: issues.filter((i) => i.level === "error").length === 0,
26
+ issues
27
+ };
28
+ }
29
+ function validateMeta(spec, issues) {
30
+ const { meta } = spec;
31
+ if (!meta.key?.trim()) issues.push({
32
+ level: "error",
33
+ message: "Policy must have a non-empty key",
34
+ path: "meta.key"
35
+ });
36
+ if (!meta.version?.trim()) issues.push({
37
+ level: "error",
38
+ message: "Policy must have a non-empty version",
39
+ path: "meta.version"
40
+ });
41
+ if (!meta.owners?.length) issues.push({
42
+ level: "warning",
43
+ message: "Policy should specify owners",
44
+ path: "meta.owners"
45
+ });
46
+ }
47
+ function validateRules(spec, issues) {
48
+ if (!spec.rules?.length) {
49
+ issues.push({
50
+ level: "warning",
51
+ message: "Policy has no rules defined",
52
+ path: "rules"
53
+ });
54
+ return;
55
+ }
56
+ const seenRateRefs = /* @__PURE__ */ new Set();
57
+ spec.rules.forEach((rule, index) => {
58
+ const path = `rules[${index}]`;
59
+ if (!rule.actions?.length) issues.push({
60
+ level: "error",
61
+ message: "Rule must specify at least one action",
62
+ path: `${path}.actions`
63
+ });
64
+ if (!["allow", "deny"].includes(rule.effect)) issues.push({
65
+ level: "error",
66
+ message: `Invalid rule effect: ${rule.effect}`,
67
+ path: `${path}.effect`
68
+ });
69
+ if (typeof rule.rateLimit === "string") {
70
+ seenRateRefs.add(rule.rateLimit);
71
+ if (!spec.rateLimits?.some((rl) => rl.id === rule.rateLimit)) issues.push({
72
+ level: "error",
73
+ message: `Rate limit "${rule.rateLimit}" referenced but not defined`,
74
+ path: `${path}.rateLimit`
75
+ });
76
+ }
77
+ if (rule.requiresConsent?.length) {
78
+ for (const consentId of rule.requiresConsent) if (!spec.consents?.some((c) => c.id === consentId)) issues.push({
79
+ level: "error",
80
+ message: `Consent "${consentId}" referenced but not defined`,
81
+ path: `${path}.requiresConsent`
82
+ });
83
+ }
84
+ validateConditions(rule.conditions, `${path}.conditions`, issues);
85
+ });
86
+ if (spec.rateLimits?.length) {
87
+ for (const rl of spec.rateLimits) if (!seenRateRefs.has(rl.id)) {
88
+ if (!spec.rules.some((r) => typeof r.rateLimit === "object" && r.rateLimit.id === rl.id)) issues.push({
89
+ level: "info",
90
+ message: `Rate limit "${rl.id}" is defined but not referenced`,
91
+ path: `rateLimits`
92
+ });
93
+ }
94
+ }
95
+ }
96
+ function validateFieldPolicies(spec, issues) {
97
+ if (!spec.fieldPolicies?.length) return;
98
+ const seenFields = /* @__PURE__ */ new Map();
99
+ spec.fieldPolicies.forEach((rule, index) => {
100
+ const path = `fieldPolicies[${index}]`;
101
+ if (!rule.field?.trim()) issues.push({
102
+ level: "error",
103
+ message: "Field policy must specify a field",
104
+ path: `${path}.field`
105
+ });
106
+ if (!rule.actions?.length) issues.push({
107
+ level: "error",
108
+ message: "Field policy must specify at least one action",
109
+ path: `${path}.actions`
110
+ });
111
+ for (const action of rule.actions) if (!["read", "write"].includes(action)) issues.push({
112
+ level: "error",
113
+ message: `Invalid field action: ${action}`,
114
+ path: `${path}.actions`
115
+ });
116
+ const existing = seenFields.get(rule.field) ?? [];
117
+ existing.push(rule);
118
+ seenFields.set(rule.field, existing);
119
+ validateConditions(rule.conditions, `${path}.conditions`, issues);
120
+ });
121
+ for (const [field, rules] of seenFields.entries()) if (rules.length > 1) {
122
+ if (rules.some((r) => r.effect === "allow") && rules.some((r) => r.effect === "deny")) issues.push({
123
+ level: "warning",
124
+ message: `Field "${field}" has potentially conflicting allow/deny policies`,
125
+ path: "fieldPolicies",
126
+ context: {
127
+ field,
128
+ ruleCount: rules.length
129
+ }
130
+ });
131
+ }
132
+ }
133
+ function validateRateLimits(spec, issues) {
134
+ if (!spec.rateLimits?.length) return;
135
+ const seenIds = /* @__PURE__ */ new Set();
136
+ spec.rateLimits.forEach((rl, index) => {
137
+ const path = `rateLimits[${index}]`;
138
+ if (!rl.id?.trim()) issues.push({
139
+ level: "error",
140
+ message: "Rate limit must have an id",
141
+ path: `${path}.id`
142
+ });
143
+ else if (seenIds.has(rl.id)) issues.push({
144
+ level: "error",
145
+ message: `Duplicate rate limit id: ${rl.id}`,
146
+ path: `${path}.id`
147
+ });
148
+ else seenIds.add(rl.id);
149
+ if (typeof rl.rpm !== "number" || rl.rpm <= 0) issues.push({
150
+ level: "error",
151
+ message: "Rate limit rpm must be a positive number",
152
+ path: `${path}.rpm`
153
+ });
154
+ if (rl.windowSeconds !== void 0 && rl.windowSeconds <= 0) issues.push({
155
+ level: "error",
156
+ message: "Rate limit windowSeconds must be positive if specified",
157
+ path: `${path}.windowSeconds`
158
+ });
159
+ if (rl.burst !== void 0 && rl.burst < 0) issues.push({
160
+ level: "error",
161
+ message: "Rate limit burst must be non-negative if specified",
162
+ path: `${path}.burst`
163
+ });
164
+ });
165
+ }
166
+ function validateConsents(spec, issues) {
167
+ if (!spec.consents?.length) return;
168
+ const seenIds = /* @__PURE__ */ new Set();
169
+ spec.consents.forEach((consent, index) => {
170
+ const path = `consents[${index}]`;
171
+ if (!consent.id?.trim()) issues.push({
172
+ level: "error",
173
+ message: "Consent must have an id",
174
+ path: `${path}.id`
175
+ });
176
+ else if (seenIds.has(consent.id)) issues.push({
177
+ level: "error",
178
+ message: `Duplicate consent id: ${consent.id}`,
179
+ path: `${path}.id`
180
+ });
181
+ else seenIds.add(consent.id);
182
+ if (!consent.scope?.trim()) issues.push({
183
+ level: "error",
184
+ message: "Consent must specify a scope",
185
+ path: `${path}.scope`
186
+ });
187
+ if (!consent.purpose?.trim()) issues.push({
188
+ level: "error",
189
+ message: "Consent must specify a purpose",
190
+ path: `${path}.purpose`
191
+ });
192
+ if (consent.expiresInDays !== void 0 && consent.expiresInDays <= 0) issues.push({
193
+ level: "error",
194
+ message: "Consent expiresInDays must be positive if specified",
195
+ path: `${path}.expiresInDays`
196
+ });
197
+ });
198
+ }
199
+ function validateRelationships(spec, issues) {
200
+ if (!spec.relationships?.length) return;
201
+ const seenRelations = /* @__PURE__ */ new Set();
202
+ spec.relationships.forEach((rel, index) => {
203
+ const path = `relationships[${index}]`;
204
+ const key = `${rel.subjectType}:${rel.relation}:${rel.objectType}`;
205
+ if (!rel.subjectType?.trim()) issues.push({
206
+ level: "error",
207
+ message: "Relationship must specify subjectType",
208
+ path: `${path}.subjectType`
209
+ });
210
+ if (!rel.relation?.trim()) issues.push({
211
+ level: "error",
212
+ message: "Relationship must specify relation",
213
+ path: `${path}.relation`
214
+ });
215
+ if (!rel.objectType?.trim()) issues.push({
216
+ level: "error",
217
+ message: "Relationship must specify objectType",
218
+ path: `${path}.objectType`
219
+ });
220
+ if (seenRelations.has(key)) issues.push({
221
+ level: "warning",
222
+ message: `Duplicate relationship definition: ${key}`,
223
+ path
224
+ });
225
+ else seenRelations.add(key);
226
+ });
227
+ }
228
+ function validateConditions(conditions, path, issues) {
229
+ if (!conditions?.length) return;
230
+ conditions.forEach((cond, index) => {
231
+ if (!cond.expression?.trim()) issues.push({
232
+ level: "error",
233
+ message: "Condition must have a non-empty expression",
234
+ path: `${path}[${index}].expression`
235
+ });
236
+ });
237
+ }
238
+ /**
239
+ * Validate policy consistency across registries.
240
+ *
241
+ * Checks that:
242
+ * - Operations reference valid policies
243
+ * - Policy actions align with operation kinds
244
+ *
245
+ * @param deps - Registry dependencies
246
+ * @returns Validation result
247
+ */
248
+ function validatePolicyConsistency(deps) {
249
+ const issues = [];
250
+ for (const policy of deps.policies.list()) {
251
+ const specResult = validatePolicySpec(policy);
252
+ issues.push(...specResult.issues.map((i) => ({
253
+ ...i,
254
+ path: `${policy.meta.key}.v${policy.meta.version}${i.path ? `.${i.path}` : ""}`
255
+ })));
256
+ }
257
+ if (deps.operations) for (const operation of deps.operations.list()) {
258
+ const policyRefs = operation.policy.policies ?? [];
259
+ for (const ref of policyRefs) if (!deps.policies.get(ref.key, ref.version)) issues.push({
260
+ level: "error",
261
+ message: `Operation "${operation.meta.key}.v${operation.meta.version}" references unknown policy "${ref.key}.v${ref.version}"`,
262
+ path: `operations.${operation.meta.key}.policy.policies`
263
+ });
264
+ const fieldPolicies = operation.policy.fieldPolicies ?? [];
265
+ for (const fp of fieldPolicies) if (fp.policy) {
266
+ if (!deps.policies.get(fp.policy.key, fp.policy.version)) issues.push({
267
+ level: "error",
268
+ message: `Operation "${operation.meta.key}.v${operation.meta.version}" references unknown field policy "${fp.policy.key}.v${fp.policy.version}"`,
269
+ path: `operations.${operation.meta.key}.policy.fieldPolicies`
270
+ });
271
+ }
272
+ }
273
+ return {
274
+ valid: issues.filter((i) => i.level === "error").length === 0,
275
+ issues
276
+ };
277
+ }
278
+ var PolicyValidationError = class extends Error {
279
+ constructor(message, issues) {
280
+ super(message);
281
+ this.issues = issues;
282
+ this.name = "PolicyValidationError";
283
+ }
284
+ };
285
+ /**
286
+ * Assert that a policy spec is valid, throwing if not.
287
+ *
288
+ * @param spec - Policy spec to validate
289
+ * @throws {PolicyValidationError} If validation fails
290
+ */
291
+ function assertPolicySpecValid(spec) {
292
+ const result = validatePolicySpec(spec);
293
+ if (!result.valid) throw new PolicyValidationError(`Policy ${spec.meta.key}.v${spec.meta.version} is invalid`, result.issues);
294
+ }
295
+ /**
296
+ * Assert policy consistency across registries.
297
+ *
298
+ * @param deps - Registry dependencies
299
+ * @throws {PolicyValidationError} If validation fails
300
+ */
301
+ function assertPolicyConsistency(deps) {
302
+ const result = validatePolicyConsistency(deps);
303
+ if (!result.valid) throw new PolicyValidationError("Policy consistency check failed", result.issues);
304
+ }
305
+
306
+ //#endregion
307
+ export { PolicyValidationError, assertPolicyConsistency, assertPolicySpecValid, validatePolicyConsistency, validatePolicySpec };
@@ -1,3 +1,4 @@
1
+ import { CapabilityRef } from "../capabilities/capabilities.js";
1
2
  import { OwnerShipMeta } from "../ownership.js";
2
3
  import { AnySchemaModel } from "@contractspec/lib.schema";
3
4
  import { BlockConfig } from "@blocknote/core";
@@ -38,6 +39,11 @@ type PresentationSource = PresentationSourceComponentReact | PresentationSourceB
38
39
  */
39
40
  interface PresentationSpec {
40
41
  meta: PresentationSpecMeta;
42
+ /**
43
+ * Optional reference to the capability that provides this presentation.
44
+ * Used for bidirectional linking between capabilities and presentations.
45
+ */
46
+ capability?: CapabilityRef;
41
47
  policy?: {
42
48
  flags?: string[];
43
49
  pii?: string[];
@@ -1,5 +1,5 @@
1
- import { PresentationSpec } from "./presentations.js";
2
1
  import { SpecContractRegistry } from "../registry.js";
2
+ import { PresentationSpec } from "./presentations.js";
3
3
 
4
4
  //#region src/presentations/registry.d.ts
5
5
  /** In-memory registry for PresentationSpec. */
@@ -1,6 +1,6 @@
1
- import { OwnerShipMeta } from "./ownership.js";
2
1
  import { GroupKeyFn, RegistryFilter } from "./registry-utils.js";
3
2
  import { ContractSpecType } from "./types.js";
3
+ import { OwnerShipMeta } from "./ownership.js";
4
4
 
5
5
  //#region src/registry.d.ts
6
6
  interface AnySpecContract {
@@ -0,0 +1,3 @@
1
+ import { SerializedDataViewSpec, SerializedEventSpec, SerializedFieldConfig, SerializedFormSpec, SerializedOperationSpec, SerializedPresentationSpec, SerializedSchemaModel } from "./types.js";
2
+ import { serializeDataViewSpec, serializeEventSpec, serializeFormSpec, serializeOperationSpec, serializePresentationSpec, serializeSchemaModel } from "./serializers.js";
3
+ export { SerializedDataViewSpec, SerializedEventSpec, SerializedFieldConfig, SerializedFormSpec, SerializedOperationSpec, SerializedPresentationSpec, SerializedSchemaModel, serializeDataViewSpec, serializeEventSpec, serializeFormSpec, serializeOperationSpec, serializePresentationSpec, serializeSchemaModel };
@@ -0,0 +1,3 @@
1
+ import { serializeDataViewSpec, serializeEventSpec, serializeFormSpec, serializeOperationSpec, serializePresentationSpec, serializeSchemaModel } from "./serializers.js";
2
+
3
+ export { serializeDataViewSpec, serializeEventSpec, serializeFormSpec, serializeOperationSpec, serializePresentationSpec, serializeSchemaModel };
@@ -0,0 +1,40 @@
1
+ import { ResourceRefDescriptor } from "../resources.js";
2
+ import { AnyOperationSpec } from "../operations/operation.js";
3
+ import { PresentationSpec } from "../presentations/presentations.js";
4
+ import "../presentations/index.js";
5
+ import { DataViewSpec } from "../data-views/spec.js";
6
+ import "../data-views/index.js";
7
+ import "../operations/index.js";
8
+ import { FormSpec } from "../forms/forms.js";
9
+ import "../forms/index.js";
10
+ import { AnyEventSpec } from "../events.js";
11
+ import { SerializedDataViewSpec, SerializedEventSpec, SerializedFormSpec, SerializedOperationSpec, SerializedPresentationSpec, SerializedSchemaModel } from "./types.js";
12
+ import { AnySchemaModel } from "@contractspec/lib.schema";
13
+
14
+ //#region src/serialization/serializers.d.ts
15
+ /**
16
+ * Serialize a SchemaModel to a plain JSON object.
17
+ */
18
+ declare function serializeSchemaModel(model: AnySchemaModel | ResourceRefDescriptor<boolean> | null | undefined): SerializedSchemaModel | null;
19
+ /**
20
+ * Serialize an OperationSpec to a plain JSON object.
21
+ */
22
+ declare function serializeOperationSpec(spec: AnyOperationSpec | null | undefined): SerializedOperationSpec | null;
23
+ /**
24
+ * Serialize an EventSpec to a plain JSON object.
25
+ */
26
+ declare function serializeEventSpec(spec: AnyEventSpec | null | undefined): SerializedEventSpec | null;
27
+ /**
28
+ * Serialize a PresentationSpec to a plain JSON object.
29
+ */
30
+ declare function serializePresentationSpec(spec: PresentationSpec | null | undefined): SerializedPresentationSpec | null;
31
+ /**
32
+ * Serialize a DataViewSpec to a plain JSON object.
33
+ */
34
+ declare function serializeDataViewSpec(spec: DataViewSpec | null | undefined): SerializedDataViewSpec | null;
35
+ /**
36
+ * Serialize a FormSpec to a plain JSON object.
37
+ */
38
+ declare function serializeFormSpec(spec: FormSpec | null | undefined): SerializedFormSpec | null;
39
+ //#endregion
40
+ export { serializeDataViewSpec, serializeEventSpec, serializeFormSpec, serializeOperationSpec, serializePresentationSpec, serializeSchemaModel };
@@ -0,0 +1,148 @@
1
+ //#region src/serialization/serializers.ts
2
+ /**
3
+ * Type guard to check if a value is a ResourceRefDescriptor.
4
+ */
5
+ function isResourceRefDescriptor(value) {
6
+ return typeof value === "object" && value !== null && "kind" in value && value.kind === "resource_ref";
7
+ }
8
+ function getTypeName(type) {
9
+ if (!type) return "unknown";
10
+ if (typeof type === "string") return type;
11
+ if (typeof type === "object") {
12
+ if ("config" in type && type.config?.name) return type.config.name;
13
+ if ("name" in type && typeof type.name === "string") return type.name;
14
+ }
15
+ return "unknown";
16
+ }
17
+ /**
18
+ * Serialize a SchemaModel to a plain JSON object.
19
+ */
20
+ function serializeSchemaModel(model) {
21
+ if (!model) return null;
22
+ if (isResourceRefDescriptor(model)) return {
23
+ name: `ResourceRef<${model.graphQLType}>`,
24
+ description: `Reference to ${model.graphQLType} resource (${model.uriTemplate})`,
25
+ fields: {}
26
+ };
27
+ if ("config" in model && model.config) {
28
+ const config = model.config;
29
+ return {
30
+ name: config.name,
31
+ description: config.description,
32
+ fields: Object.fromEntries(Object.entries(config.fields).map(([key, field]) => [key, {
33
+ typeName: getTypeName(field.type),
34
+ isOptional: field.isOptional,
35
+ isArray: field.isArray
36
+ }]))
37
+ };
38
+ }
39
+ return {
40
+ name: "unknown",
41
+ fields: {}
42
+ };
43
+ }
44
+ /**
45
+ * Serialize an OperationSpec to a plain JSON object.
46
+ */
47
+ function serializeOperationSpec(spec) {
48
+ if (!spec) return null;
49
+ return {
50
+ meta: {
51
+ key: spec.meta.key,
52
+ version: spec.meta.version,
53
+ stability: spec.meta.stability,
54
+ owners: spec.meta.owners,
55
+ tags: spec.meta.tags,
56
+ description: spec.meta.description,
57
+ goal: spec.meta.goal,
58
+ context: spec.meta.context
59
+ },
60
+ io: {
61
+ input: serializeSchemaModel(spec.io.input),
62
+ output: serializeSchemaModel(spec.io.output)
63
+ },
64
+ policy: spec.policy ? { auth: spec.policy.auth } : void 0
65
+ };
66
+ }
67
+ /**
68
+ * Serialize an EventSpec to a plain JSON object.
69
+ */
70
+ function serializeEventSpec(spec) {
71
+ if (!spec) return null;
72
+ return {
73
+ meta: {
74
+ key: spec.meta.key,
75
+ version: spec.meta.version,
76
+ stability: spec.meta.stability,
77
+ owners: spec.meta.owners,
78
+ tags: spec.meta.tags,
79
+ description: spec.meta.description
80
+ },
81
+ payload: serializeSchemaModel(spec.payload)
82
+ };
83
+ }
84
+ /**
85
+ * Serialize a PresentationSpec to a plain JSON object.
86
+ */
87
+ function serializePresentationSpec(spec) {
88
+ if (!spec) return null;
89
+ return {
90
+ meta: {
91
+ key: spec.meta.key,
92
+ version: spec.meta.version,
93
+ stability: spec.meta.stability,
94
+ owners: spec.meta.owners,
95
+ tags: spec.meta.tags,
96
+ description: spec.meta.description,
97
+ goal: spec.meta.goal,
98
+ context: spec.meta.context
99
+ },
100
+ source: {
101
+ type: spec.source.type,
102
+ framework: spec.source.type === "component" ? spec.source.framework : void 0,
103
+ componentKey: spec.source.type === "component" ? spec.source.componentKey : void 0
104
+ },
105
+ targets: spec.targets
106
+ };
107
+ }
108
+ /**
109
+ * Serialize a DataViewSpec to a plain JSON object.
110
+ */
111
+ function serializeDataViewSpec(spec) {
112
+ if (!spec) return null;
113
+ return {
114
+ meta: {
115
+ key: spec.meta.key,
116
+ version: spec.meta.version,
117
+ stability: spec.meta.stability,
118
+ owners: spec.meta.owners,
119
+ tags: spec.meta.tags,
120
+ description: spec.meta.description,
121
+ title: spec.meta.title
122
+ },
123
+ source: spec.source,
124
+ view: spec.view
125
+ };
126
+ }
127
+ /**
128
+ * Serialize a FormSpec to a plain JSON object.
129
+ */
130
+ function serializeFormSpec(spec) {
131
+ if (!spec) return null;
132
+ return {
133
+ meta: {
134
+ key: spec.meta.key,
135
+ version: spec.meta.version,
136
+ stability: spec.meta.stability,
137
+ owners: spec.meta.owners,
138
+ tags: spec.meta.tags,
139
+ description: spec.meta.description,
140
+ title: spec.meta.title
141
+ },
142
+ fields: spec.fields,
143
+ actions: spec.actions
144
+ };
145
+ }
146
+
147
+ //#endregion
148
+ export { serializeDataViewSpec, serializeEventSpec, serializeFormSpec, serializeOperationSpec, serializePresentationSpec, serializeSchemaModel };