@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.
- package/README.md +1 -1
- package/dist/app-config/app-config.feature.js +6 -6
- package/dist/app-config/contracts.d.ts +50 -50
- package/dist/app-config/contracts.js +13 -13
- package/dist/app-config/events.d.ts +27 -27
- package/dist/app-config/events.js +12 -12
- package/dist/app-config/lifecycle-contracts.d.ts +54 -54
- package/dist/app-config/lifecycle-contracts.js +19 -19
- package/dist/app-config/lifecycle.d.ts +2 -2
- package/dist/app-config/runtime.d.ts +3 -3
- package/dist/app-config/spec.d.ts +10 -12
- package/dist/app-config/spec.js +5 -26
- package/dist/capabilities/capabilities.d.ts +4 -11
- package/dist/capabilities/capabilities.js +3 -5
- package/dist/capabilities/openbanking.js +10 -10
- package/dist/client/react/form-render.js +1 -0
- package/dist/contract-registry/schemas.d.ts +8 -8
- package/dist/contract-registry/schemas.js +1 -1
- package/dist/contract-registry/types.d.ts +1 -1
- package/dist/data-views/data-views.js +1 -0
- package/dist/data-views/registry.d.ts +3 -37
- package/dist/data-views/registry.js +4 -66
- package/dist/data-views/runtime.d.ts +1 -1
- package/dist/data-views/runtime.js +2 -0
- package/dist/data-views/spec.d.ts +2 -2
- package/dist/docs/index.d.ts +2 -2
- package/dist/docs/index.js +2 -2
- package/dist/docs/presentations.d.ts +2 -4
- package/dist/docs/presentations.js +2 -4
- package/dist/docs/tech/contracts/ops-to-presentation-linking.docblock.js +1 -1
- package/dist/docs/tech/contracts/tests.docblock.js +1 -1
- package/dist/docs/tech/lifecycle-stage-system.docblock.js +1 -1
- package/dist/docs/tech-contracts.docs.js +1 -1
- package/dist/docs/types.d.ts +1 -1
- package/dist/events.d.ts +3 -3
- package/dist/examples/docs/examples.docblock.d.ts +6 -0
- package/dist/examples/docs/examples.docblock.js +165 -0
- package/dist/examples/index.d.ts +5 -0
- package/dist/examples/index.js +6 -0
- package/dist/examples/registry.d.ts +65 -0
- package/dist/examples/registry.js +144 -0
- package/dist/examples/schema.d.ts +282 -0
- package/dist/examples/schema.js +125 -0
- package/dist/examples/types.d.ts +167 -0
- package/dist/examples/types.js +43 -0
- package/dist/examples/validation.d.ts +65 -0
- package/dist/examples/validation.js +144 -0
- package/dist/experiments/docs/experiments.docblock.js +1 -1
- package/dist/experiments/evaluator.d.ts +1 -1
- package/dist/experiments/spec-resolver.d.ts +2 -2
- package/dist/experiments/spec.d.ts +6 -8
- package/dist/experiments/spec.js +5 -23
- package/dist/features/index.d.ts +9 -1
- package/dist/features/index.js +8 -1
- package/dist/features/install.js +2 -2
- package/dist/features/types.d.ts +7 -7
- package/dist/forms/forms.d.ts +2 -2
- package/dist/forms/forms.js +3 -6
- package/dist/index.d.ts +13 -8
- package/dist/index.js +11 -2
- package/dist/install.d.ts +9 -9
- package/dist/integrations/connection.d.ts +1 -1
- package/dist/integrations/docs/integrations.docblock.js +1 -1
- package/dist/integrations/integrations.feature.js +8 -8
- package/dist/integrations/openbanking/contracts/accounts.d.ts +66 -66
- package/dist/integrations/openbanking/contracts/accounts.js +3 -3
- package/dist/integrations/openbanking/contracts/balances.d.ts +34 -34
- package/dist/integrations/openbanking/contracts/balances.js +2 -2
- package/dist/integrations/openbanking/contracts/transactions.d.ts +48 -48
- package/dist/integrations/openbanking/contracts/transactions.js +2 -2
- package/dist/integrations/openbanking/models.d.ts +55 -55
- package/dist/integrations/openbanking/openbanking.feature.js +10 -10
- package/dist/integrations/operations.d.ts +102 -102
- package/dist/integrations/operations.js +10 -10
- package/dist/integrations/providers/elevenlabs.js +2 -2
- package/dist/integrations/providers/gcs-storage.js +2 -2
- package/dist/integrations/providers/gmail.js +3 -3
- package/dist/integrations/providers/google-calendar.js +2 -2
- package/dist/integrations/providers/mistral.js +3 -3
- package/dist/integrations/providers/postmark.js +2 -2
- package/dist/integrations/providers/powens.js +4 -4
- package/dist/integrations/providers/qdrant.js +3 -3
- package/dist/integrations/providers/stripe.js +2 -2
- package/dist/integrations/providers/twilio-sms.js +2 -2
- package/dist/integrations/runtime.d.ts +5 -5
- package/dist/integrations/spec.d.ts +3 -5
- package/dist/integrations/spec.js +5 -26
- package/dist/jobs/gcp-cloud-tasks.js +1 -1
- package/dist/jobs/gcp-pubsub.js +1 -1
- package/dist/jobs/handlers/ping-handler.d.ts +3 -3
- package/dist/jobs/handlers/ping-handler.js +2 -2
- package/dist/jobs/memory-queue.js +1 -1
- package/dist/jobs/queue.d.ts +4 -4
- package/dist/jobs/scaleway-sqs-queue.js +2 -2
- package/dist/jsonschema.d.ts +4 -4
- package/dist/knowledge/binding.d.ts +1 -1
- package/dist/knowledge/knowledge.feature.js +8 -8
- package/dist/knowledge/operations.d.ts +66 -66
- package/dist/knowledge/operations.js +12 -12
- package/dist/knowledge/source.d.ts +1 -1
- package/dist/knowledge/spaces/email-threads.js +2 -2
- package/dist/knowledge/spaces/financial-docs.js +2 -2
- package/dist/knowledge/spaces/financial-overview.js +2 -2
- package/dist/knowledge/spaces/product-canon.js +2 -2
- package/dist/knowledge/spaces/support-faq.js +2 -2
- package/dist/knowledge/spaces/uploaded-docs.js +2 -2
- package/dist/knowledge/spec.d.ts +3 -5
- package/dist/knowledge/spec.js +6 -28
- package/dist/llm/exporters.js +2 -2
- package/dist/llm/types.d.ts +4 -4
- package/dist/markdown.js +1 -1
- package/dist/migrations.d.ts +2 -2
- package/dist/migrations.js +7 -9
- package/dist/onboarding-base.d.ts +29 -29
- package/dist/onboarding-base.js +4 -4
- package/dist/openapi.js +9 -2
- package/dist/operations/operation.d.ts +3 -3
- package/dist/operations/registry.d.ts +9 -58
- package/dist/operations/registry.js +17 -93
- package/dist/ownership.d.ts +1 -1
- package/dist/policy/docs/policy.docblock.js +1 -1
- package/dist/policy/engine.js +2 -0
- package/dist/policy/spec.d.ts +1 -1
- package/dist/prompt.d.ts +6 -6
- package/dist/promptRegistry.d.ts +4 -4
- package/dist/promptRegistry.js +2 -5
- package/dist/regenerator/docs/regenerator.docblock.js +1 -1
- package/dist/regenerator/types.d.ts +1 -1
- package/dist/registry.d.ts +3 -2
- package/dist/registry.js +5 -5
- package/dist/resources.d.ts +5 -5
- package/dist/server/graphql-pothos.js +2 -2
- package/dist/server/mcp/registerTools.js +1 -1
- package/dist/server/rest-elysia.d.ts +1 -1
- package/dist/server/rest-elysia.js +1 -1
- package/dist/server/rest-express.d.ts +1 -1
- package/dist/server/rest-express.js +1 -1
- package/dist/server/rest-generic.d.ts +1 -1
- package/dist/server/rest-generic.js +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-mcp.js +1 -1
- package/dist/server/rest-next-pages.d.ts +1 -1
- package/dist/telemetry/docs/telemetry.docblock.js +1 -1
- package/dist/telemetry/spec.d.ts +7 -7
- package/dist/telemetry/spec.js +17 -44
- package/dist/telemetry/tracker.d.ts +2 -2
- package/dist/tests/runner.d.ts +1 -1
- package/dist/tests/spec.d.ts +5 -5
- package/dist/tests/spec.js +3 -5
- package/dist/themes.d.ts +4 -6
- package/dist/themes.js +5 -27
- package/dist/translations/catalog.d.ts +1 -1
- package/dist/types.d.ts +5 -5
- package/dist/workflow/adapters/db-adapter.js +3 -3
- package/dist/workflow/runner.d.ts +4 -2
- package/dist/workflow/spec.d.ts +4 -17
- package/dist/workflow/spec.js +4 -48
- package/dist/workflow/state.d.ts +1 -1
- package/dist/workflow/validation.js +1 -1
- package/dist/workspace-config/contractsrc-schema.d.ts +419 -419
- package/dist/workspace-config/contractsrc-schema.js +86 -86
- 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?:
|
|
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:
|
|
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?:
|
|
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?:
|
|
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:
|
|
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
|
-
|
|
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
|
package/dist/experiments/spec.js
CHANGED
|
@@ -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
|
|
5
|
-
|
|
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) {
|