@lssm/module.contractspec-workspace 0.0.0-canary-20251217083314 → 1.41.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (52) hide show
  1. package/dist/ai/code-generation.js +13 -50
  2. package/dist/ai/spec-creation.js +18 -50
  3. package/dist/analysis/deps/graph.js +2 -84
  4. package/dist/analysis/deps/parse-imports.js +1 -30
  5. package/dist/analysis/diff/semantic.js +1 -96
  6. package/dist/analysis/feature-scan.js +1 -151
  7. package/dist/analysis/spec-scan.js +1 -344
  8. package/dist/analysis/validate/spec-structure.js +1 -122
  9. package/dist/index.js +1 -25
  10. package/dist/templates/app-config.js +28 -100
  11. package/dist/templates/data-view.js +27 -41
  12. package/dist/templates/event.js +14 -28
  13. package/dist/templates/experiment.js +51 -76
  14. package/dist/templates/handler.js +17 -49
  15. package/dist/templates/integration-utils.js +26 -97
  16. package/dist/templates/integration.js +23 -46
  17. package/dist/templates/knowledge.js +19 -59
  18. package/dist/templates/migration.js +26 -49
  19. package/dist/templates/operation.js +28 -40
  20. package/dist/templates/presentation.js +20 -45
  21. package/dist/templates/telemetry.js +53 -73
  22. package/dist/templates/utils.js +1 -38
  23. package/dist/templates/workflow-runner.js +6 -12
  24. package/dist/templates/workflow.js +24 -50
  25. package/dist/types/generation-types.js +1 -20
  26. package/package.json +6 -6
  27. package/dist/ai/code-generation.d.ts +0 -27
  28. package/dist/ai/spec-creation.d.ts +0 -26
  29. package/dist/analysis/deps/graph.d.ts +0 -33
  30. package/dist/analysis/deps/parse-imports.d.ts +0 -16
  31. package/dist/analysis/diff/semantic.d.ts +0 -10
  32. package/dist/analysis/feature-scan.d.ts +0 -14
  33. package/dist/analysis/spec-scan.d.ts +0 -33
  34. package/dist/analysis/validate/spec-structure.d.ts +0 -10
  35. package/dist/index.d.ts +0 -26
  36. package/dist/templates/app-config.d.ts +0 -6
  37. package/dist/templates/data-view.d.ts +0 -6
  38. package/dist/templates/event.d.ts +0 -10
  39. package/dist/templates/experiment.d.ts +0 -6
  40. package/dist/templates/handler.d.ts +0 -19
  41. package/dist/templates/integration.d.ts +0 -6
  42. package/dist/templates/knowledge.d.ts +0 -6
  43. package/dist/templates/migration.d.ts +0 -6
  44. package/dist/templates/operation.d.ts +0 -10
  45. package/dist/templates/presentation.d.ts +0 -10
  46. package/dist/templates/telemetry.d.ts +0 -6
  47. package/dist/templates/utils.d.ts +0 -26
  48. package/dist/templates/workflow-runner.d.ts +0 -15
  49. package/dist/templates/workflow.d.ts +0 -10
  50. package/dist/types/analysis-types.d.ts +0 -125
  51. package/dist/types/generation-types.d.ts +0 -83
  52. package/dist/types/spec-types.d.ts +0 -344
@@ -1,105 +1,33 @@
1
- import { toPascalCase } from "./utils.js";
1
+ import{toPascalCase as e}from"./utils.js";function t(t){let d=e(t.name.split(`.`).pop()??`App`)+`AppConfig`,f=n(t),m=r(t),h=i(`dataViews`,t.dataViews),g=i(`workflows`,t.workflows),_=a(t),v=o(t),y=s(t),b=c(t),x=l(t),S=u(t),C=t.notes?` notes: '${p(t.notes)}',\n`:``;return`import type { AppBlueprintSpec } from '@lssm/lib.contracts/app-config';
2
2
 
3
- //#region src/templates/app-config.ts
4
- function generateAppBlueprintSpec(data) {
5
- const exportName = toPascalCase(data.name.split(".").pop() ?? "App") + "AppConfig";
6
- const capabilitiesSection = buildCapabilitiesSection(data);
7
- const featuresSection = buildFeaturesSection(data);
8
- const dataViewsSection = buildMappingSection("dataViews", data.dataViews);
9
- const workflowsSection = buildMappingSection("workflows", data.workflows);
10
- const policiesSection = buildPolicySection(data);
11
- const themeSection = buildThemeSection(data);
12
- const telemetrySection = buildTelemetrySection(data);
13
- const experimentsSection = buildExperimentsSection(data);
14
- const flagsSection = buildFeatureFlagsSection(data);
15
- const routesSection = buildRoutesSection(data);
16
- const notesSection = data.notes ? ` notes: '${escapeString(data.notes)}',\n` : "";
17
- return `import type { AppBlueprintSpec } from '@lssm/lib.contracts/app-config';
18
-
19
- export const ${exportName}: AppBlueprintSpec = {
3
+ export const ${d}: AppBlueprintSpec = {
20
4
  meta: {
21
- name: '${escapeString(data.name)}',
22
- version: ${data.version},
23
- title: '${escapeString(data.title)}',
24
- description: '${escapeString(data.description)}',
25
- domain: '${escapeString(data.domain)}',
26
- owners: [${data.owners.map((owner) => `'${escapeString(owner)}'`).join(", ")}],
27
- tags: [${data.tags.map((tag) => `'${escapeString(tag)}'`).join(", ")}],
28
- stability: '${data.stability}',
29
- appId: '${escapeString(data.appId)}',
5
+ name: '${p(t.name)}',
6
+ version: ${t.version},
7
+ title: '${p(t.title)}',
8
+ description: '${p(t.description)}',
9
+ domain: '${p(t.domain)}',
10
+ owners: [${t.owners.map(e=>`'${p(e)}'`).join(`, `)}],
11
+ tags: [${t.tags.map(e=>`'${p(e)}'`).join(`, `)}],
12
+ stability: '${t.stability}',
13
+ appId: '${p(t.appId)}',
30
14
  },
31
- ${capabilitiesSection}${featuresSection}${dataViewsSection}${workflowsSection}${policiesSection}${themeSection}${telemetrySection}${experimentsSection}${flagsSection}${routesSection}${notesSection}};\n`;
32
- }
33
- function buildCapabilitiesSection(data) {
34
- if (data.capabilitiesEnabled.length === 0 && data.capabilitiesDisabled.length === 0) return "";
35
- return ` capabilities: {\n${data.capabilitiesEnabled.length > 0 ? ` enabled: [${data.capabilitiesEnabled.map((key) => formatCapabilityRef(key)).join(", ")}],\n` : ""}${data.capabilitiesDisabled.length > 0 ? ` disabled: [${data.capabilitiesDisabled.map((key) => formatCapabilityRef(key)).join(", ")}],\n` : ""} },\n`;
36
- }
37
- function buildFeaturesSection(data) {
38
- if (data.featureIncludes.length === 0 && data.featureExcludes.length === 0) return "";
39
- return ` features: {\n${data.featureIncludes.length > 0 ? ` include: [${data.featureIncludes.map((key) => `{ key: '${escapeString(key)}' }`).join(", ")}],\n` : ""}${data.featureExcludes.length > 0 ? ` exclude: [${data.featureExcludes.map((key) => `{ key: '${escapeString(key)}' }`).join(", ")}],\n` : ""} },\n`;
40
- }
41
- function buildMappingSection(prop, mappings) {
42
- if (mappings.length === 0) return "";
43
- return ` ${prop}: {\n${mappings.map((mapping) => ` ${mapping.slot}: {
44
- name: '${escapeString(mapping.name)}',
45
- ${typeof mapping.version === "number" ? `version: ${mapping.version},` : ""}
46
- }`).join(",\n")}\n },\n`;
47
- }
48
- function buildPolicySection(data) {
49
- if (data.policyRefs.length === 0) return "";
50
- return ` policies: [\n${data.policyRefs.map((policy) => ` {
51
- name: '${escapeString(policy.name)}'${typeof policy.version === "number" ? `,\n version: ${policy.version}` : ""}
52
- }`).join(",\n")}\n ],\n`;
53
- }
54
- function buildThemeSection(data) {
55
- if (!data.theme) return "";
56
- return ` theme: {\n${` primary: { name: '${escapeString(data.theme.name)}', version: ${data.theme.version} },\n`}${data.themeFallbacks.length > 0 ? ` fallbacks: [${data.themeFallbacks.map((theme) => `{ name: '${escapeString(theme.name)}', version: ${theme.version} }`).join(", ")}],\n` : ""} },\n`;
57
- }
58
- function buildTelemetrySection(data) {
59
- if (!data.telemetry) return "";
60
- return ` telemetry: {
15
+ ${f}${m}${h}${g}${_}${v}${y}${b}${x}${S}${C}};\n`}function n(e){return e.capabilitiesEnabled.length===0&&e.capabilitiesDisabled.length===0?``:` capabilities: {\n${e.capabilitiesEnabled.length>0?` enabled: [${e.capabilitiesEnabled.map(e=>d(e)).join(`, `)}],\n`:``}${e.capabilitiesDisabled.length>0?` disabled: [${e.capabilitiesDisabled.map(e=>d(e)).join(`, `)}],\n`:``} },\n`}function r(e){return e.featureIncludes.length===0&&e.featureExcludes.length===0?``:` features: {\n${e.featureIncludes.length>0?` include: [${e.featureIncludes.map(e=>`{ key: '${p(e)}' }`).join(`, `)}],\n`:``}${e.featureExcludes.length>0?` exclude: [${e.featureExcludes.map(e=>`{ key: '${p(e)}' }`).join(`, `)}],\n`:``} },\n`}function i(e,t){return t.length===0?``:` ${e}: {\n${t.map(e=>` ${e.slot}: {
16
+ name: '${p(e.name)}',
17
+ ${typeof e.version==`number`?`version: ${e.version},`:``}
18
+ }`).join(`,
19
+ `)}\n },\n`}function a(e){return e.policyRefs.length===0?``:` policies: [\n${e.policyRefs.map(e=>` {
20
+ name: '${p(e.name)}'${typeof e.version==`number`?`,\n version: ${e.version}`:``}
21
+ }`).join(`,
22
+ `)}\n ],\n`}function o(e){return e.theme?` theme: {\n${` primary: { name: '${p(e.theme.name)}', version: ${e.theme.version} },\n`}${e.themeFallbacks.length>0?` fallbacks: [${e.themeFallbacks.map(e=>`{ name: '${p(e.name)}', version: ${e.version} }`).join(`, `)}],\n`:``} },\n`:``}function s(e){return e.telemetry?` telemetry: {
61
23
  spec: {
62
- name: '${escapeString(data.telemetry.name)}'${typeof data.telemetry.version === "number" ? `,\n version: ${data.telemetry.version}` : ""}
24
+ name: '${p(e.telemetry.name)}'${typeof e.telemetry.version==`number`?`,\n version: ${e.telemetry.version}`:``}
63
25
  },
64
- },\n`;
65
- }
66
- function buildExperimentsSection(data) {
67
- if (data.activeExperiments.length === 0 && data.pausedExperiments.length === 0) return "";
68
- return ` experiments: {\n${data.activeExperiments.length > 0 ? ` active: [${data.activeExperiments.map((exp) => formatExperimentRef(exp)).join(", ")}],\n` : ""}${data.pausedExperiments.length > 0 ? ` paused: [${data.pausedExperiments.map((exp) => formatExperimentRef(exp)).join(", ")}],\n` : ""} },\n`;
69
- }
70
- function buildFeatureFlagsSection(data) {
71
- if (data.featureFlags.length === 0) return "";
72
- return ` featureFlags: [\n${data.featureFlags.map((flag) => ` {
73
- key: '${escapeString(flag.key)}',
74
- enabled: ${flag.enabled},
75
- ${flag.variant ? `variant: '${escapeString(flag.variant)}',` : ""}
76
- ${flag.description ? `description: '${escapeString(flag.description)}',` : ""}
77
- }`).join(",\n")}\n ],\n`;
78
- }
79
- function buildRoutesSection(data) {
80
- if (data.routes.length === 0) return "";
81
- return ` routes: [\n${data.routes.map((route) => {
82
- return ` { ${[
83
- `path: '${escapeString(route.path)}'`,
84
- route.label ? `label: '${escapeString(route.label)}'` : null,
85
- route.dataView ? `dataView: '${escapeString(route.dataView)}'` : null,
86
- route.workflow ? `workflow: '${escapeString(route.workflow)}'` : null,
87
- route.guardName ? `guard: { name: '${escapeString(route.guardName)}'${typeof route.guardVersion === "number" ? `, version: ${route.guardVersion}` : ""} }` : null,
88
- route.featureFlag ? `featureFlag: '${escapeString(route.featureFlag)}'` : null,
89
- route.experimentName ? `experiment: { name: '${escapeString(route.experimentName)}'${typeof route.experimentVersion === "number" ? `, version: ${route.experimentVersion}` : ""} }` : null
90
- ].filter(Boolean).join(", ")} }`;
91
- }).join(",\n")}\n ],\n`;
92
- }
93
- function formatCapabilityRef(key) {
94
- return `{ key: '${escapeString(key)}' }`;
95
- }
96
- function formatExperimentRef(exp) {
97
- const version = typeof exp.version === "number" ? `, version: ${exp.version}` : "";
98
- return `{ name: '${escapeString(exp.name)}'${version} }`;
99
- }
100
- function escapeString(value) {
101
- return value.replace(/\\/g, "\\\\").replace(/'/g, "\\'");
102
- }
103
-
104
- //#endregion
105
- export { generateAppBlueprintSpec };
26
+ },\n`:``}function c(e){return e.activeExperiments.length===0&&e.pausedExperiments.length===0?``:` experiments: {\n${e.activeExperiments.length>0?` active: [${e.activeExperiments.map(e=>f(e)).join(`, `)}],\n`:``}${e.pausedExperiments.length>0?` paused: [${e.pausedExperiments.map(e=>f(e)).join(`, `)}],\n`:``} },\n`}function l(e){return e.featureFlags.length===0?``:` featureFlags: [\n${e.featureFlags.map(e=>` {
27
+ key: '${p(e.key)}',
28
+ enabled: ${e.enabled},
29
+ ${e.variant?`variant: '${p(e.variant)}',`:``}
30
+ ${e.description?`description: '${p(e.description)}',`:``}
31
+ }`).join(`,
32
+ `)}\n ],\n`}function u(e){return e.routes.length===0?``:` routes: [\n${e.routes.map(e=>` { ${[`path: '${p(e.path)}'`,e.label?`label: '${p(e.label)}'`:null,e.dataView?`dataView: '${p(e.dataView)}'`:null,e.workflow?`workflow: '${p(e.workflow)}'`:null,e.guardName?`guard: { name: '${p(e.guardName)}'${typeof e.guardVersion==`number`?`, version: ${e.guardVersion}`:``} }`:null,e.featureFlag?`featureFlag: '${p(e.featureFlag)}'`:null,e.experimentName?`experiment: { name: '${p(e.experimentName)}'${typeof e.experimentVersion==`number`?`, version: ${e.experimentVersion}`:``} }`:null].filter(Boolean).join(`, `)} }`).join(`,
33
+ `)}\n ],\n`}function d(e){return`{ key: '${p(e)}' }`}function f(e){let t=typeof e.version==`number`?`, version: ${e.version}`:``;return`{ name: '${p(e.name)}'${t} }`}function p(e){return e.replace(/\\/g,`\\\\`).replace(/'/g,`\\'`)}export{t as generateAppBlueprintSpec};
@@ -1,49 +1,42 @@
1
- import { escapeString, toPascalCase } from "./utils.js";
1
+ import{escapeString as e,toPascalCase as t}from"./utils.js";function n(n){let i=t(n.name.split(`.`).pop()??`DataView`)+`DataView`,a=n.fields.map(t=>` {
2
+ key: '${e(t.key)}',
3
+ label: '${r(t.label)}',
4
+ dataPath: '${e(t.dataPath)}',
5
+ ${t.format?`format: '${e(t.format)}',`:``}
6
+ ${t.sortable?`sortable: true,`:``}
7
+ ${t.filterable?`filterable: true,`:``}
8
+ }`).join(`,
9
+ `),o=n.secondaryFields?.length?`secondaryFields: [${n.secondaryFields.map(t=>`'${e(t)}'`).join(`, `)}],`:``,s=n.itemOperation?`item: { name: '${e(n.itemOperation.name)}', version: ${n.itemOperation.version} },`:``;return`import type { DataViewSpec } from '@lssm/lib.contracts/data-views';
2
10
 
3
- //#region src/templates/data-view.ts
4
- function generateDataViewSpec(data) {
5
- const viewVarName = toPascalCase(data.name.split(".").pop() ?? "DataView") + "DataView";
6
- const fields = data.fields.map((field) => ` {
7
- key: '${escapeString(field.key)}',
8
- label: '${escape(field.label)}',
9
- dataPath: '${escapeString(field.dataPath)}',
10
- ${field.format ? `format: '${escapeString(field.format)}',` : ""}
11
- ${field.sortable ? "sortable: true," : ""}
12
- ${field.filterable ? "filterable: true," : ""}
13
- }`).join(",\n");
14
- const secondaryFields = data.secondaryFields?.length ? `secondaryFields: [${data.secondaryFields.map((key) => `'${escapeString(key)}'`).join(", ")}],` : "";
15
- const itemOperation = data.itemOperation ? `item: { name: '${escapeString(data.itemOperation.name)}', version: ${data.itemOperation.version} },` : "";
16
- return `import type { DataViewSpec } from '@lssm/lib.contracts/data-views';
17
-
18
- export const ${viewVarName}: DataViewSpec = {
11
+ export const ${i}: DataViewSpec = {
19
12
  meta: {
20
- name: '${escapeString(data.name)}',
21
- version: ${data.version},
22
- entity: '${escapeString(data.entity)}',
23
- title: '${escape(data.title)}',
24
- description: '${escape(data.description || "Describe the purpose of this data view.")}',
25
- domain: '${escape(data.domain || data.entity)}',
26
- owners: [${data.owners.map((owner) => `'${escapeString(owner)}'`).join(", ")}],
27
- tags: [${data.tags.map((tag) => `'${escapeString(tag)}'`).join(", ")}],
28
- stability: '${data.stability}',
13
+ name: '${e(n.name)}',
14
+ version: ${n.version},
15
+ entity: '${e(n.entity)}',
16
+ title: '${r(n.title)}',
17
+ description: '${r(n.description||`Describe the purpose of this data view.`)}',
18
+ domain: '${r(n.domain||n.entity)}',
19
+ owners: [${n.owners.map(t=>`'${e(t)}'`).join(`, `)}],
20
+ tags: [${n.tags.map(t=>`'${e(t)}'`).join(`, `)}],
21
+ stability: '${n.stability}',
29
22
  },
30
23
  source: {
31
24
  primary: {
32
- name: '${escapeString(data.primaryOperation.name)}',
33
- version: ${data.primaryOperation.version},
25
+ name: '${e(n.primaryOperation.name)}',
26
+ version: ${n.primaryOperation.version},
34
27
  },
35
- ${itemOperation}
28
+ ${s}
36
29
  refreshEvents: [
37
30
  // { name: 'entity.updated', version: 1 },
38
31
  ],
39
32
  },
40
33
  view: {
41
- kind: '${data.kind}',
34
+ kind: '${n.kind}',
42
35
  fields: [
43
- ${fields}
36
+ ${a}
44
37
  ],
45
- ${data.primaryField ? `primaryField: '${escapeString(data.primaryField)}',` : ""}
46
- ${secondaryFields}
38
+ ${n.primaryField?`primaryField: '${e(n.primaryField)}',`:``}
39
+ ${o}
47
40
  filters: [
48
41
  // Example filter:
49
42
  // { key: 'search', label: 'Search', field: 'fullName', type: 'search' },
@@ -58,11 +51,4 @@ ${fields}
58
51
  // error: { name: 'app.data.error', version: 1 },
59
52
  },
60
53
  };
61
- `;
62
- }
63
- function escape(value) {
64
- return value.replace(/'/g, "\\'");
65
- }
66
-
67
- //#endregion
68
- export { generateDataViewSpec };
54
+ `}function r(e){return e.replace(/'/g,`\\'`)}export{n as generateDataViewSpec};
@@ -1,38 +1,24 @@
1
- import { toPascalCase } from "./utils.js";
2
-
3
- //#region src/templates/event.ts
4
- /**
5
- * Generate event spec TypeScript code.
6
- */
7
- function generateEventSpec(data) {
8
- const { name, version, description, stability, owners, tags, piiFields } = data;
9
- const eventVarName = toPascalCase(name.replace(/\./g, "_")) + "V" + version;
10
- const payloadSchemaName = eventVarName + "Payload";
11
- return `import { defineEvent } from '@lssm/lib.contracts';
1
+ import{toPascalCase as e}from"./utils.js";function t(t){let{name:n,version:r,description:i,stability:a,owners:o,tags:s,piiFields:c}=t,l=e(n.replace(/\./g,`_`))+`V`+r,u=l+`Payload`;return`import { defineEvent } from '@lssm/lib.contracts';
12
2
  import { ScalarTypeEnum, SchemaModel } from '@lssm/lib.schema';
13
3
 
14
4
  // TODO: Define event payload schema
15
- export const ${payloadSchemaName} = new SchemaModel({
16
- name: '${payloadSchemaName}',
17
- description: 'Payload for ${name}',
5
+ export const ${u} = new SchemaModel({
6
+ name: '${u}',
7
+ description: 'Payload for ${n}',
18
8
  fields: {
19
9
  // Add your payload fields here
20
10
  // example: { type: ScalarTypeEnum.String_unsecure(), isOptional: false },
21
11
  },
22
12
  });
23
13
 
24
- export const ${eventVarName} = defineEvent({
25
- name: '${name}',
26
- version: ${version},
27
- description: '${description}',
28
- stability: '${stability}',
29
- owners: [${owners.map((o) => `'${o}'`).join(", ")}],
30
- tags: [${tags.map((t) => `'${t}'`).join(", ")}],
31
- ${piiFields.length > 0 ? `pii: [${piiFields.map((f) => `'${f}'`).join(", ")}],` : "// pii: [],"}
32
- payload: ${payloadSchemaName},
14
+ export const ${l} = defineEvent({
15
+ name: '${n}',
16
+ version: ${r},
17
+ description: '${i}',
18
+ stability: '${a}',
19
+ owners: [${o.map(e=>`'${e}'`).join(`, `)}],
20
+ tags: [${s.map(e=>`'${e}'`).join(`, `)}],
21
+ ${c.length>0?`pii: [${c.map(e=>`'${e}'`).join(`, `)}],`:`// pii: [],`}
22
+ payload: ${u},
33
23
  });
34
- `;
35
- }
36
-
37
- //#endregion
38
- export { generateEventSpec };
24
+ `}export{t as generateEventSpec};
@@ -1,87 +1,62 @@
1
- import { toPascalCase } from "./utils.js";
1
+ import{toPascalCase as e}from"./utils.js";function t(t){let i=e(t.name.split(`.`).pop()??`Experiment`)+`Experiment`,a=t.variants.map(e=>{let t=e.overrides?.length?` overrides: [
2
+ ${e.overrides.map(e=>` {
3
+ type: '${e.type}',
4
+ target: '${r(e.target)}',
5
+ ${typeof e.version==`number`?`version: ${e.version},`:``}
6
+ }`).join(`,
7
+ `)}
8
+ ],`:``;return` {
9
+ id: '${r(e.id)}',
10
+ name: '${r(e.name)}',
11
+ ${e.description?`description: '${r(e.description)}',`:``}
12
+ ${typeof e.weight==`number`?`weight: ${e.weight},`:``}
13
+ ${t}
14
+ }`}).join(`,
15
+ `),o=n(t.allocation),s=t.successMetrics?.length?` successMetrics: [
16
+ ${t.successMetrics.map(e=>` {
17
+ name: '${r(e.name)}',
18
+ telemetryEvent: { name: '${r(e.eventName)}', version: ${e.eventVersion} },
19
+ aggregation: '${e.aggregation}',
20
+ ${typeof e.target==`number`?`target: ${e.target},`:``}
21
+ }`).join(`,
22
+ `)}
23
+ ],`:``;return`import type { ExperimentSpec } from '@lssm/lib.contracts/experiments';
2
24
 
3
- //#region src/templates/experiment.ts
4
- function generateExperimentSpec(data) {
5
- const specVar = toPascalCase(data.name.split(".").pop() ?? "Experiment") + "Experiment";
6
- const variants = data.variants.map((variant) => {
7
- const overrides = variant.overrides?.length ? ` overrides: [
8
- ${variant.overrides.map((override) => ` {
9
- type: '${override.type}',
10
- target: '${escapeString(override.target)}',
11
- ${typeof override.version === "number" ? `version: ${override.version},` : ""}
12
- }`).join(",\n")}
13
- ],` : "";
14
- return ` {
15
- id: '${escapeString(variant.id)}',
16
- name: '${escapeString(variant.name)}',
17
- ${variant.description ? `description: '${escapeString(variant.description)}',` : ""}
18
- ${typeof variant.weight === "number" ? `weight: ${variant.weight},` : ""}
19
- ${overrides}
20
- }`;
21
- }).join(",\n");
22
- const allocation = renderAllocation(data.allocation);
23
- const metrics = data.successMetrics?.length ? ` successMetrics: [
24
- ${data.successMetrics.map((metric) => ` {
25
- name: '${escapeString(metric.name)}',
26
- telemetryEvent: { name: '${escapeString(metric.eventName)}', version: ${metric.eventVersion} },
27
- aggregation: '${metric.aggregation}',
28
- ${typeof metric.target === "number" ? `target: ${metric.target},` : ""}
29
- }`).join(",\n")}
30
- ],` : "";
31
- return `import type { ExperimentSpec } from '@lssm/lib.contracts/experiments';
32
-
33
- export const ${specVar}: ExperimentSpec = {
25
+ export const ${i}: ExperimentSpec = {
34
26
  meta: {
35
- name: '${escapeString(data.name)}',
36
- version: ${data.version},
37
- title: '${escapeString(data.name)} experiment',
38
- description: '${escapeString(data.description || "Describe the experiment goal.")}',
39
- domain: '${escapeString(data.domain)}',
40
- owners: [${data.owners.map((owner) => `'${escapeString(owner)}'`).join(", ")}],
41
- tags: [${data.tags.map((tag) => `'${escapeString(tag)}'`).join(", ")}],
42
- stability: '${data.stability}',
27
+ name: '${r(t.name)}',
28
+ version: ${t.version},
29
+ title: '${r(t.name)} experiment',
30
+ description: '${r(t.description||`Describe the experiment goal.`)}',
31
+ domain: '${r(t.domain)}',
32
+ owners: [${t.owners.map(e=>`'${r(e)}'`).join(`, `)}],
33
+ tags: [${t.tags.map(e=>`'${r(e)}'`).join(`, `)}],
34
+ stability: '${t.stability}',
43
35
  },
44
- controlVariant: '${escapeString(data.controlVariant)}',
36
+ controlVariant: '${r(t.controlVariant)}',
45
37
  variants: [
46
- ${variants}
38
+ ${a}
47
39
  ],
48
- allocation: ${allocation},
49
- ${metrics}
40
+ allocation: ${o},
41
+ ${s}
50
42
  };
51
- `;
52
- }
53
- function renderAllocation(allocation) {
54
- switch (allocation.type) {
55
- case "random": return `{
43
+ `}function n(e){switch(e.type){case`random`:return`{
56
44
  type: 'random',
57
- ${allocation.salt ? `salt: '${escapeString(allocation.salt)}',` : ""}
58
- }`;
59
- case "sticky": return `{
45
+ ${e.salt?`salt: '${r(e.salt)}',`:``}
46
+ }`;case`sticky`:return`{
60
47
  type: 'sticky',
61
- attribute: '${allocation.attribute}',
62
- ${allocation.salt ? `salt: '${escapeString(allocation.salt)}',` : ""}
63
- }`;
64
- case "targeted": return `{
48
+ attribute: '${e.attribute}',
49
+ ${e.salt?`salt: '${r(e.salt)}',`:``}
50
+ }`;case`targeted`:return`{
65
51
  type: 'targeted',
66
52
  rules: [
67
- ${allocation.rules.map((rule) => ` {
68
- variantId: '${escapeString(rule.variantId)}',
69
- ${typeof rule.percentage === "number" ? `percentage: ${rule.percentage},` : ""}
70
- ${rule.policy ? `policy: { name: '${escapeString(rule.policy.name)}'${typeof rule.policy.version === "number" ? `, version: ${rule.policy.version}` : ""} },` : ""}
71
- ${rule.expression ? `expression: '${escapeString(rule.expression)}',` : ""}
72
- }`).join(",\n")}
53
+ ${e.rules.map(e=>` {
54
+ variantId: '${r(e.variantId)}',
55
+ ${typeof e.percentage==`number`?`percentage: ${e.percentage},`:``}
56
+ ${e.policy?`policy: { name: '${r(e.policy.name)}'${typeof e.policy.version==`number`?`, version: ${e.policy.version}`:``} },`:``}
57
+ ${e.expression?`expression: '${r(e.expression)}',`:``}
58
+ }`).join(`,
59
+ `)}
73
60
  ],
74
- fallback: '${allocation.fallback ?? "control"}',
75
- }`;
76
- default: return renderUnsupportedAllocation(allocation);
77
- }
78
- }
79
- function escapeString(value) {
80
- return value.replace(/\\/g, "\\\\").replace(/'/g, "\\'");
81
- }
82
- function renderUnsupportedAllocation(allocation) {
83
- throw new Error(`Unsupported allocation type ${allocation}`);
84
- }
85
-
86
- //#endregion
87
- export { generateExperimentSpec };
61
+ fallback: '${e.fallback??`control`}',
62
+ }`;default:return i(e)}}function r(e){return e.replace(/\\/g,`\\\\`).replace(/'/g,`\\'`)}function i(e){throw Error(`Unsupported allocation type ${e}`)}export{t as generateExperimentSpec};
@@ -1,27 +1,14 @@
1
- import { toCamelCase, toKebabCase, toPascalCase } from "./utils.js";
1
+ import{toCamelCase as e,toKebabCase as t,toPascalCase as n}from"./utils.js";function r(r,i){let a=e(r.split(`.`).pop()??`unknown`)+`Handler`,o=n(r.split(`.`).pop()??`Unknown`)+`Spec`;return`import type { ContractHandler } from '@lssm/lib.contracts';
2
+ import { ${o} } from '../contracts/${t(r)}.contracts';
2
3
 
3
- //#region src/templates/handler.ts
4
4
  /**
5
- * Handler and component template generation.
6
- * Extracted from cli-contracts/src/templates/handler.template.ts
7
- */
8
- /**
9
- * Generate handler implementation template.
10
- */
11
- function generateHandlerTemplate(specName, kind) {
12
- const handlerName = toCamelCase(specName.split(".").pop() ?? "unknown") + "Handler";
13
- const specVarName = toPascalCase(specName.split(".").pop() ?? "Unknown") + "Spec";
14
- return `import type { ContractHandler } from '@lssm/lib.contracts';
15
- import { ${specVarName} } from '../contracts/${toKebabCase(specName)}.contracts';
16
-
17
- /**
18
- * Handler for ${specName}
5
+ * Handler for ${r}
19
6
  */
20
- export const ${handlerName}: ContractHandler<typeof ${specVarName}> = async (
7
+ export const ${a}: ContractHandler<typeof ${o}> = async (
21
8
  input,
22
9
  context
23
10
  ) => {
24
- // TODO: Implement ${kind} logic
11
+ // TODO: Implement ${i} logic
25
12
 
26
13
  try {
27
14
  // 1. Validate prerequisites
@@ -37,43 +24,28 @@ export const ${handlerName}: ContractHandler<typeof ${specVarName}> = async (
37
24
  throw error;
38
25
  }
39
26
  };
40
- `;
41
- }
42
- /**
43
- * Generate React component template.
44
- */
45
- function generateComponentTemplate(componentName, description) {
46
- const pascalName = toPascalCase(componentName);
47
- return `import React from 'react';
27
+ `}function i(e,t){let r=n(e);return`import React from 'react';
48
28
 
49
- interface ${pascalName}Props {
29
+ interface ${r}Props {
50
30
  // TODO: Define props based on presentation spec
51
31
  }
52
32
 
53
33
  /**
54
- * ${description}
34
+ * ${t}
55
35
  */
56
- export const ${pascalName}: React.FC<${pascalName}Props> = (props) => {
36
+ export const ${r}: React.FC<${r}Props> = (props) => {
57
37
  return (
58
38
  <div>
59
39
  {/* TODO: Implement component UI */}
60
- <p>Component: ${pascalName}</p>
40
+ <p>Component: ${r}</p>
61
41
  </div>
62
42
  );
63
43
  };
64
- `;
65
- }
66
- /**
67
- * Generate test template.
68
- */
69
- function generateTestTemplate(targetName, type) {
70
- const importPath = type === "handler" ? "../handlers" : "../components";
71
- const testName = toPascalCase(targetName);
72
- return `import { describe, it, expect } from 'bun:test';
73
- import { ${testName} } from '${importPath}/${toKebabCase(targetName)}';
44
+ `}function a(e,r){let i=r===`handler`?`../handlers`:`../components`,a=n(e);return`import { describe, it, expect } from 'bun:test';
45
+ import { ${a} } from '${i}/${t(e)}';
74
46
 
75
- describe('${testName}', () => {
76
- it('should ${type === "handler" ? "handle valid input" : "render correctly"}', async () => {
47
+ describe('${a}', () => {
48
+ it('should ${r===`handler`?`handle valid input`:`render correctly`}', async () => {
77
49
  // TODO: Implement test
78
50
  expect(true).toBe(true);
79
51
  });
@@ -82,14 +54,10 @@ describe('${testName}', () => {
82
54
  // TODO: Test edge cases
83
55
  });
84
56
 
85
- ${type === "handler" ? `it('should handle errors appropriately', async () => {
57
+ ${r===`handler`?`it('should handle errors appropriately', async () => {
86
58
  // TODO: Test error scenarios
87
- });` : `it('should be accessible', async () => {
59
+ });`:`it('should be accessible', async () => {
88
60
  // TODO: Test accessibility
89
61
  });`}
90
62
  });
91
- `;
92
- }
93
-
94
- //#endregion
95
- export { generateComponentTemplate, generateHandlerTemplate, generateTestTemplate };
63
+ `}export{i as generateComponentTemplate,r as generateHandlerTemplate,a as generateTestTemplate};