@contractspec/lib.contracts 1.44.0 → 1.45.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 (163) hide show
  1. package/README.md +1 -1
  2. package/dist/app-config/app-config.feature.js +6 -6
  3. package/dist/app-config/contracts.d.ts +50 -50
  4. package/dist/app-config/contracts.js +13 -13
  5. package/dist/app-config/events.d.ts +27 -27
  6. package/dist/app-config/events.js +12 -12
  7. package/dist/app-config/lifecycle-contracts.d.ts +54 -54
  8. package/dist/app-config/lifecycle-contracts.js +19 -19
  9. package/dist/app-config/lifecycle.d.ts +2 -2
  10. package/dist/app-config/runtime.d.ts +3 -3
  11. package/dist/app-config/spec.d.ts +10 -12
  12. package/dist/app-config/spec.js +5 -26
  13. package/dist/capabilities/capabilities.d.ts +4 -11
  14. package/dist/capabilities/capabilities.js +3 -5
  15. package/dist/capabilities/openbanking.js +10 -10
  16. package/dist/client/react/form-render.js +1 -0
  17. package/dist/contract-registry/schemas.d.ts +8 -8
  18. package/dist/contract-registry/schemas.js +1 -1
  19. package/dist/contract-registry/types.d.ts +1 -1
  20. package/dist/data-views/data-views.js +1 -0
  21. package/dist/data-views/registry.d.ts +3 -37
  22. package/dist/data-views/registry.js +4 -66
  23. package/dist/data-views/runtime.d.ts +1 -1
  24. package/dist/data-views/runtime.js +2 -0
  25. package/dist/data-views/spec.d.ts +2 -2
  26. package/dist/docs/index.d.ts +2 -2
  27. package/dist/docs/index.js +2 -2
  28. package/dist/docs/presentations.d.ts +2 -4
  29. package/dist/docs/presentations.js +2 -4
  30. package/dist/docs/tech/contracts/ops-to-presentation-linking.docblock.js +1 -1
  31. package/dist/docs/tech/contracts/tests.docblock.js +1 -1
  32. package/dist/docs/tech/lifecycle-stage-system.docblock.js +1 -1
  33. package/dist/docs/tech-contracts.docs.js +1 -1
  34. package/dist/docs/types.d.ts +1 -1
  35. package/dist/events.d.ts +3 -3
  36. package/dist/examples/docs/examples.docblock.d.ts +6 -0
  37. package/dist/examples/docs/examples.docblock.js +165 -0
  38. package/dist/examples/index.d.ts +5 -0
  39. package/dist/examples/index.js +6 -0
  40. package/dist/examples/registry.d.ts +65 -0
  41. package/dist/examples/registry.js +144 -0
  42. package/dist/examples/schema.d.ts +282 -0
  43. package/dist/examples/schema.js +125 -0
  44. package/dist/examples/types.d.ts +167 -0
  45. package/dist/examples/types.js +43 -0
  46. package/dist/examples/validation.d.ts +65 -0
  47. package/dist/examples/validation.js +144 -0
  48. package/dist/experiments/docs/experiments.docblock.js +1 -1
  49. package/dist/experiments/evaluator.d.ts +1 -1
  50. package/dist/experiments/spec-resolver.d.ts +2 -2
  51. package/dist/experiments/spec.d.ts +6 -8
  52. package/dist/experiments/spec.js +5 -23
  53. package/dist/features/index.d.ts +9 -1
  54. package/dist/features/index.js +8 -1
  55. package/dist/features/install.js +2 -2
  56. package/dist/features/types.d.ts +7 -7
  57. package/dist/forms/forms.d.ts +2 -2
  58. package/dist/forms/forms.js +3 -6
  59. package/dist/index.d.ts +13 -8
  60. package/dist/index.js +11 -2
  61. package/dist/install.d.ts +9 -9
  62. package/dist/integrations/connection.d.ts +1 -1
  63. package/dist/integrations/docs/integrations.docblock.js +1 -1
  64. package/dist/integrations/integrations.feature.js +8 -8
  65. package/dist/integrations/openbanking/contracts/accounts.d.ts +66 -66
  66. package/dist/integrations/openbanking/contracts/accounts.js +3 -3
  67. package/dist/integrations/openbanking/contracts/balances.d.ts +34 -34
  68. package/dist/integrations/openbanking/contracts/balances.js +2 -2
  69. package/dist/integrations/openbanking/contracts/transactions.d.ts +48 -48
  70. package/dist/integrations/openbanking/contracts/transactions.js +2 -2
  71. package/dist/integrations/openbanking/models.d.ts +55 -55
  72. package/dist/integrations/openbanking/openbanking.feature.js +10 -10
  73. package/dist/integrations/operations.d.ts +102 -102
  74. package/dist/integrations/operations.js +10 -10
  75. package/dist/integrations/providers/elevenlabs.js +2 -2
  76. package/dist/integrations/providers/gcs-storage.js +2 -2
  77. package/dist/integrations/providers/gmail.js +3 -3
  78. package/dist/integrations/providers/google-calendar.js +2 -2
  79. package/dist/integrations/providers/mistral.js +3 -3
  80. package/dist/integrations/providers/postmark.js +2 -2
  81. package/dist/integrations/providers/powens.js +4 -4
  82. package/dist/integrations/providers/qdrant.js +3 -3
  83. package/dist/integrations/providers/stripe.js +2 -2
  84. package/dist/integrations/providers/twilio-sms.js +2 -2
  85. package/dist/integrations/runtime.d.ts +5 -5
  86. package/dist/integrations/spec.d.ts +3 -5
  87. package/dist/integrations/spec.js +5 -26
  88. package/dist/jobs/gcp-cloud-tasks.js +1 -1
  89. package/dist/jobs/gcp-pubsub.js +1 -1
  90. package/dist/jobs/handlers/ping-handler.d.ts +3 -3
  91. package/dist/jobs/handlers/ping-handler.js +2 -2
  92. package/dist/jobs/memory-queue.js +1 -1
  93. package/dist/jobs/queue.d.ts +4 -4
  94. package/dist/jobs/scaleway-sqs-queue.js +2 -2
  95. package/dist/jsonschema.d.ts +4 -4
  96. package/dist/knowledge/binding.d.ts +1 -1
  97. package/dist/knowledge/knowledge.feature.js +8 -8
  98. package/dist/knowledge/operations.d.ts +66 -66
  99. package/dist/knowledge/operations.js +12 -12
  100. package/dist/knowledge/source.d.ts +1 -1
  101. package/dist/knowledge/spaces/email-threads.js +2 -2
  102. package/dist/knowledge/spaces/financial-docs.js +2 -2
  103. package/dist/knowledge/spaces/financial-overview.js +2 -2
  104. package/dist/knowledge/spaces/product-canon.js +2 -2
  105. package/dist/knowledge/spaces/support-faq.js +2 -2
  106. package/dist/knowledge/spaces/uploaded-docs.js +2 -2
  107. package/dist/knowledge/spec.d.ts +3 -5
  108. package/dist/knowledge/spec.js +6 -28
  109. package/dist/llm/exporters.js +2 -2
  110. package/dist/llm/types.d.ts +4 -4
  111. package/dist/markdown.js +1 -1
  112. package/dist/migrations.d.ts +2 -2
  113. package/dist/migrations.js +7 -9
  114. package/dist/onboarding-base.d.ts +29 -29
  115. package/dist/onboarding-base.js +4 -4
  116. package/dist/openapi.js +9 -2
  117. package/dist/operations/operation.d.ts +3 -3
  118. package/dist/operations/registry.d.ts +9 -58
  119. package/dist/operations/registry.js +17 -93
  120. package/dist/ownership.d.ts +1 -1
  121. package/dist/policy/docs/policy.docblock.js +1 -1
  122. package/dist/policy/engine.js +2 -0
  123. package/dist/policy/spec.d.ts +1 -1
  124. package/dist/prompt.d.ts +6 -6
  125. package/dist/promptRegistry.d.ts +4 -4
  126. package/dist/promptRegistry.js +2 -5
  127. package/dist/regenerator/docs/regenerator.docblock.js +1 -1
  128. package/dist/regenerator/types.d.ts +1 -1
  129. package/dist/registry.d.ts +3 -2
  130. package/dist/registry.js +5 -5
  131. package/dist/resources.d.ts +5 -5
  132. package/dist/server/graphql-pothos.js +2 -2
  133. package/dist/server/mcp/registerTools.js +1 -1
  134. package/dist/server/rest-elysia.d.ts +1 -1
  135. package/dist/server/rest-elysia.js +1 -1
  136. package/dist/server/rest-express.d.ts +1 -1
  137. package/dist/server/rest-express.js +1 -1
  138. package/dist/server/rest-generic.d.ts +1 -1
  139. package/dist/server/rest-generic.js +1 -1
  140. package/dist/server/rest-next-app.d.ts +1 -1
  141. package/dist/server/rest-next-mcp.d.ts +1 -1
  142. package/dist/server/rest-next-mcp.js +1 -1
  143. package/dist/server/rest-next-pages.d.ts +1 -1
  144. package/dist/telemetry/docs/telemetry.docblock.js +1 -1
  145. package/dist/telemetry/spec.d.ts +7 -7
  146. package/dist/telemetry/spec.js +17 -44
  147. package/dist/telemetry/tracker.d.ts +2 -2
  148. package/dist/tests/runner.d.ts +1 -1
  149. package/dist/tests/spec.d.ts +5 -5
  150. package/dist/tests/spec.js +3 -5
  151. package/dist/themes.d.ts +4 -6
  152. package/dist/themes.js +5 -27
  153. package/dist/translations/catalog.d.ts +1 -1
  154. package/dist/types.d.ts +5 -5
  155. package/dist/workflow/adapters/db-adapter.js +3 -3
  156. package/dist/workflow/runner.d.ts +4 -2
  157. package/dist/workflow/spec.d.ts +4 -17
  158. package/dist/workflow/spec.js +4 -48
  159. package/dist/workflow/state.d.ts +1 -1
  160. package/dist/workflow/validation.js +1 -1
  161. package/dist/workspace-config/contractsrc-schema.d.ts +419 -419
  162. package/dist/workspace-config/contractsrc-schema.js +86 -86
  163. package/package.json +28 -12
@@ -0,0 +1,125 @@
1
+ import { z as z$1 } from "zod";
2
+
3
+ //#region src/examples/schema.ts
4
+ const ExampleKindSchema = z$1.enum([
5
+ "template",
6
+ "workflow",
7
+ "integration",
8
+ "knowledge",
9
+ "blueprint",
10
+ "ui",
11
+ "script",
12
+ "library"
13
+ ]);
14
+ const ExampleVisibilitySchema = z$1.enum([
15
+ "public",
16
+ "internal",
17
+ "experimental"
18
+ ]);
19
+ const ExampleSandboxModeSchema = z$1.enum([
20
+ "playground",
21
+ "specs",
22
+ "builder",
23
+ "markdown",
24
+ "evolution"
25
+ ]);
26
+ const StabilitySchema = z$1.enum([
27
+ "idea",
28
+ "in_creation",
29
+ "experimental",
30
+ "beta",
31
+ "stable",
32
+ "deprecated"
33
+ ]);
34
+ const ExampleDocumentationSchema = z$1.object({
35
+ rootDocId: z$1.string().optional(),
36
+ goalDocId: z$1.string().optional(),
37
+ usageDocId: z$1.string().optional(),
38
+ referenceDocId: z$1.string().optional(),
39
+ constraintsDocId: z$1.string().optional()
40
+ });
41
+ const ExampleSandboxSupportSchema = z$1.object({
42
+ enabled: z$1.boolean(),
43
+ modes: z$1.array(ExampleSandboxModeSchema)
44
+ });
45
+ const ExampleStudioSupportSchema = z$1.object({
46
+ enabled: z$1.boolean(),
47
+ installable: z$1.boolean()
48
+ });
49
+ const ExampleMcpSupportSchema = z$1.object({ enabled: z$1.boolean() });
50
+ const ExampleSurfacesSchema = z$1.object({
51
+ templates: z$1.boolean(),
52
+ sandbox: ExampleSandboxSupportSchema,
53
+ studio: ExampleStudioSupportSchema,
54
+ mcp: ExampleMcpSupportSchema
55
+ });
56
+ const ExampleEntrypointsSchema = z$1.object({
57
+ packageName: z$1.string().min(1),
58
+ feature: z$1.string().optional(),
59
+ blueprint: z$1.string().optional(),
60
+ presentations: z$1.string().optional(),
61
+ contracts: z$1.string().optional(),
62
+ handlers: z$1.string().optional(),
63
+ ui: z$1.string().optional(),
64
+ docs: z$1.string().optional()
65
+ });
66
+ const ExampleMetaSchema = z$1.object({
67
+ version: z$1.string(),
68
+ key: z$1.string().min(1),
69
+ title: z$1.string().optional(),
70
+ description: z$1.string().min(1),
71
+ domain: z$1.string().optional(),
72
+ stability: StabilitySchema,
73
+ owners: z$1.array(z$1.string()),
74
+ tags: z$1.array(z$1.string()),
75
+ docId: z$1.array(z$1.string()).optional(),
76
+ kind: ExampleKindSchema,
77
+ visibility: ExampleVisibilitySchema,
78
+ summary: z$1.string().optional()
79
+ });
80
+ const SpecPointerSchema = z$1.object({
81
+ key: z$1.string().min(1),
82
+ version: z$1.string().optional()
83
+ });
84
+ const FeatureRefSchema = z$1.object({ key: z$1.string().min(1) });
85
+ /**
86
+ * Zod schema for validating ExampleSpec objects.
87
+ *
88
+ * Note: blueprint and features are loosely validated since they can be
89
+ * inline specs or references. Full validation requires registry lookups.
90
+ */
91
+ const ExampleSpecSchema = z$1.object({
92
+ meta: ExampleMetaSchema,
93
+ docs: ExampleDocumentationSchema.optional(),
94
+ surfaces: ExampleSurfacesSchema,
95
+ entrypoints: ExampleEntrypointsSchema,
96
+ blueprint: z$1.union([z$1.record(z$1.string(), z$1.unknown()), SpecPointerSchema]).optional(),
97
+ features: z$1.array(z$1.union([z$1.record(z$1.string(), z$1.unknown()), FeatureRefSchema])).optional()
98
+ });
99
+ /** Parse and validate an ExampleSpec, throwing on failure */
100
+ function parseExampleSpec(data) {
101
+ return ExampleSpecSchema.parse(data);
102
+ }
103
+ /** Safely parse an ExampleSpec, returning result object */
104
+ function safeParseExampleSpec(data) {
105
+ return ExampleSpecSchema.safeParse(data);
106
+ }
107
+ /** Parse and validate ExampleMeta */
108
+ function parseExampleMeta(data) {
109
+ return ExampleMetaSchema.parse(data);
110
+ }
111
+ /** Parse and validate ExampleSurfaces */
112
+ function parseExampleSurfaces(data) {
113
+ return ExampleSurfacesSchema.parse(data);
114
+ }
115
+ /** Parse and validate ExampleEntrypoints */
116
+ function parseExampleEntrypoints(data) {
117
+ return ExampleEntrypointsSchema.parse(data);
118
+ }
119
+ /** Parse and validate ExampleDocumentation */
120
+ function parseExampleDocumentation(data) {
121
+ return ExampleDocumentationSchema.parse(data);
122
+ }
123
+
124
+ //#endregion
125
+ export { ExampleDocumentationSchema, ExampleEntrypointsSchema, ExampleKindSchema, ExampleMcpSupportSchema, ExampleMetaSchema, ExampleSandboxModeSchema, ExampleSandboxSupportSchema, ExampleSpecSchema, ExampleStudioSupportSchema, ExampleSurfacesSchema, ExampleVisibilitySchema, FeatureRefSchema, SpecPointerSchema, StabilitySchema, parseExampleDocumentation, parseExampleEntrypoints, parseExampleMeta, parseExampleSpec, parseExampleSurfaces, safeParseExampleSpec };
@@ -0,0 +1,167 @@
1
+ import { OwnerShipMeta } from "../ownership.js";
2
+ import { AppBlueprintSpec, SpecPointer } from "../app-config/spec.js";
3
+ import { FeatureModuleSpec, FeatureRef } from "../features/types.js";
4
+ import "../features/index.js";
5
+
6
+ //#region src/examples/types.d.ts
7
+ /** Kind of example - determines how it's presented in catalogs */
8
+ type ExampleKind = 'template' | 'workflow' | 'integration' | 'knowledge' | 'blueprint' | 'ui' | 'script' | 'library';
9
+ declare const ExampleKindEnum: {
10
+ readonly Template: "template";
11
+ readonly Workflow: "workflow";
12
+ readonly Integration: "integration";
13
+ readonly Knowledge: "knowledge";
14
+ readonly Blueprint: "blueprint";
15
+ readonly UI: "ui";
16
+ readonly Script: "script";
17
+ readonly Library: "library";
18
+ };
19
+ /** Visibility level for examples */
20
+ type ExampleVisibility = 'public' | 'internal' | 'experimental';
21
+ declare const ExampleVisibilityEnum: {
22
+ readonly Public: "public";
23
+ readonly Internal: "internal";
24
+ readonly Experimental: "experimental";
25
+ };
26
+ /** Sandbox modes supported by examples */
27
+ type ExampleSandboxMode = 'playground' | 'specs' | 'builder' | 'markdown' | 'evolution';
28
+ declare const ExampleSandboxModeEnum: {
29
+ readonly Playground: "playground";
30
+ readonly Specs: "specs";
31
+ readonly Builder: "builder";
32
+ readonly Markdown: "markdown";
33
+ readonly Evolution: "evolution";
34
+ };
35
+ /** Documentation references for the example */
36
+ interface ExampleDocumentation {
37
+ /** Root documentation block ID */
38
+ rootDocId?: string;
39
+ /** Goal/purpose documentation */
40
+ goalDocId?: string;
41
+ /** Usage/quickstart documentation */
42
+ usageDocId?: string;
43
+ /** API/reference documentation */
44
+ referenceDocId?: string;
45
+ /** Constraints/limitations documentation */
46
+ constraintsDocId?: string;
47
+ }
48
+ /** Surface support configuration for sandbox */
49
+ interface ExampleSandboxSupport {
50
+ enabled: boolean;
51
+ modes: readonly ExampleSandboxMode[];
52
+ }
53
+ /** Surface support configuration for Studio */
54
+ interface ExampleStudioSupport {
55
+ enabled: boolean;
56
+ /** If true, Studio can create a real project from this example via API. */
57
+ installable: boolean;
58
+ }
59
+ /** Surface support configuration for MCP */
60
+ interface ExampleMcpSupport {
61
+ enabled: boolean;
62
+ }
63
+ /** Surface support configuration - where the example can be used */
64
+ interface ExampleSurfaces {
65
+ /** Available as a template for new projects */
66
+ templates: boolean;
67
+ /** Sandbox/playground support */
68
+ sandbox: ExampleSandboxSupport;
69
+ /** ContractSpec Studio support */
70
+ studio: ExampleStudioSupport;
71
+ /** MCP (Model Context Protocol) support */
72
+ mcp: ExampleMcpSupport;
73
+ }
74
+ /** Package entrypoints for the example - maps to package.json exports */
75
+ interface ExampleEntrypoints {
76
+ /** Package name in the workspace (e.g., @contractspec/example.saas-boilerplate) */
77
+ packageName: string;
78
+ /** Feature module entrypoint (e.g., ./saas-boilerplate.feature) */
79
+ feature?: string;
80
+ /** Blueprint entrypoint (e.g., ./blueprint) */
81
+ blueprint?: string;
82
+ /** Presentations entrypoint (e.g., ./presentations) */
83
+ presentations?: string;
84
+ /** Contracts/operations entrypoint (e.g., ./contracts) */
85
+ contracts?: string;
86
+ /** Handlers entrypoint (e.g., ./handlers) */
87
+ handlers?: string;
88
+ /** UI components entrypoint (e.g., ./ui) */
89
+ ui?: string;
90
+ /** Documentation entrypoint (e.g., ./docs) */
91
+ docs?: string;
92
+ }
93
+ /**
94
+ * Example metadata extending OwnerShipMeta.
95
+ * Provides standard spec identification plus example-specific fields.
96
+ */
97
+ interface ExampleMeta extends OwnerShipMeta {
98
+ /** Example kind for categorization */
99
+ kind: ExampleKind;
100
+ /** Visibility level */
101
+ visibility: ExampleVisibility;
102
+ /** Short marketing summary (distinct from technical description) */
103
+ summary?: string;
104
+ }
105
+ /**
106
+ * ExampleSpec - Complete specification for a ContractSpec example.
107
+ *
108
+ * Integrates with AppBlueprintSpec for app configuration and
109
+ * FeatureModuleSpec for feature definitions.
110
+ *
111
+ * @example
112
+ * ```typescript
113
+ * const example: ExampleSpec = {
114
+ * meta: {
115
+ * key: 'saas-boilerplate',
116
+ * version: '1.0.0',
117
+ * title: 'SaaS Boilerplate',
118
+ * description: 'Multi-tenant SaaS foundation.',
119
+ * kind: 'template',
120
+ * visibility: 'public',
121
+ * stability: 'experimental',
122
+ * owners: ['@saas-team'],
123
+ * tags: ['saas', 'multi-tenant'],
124
+ * },
125
+ * surfaces: {
126
+ * templates: true,
127
+ * sandbox: { enabled: true, modes: ['playground', 'specs'] },
128
+ * studio: { enabled: true, installable: true },
129
+ * mcp: { enabled: true },
130
+ * },
131
+ * entrypoints: {
132
+ * packageName: '@contractspec/example.saas-boilerplate',
133
+ * feature: './saas-boilerplate.feature',
134
+ * },
135
+ * };
136
+ * ```
137
+ */
138
+ interface ExampleSpec {
139
+ /** Example metadata (identification, ownership, categorization) */
140
+ meta: ExampleMeta;
141
+ /** Documentation references */
142
+ docs?: ExampleDocumentation;
143
+ /** Surface support configuration */
144
+ surfaces: ExampleSurfaces;
145
+ /** Package entrypoints */
146
+ entrypoints: ExampleEntrypoints;
147
+ /**
148
+ * Inline or referenced AppBlueprintSpec.
149
+ * Use SpecPointer for external reference to a registered blueprint.
150
+ */
151
+ blueprint?: AppBlueprintSpec | SpecPointer;
152
+ /**
153
+ * Features included in this example.
154
+ * Can be inline FeatureModuleSpec objects or FeatureRef references.
155
+ */
156
+ features?: (FeatureModuleSpec | FeatureRef)[];
157
+ }
158
+ /** Check if a blueprint reference is a SpecPointer (external reference) */
159
+ declare function isSpecPointer(ref: AppBlueprintSpec | SpecPointer | undefined): ref is SpecPointer;
160
+ /** Check if a feature reference is a FeatureRef (external reference) */
161
+ declare function isFeatureRef(ref: FeatureModuleSpec | FeatureRef): ref is FeatureRef;
162
+ /** Check if a value is a valid ExampleKind */
163
+ declare function isExampleKind(value: unknown): value is ExampleKind;
164
+ /** Check if a value is a valid ExampleVisibility */
165
+ declare function isExampleVisibility(value: unknown): value is ExampleVisibility;
166
+ //#endregion
167
+ export { ExampleDocumentation, ExampleEntrypoints, ExampleKind, ExampleKindEnum, ExampleMcpSupport, ExampleMeta, ExampleSandboxMode, ExampleSandboxModeEnum, ExampleSandboxSupport, ExampleSpec, ExampleStudioSupport, ExampleSurfaces, ExampleVisibility, ExampleVisibilityEnum, isExampleKind, isExampleVisibility, isFeatureRef, isSpecPointer };
@@ -0,0 +1,43 @@
1
+ //#region src/examples/types.ts
2
+ const ExampleKindEnum = {
3
+ Template: "template",
4
+ Workflow: "workflow",
5
+ Integration: "integration",
6
+ Knowledge: "knowledge",
7
+ Blueprint: "blueprint",
8
+ UI: "ui",
9
+ Script: "script",
10
+ Library: "library"
11
+ };
12
+ const ExampleVisibilityEnum = {
13
+ Public: "public",
14
+ Internal: "internal",
15
+ Experimental: "experimental"
16
+ };
17
+ const ExampleSandboxModeEnum = {
18
+ Playground: "playground",
19
+ Specs: "specs",
20
+ Builder: "builder",
21
+ Markdown: "markdown",
22
+ Evolution: "evolution"
23
+ };
24
+ /** Check if a blueprint reference is a SpecPointer (external reference) */
25
+ function isSpecPointer(ref) {
26
+ if (!ref) return false;
27
+ return "key" in ref && !("meta" in ref);
28
+ }
29
+ /** Check if a feature reference is a FeatureRef (external reference) */
30
+ function isFeatureRef(ref) {
31
+ return "key" in ref && !("meta" in ref);
32
+ }
33
+ /** Check if a value is a valid ExampleKind */
34
+ function isExampleKind(value) {
35
+ return typeof value === "string" && Object.values(ExampleKindEnum).includes(value);
36
+ }
37
+ /** Check if a value is a valid ExampleVisibility */
38
+ function isExampleVisibility(value) {
39
+ return typeof value === "string" && Object.values(ExampleVisibilityEnum).includes(value);
40
+ }
41
+
42
+ //#endregion
43
+ export { ExampleKindEnum, ExampleSandboxModeEnum, ExampleVisibilityEnum, isExampleKind, isExampleVisibility, isFeatureRef, isSpecPointer };
@@ -0,0 +1,65 @@
1
+ import { ExampleSpec } from "./types.js";
2
+
3
+ //#region src/examples/validation.d.ts
4
+ interface ExampleValidationError {
5
+ /** Example key if available */
6
+ exampleKey?: string;
7
+ /** Error message */
8
+ message: string;
9
+ /** Path within the spec where error occurred */
10
+ path?: string;
11
+ /** Error code for programmatic handling */
12
+ code?: string;
13
+ }
14
+ interface ExampleValidationWarning {
15
+ /** Example key if available */
16
+ exampleKey?: string;
17
+ /** Warning message */
18
+ message: string;
19
+ /** Path within the spec where warning applies */
20
+ path?: string;
21
+ }
22
+ type ValidateExamplesResult = {
23
+ ok: true;
24
+ examples: ExampleSpec[];
25
+ } | {
26
+ ok: false;
27
+ errors: ExampleValidationError[];
28
+ };
29
+ interface ValidateExampleResult {
30
+ valid: boolean;
31
+ errors: ExampleValidationError[];
32
+ warnings: ExampleValidationWarning[];
33
+ }
34
+ /**
35
+ * Validate a single ExampleSpec.
36
+ *
37
+ * @param example - The example to validate
38
+ * @returns Validation result with errors and warnings
39
+ */
40
+ declare function validateExample(example: unknown): ValidateExampleResult;
41
+ /**
42
+ * Validate multiple examples, checking for duplicates.
43
+ *
44
+ * @param examples - Array of examples to validate
45
+ * @returns Validation result with all valid examples or errors
46
+ */
47
+ declare function validateExamples(examples: ExampleSpec[]): ValidateExamplesResult;
48
+ interface CrossValidationContext {
49
+ /** Available feature keys */
50
+ featureKeys?: Set<string>;
51
+ /** Available blueprint keys */
52
+ blueprintKeys?: Set<string>;
53
+ /** Available package names in workspace */
54
+ packageNames?: Set<string>;
55
+ }
56
+ /**
57
+ * Validate example references against external registries.
58
+ *
59
+ * @param example - Example to validate
60
+ * @param context - External context for cross-reference validation
61
+ * @returns Validation result with errors and warnings
62
+ */
63
+ declare function validateExampleReferences(example: ExampleSpec, context: CrossValidationContext): ValidateExampleResult;
64
+ //#endregion
65
+ export { CrossValidationContext, ExampleValidationError, ExampleValidationWarning, ValidateExampleResult, ValidateExamplesResult, validateExample, validateExampleReferences, validateExamples };
@@ -0,0 +1,144 @@
1
+ import { safeParseExampleSpec } from "./schema.js";
2
+
3
+ //#region src/examples/validation.ts
4
+ /**
5
+ * Validate a single ExampleSpec.
6
+ *
7
+ * @param example - The example to validate
8
+ * @returns Validation result with errors and warnings
9
+ */
10
+ function validateExample(example) {
11
+ const errors = [];
12
+ const warnings = [];
13
+ const parsed = safeParseExampleSpec(example);
14
+ if (!parsed.success) {
15
+ for (const issue of parsed.error.issues) errors.push({
16
+ exampleKey: example?.meta?.key,
17
+ message: issue.message,
18
+ path: issue.path.join("."),
19
+ code: issue.code
20
+ });
21
+ return {
22
+ valid: false,
23
+ errors,
24
+ warnings
25
+ };
26
+ }
27
+ const spec = parsed.data;
28
+ validateSemantics(spec, errors, warnings);
29
+ return {
30
+ valid: errors.length === 0,
31
+ errors,
32
+ warnings
33
+ };
34
+ }
35
+ function validateSemantics(spec, errors, warnings) {
36
+ const key = spec.meta.key;
37
+ if (!spec.entrypoints.packageName.startsWith("@")) warnings.push({
38
+ exampleKey: key,
39
+ message: "Package name should be scoped (e.g., @contractspec/example.name)",
40
+ path: "entrypoints.packageName"
41
+ });
42
+ if (spec.surfaces.studio.installable && !spec.surfaces.studio.enabled) errors.push({
43
+ exampleKey: key,
44
+ message: "Studio installable requires studio.enabled to be true",
45
+ path: "surfaces.studio",
46
+ code: "STUDIO_INSTALLABLE_REQUIRES_ENABLED"
47
+ });
48
+ if (spec.surfaces.sandbox.enabled && spec.surfaces.sandbox.modes.length === 0) warnings.push({
49
+ exampleKey: key,
50
+ message: "Sandbox is enabled but has no modes configured",
51
+ path: "surfaces.sandbox.modes"
52
+ });
53
+ if (spec.features && spec.features.length > 0 && !spec.entrypoints.feature) warnings.push({
54
+ exampleKey: key,
55
+ message: "Example has features but no feature entrypoint in entrypoints.feature",
56
+ path: "entrypoints.feature"
57
+ });
58
+ if (spec.blueprint && !spec.entrypoints.blueprint) warnings.push({
59
+ exampleKey: key,
60
+ message: "Example has blueprint but no blueprint entrypoint in entrypoints.blueprint",
61
+ path: "entrypoints.blueprint"
62
+ });
63
+ if (spec.meta.visibility === "public" && spec.meta.stability === "idea") warnings.push({
64
+ exampleKey: key,
65
+ message: "Public examples should not be in \"idea\" stability",
66
+ path: "meta.stability"
67
+ });
68
+ }
69
+ /**
70
+ * Validate multiple examples, checking for duplicates.
71
+ *
72
+ * @param examples - Array of examples to validate
73
+ * @returns Validation result with all valid examples or errors
74
+ */
75
+ function validateExamples(examples) {
76
+ const errors = [];
77
+ const seen = /* @__PURE__ */ new Set();
78
+ const validExamples = [];
79
+ for (const example of examples) {
80
+ if (seen.has(example.meta.key)) {
81
+ errors.push({
82
+ exampleKey: example.meta.key,
83
+ message: `Duplicate example key: ${example.meta.key}`,
84
+ code: "DUPLICATE_KEY"
85
+ });
86
+ continue;
87
+ }
88
+ seen.add(example.meta.key);
89
+ const result = validateExample(example);
90
+ if (!result.valid) errors.push(...result.errors);
91
+ else validExamples.push(example);
92
+ }
93
+ if (errors.length > 0) return {
94
+ ok: false,
95
+ errors
96
+ };
97
+ return {
98
+ ok: true,
99
+ examples: validExamples
100
+ };
101
+ }
102
+ /**
103
+ * Validate example references against external registries.
104
+ *
105
+ * @param example - Example to validate
106
+ * @param context - External context for cross-reference validation
107
+ * @returns Validation result with errors and warnings
108
+ */
109
+ function validateExampleReferences(example, context) {
110
+ const errors = [];
111
+ const warnings = [];
112
+ const key = example.meta.key;
113
+ if (context.packageNames && !context.packageNames.has(example.entrypoints.packageName)) warnings.push({
114
+ exampleKey: key,
115
+ message: `Package "${example.entrypoints.packageName}" not found in workspace`,
116
+ path: "entrypoints.packageName"
117
+ });
118
+ if (example.features && context.featureKeys) {
119
+ for (const feature of example.features) if ("key" in feature && !("meta" in feature)) {
120
+ if (!context.featureKeys.has(feature.key)) warnings.push({
121
+ exampleKey: key,
122
+ message: `Feature "${feature.key}" not found in registry`,
123
+ path: "features"
124
+ });
125
+ }
126
+ }
127
+ if (example.blueprint && context.blueprintKeys) {
128
+ if ("key" in example.blueprint && !("meta" in example.blueprint)) {
129
+ if (!context.blueprintKeys.has(example.blueprint.key)) warnings.push({
130
+ exampleKey: key,
131
+ message: `Blueprint "${example.blueprint.key}" not found in registry`,
132
+ path: "blueprint"
133
+ });
134
+ }
135
+ }
136
+ return {
137
+ valid: errors.length === 0,
138
+ errors,
139
+ warnings
140
+ };
141
+ }
142
+
143
+ //#endregion
144
+ export { validateExample, validateExampleReferences, validateExamples };
@@ -13,7 +13,7 @@ const tech_contracts_experiments_DocBlocks = [{
13
13
  "contracts",
14
14
  "experiments"
15
15
  ],
16
- body: "# ExperimentSpec & ExperimentEvaluator\n\nUse experiments to test alternative workflows, data views, or themes with controlled allocations and measurable outcomes.\n\n- Types & registry: `packages/libs/contracts/src/experiments/spec.ts`\n- Runtime evaluator: `packages/libs/contracts/src/experiments/evaluator.ts`\n- CLI wizard/template: `contractspec create experiment`\n\n## Structure\n\n```ts\nexport interface ExperimentSpec {\n meta: ExperimentMeta;\n controlVariant: string;\n variants: ExperimentVariant[];\n allocation: AllocationStrategy;\n successMetrics?: SuccessMetric[];\n}\n```\n\n- `variants`: define UI/behavior overrides (data views, workflows, themes, policies)\n- `allocation`:\n - `random`: 50/50 or weighted via `weight`\n - `sticky`: deterministic hash on user/organization/session\n - `targeted`: rule-based (policy + expression + optional percentage)\n- `successMetrics`: telemetry events + aggregation (count/avg/p95) to track outcomes\n\n### Example\n\n```ts\nexport const OnboardingSplitFormExperiment: ExperimentSpec = {\n meta: {\n name: 'sigil.onboarding.split_form',\n version: 1,\n title: 'Split onboarding form',\n description: 'Compare single vs multi-step onboarding',\n domain: 'onboarding',\n owners: ['@team.onboarding'],\n tags: ['experiment'],\n stability: StabilityEnum.Experimental,\n },\n controlVariant: 'control',\n variants: [\n { id: 'control', name: 'Single-step form' },\n {\n id: 'multi_step',\n name: 'Multi-step form',\n overrides: [\n { type: 'workflow', target: 'sigil.onboarding.workflow.multi_step' },\n ],\n },\n ],\n allocation: {\n type: 'targeted',\n rules: [\n {\n variantId: 'multi_step',\n expression: \"context.attributes?.segment === 'vip'\",\n },\n ],\n fallback: 'random',\n },\n successMetrics: [\n {\n name: 'Completion rate',\n telemetryEvent: { name: 'sigil.telemetry.onboarding_completed', version: 1 },\n aggregation: 'count',\n },\n ],\n};\n```\n\n## Variant evaluation\n\n```ts\nconst evaluator = new ExperimentEvaluator({\n registry: experimentRegistry,\n policyChecker: (policyRef, context) =>\n policyEngine.decide({\n action: 'experiment_targeting',\n subject: { roles: context.flags },\n resource: { type: 'experiment', attributes: { name: context.experiment } },\n policies: [policyRef],\n }).effect === 'allow',\n});\n\nconst assignment = await evaluator.chooseVariant({\n experiment: 'sigil.onboarding.split_form',\n userId: ctx.userId,\n organizationId: ctx.organizationId,\n attributes: ctx.attributes,\n});\n\nif (assignment) {\n // Apply overrides for the chosen variant\n applyExperimentOverrides(assignment.variant);\n}\n```\n\n- `random` uses deterministic hashing (`salt` optional) for stable splits\n- `sticky` hashes a specified attribute (userId/orgId/sessionId)\n- `targeted` evaluates rules in order; each rule can reference a PolicySpec and simple JS expressions (`context` input)\n\n## Integrations\n\n- **Feature modules**: `FeatureModuleSpec.experiments` references experiments owned by a module\n- **DataViewSpec / WorkflowSpec**: `experiments?: ExperimentRef[]` indicate which experiments modify the spec\n- **Telemetry**: success metrics reference `TelemetrySpec` events to ensure compliant tracking\n- **Policy**: targeting rules call into `PolicyEngine` via the evaluator callback to respect privacy/security\n\n## CLI workflow\n\n```\ncontractspec create experiment\n```\n\n- Prompts for control/variants, allocation strategy, targeting rules, success metrics\n- Outputs a typed `ExperimentSpec` file alongside your contracts\n\n## Best practices\n\n1. Keep experiments short-lived; increment `meta.version` when changing allocation or variants.\n2. Always declare a control variant and ensure overrides are reversible.\n3. Tie success metrics to privacy-reviewed telemetry events.\n4. Use targeting rules sparingly; combine with PolicySpec to avoid exposing experiments to unauthorized users.\n5. When an experiment wins, promote the variant to the canonical spec and retire the experiment.\n\n"
16
+ body: "# ExperimentSpec & ExperimentEvaluator\n\nUse experiments to test alternative workflows, data views, or themes with controlled allocations and measurable outcomes.\n\n- Types & registry: `packages/libs/contracts/src/experiments/spec.ts`\n- Runtime evaluator: `packages/libs/contracts/src/experiments/evaluator.ts`\n- CLI wizard/template: `contractspec create experiment`\n\n## Structure\n\n```ts\nexport interface ExperimentSpec {\n meta: ExperimentMeta;\n controlVariant: string;\n variants: ExperimentVariant[];\n allocation: AllocationStrategy;\n successMetrics?: SuccessMetric[];\n}\n```\n\n- `variants`: define UI/behavior overrides (data views, workflows, themes, policies)\n- `allocation`:\n - `random`: 50/50 or weighted via `weight`\n - `sticky`: deterministic hash on user/organization/session\n - `targeted`: rule-based (policy + expression + optional percentage)\n- `successMetrics`: telemetry events + aggregation (count/avg/p95) to track outcomes\n\n### Example\n\n```ts\nexport const OnboardingSplitFormExperiment: ExperimentSpec = {\n meta: {\n name: 'sigil.onboarding.split_form',\n version: '1.0.0',\n title: 'Split onboarding form',\n description: 'Compare single vs multi-step onboarding',\n domain: 'onboarding',\n owners: ['@team.onboarding'],\n tags: ['experiment'],\n stability: StabilityEnum.Experimental,\n },\n controlVariant: 'control',\n variants: [\n { id: 'control', name: 'Single-step form' },\n {\n id: 'multi_step',\n name: 'Multi-step form',\n overrides: [\n { type: 'workflow', target: 'sigil.onboarding.workflow.multi_step' },\n ],\n },\n ],\n allocation: {\n type: 'targeted',\n rules: [\n {\n variantId: 'multi_step',\n expression: \"context.attributes?.segment === 'vip'\",\n },\n ],\n fallback: 'random',\n },\n successMetrics: [\n {\n name: 'Completion rate',\n telemetryEvent: { name: 'sigil.telemetry.onboarding_completed', version: '1.0.0' },\n aggregation: 'count',\n },\n ],\n};\n```\n\n## Variant evaluation\n\n```ts\nconst evaluator = new ExperimentEvaluator({\n registry: experimentRegistry,\n policyChecker: (policyRef, context) =>\n policyEngine.decide({\n action: 'experiment_targeting',\n subject: { roles: context.flags },\n resource: { type: 'experiment', attributes: { name: context.experiment } },\n policies: [policyRef],\n }).effect === 'allow',\n});\n\nconst assignment = await evaluator.chooseVariant({\n experiment: 'sigil.onboarding.split_form',\n userId: ctx.userId,\n organizationId: ctx.organizationId,\n attributes: ctx.attributes,\n});\n\nif (assignment) {\n // Apply overrides for the chosen variant\n applyExperimentOverrides(assignment.variant);\n}\n```\n\n- `random` uses deterministic hashing (`salt` optional) for stable splits\n- `sticky` hashes a specified attribute (userId/orgId/sessionId)\n- `targeted` evaluates rules in order; each rule can reference a PolicySpec and simple JS expressions (`context` input)\n\n## Integrations\n\n- **Feature modules**: `FeatureModuleSpec.experiments` references experiments owned by a module\n- **DataViewSpec / WorkflowSpec**: `experiments?: ExperimentRef[]` indicate which experiments modify the spec\n- **Telemetry**: success metrics reference `TelemetrySpec` events to ensure compliant tracking\n- **Policy**: targeting rules call into `PolicyEngine` via the evaluator callback to respect privacy/security\n\n## CLI workflow\n\n```\ncontractspec create experiment\n```\n\n- Prompts for control/variants, allocation strategy, targeting rules, success metrics\n- Outputs a typed `ExperimentSpec` file alongside your contracts\n\n## Best practices\n\n1. Keep experiments short-lived; increment `meta.version` when changing allocation or variants.\n2. Always declare a control variant and ensure overrides are reversible.\n3. Tie success metrics to privacy-reviewed telemetry events.\n4. Use targeting rules sparingly; combine with PolicySpec to avoid exposing experiments to unauthorized users.\n5. When an experiment wins, promote the variant to the canonical spec and retire the experiment.\n\n"
17
17
  }];
18
18
  registerDocBlocks(tech_contracts_experiments_DocBlocks);
19
19
 
@@ -4,7 +4,7 @@ import { ExperimentRegistry, ExperimentVariant } from "./spec.js";
4
4
  //#region src/experiments/evaluator.d.ts
5
5
  interface ExperimentContext {
6
6
  experiment: string;
7
- version?: number;
7
+ version?: string;
8
8
  userId?: string | null;
9
9
  organizationId?: string | null;
10
10
  sessionId?: string | null;
@@ -1,7 +1,7 @@
1
1
  import { ResourceRefDescriptor } from "../resources.js";
2
- import { HandlerCtx } from "../types.js";
3
2
  import { OpKind, OperationSpec } from "../operations/operation.js";
4
3
  import "../operations/index.js";
4
+ import { HandlerCtx } from "../types.js";
5
5
  import { AnySchemaModel } from "@contractspec/lib.schema";
6
6
 
7
7
  //#region src/experiments/spec-resolver.d.ts
@@ -9,7 +9,7 @@ type RuntimeContract = OperationSpec<AnySchemaModel, AnySchemaModel | ResourceRe
9
9
  interface SpecVariantResolver {
10
10
  resolve(operation: {
11
11
  name: string;
12
- version: number;
12
+ version: string;
13
13
  kind: OpKind;
14
14
  }, ctx: HandlerCtx): Promise<RuntimeContract | undefined> | RuntimeContract | undefined;
15
15
  }
@@ -1,12 +1,13 @@
1
1
  import { OwnerShipMeta } from "../ownership.js";
2
2
  import { PolicyRef } from "../policy/spec.js";
3
3
  import { TelemetryEventDef } from "../telemetry/spec.js";
4
+ import { SpecContractRegistry } from "../registry.js";
4
5
 
5
6
  //#region src/experiments/spec.d.ts
6
7
  type ExperimentMeta = OwnerShipMeta;
7
8
  interface ExperimentRef {
8
9
  key: string;
9
- version?: number;
10
+ version?: string;
10
11
  }
11
12
  type ExperimentOverrideType = 'dataView' | 'workflow' | 'theme' | 'policy' | 'presentation';
12
13
  interface ExperimentOverride {
@@ -14,7 +15,7 @@ interface ExperimentOverride {
14
15
  /** Target spec meta name (e.g., DataViewspec.meta.key). */
15
16
  target: string;
16
17
  /** Target version. Optional; evaluator may choose latest when omitted. */
17
- version?: number;
18
+ version?: string;
18
19
  /** Optional configuration applied when this variant is active. */
19
20
  config?: Record<string, unknown>;
20
21
  }
@@ -57,7 +58,7 @@ interface SuccessMetric {
57
58
  key: string;
58
59
  telemetryEvent: {
59
60
  key: TelemetryEventDef['key'];
60
- version: number;
61
+ version: string;
61
62
  };
62
63
  aggregation: MetricAggregation;
63
64
  target?: number;
@@ -71,11 +72,8 @@ interface ExperimentSpec {
71
72
  successMetrics?: SuccessMetric[];
72
73
  tags?: string[];
73
74
  }
74
- declare class ExperimentRegistry {
75
- private readonly items;
76
- register(spec: ExperimentSpec): this;
77
- list(): ExperimentSpec[];
78
- get(name: string, version?: number): ExperimentSpec | undefined;
75
+ declare class ExperimentRegistry extends SpecContractRegistry<'experiment', ExperimentSpec> {
76
+ constructor(items?: ExperimentSpec[]);
79
77
  }
80
78
  declare function makeExperimentKey(meta: ExperimentMeta): string;
81
79
  //#endregion
@@ -1,28 +1,10 @@
1
+ import { SpecContractRegistry } from "../registry.js";
2
+
1
3
  //#region src/experiments/spec.ts
2
4
  const experimentKey = (meta) => `${meta.key}.v${meta.version}`;
3
- var ExperimentRegistry = class {
4
- items = /* @__PURE__ */ new Map();
5
- register(spec) {
6
- const key = experimentKey(spec.meta);
7
- if (this.items.has(key)) throw new Error(`Duplicate experiment ${key}`);
8
- this.items.set(key, spec);
9
- return this;
10
- }
11
- list() {
12
- return [...this.items.values()];
13
- }
14
- get(name, version) {
15
- if (version != null) return this.items.get(`${name}.v${version}`);
16
- let latest;
17
- let maxVersion = -Infinity;
18
- for (const spec of this.items.values()) {
19
- if (spec.meta.key !== name) continue;
20
- if (spec.meta.version > maxVersion) {
21
- maxVersion = spec.meta.version;
22
- latest = spec;
23
- }
24
- }
25
- return latest;
5
+ var ExperimentRegistry = class extends SpecContractRegistry {
6
+ constructor(items) {
7
+ super("experiment", items);
26
8
  }
27
9
  };
28
10
  function makeExperimentKey(meta) {