@contractspec/module.examples 0.0.0-canary-20260113170453
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/LICENSE +21 -0
- package/README.md +21 -0
- package/dist/builtins.js +76 -0
- package/dist/builtins.js.map +1 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.js +7 -0
- package/dist/registry.d.ts +24 -0
- package/dist/registry.d.ts.map +1 -0
- package/dist/registry.js +33 -0
- package/dist/registry.js.map +1 -0
- package/dist/runtime/context.d.ts +45 -0
- package/dist/runtime/context.d.ts.map +1 -0
- package/dist/runtime/context.js +328 -0
- package/dist/runtime/context.js.map +1 -0
- package/dist/runtime/engine.d.ts +19 -0
- package/dist/runtime/engine.d.ts.map +1 -0
- package/dist/runtime/engine.js +81 -0
- package/dist/runtime/engine.js.map +1 -0
- package/dist/runtime/installer.d.ts +40 -0
- package/dist/runtime/installer.d.ts.map +1 -0
- package/dist/runtime/installer.js +103 -0
- package/dist/runtime/installer.js.map +1 -0
- package/dist/runtime/registry-client.d.ts +14 -0
- package/dist/runtime/registry-client.d.ts.map +1 -0
- package/dist/runtime/registry-client.js +42 -0
- package/dist/runtime/registry-client.js.map +1 -0
- package/dist/runtime/registry.d.ts +17 -0
- package/dist/runtime/registry.d.ts.map +1 -0
- package/dist/runtime/registry.js +225 -0
- package/dist/runtime/registry.js.map +1 -0
- package/package.json +97 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"engine.d.ts","names":[],"sources":["../../src/runtime/engine.ts"],"sourcesContent":[],"mappings":";;;;;;;iBA8DgB,6BAAA,CAAA,GAAiC;;;;iBAqEjC,iBAAA,CAAA,GAAqB;;;;iBAUrB,mBAAA,CAAA"}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { createDefaultTransformEngine, registerBasicValidation, registerDefaultReactRenderer } from "@contractspec/lib.contracts/presentations";
|
|
2
|
+
import { agentDashboardMarkdownRenderer, agentListMarkdownRenderer, agentListReactRenderer, runListMarkdownRenderer, toolRegistryMarkdownRenderer } from "@contractspec/example.agent-console";
|
|
3
|
+
import { projectListMarkdownRenderer, projectListReactRenderer, saasBillingMarkdownRenderer, saasDashboardMarkdownRenderer } from "@contractspec/example.saas-boilerplate";
|
|
4
|
+
import { crmDashboardMarkdownRenderer, crmPipelineMarkdownRenderer, crmPipelineReactRenderer } from "@contractspec/example.crm-pipeline";
|
|
5
|
+
import { workflowDashboardMarkdownRenderer, workflowDefinitionListMarkdownRenderer, workflowInstanceDetailMarkdownRenderer } from "@contractspec/example.workflow-system";
|
|
6
|
+
import { marketplaceDashboardMarkdownRenderer, orderListMarkdownRenderer, productCatalogMarkdownRenderer } from "@contractspec/example.marketplace";
|
|
7
|
+
import { connectionListMarkdownRenderer, integrationDashboardMarkdownRenderer, syncConfigMarkdownRenderer } from "@contractspec/example.integration-hub";
|
|
8
|
+
import { analyticsDashboardMarkdownRenderer, dashboardListMarkdownRenderer, queryBuilderMarkdownRenderer } from "@contractspec/example.analytics-dashboard";
|
|
9
|
+
|
|
10
|
+
//#region src/runtime/engine.ts
|
|
11
|
+
/**
|
|
12
|
+
* Template TransformEngine Setup
|
|
13
|
+
*
|
|
14
|
+
* Creates and configures a TransformEngine instance with all template-specific
|
|
15
|
+
* renderers for React and Markdown targets.
|
|
16
|
+
*/
|
|
17
|
+
/**
|
|
18
|
+
* Create a TransformEngine configured for template rendering
|
|
19
|
+
*/
|
|
20
|
+
function createTemplateTransformEngine() {
|
|
21
|
+
const engine = createDefaultTransformEngine();
|
|
22
|
+
registerDefaultReactRenderer(engine);
|
|
23
|
+
registerBasicValidation(engine);
|
|
24
|
+
registerTemplateRenderers(engine);
|
|
25
|
+
return engine;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Register all template-specific renderers
|
|
29
|
+
*
|
|
30
|
+
* Custom markdown renderers are registered here and will be tried AFTER
|
|
31
|
+
* the default schema-driven renderer. Each custom renderer is responsible
|
|
32
|
+
* for checking if it handles the specific presentation (via componentKey)
|
|
33
|
+
* and throwing if not, allowing the next renderer in the chain to try.
|
|
34
|
+
*/
|
|
35
|
+
function registerTemplateRenderers(engine) {
|
|
36
|
+
engine.register(agentListReactRenderer);
|
|
37
|
+
engine.register(agentListMarkdownRenderer);
|
|
38
|
+
engine.register(runListMarkdownRenderer);
|
|
39
|
+
engine.register(toolRegistryMarkdownRenderer);
|
|
40
|
+
engine.register(agentDashboardMarkdownRenderer);
|
|
41
|
+
engine.register(projectListReactRenderer);
|
|
42
|
+
engine.register(projectListMarkdownRenderer);
|
|
43
|
+
engine.register(saasDashboardMarkdownRenderer);
|
|
44
|
+
engine.register(saasBillingMarkdownRenderer);
|
|
45
|
+
engine.register(crmPipelineReactRenderer);
|
|
46
|
+
engine.register(crmPipelineMarkdownRenderer);
|
|
47
|
+
engine.register(crmDashboardMarkdownRenderer);
|
|
48
|
+
engine.register(workflowDashboardMarkdownRenderer);
|
|
49
|
+
engine.register(workflowDefinitionListMarkdownRenderer);
|
|
50
|
+
engine.register(workflowInstanceDetailMarkdownRenderer);
|
|
51
|
+
engine.register(marketplaceDashboardMarkdownRenderer);
|
|
52
|
+
engine.register(productCatalogMarkdownRenderer);
|
|
53
|
+
engine.register(orderListMarkdownRenderer);
|
|
54
|
+
engine.register(integrationDashboardMarkdownRenderer);
|
|
55
|
+
engine.register(connectionListMarkdownRenderer);
|
|
56
|
+
engine.register(syncConfigMarkdownRenderer);
|
|
57
|
+
engine.register(analyticsDashboardMarkdownRenderer);
|
|
58
|
+
engine.register(dashboardListMarkdownRenderer);
|
|
59
|
+
engine.register(queryBuilderMarkdownRenderer);
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Singleton instance for shared engine
|
|
63
|
+
*/
|
|
64
|
+
let templateEngine = null;
|
|
65
|
+
/**
|
|
66
|
+
* Get or create the shared template engine instance
|
|
67
|
+
*/
|
|
68
|
+
function getTemplateEngine() {
|
|
69
|
+
if (!templateEngine) templateEngine = createTemplateTransformEngine();
|
|
70
|
+
return templateEngine;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Reset the shared engine (useful for testing)
|
|
74
|
+
*/
|
|
75
|
+
function resetTemplateEngine() {
|
|
76
|
+
templateEngine = null;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
//#endregion
|
|
80
|
+
export { createTemplateTransformEngine, getTemplateEngine, resetTemplateEngine };
|
|
81
|
+
//# sourceMappingURL=engine.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"engine.js","names":[],"sources":["../../src/runtime/engine.ts"],"sourcesContent":["/**\n * Template TransformEngine Setup\n *\n * Creates and configures a TransformEngine instance with all template-specific\n * renderers for React and Markdown targets.\n */\nimport {\n createDefaultTransformEngine,\n registerBasicValidation,\n registerDefaultReactRenderer,\n type TransformEngine,\n} from '@contractspec/lib.contracts/presentations';\n\n// Import template renderers from example packages\nimport {\n agentDashboardMarkdownRenderer,\n agentListMarkdownRenderer,\n agentListReactRenderer,\n runListMarkdownRenderer,\n toolRegistryMarkdownRenderer,\n} from '@contractspec/example.agent-console';\n\nimport {\n projectListMarkdownRenderer,\n projectListReactRenderer,\n saasBillingMarkdownRenderer,\n saasDashboardMarkdownRenderer,\n} from '@contractspec/example.saas-boilerplate';\n\nimport {\n crmDashboardMarkdownRenderer,\n crmPipelineMarkdownRenderer,\n crmPipelineReactRenderer,\n} from '@contractspec/example.crm-pipeline';\n\nimport {\n workflowDashboardMarkdownRenderer,\n workflowDefinitionListMarkdownRenderer,\n workflowInstanceDetailMarkdownRenderer,\n} from '@contractspec/example.workflow-system';\n\nimport {\n marketplaceDashboardMarkdownRenderer,\n orderListMarkdownRenderer,\n productCatalogMarkdownRenderer,\n} from '@contractspec/example.marketplace';\n\nimport {\n connectionListMarkdownRenderer,\n integrationDashboardMarkdownRenderer,\n syncConfigMarkdownRenderer,\n} from '@contractspec/example.integration-hub';\n\nimport {\n analyticsDashboardMarkdownRenderer,\n dashboardListMarkdownRenderer,\n queryBuilderMarkdownRenderer,\n} from '@contractspec/example.analytics-dashboard';\n\n/**\n * Create a TransformEngine configured for template rendering\n */\nexport function createTemplateTransformEngine(): TransformEngine {\n const engine = createDefaultTransformEngine();\n\n // Register default renderers\n registerDefaultReactRenderer(engine);\n registerBasicValidation(engine);\n\n // Register template-specific renderers\n registerTemplateRenderers(engine);\n\n return engine;\n}\n\n/**\n * Register all template-specific renderers\n *\n * Custom markdown renderers are registered here and will be tried AFTER\n * the default schema-driven renderer. Each custom renderer is responsible\n * for checking if it handles the specific presentation (via componentKey)\n * and throwing if not, allowing the next renderer in the chain to try.\n */\nfunction registerTemplateRenderers(engine: TransformEngine): void {\n // Agent Console renderers\n engine.register(agentListReactRenderer);\n engine.register(agentListMarkdownRenderer);\n engine.register(runListMarkdownRenderer);\n engine.register(toolRegistryMarkdownRenderer);\n engine.register(agentDashboardMarkdownRenderer);\n\n // SaaS Boilerplate renderers\n engine.register(projectListReactRenderer);\n engine.register(projectListMarkdownRenderer);\n engine.register(saasDashboardMarkdownRenderer);\n engine.register(saasBillingMarkdownRenderer);\n\n // CRM Pipeline renderers\n engine.register(crmPipelineReactRenderer);\n engine.register(crmPipelineMarkdownRenderer);\n engine.register(crmDashboardMarkdownRenderer);\n\n // Workflow System renderers\n engine.register(workflowDashboardMarkdownRenderer);\n engine.register(workflowDefinitionListMarkdownRenderer);\n engine.register(workflowInstanceDetailMarkdownRenderer);\n\n // Marketplace renderers\n engine.register(marketplaceDashboardMarkdownRenderer);\n engine.register(productCatalogMarkdownRenderer);\n engine.register(orderListMarkdownRenderer);\n\n // Integration Hub renderers\n engine.register(integrationDashboardMarkdownRenderer);\n engine.register(connectionListMarkdownRenderer);\n engine.register(syncConfigMarkdownRenderer);\n\n // Analytics Dashboard renderers\n engine.register(analyticsDashboardMarkdownRenderer);\n engine.register(dashboardListMarkdownRenderer);\n engine.register(queryBuilderMarkdownRenderer);\n}\n\n/**\n * Singleton instance for shared engine\n */\nlet templateEngine: TransformEngine | null = null;\n\n/**\n * Get or create the shared template engine instance\n */\nexport function getTemplateEngine(): TransformEngine {\n if (!templateEngine) {\n templateEngine = createTemplateTransformEngine();\n }\n return templateEngine;\n}\n\n/**\n * Reset the shared engine (useful for testing)\n */\nexport function resetTemplateEngine(): void {\n templateEngine = null;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;AA8DA,SAAgB,gCAAiD;CAC/D,MAAM,SAAS,8BAA8B;AAG7C,8BAA6B,OAAO;AACpC,yBAAwB,OAAO;AAG/B,2BAA0B,OAAO;AAEjC,QAAO;;;;;;;;;;AAWT,SAAS,0BAA0B,QAA+B;AAEhE,QAAO,SAAS,uBAAuB;AACvC,QAAO,SAAS,0BAA0B;AAC1C,QAAO,SAAS,wBAAwB;AACxC,QAAO,SAAS,6BAA6B;AAC7C,QAAO,SAAS,+BAA+B;AAG/C,QAAO,SAAS,yBAAyB;AACzC,QAAO,SAAS,4BAA4B;AAC5C,QAAO,SAAS,8BAA8B;AAC9C,QAAO,SAAS,4BAA4B;AAG5C,QAAO,SAAS,yBAAyB;AACzC,QAAO,SAAS,4BAA4B;AAC5C,QAAO,SAAS,6BAA6B;AAG7C,QAAO,SAAS,kCAAkC;AAClD,QAAO,SAAS,uCAAuC;AACvD,QAAO,SAAS,uCAAuC;AAGvD,QAAO,SAAS,qCAAqC;AACrD,QAAO,SAAS,+BAA+B;AAC/C,QAAO,SAAS,0BAA0B;AAG1C,QAAO,SAAS,qCAAqC;AACrD,QAAO,SAAS,+BAA+B;AAC/C,QAAO,SAAS,2BAA2B;AAG3C,QAAO,SAAS,mCAAmC;AACnD,QAAO,SAAS,8BAA8B;AAC9C,QAAO,SAAS,6BAA6B;;;;;AAM/C,IAAI,iBAAyC;;;;AAK7C,SAAgB,oBAAqC;AACnD,KAAI,CAAC,eACH,kBAAiB,+BAA+B;AAElD,QAAO;;;;;AAMT,SAAgB,sBAA4B;AAC1C,kBAAiB"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { RegistryTemplateSummary } from "./registry-client.js";
|
|
2
|
+
import { web } from "@contractspec/lib.runtime-sandbox";
|
|
3
|
+
import { InstallTemplateOptions, SaveTemplateOptions, SaveTemplateResult, TemplateDefinition, TemplateFilter, TemplateId } from "@contractspec/lib.example-shared-ui";
|
|
4
|
+
|
|
5
|
+
//#region src/runtime/installer.d.ts
|
|
6
|
+
declare const LocalRuntimeServices: typeof web.LocalRuntimeServices;
|
|
7
|
+
type LocalRuntimeServices = InstanceType<typeof LocalRuntimeServices>;
|
|
8
|
+
interface TemplateInstallerOptions {
|
|
9
|
+
runtime?: LocalRuntimeServices;
|
|
10
|
+
endpoint?: string;
|
|
11
|
+
/** Optional registry server base URL (enables listing remote/community templates) */
|
|
12
|
+
registryUrl?: string;
|
|
13
|
+
fetchImpl?: typeof fetch;
|
|
14
|
+
}
|
|
15
|
+
declare class TemplateInstaller {
|
|
16
|
+
private readonly runtime;
|
|
17
|
+
private readonly endpoint;
|
|
18
|
+
private readonly registryUrl;
|
|
19
|
+
private readonly fetchImpl;
|
|
20
|
+
constructor(options?: TemplateInstallerOptions);
|
|
21
|
+
init(): Promise<void>;
|
|
22
|
+
list(filter?: TemplateFilter): TemplateDefinition[];
|
|
23
|
+
/**
|
|
24
|
+
* List templates published to the ContractSpec Registry.
|
|
25
|
+
*
|
|
26
|
+
* Note: this returns *metadata* only. Installing still requires a local template
|
|
27
|
+
* implementation unless/until we support seeding templates from registry payloads.
|
|
28
|
+
*/
|
|
29
|
+
listRemoteTemplates(): Promise<RegistryTemplateSummary[]>;
|
|
30
|
+
/**
|
|
31
|
+
* Resolve a registry template id to a local TemplateId if available.
|
|
32
|
+
*/
|
|
33
|
+
resolveLocalTemplateId(id: string): TemplateId | null;
|
|
34
|
+
get(templateId: TemplateId): TemplateDefinition | undefined;
|
|
35
|
+
install(templateId: TemplateId, options?: InstallTemplateOptions): Promise<void>;
|
|
36
|
+
saveToStudio(options: SaveTemplateOptions): Promise<SaveTemplateResult>;
|
|
37
|
+
}
|
|
38
|
+
//#endregion
|
|
39
|
+
export { TemplateInstaller, TemplateInstallerOptions };
|
|
40
|
+
//# sourceMappingURL=installer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"installer.d.ts","names":[],"sources":["../../src/runtime/installer.ts"],"sourcesContent":[],"mappings":";;;;;cAiBQ,6BAAoB,GAAA,CAAA;KACvB,oBAAA,GAAuB,oBAAoB;AADxC,UAYS,wBAAA,CAZW;EACvB,OAAA,CAAA,EAYO,oBAZa;EAWR,QAAA,CAAA,EAAA,MAAA;EAQJ;EAMU,WAAA,CAAA,EAAA,MAAA;EASP,SAAA,CAAA,EAAA,OAlBK,KAkBL;;AAIiB,cAnBpB,iBAAA,CAmBoB;EAuBM,iBAAA,OAAA;EAAR,iBAAA,QAAA;EAYO,iBAAA,WAAA;EAKpB,iBAAA,SAAA;EAAa,WAAA,CAAA,OAAA,CAAA,EArDR,wBAqDQ;EAKf,IAAA,CAAA,CAAA,EAjDA,OAiDA,CAAA,IAAA,CAAA;EACH,IAAA,CAAA,MAAA,CAAA,EA9CG,cA8CH,CAAA,EA9CoB,kBA8CpB,EAAA;EACR;;;;;;yBAxB0B,QAAQ;;;;sCAYD;kBAKpB,aAAa;sBAKf,sBACH,yBACR;wBAQQ,sBACR,QAAQ"}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { TEMPLATE_REGISTRY, getTemplate } from "./registry.js";
|
|
4
|
+
import { ContractSpecRegistryClient } from "./registry-client.js";
|
|
5
|
+
import { web } from "@contractspec/lib.runtime-sandbox";
|
|
6
|
+
|
|
7
|
+
//#region src/runtime/installer.ts
|
|
8
|
+
const { LocalRuntimeServices } = web;
|
|
9
|
+
const SAVE_TEMPLATE_MUTATION = `
|
|
10
|
+
mutation SaveTemplateToStudio($input: SaveTemplateInput!) {
|
|
11
|
+
saveTemplateToStudio(input: $input) {
|
|
12
|
+
projectId
|
|
13
|
+
status
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
`;
|
|
17
|
+
var TemplateInstaller = class {
|
|
18
|
+
runtime;
|
|
19
|
+
endpoint;
|
|
20
|
+
registryUrl;
|
|
21
|
+
fetchImpl;
|
|
22
|
+
constructor(options = {}) {
|
|
23
|
+
this.runtime = options.runtime ?? new LocalRuntimeServices();
|
|
24
|
+
this.endpoint = options.endpoint ?? "/api/graphql";
|
|
25
|
+
this.registryUrl = options.registryUrl ? options.registryUrl.replace(/\/+$/, "") : null;
|
|
26
|
+
this.fetchImpl = options.fetchImpl ?? fetch;
|
|
27
|
+
}
|
|
28
|
+
async init() {
|
|
29
|
+
await this.runtime.init();
|
|
30
|
+
}
|
|
31
|
+
list(filter) {
|
|
32
|
+
return filter ? TEMPLATE_REGISTRY.filter((template) => {
|
|
33
|
+
if (filter.category && filter.category !== template.category) return false;
|
|
34
|
+
if (filter.complexity && filter.complexity !== template.complexity) return false;
|
|
35
|
+
if (filter.tag && !template.tags.includes(filter.tag)) return false;
|
|
36
|
+
return true;
|
|
37
|
+
}) : TEMPLATE_REGISTRY;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* List templates published to the ContractSpec Registry.
|
|
41
|
+
*
|
|
42
|
+
* Note: this returns *metadata* only. Installing still requires a local template
|
|
43
|
+
* implementation unless/until we support seeding templates from registry payloads.
|
|
44
|
+
*/
|
|
45
|
+
async listRemoteTemplates() {
|
|
46
|
+
if (!this.registryUrl) return [];
|
|
47
|
+
return await new ContractSpecRegistryClient({
|
|
48
|
+
registryUrl: this.registryUrl,
|
|
49
|
+
fetchImpl: this.fetchImpl
|
|
50
|
+
}).listTemplateSummaries();
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Resolve a registry template id to a local TemplateId if available.
|
|
54
|
+
*/
|
|
55
|
+
resolveLocalTemplateId(id) {
|
|
56
|
+
return getTemplate(id) ? id : null;
|
|
57
|
+
}
|
|
58
|
+
get(templateId) {
|
|
59
|
+
return getTemplate(templateId);
|
|
60
|
+
}
|
|
61
|
+
async install(templateId, options = {}) {
|
|
62
|
+
await this.runtime.seedTemplate({
|
|
63
|
+
templateId,
|
|
64
|
+
projectId: options.projectId
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
async saveToStudio(options) {
|
|
68
|
+
const payload = toBase64(await this.runtime.db.export());
|
|
69
|
+
const response = await this.fetchImpl(options.endpoint ?? this.endpoint, {
|
|
70
|
+
method: "POST",
|
|
71
|
+
headers: {
|
|
72
|
+
"Content-Type": "application/json",
|
|
73
|
+
...options.token ? { Authorization: `Bearer ${options.token}` } : {}
|
|
74
|
+
},
|
|
75
|
+
body: JSON.stringify({
|
|
76
|
+
query: SAVE_TEMPLATE_MUTATION,
|
|
77
|
+
variables: { input: {
|
|
78
|
+
templateId: options.templateId,
|
|
79
|
+
projectName: options.projectName,
|
|
80
|
+
organizationId: options.organizationId,
|
|
81
|
+
payload
|
|
82
|
+
} }
|
|
83
|
+
})
|
|
84
|
+
});
|
|
85
|
+
if (!response.ok) throw new Error(`Failed to save template: ${response.status} ${response.statusText}`);
|
|
86
|
+
const json = await response.json();
|
|
87
|
+
if (json.errors?.length) throw new Error(json.errors[0]?.message ?? "Unknown error");
|
|
88
|
+
if (!json.data?.saveTemplateToStudio) throw new Error("Invalid response from Studio API");
|
|
89
|
+
return json.data.saveTemplateToStudio;
|
|
90
|
+
}
|
|
91
|
+
};
|
|
92
|
+
function toBase64(bytes) {
|
|
93
|
+
if (typeof Buffer !== "undefined") return Buffer.from(bytes).toString("base64");
|
|
94
|
+
let binary = "";
|
|
95
|
+
bytes.forEach((value) => {
|
|
96
|
+
binary += String.fromCharCode(value);
|
|
97
|
+
});
|
|
98
|
+
return btoa(binary);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
//#endregion
|
|
102
|
+
export { TemplateInstaller };
|
|
103
|
+
//# sourceMappingURL=installer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"installer.js","names":[],"sources":["../../src/runtime/installer.ts"],"sourcesContent":["'use client';\n\nimport { getTemplate, TEMPLATE_REGISTRY } from './registry';\nimport type {\n TemplateDefinition,\n TemplateFilter,\n TemplateId,\n InstallTemplateOptions,\n SaveTemplateOptions,\n SaveTemplateResult,\n} from '@contractspec/lib.example-shared-ui';\nimport {\n ContractSpecRegistryClient,\n type RegistryTemplateSummary,\n} from './registry-client';\nimport { web } from '@contractspec/lib.runtime-sandbox';\n\nconst { LocalRuntimeServices } = web;\ntype LocalRuntimeServices = InstanceType<typeof LocalRuntimeServices>;\n\nconst SAVE_TEMPLATE_MUTATION = `\n mutation SaveTemplateToStudio($input: SaveTemplateInput!) {\n saveTemplateToStudio(input: $input) {\n projectId\n status\n }\n }\n`;\n\nexport interface TemplateInstallerOptions {\n runtime?: LocalRuntimeServices;\n endpoint?: string;\n /** Optional registry server base URL (enables listing remote/community templates) */\n registryUrl?: string;\n fetchImpl?: typeof fetch;\n}\n\nexport class TemplateInstaller {\n private readonly runtime: LocalRuntimeServices;\n private readonly endpoint: string;\n private readonly registryUrl: string | null;\n private readonly fetchImpl: typeof fetch;\n\n constructor(options: TemplateInstallerOptions = {}) {\n this.runtime = options.runtime ?? new LocalRuntimeServices();\n this.endpoint = options.endpoint ?? '/api/graphql';\n this.registryUrl = options.registryUrl\n ? options.registryUrl.replace(/\\/+$/, '')\n : null;\n this.fetchImpl = options.fetchImpl ?? fetch;\n }\n\n async init(): Promise<void> {\n await this.runtime.init();\n }\n\n list(filter?: TemplateFilter): TemplateDefinition[] {\n return filter\n ? TEMPLATE_REGISTRY.filter((template) => {\n if (filter.category && filter.category !== template.category) {\n return false;\n }\n if (filter.complexity && filter.complexity !== template.complexity) {\n return false;\n }\n if (filter.tag && !template.tags.includes(filter.tag)) {\n return false;\n }\n return true;\n })\n : TEMPLATE_REGISTRY;\n }\n\n /**\n * List templates published to the ContractSpec Registry.\n *\n * Note: this returns *metadata* only. Installing still requires a local template\n * implementation unless/until we support seeding templates from registry payloads.\n */\n async listRemoteTemplates(): Promise<RegistryTemplateSummary[]> {\n if (!this.registryUrl) return [];\n const client = new ContractSpecRegistryClient({\n registryUrl: this.registryUrl,\n fetchImpl: this.fetchImpl,\n });\n return await client.listTemplateSummaries();\n }\n\n /**\n * Resolve a registry template id to a local TemplateId if available.\n */\n resolveLocalTemplateId(id: string): TemplateId | null {\n const found = getTemplate(id as TemplateId);\n return found ? (id as TemplateId) : null;\n }\n\n get(templateId: TemplateId): TemplateDefinition | undefined {\n return getTemplate(templateId);\n }\n\n async install(\n templateId: TemplateId,\n options: InstallTemplateOptions = {}\n ): Promise<void> {\n await this.runtime.seedTemplate({\n templateId,\n projectId: options.projectId,\n });\n }\n\n async saveToStudio(\n options: SaveTemplateOptions\n ): Promise<SaveTemplateResult> {\n const snapshot = await this.runtime.db.export();\n const payload = toBase64(snapshot);\n const response = await this.fetchImpl(options.endpoint ?? this.endpoint, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n ...(options.token ? { Authorization: `Bearer ${options.token}` } : {}),\n },\n body: JSON.stringify({\n query: SAVE_TEMPLATE_MUTATION,\n variables: {\n input: {\n templateId: options.templateId,\n projectName: options.projectName,\n organizationId: options.organizationId,\n payload,\n },\n },\n }),\n });\n\n if (!response.ok) {\n throw new Error(\n `Failed to save template: ${response.status} ${response.statusText}`\n );\n }\n\n const json = (await response.json()) as {\n data?: {\n saveTemplateToStudio: SaveTemplateResult;\n };\n errors?: { message: string }[];\n };\n\n if (json.errors?.length) {\n throw new Error(json.errors[0]?.message ?? 'Unknown error');\n }\n\n if (!json.data?.saveTemplateToStudio) {\n throw new Error('Invalid response from Studio API');\n }\n\n return json.data.saveTemplateToStudio;\n }\n}\n\nfunction toBase64(bytes: Uint8Array): string {\n if (typeof Buffer !== 'undefined') {\n return Buffer.from(bytes).toString('base64');\n }\n let binary = '';\n bytes.forEach((value) => {\n binary += String.fromCharCode(value);\n });\n return btoa(binary);\n}\n"],"mappings":";;;;;;;AAiBA,MAAM,EAAE,yBAAyB;AAGjC,MAAM,yBAAyB;;;;;;;;AAiB/B,IAAa,oBAAb,MAA+B;CAC7B,AAAiB;CACjB,AAAiB;CACjB,AAAiB;CACjB,AAAiB;CAEjB,YAAY,UAAoC,EAAE,EAAE;AAClD,OAAK,UAAU,QAAQ,WAAW,IAAI,sBAAsB;AAC5D,OAAK,WAAW,QAAQ,YAAY;AACpC,OAAK,cAAc,QAAQ,cACvB,QAAQ,YAAY,QAAQ,QAAQ,GAAG,GACvC;AACJ,OAAK,YAAY,QAAQ,aAAa;;CAGxC,MAAM,OAAsB;AAC1B,QAAM,KAAK,QAAQ,MAAM;;CAG3B,KAAK,QAA+C;AAClD,SAAO,SACH,kBAAkB,QAAQ,aAAa;AACrC,OAAI,OAAO,YAAY,OAAO,aAAa,SAAS,SAClD,QAAO;AAET,OAAI,OAAO,cAAc,OAAO,eAAe,SAAS,WACtD,QAAO;AAET,OAAI,OAAO,OAAO,CAAC,SAAS,KAAK,SAAS,OAAO,IAAI,CACnD,QAAO;AAET,UAAO;IACP,GACF;;;;;;;;CASN,MAAM,sBAA0D;AAC9D,MAAI,CAAC,KAAK,YAAa,QAAO,EAAE;AAKhC,SAAO,MAJQ,IAAI,2BAA2B;GAC5C,aAAa,KAAK;GAClB,WAAW,KAAK;GACjB,CAAC,CACkB,uBAAuB;;;;;CAM7C,uBAAuB,IAA+B;AAEpD,SADc,YAAY,GAAiB,GAC3B,KAAoB;;CAGtC,IAAI,YAAwD;AAC1D,SAAO,YAAY,WAAW;;CAGhC,MAAM,QACJ,YACA,UAAkC,EAAE,EACrB;AACf,QAAM,KAAK,QAAQ,aAAa;GAC9B;GACA,WAAW,QAAQ;GACpB,CAAC;;CAGJ,MAAM,aACJ,SAC6B;EAE7B,MAAM,UAAU,SADC,MAAM,KAAK,QAAQ,GAAG,QAAQ,CACb;EAClC,MAAM,WAAW,MAAM,KAAK,UAAU,QAAQ,YAAY,KAAK,UAAU;GACvE,QAAQ;GACR,SAAS;IACP,gBAAgB;IAChB,GAAI,QAAQ,QAAQ,EAAE,eAAe,UAAU,QAAQ,SAAS,GAAG,EAAE;IACtE;GACD,MAAM,KAAK,UAAU;IACnB,OAAO;IACP,WAAW,EACT,OAAO;KACL,YAAY,QAAQ;KACpB,aAAa,QAAQ;KACrB,gBAAgB,QAAQ;KACxB;KACD,EACF;IACF,CAAC;GACH,CAAC;AAEF,MAAI,CAAC,SAAS,GACZ,OAAM,IAAI,MACR,4BAA4B,SAAS,OAAO,GAAG,SAAS,aACzD;EAGH,MAAM,OAAQ,MAAM,SAAS,MAAM;AAOnC,MAAI,KAAK,QAAQ,OACf,OAAM,IAAI,MAAM,KAAK,OAAO,IAAI,WAAW,gBAAgB;AAG7D,MAAI,CAAC,KAAK,MAAM,qBACd,OAAM,IAAI,MAAM,mCAAmC;AAGrD,SAAO,KAAK,KAAK;;;AAIrB,SAAS,SAAS,OAA2B;AAC3C,KAAI,OAAO,WAAW,YACpB,QAAO,OAAO,KAAK,MAAM,CAAC,SAAS,SAAS;CAE9C,IAAI,SAAS;AACb,OAAM,SAAS,UAAU;AACvB,YAAU,OAAO,aAAa,MAAM;GACpC;AACF,QAAO,KAAK,OAAO"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import "@contractspec/lib.contracts";
|
|
2
|
+
|
|
3
|
+
//#region src/runtime/registry-client.d.ts
|
|
4
|
+
interface RegistryTemplateSummary {
|
|
5
|
+
id: string;
|
|
6
|
+
title: string;
|
|
7
|
+
description: string;
|
|
8
|
+
version: string;
|
|
9
|
+
tags: string[];
|
|
10
|
+
source: 'registry';
|
|
11
|
+
}
|
|
12
|
+
//#endregion
|
|
13
|
+
export { RegistryTemplateSummary };
|
|
14
|
+
//# sourceMappingURL=registry-client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"registry-client.d.ts","names":[],"sources":["../../src/runtime/registry-client.ts"],"sourcesContent":[],"mappings":";;;UAMiB,uBAAA;;EAAA,KAAA,EAAA,MAAA"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
//#region src/runtime/registry-client.ts
|
|
2
|
+
var ContractSpecRegistryClient = class {
|
|
3
|
+
baseUrl;
|
|
4
|
+
fetchImpl;
|
|
5
|
+
constructor(options) {
|
|
6
|
+
this.baseUrl = options.registryUrl.replace(/\/+$/, "");
|
|
7
|
+
this.fetchImpl = options.fetchImpl ?? fetch;
|
|
8
|
+
}
|
|
9
|
+
async getManifest() {
|
|
10
|
+
const res = await this.fetchImpl(`${this.baseUrl}/r/contractspec.json`, {
|
|
11
|
+
method: "GET",
|
|
12
|
+
headers: { Accept: "application/json" }
|
|
13
|
+
});
|
|
14
|
+
if (!res.ok) throw new Error(`Registry manifest fetch failed: ${res.status} ${res.statusText}`);
|
|
15
|
+
return await res.json();
|
|
16
|
+
}
|
|
17
|
+
async listByType(type) {
|
|
18
|
+
return (await this.getManifest()).items.filter((i) => i.type === type);
|
|
19
|
+
}
|
|
20
|
+
async listTemplateSummaries() {
|
|
21
|
+
return (await this.listByType("contractspec:template")).map((t) => ({
|
|
22
|
+
id: t.key,
|
|
23
|
+
title: t.title,
|
|
24
|
+
description: t.description,
|
|
25
|
+
version: t.version,
|
|
26
|
+
tags: t.meta.tags,
|
|
27
|
+
source: "registry"
|
|
28
|
+
}));
|
|
29
|
+
}
|
|
30
|
+
async getItem(typeSegment, name) {
|
|
31
|
+
const res = await this.fetchImpl(`${this.baseUrl}/r/contractspec/${typeSegment}/${name}.json`, {
|
|
32
|
+
method: "GET",
|
|
33
|
+
headers: { Accept: "application/json" }
|
|
34
|
+
});
|
|
35
|
+
if (!res.ok) throw new Error(`Registry item fetch failed: ${res.status} ${res.statusText}`);
|
|
36
|
+
return await res.json();
|
|
37
|
+
}
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
//#endregion
|
|
41
|
+
export { ContractSpecRegistryClient };
|
|
42
|
+
//# sourceMappingURL=registry-client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"registry-client.js","names":[],"sources":["../../src/runtime/registry-client.ts"],"sourcesContent":["import type {\n ContractRegistryItem,\n ContractRegistryItemType,\n ContractRegistryManifest,\n} from '@contractspec/lib.contracts';\n\nexport interface RegistryTemplateSummary {\n id: string;\n title: string;\n description: string;\n version: string;\n tags: string[];\n source: 'registry';\n}\n\nexport interface RegistryClientOptions {\n registryUrl: string;\n fetchImpl?: typeof fetch;\n}\n\nexport class ContractSpecRegistryClient {\n private readonly baseUrl: string;\n private readonly fetchImpl: typeof fetch;\n\n constructor(options: RegistryClientOptions) {\n this.baseUrl = options.registryUrl.replace(/\\/+$/, '');\n this.fetchImpl = options.fetchImpl ?? fetch;\n }\n\n async getManifest(): Promise<ContractRegistryManifest> {\n const res = await this.fetchImpl(`${this.baseUrl}/r/contractspec.json`, {\n method: 'GET',\n headers: { Accept: 'application/json' },\n });\n if (!res.ok) {\n throw new Error(\n `Registry manifest fetch failed: ${res.status} ${res.statusText}`\n );\n }\n return (await res.json()) as ContractRegistryManifest;\n }\n\n async listByType(\n type: ContractRegistryItemType\n ): Promise<ContractRegistryItem[]> {\n const manifest = await this.getManifest();\n return manifest.items.filter((i) => i.type === type);\n }\n\n async listTemplateSummaries(): Promise<RegistryTemplateSummary[]> {\n const templates = await this.listByType('contractspec:template');\n return templates.map((t) => ({\n id: t.key,\n title: t.title,\n description: t.description,\n version: t.version,\n tags: t.meta.tags,\n source: 'registry',\n }));\n }\n\n async getItem(\n typeSegment: string,\n name: string\n ): Promise<ContractRegistryItem> {\n const res = await this.fetchImpl(\n `${this.baseUrl}/r/contractspec/${typeSegment}/${name}.json`,\n {\n method: 'GET',\n headers: { Accept: 'application/json' },\n }\n );\n if (!res.ok) {\n throw new Error(\n `Registry item fetch failed: ${res.status} ${res.statusText}`\n );\n }\n return (await res.json()) as ContractRegistryItem;\n }\n}\n"],"mappings":";AAoBA,IAAa,6BAAb,MAAwC;CACtC,AAAiB;CACjB,AAAiB;CAEjB,YAAY,SAAgC;AAC1C,OAAK,UAAU,QAAQ,YAAY,QAAQ,QAAQ,GAAG;AACtD,OAAK,YAAY,QAAQ,aAAa;;CAGxC,MAAM,cAAiD;EACrD,MAAM,MAAM,MAAM,KAAK,UAAU,GAAG,KAAK,QAAQ,uBAAuB;GACtE,QAAQ;GACR,SAAS,EAAE,QAAQ,oBAAoB;GACxC,CAAC;AACF,MAAI,CAAC,IAAI,GACP,OAAM,IAAI,MACR,mCAAmC,IAAI,OAAO,GAAG,IAAI,aACtD;AAEH,SAAQ,MAAM,IAAI,MAAM;;CAG1B,MAAM,WACJ,MACiC;AAEjC,UADiB,MAAM,KAAK,aAAa,EACzB,MAAM,QAAQ,MAAM,EAAE,SAAS,KAAK;;CAGtD,MAAM,wBAA4D;AAEhE,UADkB,MAAM,KAAK,WAAW,wBAAwB,EAC/C,KAAK,OAAO;GAC3B,IAAI,EAAE;GACN,OAAO,EAAE;GACT,aAAa,EAAE;GACf,SAAS,EAAE;GACX,MAAM,EAAE,KAAK;GACb,QAAQ;GACT,EAAE;;CAGL,MAAM,QACJ,aACA,MAC+B;EAC/B,MAAM,MAAM,MAAM,KAAK,UACrB,GAAG,KAAK,QAAQ,kBAAkB,YAAY,GAAG,KAAK,QACtD;GACE,QAAQ;GACR,SAAS,EAAE,QAAQ,oBAAoB;GACxC,CACF;AACD,MAAI,CAAC,IAAI,GACP,OAAM,IAAI,MACR,+BAA+B,IAAI,OAAO,GAAG,IAAI,aAClD;AAEH,SAAQ,MAAM,IAAI,MAAM"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { TemplateDefinition, TemplateFilter, TemplateId } from "@contractspec/lib.example-shared-ui";
|
|
2
|
+
|
|
3
|
+
//#region src/runtime/registry.d.ts
|
|
4
|
+
declare const TEMPLATE_REGISTRY: TemplateDefinition[];
|
|
5
|
+
declare function listTemplates(filter?: TemplateFilter): TemplateDefinition[];
|
|
6
|
+
declare function getTemplate(id: TemplateId): TemplateDefinition | undefined;
|
|
7
|
+
/**
|
|
8
|
+
* Get templates that use a specific cross-cutting module
|
|
9
|
+
*/
|
|
10
|
+
declare function getTemplatesByModule(modulePackage: string): TemplateDefinition[];
|
|
11
|
+
/**
|
|
12
|
+
* Get all templates with external packages (clonable via Git)
|
|
13
|
+
*/
|
|
14
|
+
declare function getClonableTemplates(): TemplateDefinition[];
|
|
15
|
+
//#endregion
|
|
16
|
+
export { TEMPLATE_REGISTRY, getClonableTemplates, getTemplate, getTemplatesByModule, listTemplates };
|
|
17
|
+
//# sourceMappingURL=registry.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"registry.d.ts","names":[],"sources":["../../src/runtime/registry.ts"],"sourcesContent":[],"mappings":";;;cA2Ea,mBAAmB;iBA4HhB,aAAA,UAAuB,iBAAiB;AA5H3C,iBAiJG,WAAA,CAjJgB,EAAA,EAiJA,UAjJA,CAAA,EAiJa,kBAjJK,GAAA,SAAA;AA4HlD;AAqBA;AAOA;AAWgB,iBAXA,oBAAA,CAWwB,aAAkB,EAAA,MAAA,CAAA,EATvD,kBASuD,EAAA;;;;iBAA1C,oBAAA,CAAA,GAAwB"}
|
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
import { listExamples } from "../registry.js";
|
|
2
|
+
|
|
3
|
+
//#region src/runtime/registry.ts
|
|
4
|
+
const PRESENTATIONS_BY_TEMPLATE = {
|
|
5
|
+
"saas-boilerplate": [
|
|
6
|
+
"saas-boilerplate.dashboard",
|
|
7
|
+
"saas-boilerplate.project.list",
|
|
8
|
+
"saas-boilerplate.billing.settings"
|
|
9
|
+
],
|
|
10
|
+
"crm-pipeline": ["crm-pipeline.dashboard", "crm-pipeline.deal.pipeline"],
|
|
11
|
+
"agent-console": [
|
|
12
|
+
"agent-console.dashboard",
|
|
13
|
+
"agent-console.agent.list",
|
|
14
|
+
"agent-console.run.list",
|
|
15
|
+
"agent-console.tool.registry"
|
|
16
|
+
],
|
|
17
|
+
"workflow-system": [
|
|
18
|
+
"workflow-system.dashboard",
|
|
19
|
+
"workflow-system.definition.list",
|
|
20
|
+
"workflow-system.instance.detail"
|
|
21
|
+
],
|
|
22
|
+
marketplace: [
|
|
23
|
+
"marketplace.dashboard",
|
|
24
|
+
"marketplace.product.catalog",
|
|
25
|
+
"marketplace.order.list",
|
|
26
|
+
"marketplace.store.manage"
|
|
27
|
+
],
|
|
28
|
+
"integration-hub": [
|
|
29
|
+
"integration-hub.dashboard",
|
|
30
|
+
"integration-hub.connection.list",
|
|
31
|
+
"integration-hub.sync.config"
|
|
32
|
+
],
|
|
33
|
+
"analytics-dashboard": [
|
|
34
|
+
"analytics-dashboard.dashboard",
|
|
35
|
+
"analytics-dashboard.list",
|
|
36
|
+
"analytics-dashboard.query.builder"
|
|
37
|
+
],
|
|
38
|
+
"learning-journey-studio-onboarding": [
|
|
39
|
+
"learning.journey.track_list",
|
|
40
|
+
"learning.journey.track_detail",
|
|
41
|
+
"learning.journey.progress_widget"
|
|
42
|
+
],
|
|
43
|
+
"learning-journey-platform-tour": [
|
|
44
|
+
"learning.journey.track_list",
|
|
45
|
+
"learning.journey.track_detail",
|
|
46
|
+
"learning.journey.progress_widget"
|
|
47
|
+
],
|
|
48
|
+
"learning-journey-crm-onboarding": [
|
|
49
|
+
"learning.journey.track_list",
|
|
50
|
+
"learning.journey.track_detail",
|
|
51
|
+
"learning.journey.progress_widget"
|
|
52
|
+
],
|
|
53
|
+
"learning-journey-duo-drills": [
|
|
54
|
+
"learning.journey.track_list",
|
|
55
|
+
"learning.journey.track_detail",
|
|
56
|
+
"learning.journey.progress_widget"
|
|
57
|
+
],
|
|
58
|
+
"learning-journey-ambient-coach": [
|
|
59
|
+
"learning.journey.track_list",
|
|
60
|
+
"learning.journey.track_detail",
|
|
61
|
+
"learning.journey.progress_widget"
|
|
62
|
+
],
|
|
63
|
+
"learning-journey-quest-challenges": [
|
|
64
|
+
"learning.journey.track_list",
|
|
65
|
+
"learning.journey.track_detail",
|
|
66
|
+
"learning.journey.progress_widget"
|
|
67
|
+
]
|
|
68
|
+
};
|
|
69
|
+
const TEMPLATE_REGISTRY = [
|
|
70
|
+
{
|
|
71
|
+
id: "todos-app",
|
|
72
|
+
name: "To-dos List App",
|
|
73
|
+
description: "CRUD flows, filtering, priorities, and ceremonies for handing off work across crews.",
|
|
74
|
+
category: "productivity",
|
|
75
|
+
complexity: "beginner",
|
|
76
|
+
icon: "✅",
|
|
77
|
+
features: [
|
|
78
|
+
"CRUD tasks",
|
|
79
|
+
"Filters",
|
|
80
|
+
"Categories",
|
|
81
|
+
"Search",
|
|
82
|
+
"Priorities"
|
|
83
|
+
],
|
|
84
|
+
tags: [
|
|
85
|
+
"tasks",
|
|
86
|
+
"ops",
|
|
87
|
+
"starter"
|
|
88
|
+
],
|
|
89
|
+
schema: {
|
|
90
|
+
models: ["Task", "TaskCategory"],
|
|
91
|
+
contracts: ["template.todos.crud"]
|
|
92
|
+
},
|
|
93
|
+
components: {
|
|
94
|
+
list: "TaskList",
|
|
95
|
+
detail: "TaskDetailPanel",
|
|
96
|
+
form: "TaskForm"
|
|
97
|
+
},
|
|
98
|
+
preview: { demoUrl: "/sandbox?template=todos-app" },
|
|
99
|
+
docs: { quickstart: "/docs/templates/todos-app" }
|
|
100
|
+
},
|
|
101
|
+
{
|
|
102
|
+
id: "messaging-app",
|
|
103
|
+
name: "Messaging Application",
|
|
104
|
+
description: "Conversation roster, optimistic sending, typing indicators, and delivery telemetry.",
|
|
105
|
+
category: "communication",
|
|
106
|
+
complexity: "intermediate",
|
|
107
|
+
icon: "💬",
|
|
108
|
+
features: [
|
|
109
|
+
"Conversations",
|
|
110
|
+
"Real-time feed",
|
|
111
|
+
"Typing indicators",
|
|
112
|
+
"Read receipts"
|
|
113
|
+
],
|
|
114
|
+
tags: [
|
|
115
|
+
"messaging",
|
|
116
|
+
"realtime",
|
|
117
|
+
"ws"
|
|
118
|
+
],
|
|
119
|
+
schema: {
|
|
120
|
+
models: [
|
|
121
|
+
"Conversation",
|
|
122
|
+
"ConversationParticipant",
|
|
123
|
+
"Message"
|
|
124
|
+
],
|
|
125
|
+
contracts: ["template.messaging.core"]
|
|
126
|
+
},
|
|
127
|
+
components: {
|
|
128
|
+
list: "ConversationList",
|
|
129
|
+
detail: "MessageThread",
|
|
130
|
+
form: "MessageComposer"
|
|
131
|
+
},
|
|
132
|
+
preview: { demoUrl: "/sandbox?template=messaging-app" },
|
|
133
|
+
docs: { quickstart: "/docs/templates/messaging-app" }
|
|
134
|
+
},
|
|
135
|
+
{
|
|
136
|
+
id: "recipe-app-i18n",
|
|
137
|
+
name: "Recipe App (i18n)",
|
|
138
|
+
description: "Localized browsing experience with bilingual content, categories, and favorites.",
|
|
139
|
+
category: "content",
|
|
140
|
+
complexity: "intermediate",
|
|
141
|
+
icon: "🍲",
|
|
142
|
+
features: [
|
|
143
|
+
"i18n",
|
|
144
|
+
"Favorites",
|
|
145
|
+
"Categories",
|
|
146
|
+
"Search",
|
|
147
|
+
"RTL-ready"
|
|
148
|
+
],
|
|
149
|
+
tags: [
|
|
150
|
+
"content",
|
|
151
|
+
"i18n",
|
|
152
|
+
"localization"
|
|
153
|
+
],
|
|
154
|
+
schema: {
|
|
155
|
+
models: [
|
|
156
|
+
"RecipeCategory",
|
|
157
|
+
"Recipe",
|
|
158
|
+
"RecipeIngredient",
|
|
159
|
+
"RecipeInstruction"
|
|
160
|
+
],
|
|
161
|
+
contracts: ["template.recipes.browse"]
|
|
162
|
+
},
|
|
163
|
+
components: {
|
|
164
|
+
list: "RecipeList",
|
|
165
|
+
detail: "RecipeDetail",
|
|
166
|
+
form: "RecipeForm"
|
|
167
|
+
},
|
|
168
|
+
preview: { demoUrl: "/sandbox?template=recipe-app-i18n" },
|
|
169
|
+
docs: { quickstart: "/docs/templates/recipe-app-i18n" }
|
|
170
|
+
},
|
|
171
|
+
...listExamples().map((example) => {
|
|
172
|
+
const tags = example.meta.keywords ?? [];
|
|
173
|
+
const category = tags.some((t) => t.toLowerCase() === "ai") || tags.some((t) => t.toLowerCase() === "assistant") ? "ai" : "business";
|
|
174
|
+
return {
|
|
175
|
+
id: example.meta.key,
|
|
176
|
+
name: example.meta.title ?? example.meta.key,
|
|
177
|
+
description: example.meta.description ?? "",
|
|
178
|
+
category,
|
|
179
|
+
complexity: "beginner",
|
|
180
|
+
icon: "📦",
|
|
181
|
+
features: [],
|
|
182
|
+
tags: [...tags],
|
|
183
|
+
schema: {
|
|
184
|
+
models: [],
|
|
185
|
+
contracts: []
|
|
186
|
+
},
|
|
187
|
+
components: {
|
|
188
|
+
list: "ExampleList",
|
|
189
|
+
detail: "ExampleDetail"
|
|
190
|
+
},
|
|
191
|
+
preview: { demoUrl: `/sandbox?template=${encodeURIComponent(example.meta.key)}` },
|
|
192
|
+
package: example.entrypoints.packageName,
|
|
193
|
+
presentations: PRESENTATIONS_BY_TEMPLATE[example.meta.key] ?? [],
|
|
194
|
+
renderTargets: ["react", "markdown"]
|
|
195
|
+
};
|
|
196
|
+
})
|
|
197
|
+
];
|
|
198
|
+
function listTemplates(filter) {
|
|
199
|
+
if (!filter) return TEMPLATE_REGISTRY;
|
|
200
|
+
return TEMPLATE_REGISTRY.filter((template) => {
|
|
201
|
+
if (filter.category && template.category !== filter.category) return false;
|
|
202
|
+
if (filter.complexity && template.complexity !== filter.complexity) return false;
|
|
203
|
+
if (filter.tag && !template.tags.some((tag) => tag.toLowerCase() === filter.tag?.toLowerCase())) return false;
|
|
204
|
+
return true;
|
|
205
|
+
});
|
|
206
|
+
}
|
|
207
|
+
function getTemplate(id) {
|
|
208
|
+
return TEMPLATE_REGISTRY.find((template) => template.id === id);
|
|
209
|
+
}
|
|
210
|
+
/**
|
|
211
|
+
* Get templates that use a specific cross-cutting module
|
|
212
|
+
*/
|
|
213
|
+
function getTemplatesByModule(modulePackage) {
|
|
214
|
+
return TEMPLATE_REGISTRY.filter((template) => template.usesModules?.includes(modulePackage));
|
|
215
|
+
}
|
|
216
|
+
/**
|
|
217
|
+
* Get all templates with external packages (clonable via Git)
|
|
218
|
+
*/
|
|
219
|
+
function getClonableTemplates() {
|
|
220
|
+
return TEMPLATE_REGISTRY.filter((template) => !!template.package);
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
//#endregion
|
|
224
|
+
export { TEMPLATE_REGISTRY, getClonableTemplates, getTemplate, getTemplatesByModule, listTemplates };
|
|
225
|
+
//# sourceMappingURL=registry.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"registry.js","names":[],"sources":["../../src/runtime/registry.ts"],"sourcesContent":["import { listExamples } from '../registry';\nimport type {\n TemplateCategory,\n TemplateComplexity,\n TemplateDefinition,\n TemplateId,\n TemplateFilter,\n} from '@contractspec/lib.example-shared-ui';\n\nconst PRESENTATIONS_BY_TEMPLATE: Record<string, string[]> = {\n 'saas-boilerplate': [\n 'saas-boilerplate.dashboard',\n 'saas-boilerplate.project.list',\n 'saas-boilerplate.billing.settings',\n ],\n 'crm-pipeline': ['crm-pipeline.dashboard', 'crm-pipeline.deal.pipeline'],\n 'agent-console': [\n 'agent-console.dashboard',\n 'agent-console.agent.list',\n 'agent-console.run.list',\n 'agent-console.tool.registry',\n ],\n 'workflow-system': [\n 'workflow-system.dashboard',\n 'workflow-system.definition.list',\n 'workflow-system.instance.detail',\n ],\n marketplace: [\n 'marketplace.dashboard',\n 'marketplace.product.catalog',\n 'marketplace.order.list',\n 'marketplace.store.manage',\n ],\n 'integration-hub': [\n 'integration-hub.dashboard',\n 'integration-hub.connection.list',\n 'integration-hub.sync.config',\n ],\n 'analytics-dashboard': [\n 'analytics-dashboard.dashboard',\n 'analytics-dashboard.list',\n 'analytics-dashboard.query.builder',\n ],\n 'learning-journey-studio-onboarding': [\n 'learning.journey.track_list',\n 'learning.journey.track_detail',\n 'learning.journey.progress_widget',\n ],\n 'learning-journey-platform-tour': [\n 'learning.journey.track_list',\n 'learning.journey.track_detail',\n 'learning.journey.progress_widget',\n ],\n 'learning-journey-crm-onboarding': [\n 'learning.journey.track_list',\n 'learning.journey.track_detail',\n 'learning.journey.progress_widget',\n ],\n 'learning-journey-duo-drills': [\n 'learning.journey.track_list',\n 'learning.journey.track_detail',\n 'learning.journey.progress_widget',\n ],\n 'learning-journey-ambient-coach': [\n 'learning.journey.track_list',\n 'learning.journey.track_detail',\n 'learning.journey.progress_widget',\n ],\n 'learning-journey-quest-challenges': [\n 'learning.journey.track_list',\n 'learning.journey.track_detail',\n 'learning.journey.progress_widget',\n ],\n};\n\nexport const TEMPLATE_REGISTRY: TemplateDefinition[] = [\n {\n id: 'todos-app',\n name: 'To-dos List App',\n description:\n 'CRUD flows, filtering, priorities, and ceremonies for handing off work across crews.',\n category: 'productivity',\n complexity: 'beginner',\n icon: '✅',\n features: ['CRUD tasks', 'Filters', 'Categories', 'Search', 'Priorities'],\n tags: ['tasks', 'ops', 'starter'],\n schema: {\n models: ['Task', 'TaskCategory'],\n contracts: ['template.todos.crud'],\n },\n components: {\n list: 'TaskList',\n detail: 'TaskDetailPanel',\n form: 'TaskForm',\n },\n preview: {\n demoUrl: '/sandbox?template=todos-app',\n },\n docs: {\n quickstart: '/docs/templates/todos-app',\n },\n },\n {\n id: 'messaging-app',\n name: 'Messaging Application',\n description:\n 'Conversation roster, optimistic sending, typing indicators, and delivery telemetry.',\n category: 'communication',\n complexity: 'intermediate',\n icon: '💬',\n features: [\n 'Conversations',\n 'Real-time feed',\n 'Typing indicators',\n 'Read receipts',\n ],\n tags: ['messaging', 'realtime', 'ws'],\n schema: {\n models: ['Conversation', 'ConversationParticipant', 'Message'],\n contracts: ['template.messaging.core'],\n },\n components: {\n list: 'ConversationList',\n detail: 'MessageThread',\n form: 'MessageComposer',\n },\n preview: {\n demoUrl: '/sandbox?template=messaging-app',\n },\n docs: {\n quickstart: '/docs/templates/messaging-app',\n },\n },\n {\n id: 'recipe-app-i18n',\n name: 'Recipe App (i18n)',\n description:\n 'Localized browsing experience with bilingual content, categories, and favorites.',\n category: 'content',\n complexity: 'intermediate',\n icon: '🍲',\n features: ['i18n', 'Favorites', 'Categories', 'Search', 'RTL-ready'],\n tags: ['content', 'i18n', 'localization'],\n schema: {\n models: [\n 'RecipeCategory',\n 'Recipe',\n 'RecipeIngredient',\n 'RecipeInstruction',\n ],\n contracts: ['template.recipes.browse'],\n },\n components: {\n list: 'RecipeList',\n detail: 'RecipeDetail',\n form: 'RecipeForm',\n },\n preview: {\n demoUrl: '/sandbox?template=recipe-app-i18n',\n },\n docs: {\n quickstart: '/docs/templates/recipe-app-i18n',\n },\n },\n ...listExamples().map((example) => {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any -- meta.keywords fallback if tags missing\n const tags = (example.meta as any).keywords ?? [];\n\n const category: TemplateCategory =\n tags.some((t: string) => t.toLowerCase() === 'ai') ||\n tags.some((t: string) => t.toLowerCase() === 'assistant')\n ? 'ai'\n : 'business';\n\n const complexity: TemplateComplexity = 'beginner';\n\n const icon = '📦';\n\n return {\n id: example.meta.key,\n name: example.meta.title ?? example.meta.key,\n description: example.meta.description ?? '',\n category,\n complexity,\n icon,\n features: [],\n tags: [...tags],\n schema: { models: [], contracts: [] },\n components: { list: 'ExampleList', detail: 'ExampleDetail' },\n preview: {\n demoUrl: `/sandbox?template=${encodeURIComponent(example.meta.key)}`,\n },\n package: example.entrypoints.packageName,\n presentations: PRESENTATIONS_BY_TEMPLATE[example.meta.key] ?? [],\n renderTargets: ['react', 'markdown'],\n } satisfies TemplateDefinition;\n }),\n];\n\nexport function listTemplates(filter?: TemplateFilter): TemplateDefinition[] {\n if (!filter) return TEMPLATE_REGISTRY;\n return TEMPLATE_REGISTRY.filter((template) => {\n if (filter.category && template.category !== filter.category) {\n return false;\n }\n if (filter.complexity && template.complexity !== filter.complexity) {\n return false;\n }\n if (\n filter.tag &&\n !template.tags.some(\n (tag: string) => tag.toLowerCase() === filter.tag?.toLowerCase()\n )\n ) {\n return false;\n }\n return true;\n });\n}\n\nexport function getTemplate(id: TemplateId): TemplateDefinition | undefined {\n return TEMPLATE_REGISTRY.find((template) => template.id === id);\n}\n\n/**\n * Get templates that use a specific cross-cutting module\n */\nexport function getTemplatesByModule(\n modulePackage: string\n): TemplateDefinition[] {\n return TEMPLATE_REGISTRY.filter((template) =>\n template.usesModules?.includes(modulePackage)\n );\n}\n\n/**\n * Get all templates with external packages (clonable via Git)\n */\nexport function getClonableTemplates(): TemplateDefinition[] {\n return TEMPLATE_REGISTRY.filter((template) => !!template.package);\n}\n"],"mappings":";;;AASA,MAAM,4BAAsD;CAC1D,oBAAoB;EAClB;EACA;EACA;EACD;CACD,gBAAgB,CAAC,0BAA0B,6BAA6B;CACxE,iBAAiB;EACf;EACA;EACA;EACA;EACD;CACD,mBAAmB;EACjB;EACA;EACA;EACD;CACD,aAAa;EACX;EACA;EACA;EACA;EACD;CACD,mBAAmB;EACjB;EACA;EACA;EACD;CACD,uBAAuB;EACrB;EACA;EACA;EACD;CACD,sCAAsC;EACpC;EACA;EACA;EACD;CACD,kCAAkC;EAChC;EACA;EACA;EACD;CACD,mCAAmC;EACjC;EACA;EACA;EACD;CACD,+BAA+B;EAC7B;EACA;EACA;EACD;CACD,kCAAkC;EAChC;EACA;EACA;EACD;CACD,qCAAqC;EACnC;EACA;EACA;EACD;CACF;AAED,MAAa,oBAA0C;CACrD;EACE,IAAI;EACJ,MAAM;EACN,aACE;EACF,UAAU;EACV,YAAY;EACZ,MAAM;EACN,UAAU;GAAC;GAAc;GAAW;GAAc;GAAU;GAAa;EACzE,MAAM;GAAC;GAAS;GAAO;GAAU;EACjC,QAAQ;GACN,QAAQ,CAAC,QAAQ,eAAe;GAChC,WAAW,CAAC,sBAAsB;GACnC;EACD,YAAY;GACV,MAAM;GACN,QAAQ;GACR,MAAM;GACP;EACD,SAAS,EACP,SAAS,+BACV;EACD,MAAM,EACJ,YAAY,6BACb;EACF;CACD;EACE,IAAI;EACJ,MAAM;EACN,aACE;EACF,UAAU;EACV,YAAY;EACZ,MAAM;EACN,UAAU;GACR;GACA;GACA;GACA;GACD;EACD,MAAM;GAAC;GAAa;GAAY;GAAK;EACrC,QAAQ;GACN,QAAQ;IAAC;IAAgB;IAA2B;IAAU;GAC9D,WAAW,CAAC,0BAA0B;GACvC;EACD,YAAY;GACV,MAAM;GACN,QAAQ;GACR,MAAM;GACP;EACD,SAAS,EACP,SAAS,mCACV;EACD,MAAM,EACJ,YAAY,iCACb;EACF;CACD;EACE,IAAI;EACJ,MAAM;EACN,aACE;EACF,UAAU;EACV,YAAY;EACZ,MAAM;EACN,UAAU;GAAC;GAAQ;GAAa;GAAc;GAAU;GAAY;EACpE,MAAM;GAAC;GAAW;GAAQ;GAAe;EACzC,QAAQ;GACN,QAAQ;IACN;IACA;IACA;IACA;IACD;GACD,WAAW,CAAC,0BAA0B;GACvC;EACD,YAAY;GACV,MAAM;GACN,QAAQ;GACR,MAAM;GACP;EACD,SAAS,EACP,SAAS,qCACV;EACD,MAAM,EACJ,YAAY,mCACb;EACF;CACD,GAAG,cAAc,CAAC,KAAK,YAAY;EAEjC,MAAM,OAAQ,QAAQ,KAAa,YAAY,EAAE;EAEjD,MAAM,WACJ,KAAK,MAAM,MAAc,EAAE,aAAa,KAAK,KAAK,IAClD,KAAK,MAAM,MAAc,EAAE,aAAa,KAAK,YAAY,GACrD,OACA;AAMN,SAAO;GACL,IAAI,QAAQ,KAAK;GACjB,MAAM,QAAQ,KAAK,SAAS,QAAQ,KAAK;GACzC,aAAa,QAAQ,KAAK,eAAe;GACzC;GACA,YATqC;GAUrC,MARW;GASX,UAAU,EAAE;GACZ,MAAM,CAAC,GAAG,KAAK;GACf,QAAQ;IAAE,QAAQ,EAAE;IAAE,WAAW,EAAE;IAAE;GACrC,YAAY;IAAE,MAAM;IAAe,QAAQ;IAAiB;GAC5D,SAAS,EACP,SAAS,qBAAqB,mBAAmB,QAAQ,KAAK,IAAI,IACnE;GACD,SAAS,QAAQ,YAAY;GAC7B,eAAe,0BAA0B,QAAQ,KAAK,QAAQ,EAAE;GAChE,eAAe,CAAC,SAAS,WAAW;GACrC;GACD;CACH;AAED,SAAgB,cAAc,QAA+C;AAC3E,KAAI,CAAC,OAAQ,QAAO;AACpB,QAAO,kBAAkB,QAAQ,aAAa;AAC5C,MAAI,OAAO,YAAY,SAAS,aAAa,OAAO,SAClD,QAAO;AAET,MAAI,OAAO,cAAc,SAAS,eAAe,OAAO,WACtD,QAAO;AAET,MACE,OAAO,OACP,CAAC,SAAS,KAAK,MACZ,QAAgB,IAAI,aAAa,KAAK,OAAO,KAAK,aAAa,CACjE,CAED,QAAO;AAET,SAAO;GACP;;AAGJ,SAAgB,YAAY,IAAgD;AAC1E,QAAO,kBAAkB,MAAM,aAAa,SAAS,OAAO,GAAG;;;;;AAMjE,SAAgB,qBACd,eACsB;AACtB,QAAO,kBAAkB,QAAQ,aAC/B,SAAS,aAAa,SAAS,cAAc,CAC9C;;;;;AAMH,SAAgB,uBAA6C;AAC3D,QAAO,kBAAkB,QAAQ,aAAa,CAAC,CAAC,SAAS,QAAQ"}
|