@lssm/module.contractspec-workspace 0.0.0-canary-20251220041653 → 0.0.0-canary-20251221132705

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.
@@ -13,14 +13,14 @@
13
13
  */
14
14
  function parseImportedSpecNames(sourceCode, _fromFilePath) {
15
15
  const imports = [];
16
- const importRegex = /import\s+.*?\s+from\s+['"]([^'"]+\.(?:contracts|event|presentation|workflow|data-view|migration|telemetry|experiment|app-config|integration|knowledge)(?:\.[jt]s)?)['"]/g;
16
+ const importRegex = /import\s+.*?\s+from\s+['"]([^'"]+\.(?:contracts|contract|operation|operations|event|presentation|workflow|data-view|migration|telemetry|experiment|app-config|integration|knowledge)(?:\.[jt]s)?)['"]/g;
17
17
  let match;
18
18
  while ((match = importRegex.exec(sourceCode)) !== null) {
19
19
  const importPath = match[1];
20
20
  if (!importPath) continue;
21
21
  if (!importPath.startsWith(".") && !importPath.startsWith("/")) continue;
22
22
  const pathParts = importPath.split("/");
23
- const name = (pathParts[pathParts.length - 1] ?? "").replace(/\.(ts|js)$/, "").replace(/\.(contracts|event|presentation|workflow|data-view|migration|telemetry|experiment|app-config|integration|knowledge)$/, "");
23
+ const name = (pathParts[pathParts.length - 1] ?? "").replace(/\.(ts|js)$/, "").replace(/\.(contracts|contract|operation|operations|event|presentation|workflow|data-view|migration|telemetry|experiment|app-config|integration|knowledge)$/, "");
24
24
  if (name.length > 0) imports.push(name);
25
25
  }
26
26
  return Array.from(new Set(imports)).sort((a, b) => a.localeCompare(b));
@@ -1 +1 @@
1
- {"version":3,"file":"parse-imports.js","names":["imports: string[]","match: RegExpExecArray | null"],"sources":["../../../src/analysis/deps/parse-imports.ts"],"sourcesContent":["/**\n * Import parsing utilities for dependency analysis.\n * Extracted from cli-contracts/src/commands/deps/parse-imports.ts\n */\n\n/**\n * Parse spec imports from source code.\n * Returns the names of imported specs based on file naming conventions.\n *\n * @param sourceCode - The source code to parse\n * @param fromFilePath - The path of the file being parsed (for relative resolution)\n * @returns Array of imported spec names\n */\nexport function parseImportedSpecNames(\n sourceCode: string,\n _fromFilePath: string\n): string[] {\n const imports: string[] = [];\n\n // Capture relative imports that reference spec-ish files.\n // Examples:\n // import x from './foo.contracts'\n // import { y } from '../bar.event.ts'\n const importRegex =\n /import\\s+.*?\\s+from\\s+['\"]([^'\"]+\\.(?:contracts|event|presentation|workflow|data-view|migration|telemetry|experiment|app-config|integration|knowledge)(?:\\.[jt]s)?)['\"]/g;\n\n let match: RegExpExecArray | null;\n while ((match = importRegex.exec(sourceCode)) !== null) {\n const importPath = match[1];\n if (!importPath) continue;\n if (!importPath.startsWith('.') && !importPath.startsWith('/')) continue;\n\n // Extract base name from the import path\n const pathParts = importPath.split('/');\n const base = pathParts[pathParts.length - 1] ?? '';\n\n const name = base\n .replace(/\\.(ts|js)$/, '')\n .replace(\n /\\.(contracts|event|presentation|workflow|data-view|migration|telemetry|experiment|app-config|integration|knowledge)$/,\n ''\n );\n\n if (name.length > 0) {\n imports.push(name);\n }\n }\n\n return Array.from(new Set(imports)).sort((a, b) => a.localeCompare(b));\n}\n"],"mappings":";;;;;;;;;;;;;AAaA,SAAgB,uBACd,YACA,eACU;CACV,MAAMA,UAAoB,EAAE;CAM5B,MAAM,cACJ;CAEF,IAAIC;AACJ,SAAQ,QAAQ,YAAY,KAAK,WAAW,MAAM,MAAM;EACtD,MAAM,aAAa,MAAM;AACzB,MAAI,CAAC,WAAY;AACjB,MAAI,CAAC,WAAW,WAAW,IAAI,IAAI,CAAC,WAAW,WAAW,IAAI,CAAE;EAGhE,MAAM,YAAY,WAAW,MAAM,IAAI;EAGvC,MAAM,QAFO,UAAU,UAAU,SAAS,MAAM,IAG7C,QAAQ,cAAc,GAAG,CACzB,QACC,wHACA,GACD;AAEH,MAAI,KAAK,SAAS,EAChB,SAAQ,KAAK,KAAK;;AAItB,QAAO,MAAM,KAAK,IAAI,IAAI,QAAQ,CAAC,CAAC,MAAM,GAAG,MAAM,EAAE,cAAc,EAAE,CAAC"}
1
+ {"version":3,"file":"parse-imports.js","names":["imports: string[]","match: RegExpExecArray | null"],"sources":["../../../src/analysis/deps/parse-imports.ts"],"sourcesContent":["/**\n * Import parsing utilities for dependency analysis.\n * Extracted from cli-contracts/src/commands/deps/parse-imports.ts\n */\n\n/**\n * Parse spec imports from source code.\n * Returns the names of imported specs based on file naming conventions.\n *\n * @param sourceCode - The source code to parse\n * @param fromFilePath - The path of the file being parsed (for relative resolution)\n * @returns Array of imported spec names\n */\nexport function parseImportedSpecNames(\n sourceCode: string,\n _fromFilePath: string\n): string[] {\n const imports: string[] = [];\n\n // Capture relative imports that reference spec-ish files.\n // Examples:\n // import x from './foo.contracts'\n // import { y } from '../bar.event.ts'\n const importRegex =\n /import\\s+.*?\\s+from\\s+['\"]([^'\"]+\\.(?:contracts|contract|operation|operations|event|presentation|workflow|data-view|migration|telemetry|experiment|app-config|integration|knowledge)(?:\\.[jt]s)?)['\"]/g;\n\n let match: RegExpExecArray | null;\n while ((match = importRegex.exec(sourceCode)) !== null) {\n const importPath = match[1];\n if (!importPath) continue;\n if (!importPath.startsWith('.') && !importPath.startsWith('/')) continue;\n\n // Extract base name from the import path\n const pathParts = importPath.split('/');\n const base = pathParts[pathParts.length - 1] ?? '';\n\n const name = base\n .replace(/\\.(ts|js)$/, '')\n .replace(\n /\\.(contracts|contract|operation|operations|event|presentation|workflow|data-view|migration|telemetry|experiment|app-config|integration|knowledge)$/,\n ''\n );\n\n if (name.length > 0) {\n imports.push(name);\n }\n }\n\n return Array.from(new Set(imports)).sort((a, b) => a.localeCompare(b));\n}\n"],"mappings":";;;;;;;;;;;;;AAaA,SAAgB,uBACd,YACA,eACU;CACV,MAAMA,UAAoB,EAAE;CAM5B,MAAM,cACJ;CAEF,IAAIC;AACJ,SAAQ,QAAQ,YAAY,KAAK,WAAW,MAAM,MAAM;EACtD,MAAM,aAAa,MAAM;AACzB,MAAI,CAAC,WAAY;AACjB,MAAI,CAAC,WAAW,WAAW,IAAI,IAAI,CAAC,WAAW,WAAW,IAAI,CAAE;EAGhE,MAAM,YAAY,WAAW,MAAM,IAAI;EAGvC,MAAM,QAFO,UAAU,UAAU,SAAS,MAAM,IAG7C,QAAQ,cAAc,GAAG,CACzB,QACC,sJACA,GACD;AAEH,MAAI,KAAK,SAAS,EAChB,SAAQ,KAAK,KAAK;;AAItB,QAAO,MAAM,KAAK,IAAI,IAAI,QAAQ,CAAC,CAAC,MAAM,GAAG,MAAM,EAAE,cAAc,EAAE,CAAC"}
@@ -0,0 +1,79 @@
1
+ import { Stability } from "../types/spec-types.js";
2
+ import { FeatureScanResult, SpecScanResult } from "../types/analysis-types.js";
3
+
4
+ //#region src/analysis/grouping.d.ts
5
+
6
+ /**
7
+ * Filter criteria for spec scan results.
8
+ */
9
+ interface SpecFilter {
10
+ /** Filter by tags (item must have at least one matching tag) */
11
+ tags?: string[];
12
+ /** Filter by owners (item must have at least one matching owner) */
13
+ owners?: string[];
14
+ /** Filter by stability levels */
15
+ stability?: Stability[];
16
+ /** Filter by spec type */
17
+ specType?: SpecScanResult['specType'][];
18
+ /** Filter by name pattern (glob) */
19
+ namePattern?: string;
20
+ }
21
+ /**
22
+ * Grouping key function type.
23
+ */
24
+ type GroupKeyFn<T> = (item: T) => string;
25
+ /**
26
+ * Grouped items result.
27
+ */
28
+ interface GroupedItems<T> {
29
+ key: string;
30
+ items: T[];
31
+ }
32
+ /**
33
+ * Pre-built grouping strategies for spec scan results.
34
+ */
35
+ declare const SpecGroupingStrategies: {
36
+ /** Group by first tag. */
37
+ byTag: (item: SpecScanResult) => string;
38
+ /** Group by first owner. */
39
+ byOwner: (item: SpecScanResult) => string;
40
+ /** Group by domain (first segment of name). */
41
+ byDomain: (item: SpecScanResult) => string;
42
+ /** Group by stability. */
43
+ byStability: (item: SpecScanResult) => string;
44
+ /** Group by spec type. */
45
+ bySpecType: (item: SpecScanResult) => string;
46
+ /** Group by file directory. */
47
+ byDirectory: (item: SpecScanResult) => string;
48
+ };
49
+ /**
50
+ * Filter specs by criteria.
51
+ */
52
+ declare function filterSpecs(specs: SpecScanResult[], filter: SpecFilter): SpecScanResult[];
53
+ /**
54
+ * Group specs by key function.
55
+ */
56
+ declare function groupSpecs<T>(items: T[], keyFn: GroupKeyFn<T>): Map<string, T[]>;
57
+ /**
58
+ * Group specs and return as array.
59
+ */
60
+ declare function groupSpecsToArray<T>(items: T[], keyFn: GroupKeyFn<T>): GroupedItems<T>[];
61
+ /**
62
+ * Get unique tags from spec results.
63
+ */
64
+ declare function getUniqueSpecTags(specs: SpecScanResult[]): string[];
65
+ /**
66
+ * Get unique owners from spec results.
67
+ */
68
+ declare function getUniqueSpecOwners(specs: SpecScanResult[]): string[];
69
+ /**
70
+ * Get unique domains from spec results.
71
+ */
72
+ declare function getUniqueSpecDomains(specs: SpecScanResult[]): string[];
73
+ /**
74
+ * Filter features by criteria.
75
+ */
76
+ declare function filterFeatures(features: FeatureScanResult[], filter: SpecFilter): FeatureScanResult[];
77
+ //#endregion
78
+ export { GroupKeyFn, GroupedItems, SpecFilter, SpecGroupingStrategies, filterFeatures, filterSpecs, getUniqueSpecDomains, getUniqueSpecOwners, getUniqueSpecTags, groupSpecs, groupSpecsToArray };
79
+ //# sourceMappingURL=grouping.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"grouping.d.ts","names":[],"sources":["../../src/analysis/grouping.ts"],"sourcesContent":[],"mappings":";;;;;AAgCA;AAQA;;AAKkB,UAlCD,UAAA,CAkCC;EAGC;EASG,IAAA,CAAA,EAAA,MAAA,EAAA;EAGD;EAGC,MAAA,CAAA,EAAA,MAAA,EAAA;EAAc;EAUpB,SAAA,CAAA,EAxDF,SAwDa,EAAA;EAClB;EACC,QAAA,CAAA,EAxDG,cAwDH,CAAA,UAAA,CAAA,EAAA;EACP;EAAc,WAAA,CAAA,EAAA,MAAA;AAiDjB;;;;AAA6E,KAlGjE,UAkGiE,CAAA,CAAA,CAAA,GAAA,CAAA,IAAA,EAlG1C,CAkG0C,EAAA,GAAA,MAAA;;;AAmB7E;AACS,UAjHQ,YAiHR,CAAA,CAAA,CAAA,CAAA;EACW,GAAA,EAAA,MAAA;EAAX,KAAA,EAhHA,CAgHA,EAAA;;;;AAWT;AAagB,cAlIH,sBAkI8B,EAAA;EAa3B;EAWA,KAAA,EAAA,CAAA,IAAA,EAxJA,cAwJc,EAAA,GAAA,MAAA;EAClB;EACF,OAAA,EAAA,CAAA,IAAA,EAvJQ,cAuJR,EAAA,GAAA,MAAA;EACP;EAAiB,QAAA,EAAA,CAAA,IAAA,EArJD,cAqJC,EAAA,GAAA,MAAA;;sBA5IE;;qBAGD;;sBAGC;;;;;iBAUN,WAAA,QACP,0BACC,aACP;;;;iBAiDa,qBAAqB,YAAY,WAAW,KAAK,YAAY;;;;iBAmB7D,4BACP,YACA,WAAW,KACjB,aAAa;;;;iBAUA,iBAAA,QAAyB;;;;iBAazB,mBAAA,QAA2B;;;;iBAa3B,oBAAA,QAA4B;;;;iBAW5B,cAAA,WACJ,6BACF,aACP"}
@@ -0,0 +1,115 @@
1
+ //#region src/analysis/grouping.ts
2
+ /**
3
+ * Pre-built grouping strategies for spec scan results.
4
+ */
5
+ const SpecGroupingStrategies = {
6
+ byTag: (item) => item.tags?.[0] ?? "untagged",
7
+ byOwner: (item) => item.owners?.[0] ?? "unowned",
8
+ byDomain: (item) => {
9
+ const name = item.name ?? "";
10
+ if (name.includes(".")) return name.split(".")[0] ?? "default";
11
+ return "default";
12
+ },
13
+ byStability: (item) => item.stability ?? "stable",
14
+ bySpecType: (item) => item.specType,
15
+ byDirectory: (item) => {
16
+ return item.filePath.split("/").slice(0, -1).join("/") || ".";
17
+ }
18
+ };
19
+ /**
20
+ * Filter specs by criteria.
21
+ */
22
+ function filterSpecs(specs, filter) {
23
+ return specs.filter((spec) => {
24
+ if (filter.tags?.length) {
25
+ if (!filter.tags.some((tag) => spec.tags?.includes(tag))) return false;
26
+ }
27
+ if (filter.owners?.length) {
28
+ if (!filter.owners.some((owner) => spec.owners?.includes(owner))) return false;
29
+ }
30
+ if (filter.stability?.length) {
31
+ if (!filter.stability.includes(spec.stability ?? "stable")) return false;
32
+ }
33
+ if (filter.specType?.length) {
34
+ if (!filter.specType.includes(spec.specType)) return false;
35
+ }
36
+ if (filter.namePattern) {
37
+ const name = spec.name ?? "";
38
+ const pattern = filter.namePattern.replace(/\*/g, ".*").replace(/\?/g, ".");
39
+ if (!new RegExp(`^${pattern}$`, "i").test(name)) return false;
40
+ }
41
+ return true;
42
+ });
43
+ }
44
+ /**
45
+ * Group specs by key function.
46
+ */
47
+ function groupSpecs(items, keyFn) {
48
+ const groups = /* @__PURE__ */ new Map();
49
+ for (const item of items) {
50
+ const key = keyFn(item);
51
+ const existing = groups.get(key);
52
+ if (existing) existing.push(item);
53
+ else groups.set(key, [item]);
54
+ }
55
+ return groups;
56
+ }
57
+ /**
58
+ * Group specs and return as array.
59
+ */
60
+ function groupSpecsToArray(items, keyFn) {
61
+ const map = groupSpecs(items, keyFn);
62
+ return Array.from(map.entries()).map(([key, items$1]) => ({
63
+ key,
64
+ items: items$1
65
+ })).sort((a, b) => a.key.localeCompare(b.key));
66
+ }
67
+ /**
68
+ * Get unique tags from spec results.
69
+ */
70
+ function getUniqueSpecTags(specs) {
71
+ const tags = /* @__PURE__ */ new Set();
72
+ for (const spec of specs) for (const tag of spec.tags ?? []) tags.add(tag);
73
+ return Array.from(tags).sort();
74
+ }
75
+ /**
76
+ * Get unique owners from spec results.
77
+ */
78
+ function getUniqueSpecOwners(specs) {
79
+ const owners = /* @__PURE__ */ new Set();
80
+ for (const spec of specs) for (const owner of spec.owners ?? []) owners.add(owner);
81
+ return Array.from(owners).sort();
82
+ }
83
+ /**
84
+ * Get unique domains from spec results.
85
+ */
86
+ function getUniqueSpecDomains(specs) {
87
+ const domains = /* @__PURE__ */ new Set();
88
+ for (const spec of specs) domains.add(SpecGroupingStrategies.byDomain(spec));
89
+ return Array.from(domains).sort();
90
+ }
91
+ /**
92
+ * Filter features by criteria.
93
+ */
94
+ function filterFeatures(features, filter) {
95
+ return features.filter((feature) => {
96
+ if (filter.tags?.length) {
97
+ if (!filter.tags.some((tag) => feature.tags?.includes(tag))) return false;
98
+ }
99
+ if (filter.owners?.length) {
100
+ if (!filter.owners.some((owner) => feature.owners?.includes(owner))) return false;
101
+ }
102
+ if (filter.stability?.length) {
103
+ if (!filter.stability.includes(feature.stability ?? "stable")) return false;
104
+ }
105
+ if (filter.namePattern) {
106
+ const pattern = filter.namePattern.replace(/\*/g, ".*").replace(/\?/g, ".");
107
+ if (!new RegExp(`^${pattern}$`, "i").test(feature.key)) return false;
108
+ }
109
+ return true;
110
+ });
111
+ }
112
+
113
+ //#endregion
114
+ export { SpecGroupingStrategies, filterFeatures, filterSpecs, getUniqueSpecDomains, getUniqueSpecOwners, getUniqueSpecTags, groupSpecs, groupSpecsToArray };
115
+ //# sourceMappingURL=grouping.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"grouping.js","names":["items"],"sources":["../../src/analysis/grouping.ts"],"sourcesContent":["/**\n * Grouping and filtering utilities for ContractSpec workspace analysis.\n * Provides services to filter and group scan results.\n */\n\nimport type { SpecScanResult, FeatureScanResult } from '../types/analysis-types';\nimport type { Stability } from '../types/spec-types';\n\n/**\n * Filter criteria for spec scan results.\n */\nexport interface SpecFilter {\n /** Filter by tags (item must have at least one matching tag) */\n tags?: string[];\n /** Filter by owners (item must have at least one matching owner) */\n owners?: string[];\n /** Filter by stability levels */\n stability?: Stability[];\n /** Filter by spec type */\n specType?: SpecScanResult['specType'][];\n /** Filter by name pattern (glob) */\n namePattern?: string;\n}\n\n/**\n * Grouping key function type.\n */\nexport type GroupKeyFn<T> = (item: T) => string;\n\n/**\n * Grouped items result.\n */\nexport interface GroupedItems<T> {\n key: string;\n items: T[];\n}\n\n/**\n * Pre-built grouping strategies for spec scan results.\n */\nexport const SpecGroupingStrategies = {\n /** Group by first tag. */\n byTag: (item: SpecScanResult): string => item.tags?.[0] ?? 'untagged',\n\n /** Group by first owner. */\n byOwner: (item: SpecScanResult): string => item.owners?.[0] ?? 'unowned',\n\n /** Group by domain (first segment of name). */\n byDomain: (item: SpecScanResult): string => {\n const name = item.name ?? '';\n if (name.includes('.')) {\n return name.split('.')[0] ?? 'default';\n }\n return 'default';\n },\n\n /** Group by stability. */\n byStability: (item: SpecScanResult): string => item.stability ?? 'stable',\n\n /** Group by spec type. */\n bySpecType: (item: SpecScanResult): string => item.specType,\n\n /** Group by file directory. */\n byDirectory: (item: SpecScanResult): string => {\n const parts = item.filePath.split('/');\n // Return parent directory\n return parts.slice(0, -1).join('/') || '.';\n },\n};\n\n/**\n * Filter specs by criteria.\n */\nexport function filterSpecs(\n specs: SpecScanResult[],\n filter: SpecFilter\n): SpecScanResult[] {\n return specs.filter((spec) => {\n // Filter by tags\n if (filter.tags?.length) {\n const hasMatchingTag = filter.tags.some((tag) =>\n spec.tags?.includes(tag)\n );\n if (!hasMatchingTag) return false;\n }\n\n // Filter by owners\n if (filter.owners?.length) {\n const hasMatchingOwner = filter.owners.some((owner) =>\n spec.owners?.includes(owner)\n );\n if (!hasMatchingOwner) return false;\n }\n\n // Filter by stability\n if (filter.stability?.length) {\n if (!filter.stability.includes(spec.stability ?? 'stable')) {\n return false;\n }\n }\n\n // Filter by spec type\n if (filter.specType?.length) {\n if (!filter.specType.includes(spec.specType)) {\n return false;\n }\n }\n\n // Filter by name pattern\n if (filter.namePattern) {\n const name = spec.name ?? '';\n const pattern = filter.namePattern\n .replace(/\\*/g, '.*')\n .replace(/\\?/g, '.');\n const regex = new RegExp(`^${pattern}$`, 'i');\n if (!regex.test(name)) return false;\n }\n\n return true;\n });\n}\n\n/**\n * Group specs by key function.\n */\nexport function groupSpecs<T>(items: T[], keyFn: GroupKeyFn<T>): Map<string, T[]> {\n const groups = new Map<string, T[]>();\n\n for (const item of items) {\n const key = keyFn(item);\n const existing = groups.get(key);\n if (existing) {\n existing.push(item);\n } else {\n groups.set(key, [item]);\n }\n }\n\n return groups;\n}\n\n/**\n * Group specs and return as array.\n */\nexport function groupSpecsToArray<T>(\n items: T[],\n keyFn: GroupKeyFn<T>\n): GroupedItems<T>[] {\n const map = groupSpecs(items, keyFn);\n return Array.from(map.entries())\n .map(([key, items]) => ({ key, items }))\n .sort((a, b) => a.key.localeCompare(b.key));\n}\n\n/**\n * Get unique tags from spec results.\n */\nexport function getUniqueSpecTags(specs: SpecScanResult[]): string[] {\n const tags = new Set<string>();\n for (const spec of specs) {\n for (const tag of spec.tags ?? []) {\n tags.add(tag);\n }\n }\n return Array.from(tags).sort();\n}\n\n/**\n * Get unique owners from spec results.\n */\nexport function getUniqueSpecOwners(specs: SpecScanResult[]): string[] {\n const owners = new Set<string>();\n for (const spec of specs) {\n for (const owner of spec.owners ?? []) {\n owners.add(owner);\n }\n }\n return Array.from(owners).sort();\n}\n\n/**\n * Get unique domains from spec results.\n */\nexport function getUniqueSpecDomains(specs: SpecScanResult[]): string[] {\n const domains = new Set<string>();\n for (const spec of specs) {\n domains.add(SpecGroupingStrategies.byDomain(spec));\n }\n return Array.from(domains).sort();\n}\n\n/**\n * Filter features by criteria.\n */\nexport function filterFeatures(\n features: FeatureScanResult[],\n filter: SpecFilter\n): FeatureScanResult[] {\n return features.filter((feature) => {\n // Filter by tags\n if (filter.tags?.length) {\n const hasMatchingTag = filter.tags.some((tag) =>\n feature.tags?.includes(tag)\n );\n if (!hasMatchingTag) return false;\n }\n\n // Filter by owners\n if (filter.owners?.length) {\n const hasMatchingOwner = filter.owners.some((owner) =>\n feature.owners?.includes(owner)\n );\n if (!hasMatchingOwner) return false;\n }\n\n // Filter by stability\n if (filter.stability?.length) {\n if (!filter.stability.includes(feature.stability ?? 'stable')) {\n return false;\n }\n }\n\n // Filter by name pattern\n if (filter.namePattern) {\n const pattern = filter.namePattern\n .replace(/\\*/g, '.*')\n .replace(/\\?/g, '.');\n const regex = new RegExp(`^${pattern}$`, 'i');\n if (!regex.test(feature.key)) return false;\n }\n\n return true;\n });\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"spec-scan.d.ts","names":[],"sources":["../../src/analysis/spec-scan.ts"],"sourcesContent":[],"mappings":";;;;AA+GA;AAgCA;AA0BA;AAgMA;iBAxVgB,yBAAA,oBAA6C;;;;iBAyC7C,cAAA,kCAAgD;;;;;iBAqDhD,oBAAA,gBAAoC;;;;iBAgCpC,iBAAA,gBAAiC;;;;iBA0BjC,eAAA,gBAA+B;;;;;iBAgM/B,sBAAA,kCAGb"}
1
+ {"version":3,"file":"spec-scan.d.ts","names":[],"sources":["../../src/analysis/spec-scan.ts"],"sourcesContent":[],"mappings":";;;;AAoHA;AAgCA;AA0BA;AAgMA;iBA7VgB,yBAAA,oBAA6C;;;;iBA8C7C,cAAA,kCAAgD;;;;;iBAqDhD,oBAAA,gBAAoC;;;;iBAgCpC,iBAAA,gBAAiC;;;;iBA0BjC,eAAA,gBAA+B;;;;;iBAgM/B,sBAAA,kCAGb"}
@@ -4,7 +4,7 @@
4
4
  * Supports all contract types from @lssm/lib.contracts.
5
5
  */
6
6
  function inferSpecTypeFromFilePath(filePath) {
7
- if (filePath.includes(".contracts.") || filePath.includes("/contracts/")) return "operation";
7
+ if (filePath.includes(".operations.") || filePath.includes("/operations/") || filePath.includes(".operation.") || filePath.includes("/operation/")) return "operation";
8
8
  if (filePath.includes(".event.") || filePath.includes("/events/") || filePath.endsWith("/events.ts")) return "event";
9
9
  if (filePath.includes(".presentation.") || filePath.includes("/presentations/") || filePath.endsWith("/presentations.ts")) return "presentation";
10
10
  if (filePath.includes(".feature.")) return "feature";
@@ -1 +1 @@
1
- {"version":3,"file":"spec-scan.js","names":["events: RefInfo[]","policies: RefInfo[]","tests: RefInfo[]","results: SpecScanResult[]"],"sources":["../../src/analysis/spec-scan.ts"],"sourcesContent":["/**\n * Spec source scanning utilities.\n * Extracted from cli-contracts/src/utils/spec-scan.ts\n */\n\nimport type {\n AnalyzedSpecType,\n AnalyzedOperationKind,\n SpecScanResult,\n RefInfo,\n} from '../types/analysis-types';\nimport type { Stability } from '../types/spec-types';\n\n/**\n * Infer spec type from file path based on naming conventions.\n * Supports all contract types from @lssm/lib.contracts.\n */\nexport function inferSpecTypeFromFilePath(filePath: string): AnalyzedSpecType {\n // Check more specific patterns first\n // Operation patterns: .contracts. OR /contracts/ directory\n if (filePath.includes('.contracts.') || filePath.includes('/contracts/'))\n return 'operation';\n\n // Event patterns: .event. OR /events/ OR /events.ts\n if (\n filePath.includes('.event.') ||\n filePath.includes('/events/') ||\n filePath.endsWith('/events.ts')\n )\n return 'event';\n\n // Presentation patterns: .presentation. OR /presentations/ OR /presentations.ts\n if (\n filePath.includes('.presentation.') ||\n filePath.includes('/presentations/') ||\n filePath.endsWith('/presentations.ts')\n )\n return 'presentation';\n\n if (filePath.includes('.feature.')) return 'feature';\n if (filePath.includes('.capability.')) return 'capability';\n if (filePath.includes('.data-view.')) return 'data-view';\n if (filePath.includes('.form.')) return 'form';\n if (filePath.includes('.migration.')) return 'migration';\n if (filePath.includes('.workflow.')) return 'workflow';\n if (filePath.includes('.experiment.')) return 'experiment';\n if (filePath.includes('.integration.')) return 'integration';\n if (filePath.includes('.knowledge.')) return 'knowledge';\n if (filePath.includes('.telemetry.')) return 'telemetry';\n if (filePath.includes('.app-config.')) return 'app-config';\n if (filePath.includes('.policy.')) return 'policy';\n if (filePath.includes('.test-spec.')) return 'test-spec';\n return 'unknown';\n}\n\n/**\n * Scan spec source code to extract metadata without executing it.\n */\nexport function scanSpecSource(code: string, filePath: string): SpecScanResult {\n const specType = inferSpecTypeFromFilePath(filePath);\n\n const name = matchStringField(code, 'name');\n const description = matchStringField(code, 'description');\n const stabilityRaw = matchStringField(code, 'stability');\n const stability = isStability(stabilityRaw) ? stabilityRaw : undefined;\n const owners = matchStringArrayField(code, 'owners');\n const tags = matchStringArrayField(code, 'tags');\n\n const version = matchNumberField(code, 'version');\n const kind = inferOperationKind(code);\n\n const hasMeta = /meta\\s*:\\s*{/.test(code);\n const hasIo = /\\bio\\s*:\\s*{/.test(code);\n const hasPolicy = /\\bpolicy\\s*:\\s*{/.test(code);\n const hasPayload = /\\bpayload\\s*:\\s*{/.test(code);\n const hasContent = /\\bcontent\\s*:\\s*{/.test(code);\n const hasDefinition = /\\bdefinition\\s*:\\s*{/.test(code);\n\n // Extract references from operations\n const emittedEvents =\n specType === 'operation' ? extractEmittedEvents(code) : undefined;\n const policyRefs =\n specType === 'operation' ? extractPolicyRefs(code) : undefined;\n const testRefs = extractTestRefs(code);\n\n return {\n filePath,\n specType,\n name: name ?? undefined,\n description: description ?? undefined,\n stability,\n owners,\n tags,\n version: version ?? undefined,\n kind,\n hasMeta,\n hasIo,\n hasPolicy,\n hasPayload,\n hasContent,\n hasDefinition,\n emittedEvents,\n policyRefs,\n testRefs,\n };\n}\n\n/**\n * Extract emitted event refs from operation spec source.\n * Looks for sideEffects.emits array entries.\n */\nexport function extractEmittedEvents(code: string): RefInfo[] | undefined {\n const events: RefInfo[] = [];\n\n // Match inline emit declarations: { name: 'x', version: N, ... }\n const inlinePattern = /\\{\\s*name:\\s*['\"]([^'\"]+)['\"]\\s*,\\s*version:\\s*(\\d+)/g;\n let match;\n while ((match = inlinePattern.exec(code)) !== null) {\n if (match[1] && match[2]) {\n events.push({\n name: match[1],\n version: Number(match[2]),\n });\n }\n }\n\n // Match ref pattern: { ref: SomeEventSpec, ... }\n // We can't fully resolve these without execution, but we can note they exist\n const refPattern = /\\{\\s*ref:\\s*(\\w+)/g;\n while ((match = refPattern.exec(code)) !== null) {\n // Store a placeholder - actual resolution needs runtime\n if (match[1] && !match[1].startsWith('when')) {\n // We can't extract name/version from a variable ref statically\n // This is noted for completeness but won't be fully resolvable\n }\n }\n\n return events.length > 0 ? events : undefined;\n}\n\n/**\n * Extract policy refs from operation spec source.\n */\nexport function extractPolicyRefs(code: string): RefInfo[] | undefined {\n const policies: RefInfo[] = [];\n\n // Match policy ref pattern in policies array\n const policyPattern = /\\{\\s*name:\\s*['\"]([^'\"]+)['\"]\\s*,\\s*version:\\s*(\\d+)/g;\n\n // Only look within policy section\n const policySectionMatch = code.match(/policies\\s*:\\s*\\[([\\s\\S]*?)\\]/);\n if (policySectionMatch?.[1]) {\n let match;\n while ((match = policyPattern.exec(policySectionMatch[1])) !== null) {\n if (match[1] && match[2]) {\n policies.push({\n name: match[1],\n version: Number(match[2]),\n });\n }\n }\n }\n\n return policies.length > 0 ? policies : undefined;\n}\n\n/**\n * Extract test spec refs.\n */\nexport function extractTestRefs(code: string): RefInfo[] | undefined {\n const tests: RefInfo[] = [];\n\n // Look for tests array\n const testsSectionMatch = code.match(/tests\\s*:\\s*\\[([\\s\\S]*?)\\]/);\n if (testsSectionMatch?.[1]) {\n const refPattern = /\\{\\s*name:\\s*['\"]([^'\"]+)['\"]\\s*,\\s*version:\\s*(\\d+)/g;\n let match;\n while ((match = refPattern.exec(testsSectionMatch[1])) !== null) {\n if (match[1] && match[2]) {\n tests.push({\n name: match[1],\n version: Number(match[2]),\n });\n }\n }\n }\n\n return tests.length > 0 ? tests : undefined;\n}\n\nfunction matchStringField(code: string, field: string): string | null {\n const regex = new RegExp(`${escapeRegex(field)}\\\\s*:\\\\s*['\"]([^'\"]+)['\"]`);\n const match = code.match(regex);\n return match?.[1] ?? null;\n}\n\nfunction matchNumberField(code: string, field: string): number | null {\n const regex = new RegExp(`${escapeRegex(field)}\\\\s*:\\\\s*(\\\\d+)`);\n const match = code.match(regex);\n if (!match?.[1]) return null;\n const parsed = Number(match[1]);\n return Number.isFinite(parsed) ? parsed : null;\n}\n\nfunction matchStringArrayField(\n code: string,\n field: string\n): string[] | undefined {\n const regex = new RegExp(`${escapeRegex(field)}\\\\s*:\\\\s*\\\\[([\\\\s\\\\S]*?)\\\\]`);\n const match = code.match(regex);\n if (!match?.[1]) return undefined;\n\n const inner = match[1];\n const items = Array.from(inner.matchAll(/['\"]([^'\"]+)['\"]/g))\n .map((m) => m[1])\n .filter(\n (value): value is string => typeof value === 'string' && value.length > 0\n );\n\n return items.length > 0 ? items : undefined;\n}\n\nfunction isStability(value: string | null): value is Stability {\n return (\n value === 'experimental' ||\n value === 'beta' ||\n value === 'stable' ||\n value === 'deprecated'\n );\n}\n\nfunction escapeRegex(value: string): string {\n return value.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\n}\n\n/**\n * Infer operation kind from source code.\n * First checks for defineCommand/defineQuery usage (which set kind automatically),\n * then falls back to explicit kind field.\n */\nfunction inferOperationKind(code: string): AnalyzedOperationKind {\n // Check for defineCommand/defineQuery usage first (they set kind automatically)\n if (/defineCommand\\s*\\(/.test(code)) return 'command';\n if (/defineQuery\\s*\\(/.test(code)) return 'query';\n // Fall back to explicit kind field\n const kindRaw = matchStringField(code, 'kind');\n return kindRaw === 'command' || kindRaw === 'query' ? kindRaw : 'unknown';\n}\n\n/**\n * Infer operation kind from a specific code block.\n */\nfunction inferOperationKindFromBlock(block: string): AnalyzedOperationKind {\n if (/defineCommand\\s*\\(/.test(block)) return 'command';\n if (/defineQuery\\s*\\(/.test(block)) return 'query';\n const kindRaw = matchStringField(block, 'kind');\n return kindRaw === 'command' || kindRaw === 'query' ? kindRaw : 'unknown';\n}\n\n/**\n * Extract name and version from a meta block.\n */\nfunction extractMetaFromBlock(\n block: string\n): { name: string; version: number } | null {\n const name = matchStringField(block, 'name');\n const version = matchNumberField(block, 'version');\n if (name && version !== null) {\n return { name, version };\n }\n return null;\n}\n\n/**\n * Define function patterns for all spec types.\n */\nconst DEFINE_FUNCTION_PATTERNS = [\n // Operations\n { pattern: /defineCommand\\s*\\(\\s*\\{/g, type: 'operation' as const },\n { pattern: /defineQuery\\s*\\(\\s*\\{/g, type: 'operation' as const },\n // Events\n { pattern: /defineEvent\\s*\\(\\s*\\{/g, type: 'event' as const },\n // Presentations (both v1 and v2 patterns)\n {\n pattern: /:\\s*PresentationDescriptorV2\\s*=\\s*\\{/g,\n type: 'presentation' as const,\n },\n {\n pattern: /:\\s*PresentationDescriptor\\s*=\\s*\\{/g,\n type: 'presentation' as const,\n },\n { pattern: /definePresentation\\s*\\(\\s*\\{/g, type: 'presentation' as const },\n // Capabilities\n { pattern: /defineCapability\\s*\\(\\s*\\{/g, type: 'capability' as const },\n // Workflows\n { pattern: /defineWorkflow\\s*\\(\\s*\\{/g, type: 'workflow' as const },\n // Experiments\n { pattern: /defineExperiment\\s*\\(\\s*\\{/g, type: 'experiment' as const },\n // Integrations\n { pattern: /defineIntegration\\s*\\(\\s*\\{/g, type: 'integration' as const },\n // Knowledge\n { pattern: /defineKnowledge\\s*\\(\\s*\\{/g, type: 'knowledge' as const },\n // Telemetry\n { pattern: /defineTelemetry\\s*\\(\\s*\\{/g, type: 'telemetry' as const },\n // App config\n { pattern: /defineAppConfig\\s*\\(\\s*\\{/g, type: 'app-config' as const },\n // Policy\n { pattern: /definePolicy\\s*\\(\\s*\\{/g, type: 'policy' as const },\n // Test spec\n { pattern: /defineTestSpec\\s*\\(\\s*\\{/g, type: 'test-spec' as const },\n // Data view\n { pattern: /defineDataView\\s*\\(\\s*\\{/g, type: 'data-view' as const },\n // Form\n { pattern: /defineForm\\s*\\(\\s*\\{/g, type: 'form' as const },\n // Migration\n { pattern: /defineMigration\\s*\\(\\s*\\{/g, type: 'migration' as const },\n];\n\n/**\n * Find matching closing brace for an opening brace.\n * Returns the index of the closing brace or -1 if not found.\n */\nfunction findMatchingBrace(code: string, startIndex: number): number {\n let depth = 0;\n let inString = false;\n let stringChar = '';\n\n for (let i = startIndex; i < code.length; i++) {\n const char = code[i];\n const prevChar = i > 0 ? code[i - 1] : '';\n\n // Handle string literals\n if ((char === '\"' || char === \"'\" || char === '`') && prevChar !== '\\\\') {\n if (!inString) {\n inString = true;\n stringChar = char;\n } else if (char === stringChar) {\n inString = false;\n }\n continue;\n }\n\n if (inString) continue;\n\n if (char === '{') {\n depth++;\n } else if (char === '}') {\n depth--;\n if (depth === 0) {\n return i;\n }\n }\n }\n\n return -1;\n}\n\n/**\n * Scan spec source code to extract ALL specs from a file.\n * This function finds multiple spec definitions in a single file.\n */\nexport function scanAllSpecsFromSource(\n code: string,\n filePath: string\n): SpecScanResult[] {\n const results: SpecScanResult[] = [];\n const baseSpecType = inferSpecTypeFromFilePath(filePath);\n\n // Track positions we've already processed to avoid duplicates\n const processedPositions = new Set<number>();\n\n for (const { pattern, type } of DEFINE_FUNCTION_PATTERNS) {\n // Reset the regex lastIndex\n pattern.lastIndex = 0;\n\n let match;\n while ((match = pattern.exec(code)) !== null) {\n const startPos = match.index;\n\n // Skip if we've already processed this position\n if (processedPositions.has(startPos)) continue;\n processedPositions.add(startPos);\n\n // Find the opening brace position\n const openBracePos = code.indexOf('{', startPos);\n if (openBracePos === -1) continue;\n\n // Find the matching closing brace\n const closeBracePos = findMatchingBrace(code, openBracePos);\n if (closeBracePos === -1) continue;\n\n // Extract the block content\n const block = code.slice(openBracePos, closeBracePos + 1);\n\n // Extract meta information\n const meta = extractMetaFromBlock(block);\n if (!meta) continue;\n\n // Extract additional metadata\n const description = matchStringField(block, 'description');\n const stabilityRaw = matchStringField(block, 'stability');\n const stability = isStability(stabilityRaw) ? stabilityRaw : undefined;\n const owners = matchStringArrayField(block, 'owners');\n const tags = matchStringArrayField(block, 'tags');\n\n const hasMeta = /meta\\s*:\\s*{/.test(block);\n const hasIo = /\\bio\\s*:\\s*{/.test(block);\n const hasPolicy = /\\bpolicy\\s*:\\s*{/.test(block);\n const hasPayload = /\\bpayload\\s*:\\s*{/.test(block);\n const hasContent = /\\bcontent\\s*:\\s*{/.test(block);\n const hasDefinition = /\\bdefinition\\s*:\\s*{/.test(block);\n\n // Infer kind for operations\n const kind =\n type === 'operation' ? inferOperationKindFromBlock(block) : 'unknown';\n\n // Extract references from operations\n const emittedEvents =\n type === 'operation' ? extractEmittedEvents(block) : undefined;\n const policyRefs =\n type === 'operation' ? extractPolicyRefs(block) : undefined;\n const testRefs = extractTestRefs(block);\n\n results.push({\n filePath,\n specType: type,\n name: meta.name,\n version: meta.version,\n description: description ?? undefined,\n stability,\n owners,\n tags,\n kind,\n hasMeta,\n hasIo,\n hasPolicy,\n hasPayload,\n hasContent,\n hasDefinition,\n emittedEvents,\n policyRefs,\n testRefs,\n });\n }\n }\n\n // If no specs found via patterns, fall back to file-type based scanning\n // This handles cases where the file uses non-standard patterns\n if (results.length === 0 && baseSpecType !== 'unknown') {\n const fallback = scanSpecSource(code, filePath);\n if (fallback.name && fallback.version !== undefined) {\n results.push(fallback);\n }\n }\n\n return results;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"spec-scan.js","names":["events: RefInfo[]","policies: RefInfo[]","tests: RefInfo[]","results: SpecScanResult[]"],"sources":["../../src/analysis/spec-scan.ts"],"sourcesContent":["/**\n * Spec source scanning utilities.\n * Extracted from cli-contracts/src/utils/spec-scan.ts\n */\n\nimport type {\n AnalyzedOperationKind,\n AnalyzedSpecType,\n RefInfo,\n SpecScanResult,\n} from '../types/analysis-types';\nimport type { Stability } from '../types/spec-types';\n\n/**\n * Infer spec type from file path based on naming conventions.\n * Supports all contract types from @lssm/lib.contracts.\n */\nexport function inferSpecTypeFromFilePath(filePath: string): AnalyzedSpecType {\n // Check more specific patterns first\n // Operation patterns: .contracts. OR /contracts/ directory\n if (\n filePath.includes('.operations.') ||\n filePath.includes('/operations/') ||\n filePath.includes('.operation.') ||\n filePath.includes('/operation/')\n )\n return 'operation';\n\n // Event patterns: .event. OR /events/ OR /events.ts\n if (\n filePath.includes('.event.') ||\n filePath.includes('/events/') ||\n filePath.endsWith('/events.ts')\n )\n return 'event';\n\n // Presentation patterns: .presentation. OR /presentations/ OR /presentations.ts\n if (\n filePath.includes('.presentation.') ||\n filePath.includes('/presentations/') ||\n filePath.endsWith('/presentations.ts')\n )\n return 'presentation';\n\n if (filePath.includes('.feature.')) return 'feature';\n if (filePath.includes('.capability.')) return 'capability';\n if (filePath.includes('.data-view.')) return 'data-view';\n if (filePath.includes('.form.')) return 'form';\n if (filePath.includes('.migration.')) return 'migration';\n if (filePath.includes('.workflow.')) return 'workflow';\n if (filePath.includes('.experiment.')) return 'experiment';\n if (filePath.includes('.integration.')) return 'integration';\n if (filePath.includes('.knowledge.')) return 'knowledge';\n if (filePath.includes('.telemetry.')) return 'telemetry';\n if (filePath.includes('.app-config.')) return 'app-config';\n if (filePath.includes('.policy.')) return 'policy';\n if (filePath.includes('.test-spec.')) return 'test-spec';\n return 'unknown';\n}\n\n/**\n * Scan spec source code to extract metadata without executing it.\n */\nexport function scanSpecSource(code: string, filePath: string): SpecScanResult {\n const specType = inferSpecTypeFromFilePath(filePath);\n\n const name = matchStringField(code, 'name');\n const description = matchStringField(code, 'description');\n const stabilityRaw = matchStringField(code, 'stability');\n const stability = isStability(stabilityRaw) ? stabilityRaw : undefined;\n const owners = matchStringArrayField(code, 'owners');\n const tags = matchStringArrayField(code, 'tags');\n\n const version = matchNumberField(code, 'version');\n const kind = inferOperationKind(code);\n\n const hasMeta = /meta\\s*:\\s*{/.test(code);\n const hasIo = /\\bio\\s*:\\s*{/.test(code);\n const hasPolicy = /\\bpolicy\\s*:\\s*{/.test(code);\n const hasPayload = /\\bpayload\\s*:\\s*{/.test(code);\n const hasContent = /\\bcontent\\s*:\\s*{/.test(code);\n const hasDefinition = /\\bdefinition\\s*:\\s*{/.test(code);\n\n // Extract references from operations\n const emittedEvents =\n specType === 'operation' ? extractEmittedEvents(code) : undefined;\n const policyRefs =\n specType === 'operation' ? extractPolicyRefs(code) : undefined;\n const testRefs = extractTestRefs(code);\n\n return {\n filePath,\n specType,\n name: name ?? undefined,\n description: description ?? undefined,\n stability,\n owners,\n tags,\n version: version ?? undefined,\n kind,\n hasMeta,\n hasIo,\n hasPolicy,\n hasPayload,\n hasContent,\n hasDefinition,\n emittedEvents,\n policyRefs,\n testRefs,\n };\n}\n\n/**\n * Extract emitted event refs from operation spec source.\n * Looks for sideEffects.emits array entries.\n */\nexport function extractEmittedEvents(code: string): RefInfo[] | undefined {\n const events: RefInfo[] = [];\n\n // Match inline emit declarations: { name: 'x', version: N, ... }\n const inlinePattern = /\\{\\s*name:\\s*['\"]([^'\"]+)['\"]\\s*,\\s*version:\\s*(\\d+)/g;\n let match;\n while ((match = inlinePattern.exec(code)) !== null) {\n if (match[1] && match[2]) {\n events.push({\n name: match[1],\n version: Number(match[2]),\n });\n }\n }\n\n // Match ref pattern: { ref: SomeEventSpec, ... }\n // We can't fully resolve these without execution, but we can note they exist\n const refPattern = /\\{\\s*ref:\\s*(\\w+)/g;\n while ((match = refPattern.exec(code)) !== null) {\n // Store a placeholder - actual resolution needs runtime\n if (match[1] && !match[1].startsWith('when')) {\n // We can't extract name/version from a variable ref statically\n // This is noted for completeness but won't be fully resolvable\n }\n }\n\n return events.length > 0 ? events : undefined;\n}\n\n/**\n * Extract policy refs from operation spec source.\n */\nexport function extractPolicyRefs(code: string): RefInfo[] | undefined {\n const policies: RefInfo[] = [];\n\n // Match policy ref pattern in policies array\n const policyPattern = /\\{\\s*name:\\s*['\"]([^'\"]+)['\"]\\s*,\\s*version:\\s*(\\d+)/g;\n\n // Only look within policy section\n const policySectionMatch = code.match(/policies\\s*:\\s*\\[([\\s\\S]*?)\\]/);\n if (policySectionMatch?.[1]) {\n let match;\n while ((match = policyPattern.exec(policySectionMatch[1])) !== null) {\n if (match[1] && match[2]) {\n policies.push({\n name: match[1],\n version: Number(match[2]),\n });\n }\n }\n }\n\n return policies.length > 0 ? policies : undefined;\n}\n\n/**\n * Extract test spec refs.\n */\nexport function extractTestRefs(code: string): RefInfo[] | undefined {\n const tests: RefInfo[] = [];\n\n // Look for tests array\n const testsSectionMatch = code.match(/tests\\s*:\\s*\\[([\\s\\S]*?)\\]/);\n if (testsSectionMatch?.[1]) {\n const refPattern = /\\{\\s*name:\\s*['\"]([^'\"]+)['\"]\\s*,\\s*version:\\s*(\\d+)/g;\n let match;\n while ((match = refPattern.exec(testsSectionMatch[1])) !== null) {\n if (match[1] && match[2]) {\n tests.push({\n name: match[1],\n version: Number(match[2]),\n });\n }\n }\n }\n\n return tests.length > 0 ? tests : undefined;\n}\n\nfunction matchStringField(code: string, field: string): string | null {\n const regex = new RegExp(`${escapeRegex(field)}\\\\s*:\\\\s*['\"]([^'\"]+)['\"]`);\n const match = code.match(regex);\n return match?.[1] ?? null;\n}\n\nfunction matchNumberField(code: string, field: string): number | null {\n const regex = new RegExp(`${escapeRegex(field)}\\\\s*:\\\\s*(\\\\d+)`);\n const match = code.match(regex);\n if (!match?.[1]) return null;\n const parsed = Number(match[1]);\n return Number.isFinite(parsed) ? parsed : null;\n}\n\nfunction matchStringArrayField(\n code: string,\n field: string\n): string[] | undefined {\n const regex = new RegExp(`${escapeRegex(field)}\\\\s*:\\\\s*\\\\[([\\\\s\\\\S]*?)\\\\]`);\n const match = code.match(regex);\n if (!match?.[1]) return undefined;\n\n const inner = match[1];\n const items = Array.from(inner.matchAll(/['\"]([^'\"]+)['\"]/g))\n .map((m) => m[1])\n .filter(\n (value): value is string => typeof value === 'string' && value.length > 0\n );\n\n return items.length > 0 ? items : undefined;\n}\n\nfunction isStability(value: string | null): value is Stability {\n return (\n value === 'experimental' ||\n value === 'beta' ||\n value === 'stable' ||\n value === 'deprecated'\n );\n}\n\nfunction escapeRegex(value: string): string {\n return value.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\n}\n\n/**\n * Infer operation kind from source code.\n * First checks for defineCommand/defineQuery usage (which set kind automatically),\n * then falls back to explicit kind field.\n */\nfunction inferOperationKind(code: string): AnalyzedOperationKind {\n // Check for defineCommand/defineQuery usage first (they set kind automatically)\n if (/defineCommand\\s*\\(/.test(code)) return 'command';\n if (/defineQuery\\s*\\(/.test(code)) return 'query';\n // Fall back to explicit kind field\n const kindRaw = matchStringField(code, 'kind');\n return kindRaw === 'command' || kindRaw === 'query' ? kindRaw : 'unknown';\n}\n\n/**\n * Infer operation kind from a specific code block.\n */\nfunction inferOperationKindFromBlock(block: string): AnalyzedOperationKind {\n if (/defineCommand\\s*\\(/.test(block)) return 'command';\n if (/defineQuery\\s*\\(/.test(block)) return 'query';\n const kindRaw = matchStringField(block, 'kind');\n return kindRaw === 'command' || kindRaw === 'query' ? kindRaw : 'unknown';\n}\n\n/**\n * Extract name and version from a meta block.\n */\nfunction extractMetaFromBlock(\n block: string\n): { name: string; version: number } | null {\n const name = matchStringField(block, 'name');\n const version = matchNumberField(block, 'version');\n if (name && version !== null) {\n return { name, version };\n }\n return null;\n}\n\n/**\n * Define function patterns for all spec types.\n */\nconst DEFINE_FUNCTION_PATTERNS = [\n // Operations\n { pattern: /defineCommand\\s*\\(\\s*\\{/g, type: 'operation' as const },\n { pattern: /defineQuery\\s*\\(\\s*\\{/g, type: 'operation' as const },\n // Events\n { pattern: /defineEvent\\s*\\(\\s*\\{/g, type: 'event' as const },\n // Presentations (both v1 and v2 patterns)\n {\n pattern: /:\\s*PresentationDescriptorV2\\s*=\\s*\\{/g,\n type: 'presentation' as const,\n },\n {\n pattern: /:\\s*PresentationDescriptor\\s*=\\s*\\{/g,\n type: 'presentation' as const,\n },\n { pattern: /definePresentation\\s*\\(\\s*\\{/g, type: 'presentation' as const },\n // Capabilities\n { pattern: /defineCapability\\s*\\(\\s*\\{/g, type: 'capability' as const },\n // Workflows\n { pattern: /defineWorkflow\\s*\\(\\s*\\{/g, type: 'workflow' as const },\n // Experiments\n { pattern: /defineExperiment\\s*\\(\\s*\\{/g, type: 'experiment' as const },\n // Integrations\n { pattern: /defineIntegration\\s*\\(\\s*\\{/g, type: 'integration' as const },\n // Knowledge\n { pattern: /defineKnowledge\\s*\\(\\s*\\{/g, type: 'knowledge' as const },\n // Telemetry\n { pattern: /defineTelemetry\\s*\\(\\s*\\{/g, type: 'telemetry' as const },\n // App config\n { pattern: /defineAppConfig\\s*\\(\\s*\\{/g, type: 'app-config' as const },\n // Policy\n { pattern: /definePolicy\\s*\\(\\s*\\{/g, type: 'policy' as const },\n // Test spec\n { pattern: /defineTestSpec\\s*\\(\\s*\\{/g, type: 'test-spec' as const },\n // Data view\n { pattern: /defineDataView\\s*\\(\\s*\\{/g, type: 'data-view' as const },\n // Form\n { pattern: /defineForm\\s*\\(\\s*\\{/g, type: 'form' as const },\n // Migration\n { pattern: /defineMigration\\s*\\(\\s*\\{/g, type: 'migration' as const },\n];\n\n/**\n * Find matching closing brace for an opening brace.\n * Returns the index of the closing brace or -1 if not found.\n */\nfunction findMatchingBrace(code: string, startIndex: number): number {\n let depth = 0;\n let inString = false;\n let stringChar = '';\n\n for (let i = startIndex; i < code.length; i++) {\n const char = code[i];\n const prevChar = i > 0 ? code[i - 1] : '';\n\n // Handle string literals\n if ((char === '\"' || char === \"'\" || char === '`') && prevChar !== '\\\\') {\n if (!inString) {\n inString = true;\n stringChar = char;\n } else if (char === stringChar) {\n inString = false;\n }\n continue;\n }\n\n if (inString) continue;\n\n if (char === '{') {\n depth++;\n } else if (char === '}') {\n depth--;\n if (depth === 0) {\n return i;\n }\n }\n }\n\n return -1;\n}\n\n/**\n * Scan spec source code to extract ALL specs from a file.\n * This function finds multiple spec definitions in a single file.\n */\nexport function scanAllSpecsFromSource(\n code: string,\n filePath: string\n): SpecScanResult[] {\n const results: SpecScanResult[] = [];\n const baseSpecType = inferSpecTypeFromFilePath(filePath);\n\n // Track positions we've already processed to avoid duplicates\n const processedPositions = new Set<number>();\n\n for (const { pattern, type } of DEFINE_FUNCTION_PATTERNS) {\n // Reset the regex lastIndex\n pattern.lastIndex = 0;\n\n let match;\n while ((match = pattern.exec(code)) !== null) {\n const startPos = match.index;\n\n // Skip if we've already processed this position\n if (processedPositions.has(startPos)) continue;\n processedPositions.add(startPos);\n\n // Find the opening brace position\n const openBracePos = code.indexOf('{', startPos);\n if (openBracePos === -1) continue;\n\n // Find the matching closing brace\n const closeBracePos = findMatchingBrace(code, openBracePos);\n if (closeBracePos === -1) continue;\n\n // Extract the block content\n const block = code.slice(openBracePos, closeBracePos + 1);\n\n // Extract meta information\n const meta = extractMetaFromBlock(block);\n if (!meta) continue;\n\n // Extract additional metadata\n const description = matchStringField(block, 'description');\n const stabilityRaw = matchStringField(block, 'stability');\n const stability = isStability(stabilityRaw) ? stabilityRaw : undefined;\n const owners = matchStringArrayField(block, 'owners');\n const tags = matchStringArrayField(block, 'tags');\n\n const hasMeta = /meta\\s*:\\s*{/.test(block);\n const hasIo = /\\bio\\s*:\\s*{/.test(block);\n const hasPolicy = /\\bpolicy\\s*:\\s*{/.test(block);\n const hasPayload = /\\bpayload\\s*:\\s*{/.test(block);\n const hasContent = /\\bcontent\\s*:\\s*{/.test(block);\n const hasDefinition = /\\bdefinition\\s*:\\s*{/.test(block);\n\n // Infer kind for operations\n const kind =\n type === 'operation' ? inferOperationKindFromBlock(block) : 'unknown';\n\n // Extract references from operations\n const emittedEvents =\n type === 'operation' ? extractEmittedEvents(block) : undefined;\n const policyRefs =\n type === 'operation' ? extractPolicyRefs(block) : undefined;\n const testRefs = extractTestRefs(block);\n\n results.push({\n filePath,\n specType: type,\n name: meta.name,\n version: meta.version,\n description: description ?? undefined,\n stability,\n owners,\n tags,\n kind,\n hasMeta,\n hasIo,\n hasPolicy,\n hasPayload,\n hasContent,\n hasDefinition,\n emittedEvents,\n policyRefs,\n testRefs,\n });\n }\n }\n\n // If no specs found via patterns, fall back to file-type based scanning\n // This handles cases where the file uses non-standard patterns\n if (results.length === 0 && baseSpecType !== 'unknown') {\n const fallback = scanSpecSource(code, filePath);\n if (fallback.name && fallback.version !== undefined) {\n results.push(fallback);\n }\n }\n\n return results;\n}\n"],"mappings":"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"}
@@ -6,7 +6,7 @@ function validateSpecStructure(code, fileName) {
6
6
  const errors = [];
7
7
  const warnings = [];
8
8
  if (!/export\s+(const|let)\s+\w+/.test(code)) errors.push("No exported spec found");
9
- if (fileName.includes(".contracts.")) validateOperationSpec(code, errors, warnings);
9
+ if (fileName.includes(".contracts.") || fileName.includes(".contract.") || fileName.includes(".operations.") || fileName.includes(".operation.")) validateOperationSpec(code, errors, warnings);
10
10
  if (fileName.includes(".event.")) validateEventSpec(code, errors, warnings);
11
11
  if (fileName.includes(".presentation.")) validatePresentationSpec(code, errors, warnings);
12
12
  if (fileName.includes(".workflow.")) validateWorkflowSpec(code, errors, warnings);
@@ -1 +1 @@
1
- {"version":3,"file":"spec-structure.js","names":["errors: string[]","warnings: string[]"],"sources":["../../../src/analysis/validate/spec-structure.ts"],"sourcesContent":["/**\n * Spec structure validation utilities.\n * Extracted from cli-contracts/src/commands/validate/spec-checker.ts\n */\n\nimport type { ValidationResult } from '../../types/analysis-types';\n\nexport type { ValidationResult };\n\n/**\n * Validate spec structure based on source code and filename.\n */\nexport function validateSpecStructure(\n code: string,\n fileName: string\n): ValidationResult {\n const errors: string[] = [];\n const warnings: string[] = [];\n\n // Check for required exports\n const hasExport = /export\\s+(const|let)\\s+\\w+/.test(code);\n if (!hasExport) {\n errors.push('No exported spec found');\n }\n\n // Validate operation specs\n if (fileName.includes('.contracts.')) {\n validateOperationSpec(code, errors, warnings);\n }\n\n // Validate event specs\n if (fileName.includes('.event.')) {\n validateEventSpec(code, errors, warnings);\n }\n\n // Validate presentation specs\n if (fileName.includes('.presentation.')) {\n validatePresentationSpec(code, errors, warnings);\n }\n\n if (fileName.includes('.workflow.')) {\n validateWorkflowSpec(code, errors, warnings);\n }\n\n if (fileName.includes('.data-view.')) {\n validateDataViewSpec(code, errors, warnings);\n }\n\n if (fileName.includes('.migration.')) {\n validateMigrationSpec(code, errors, warnings);\n }\n\n if (fileName.includes('.telemetry.')) {\n validateTelemetrySpec(code, errors, warnings);\n }\n\n if (fileName.includes('.experiment.')) {\n validateExperimentSpec(code, errors, warnings);\n }\n\n if (fileName.includes('.app-config.')) {\n validateAppConfigSpec(code, errors, warnings);\n }\n\n // Common validations\n validateCommonFields(code, errors, warnings);\n\n return {\n valid: errors.length === 0,\n errors,\n warnings,\n };\n}\n\n/**\n * Validate operation spec\n */\nfunction validateOperationSpec(\n code: string,\n errors: string[],\n warnings: string[]\n) {\n // Check for defineCommand or defineQuery\n const hasDefine = /define(Command|Query)/.test(code);\n if (!hasDefine) {\n errors.push('Missing defineCommand or defineQuery call');\n }\n\n // Check for required meta fields\n if (!code.includes('meta:')) {\n errors.push('Missing meta section');\n }\n\n if (!code.includes('io:')) {\n errors.push('Missing io section');\n }\n\n if (!code.includes('policy:')) {\n errors.push('Missing policy section');\n }\n\n // Check for name\n if (!code.match(/name:\\s*['\"][^'\"]+['\"]/)) {\n errors.push('Missing or invalid name field');\n }\n\n // Check for version\n if (!code.match(/version:\\s*\\d+/)) {\n errors.push('Missing or invalid version field');\n }\n\n // Check for kind (defineCommand/defineQuery set it automatically, or explicit kind field)\n const hasDefineCommand = /defineCommand\\s*\\(/.test(code);\n const hasDefineQuery = /defineQuery\\s*\\(/.test(code);\n const hasExplicitKind = /kind:\\s*['\"](?:command|query)['\"]/.test(code);\n if (!hasDefineCommand && !hasDefineQuery && !hasExplicitKind) {\n errors.push(\n 'Missing kind: use defineCommand(), defineQuery(), or explicit kind field'\n );\n }\n\n // Warnings\n if (!code.includes('acceptance:')) {\n warnings.push('No acceptance scenarios defined');\n }\n\n if (!code.includes('examples:')) {\n warnings.push('No examples provided');\n }\n\n if (code.includes('TODO')) {\n warnings.push('Contains TODO items that need completion');\n }\n}\n\nfunction validateTelemetrySpec(\n code: string,\n errors: string[],\n warnings: string[]\n) {\n if (!code.match(/:\\s*TelemetrySpec\\s*=/)) {\n errors.push('Missing TelemetrySpec type annotation');\n }\n\n if (!code.match(/meta:\\s*{[\\s\\S]*name:/)) {\n errors.push('TelemetrySpec.meta is required');\n }\n\n if (!code.includes('events:')) {\n errors.push('TelemetrySpec must declare events');\n }\n\n if (!code.match(/privacy:\\s*'(public|internal|pii|sensitive)'/)) {\n warnings.push('No explicit privacy classification found');\n }\n}\n\nfunction validateExperimentSpec(\n code: string,\n errors: string[],\n warnings: string[]\n) {\n if (!code.match(/:\\s*ExperimentSpec\\s*=/)) {\n errors.push('Missing ExperimentSpec type annotation');\n }\n if (!code.includes('controlVariant')) {\n errors.push('ExperimentSpec must declare controlVariant');\n }\n if (!code.includes('variants:')) {\n errors.push('ExperimentSpec must declare variants');\n }\n if (!code.match(/allocation:\\s*{/)) {\n warnings.push('ExperimentSpec missing allocation configuration');\n }\n}\n\nfunction validateAppConfigSpec(\n code: string,\n errors: string[],\n warnings: string[]\n) {\n if (!code.match(/:\\s*AppBlueprintSpec\\s*=/)) {\n errors.push('Missing AppBlueprintSpec type annotation');\n }\n if (!code.includes('meta:')) {\n errors.push('AppBlueprintSpec must define meta');\n }\n if (!code.includes('appId')) {\n warnings.push('AppBlueprint meta missing appId assignment');\n }\n if (!code.includes('capabilities')) {\n warnings.push('App blueprint spec does not declare capabilities');\n }\n}\n\n/**\n * Validate event spec\n */\nfunction validateEventSpec(code: string, errors: string[], warnings: string[]) {\n if (!code.includes('defineEvent')) {\n errors.push('Missing defineEvent call');\n }\n\n if (!code.match(/name:\\s*['\"][^'\"]+['\"]/)) {\n errors.push('Missing or invalid name field');\n }\n\n if (!code.match(/version:\\s*\\d+/)) {\n errors.push('Missing or invalid version field');\n }\n\n if (!code.includes('payload:')) {\n errors.push('Missing payload field');\n }\n\n // Check for past tense naming convention\n const nameMatch = code.match(/name:\\s*['\"]([^'\"]+)['\"]/);\n if (nameMatch?.[1]) {\n const eventName = nameMatch[1].split('.').pop() ?? '';\n if (!eventName.match(/(ed|created|updated|deleted|completed)$/i)) {\n warnings.push(\n 'Event name should use past tense (e.g., \"created\", \"updated\")'\n );\n }\n }\n}\n\n/**\n * Validate presentation spec\n */\nfunction validatePresentationSpec(\n code: string,\n errors: string[],\n _warnings: string[]\n) {\n if (!code.match(/:\\s*PresentationSpec\\s*=/)) {\n errors.push('Missing PresentationSpec type annotation');\n }\n\n if (!code.includes('meta:')) {\n errors.push('Missing meta section');\n }\n\n if (!code.includes('content:')) {\n errors.push('Missing content section');\n }\n\n if (!code.match(/kind:\\s*['\"](?:web_component|markdown|data)['\"]/)) {\n errors.push('Missing or invalid kind field');\n }\n}\n\nfunction validateWorkflowSpec(\n code: string,\n errors: string[],\n warnings: string[]\n) {\n if (!code.match(/:\\s*WorkflowSpec\\s*=/)) {\n errors.push('Missing WorkflowSpec type annotation');\n }\n\n if (!code.includes('definition:')) {\n errors.push('Missing definition section');\n }\n\n if (!code.includes('steps:')) {\n errors.push('Workflow must declare steps');\n }\n\n if (!code.includes('transitions:')) {\n warnings.push(\n 'No transitions declared; workflow will complete after first step.'\n );\n }\n\n if (!code.match(/title:\\s*['\"][^'\"]+['\"]/)) {\n warnings.push('Missing workflow title');\n }\n\n if (!code.match(/domain:\\s*['\"][^'\"]+['\"]/)) {\n warnings.push('Missing domain field');\n }\n\n if (code.includes('TODO')) {\n warnings.push('Contains TODO items that need completion');\n }\n}\n\nfunction validateMigrationSpec(\n code: string,\n errors: string[],\n warnings: string[]\n) {\n if (!code.match(/:\\s*MigrationSpec\\s*=/)) {\n errors.push('Missing MigrationSpec type annotation');\n }\n\n if (!code.includes('plan:')) {\n errors.push('Missing plan section');\n } else {\n if (!code.includes('up:')) {\n errors.push('Migration must define at least one up step');\n }\n }\n\n if (!code.match(/name:\\s*['\"][^'\"]+['\"]/)) {\n errors.push('Missing or invalid migration name');\n }\n\n if (!code.match(/version:\\s*\\d+/)) {\n errors.push('Missing or invalid migration version');\n }\n\n if (code.includes('TODO')) {\n warnings.push('Contains TODO items that need completion');\n }\n}\n\n/**\n * Validate common fields across all spec types\n */\nfunction validateCommonFields(\n code: string,\n errors: string[],\n warnings: string[]\n) {\n // Check for SchemaModel import\n if (\n code.includes('SchemaModel') &&\n !code.includes(\"from '@lssm/lib.schema'\")\n ) {\n errors.push('Missing import for SchemaModel from @lssm/lib.schema');\n }\n\n // Check for contracts import\n if (!code.includes(\"from '@lssm/lib.contracts'\")) {\n errors.push('Missing import from @lssm/lib.contracts');\n }\n\n // Check owners format\n const ownersMatch = code.match(/owners:\\s*\\[(.*?)\\]/s);\n if (ownersMatch?.[1]) {\n const ownersContent = ownersMatch[1];\n if (!ownersContent.includes('@')) {\n warnings.push('Owners should start with @ (e.g., \"@team\")');\n }\n }\n\n // Check for stability\n if (\n !code.match(/stability:\\s*['\"](?:experimental|beta|stable|deprecated)['\"]/)\n ) {\n warnings.push('Missing or invalid stability field');\n }\n}\n\nfunction validateDataViewSpec(\n code: string,\n errors: string[],\n _warnings: string[]\n) {\n if (!code.match(/:\\s*DataViewSpec\\s*=/)) {\n errors.push('Missing DataViewSpec type annotation');\n }\n if (!code.includes('meta:')) {\n errors.push('Missing meta section');\n }\n if (!code.includes('source:')) {\n errors.push('Missing source section');\n }\n if (!code.includes('view:')) {\n errors.push('Missing view section');\n }\n if (!code.match(/kind:\\s*['\"](list|table|detail|grid)['\"]/)) {\n errors.push('Missing or invalid view.kind (list/table/detail/grid)');\n }\n if (!code.match(/fields:\\s*\\[/)) {\n // Note: _warnings unused in this case, but kept for consistency with other validators\n errors.push('No fields defined for data view');\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"spec-structure.js","names":["errors: string[]","warnings: string[]"],"sources":["../../../src/analysis/validate/spec-structure.ts"],"sourcesContent":["/**\n * Spec structure validation utilities.\n * Extracted from cli-contracts/src/commands/validate/spec-checker.ts\n */\n\nimport type { ValidationResult } from '../../types/analysis-types';\n\nexport type { ValidationResult };\n\n/**\n * Validate spec structure based on source code and filename.\n */\nexport function validateSpecStructure(\n code: string,\n fileName: string\n): ValidationResult {\n const errors: string[] = [];\n const warnings: string[] = [];\n\n // Check for required exports\n const hasExport = /export\\s+(const|let)\\s+\\w+/.test(code);\n if (!hasExport) {\n errors.push('No exported spec found');\n }\n\n // Validate operation specs\n if (\n fileName.includes('.contracts.') ||\n fileName.includes('.contract.') ||\n fileName.includes('.operations.') ||\n fileName.includes('.operation.')\n ) {\n validateOperationSpec(code, errors, warnings);\n }\n\n // Validate event specs\n if (fileName.includes('.event.')) {\n validateEventSpec(code, errors, warnings);\n }\n\n // Validate presentation specs\n if (fileName.includes('.presentation.')) {\n validatePresentationSpec(code, errors, warnings);\n }\n\n if (fileName.includes('.workflow.')) {\n validateWorkflowSpec(code, errors, warnings);\n }\n\n if (fileName.includes('.data-view.')) {\n validateDataViewSpec(code, errors, warnings);\n }\n\n if (fileName.includes('.migration.')) {\n validateMigrationSpec(code, errors, warnings);\n }\n\n if (fileName.includes('.telemetry.')) {\n validateTelemetrySpec(code, errors, warnings);\n }\n\n if (fileName.includes('.experiment.')) {\n validateExperimentSpec(code, errors, warnings);\n }\n\n if (fileName.includes('.app-config.')) {\n validateAppConfigSpec(code, errors, warnings);\n }\n\n // Common validations\n validateCommonFields(code, errors, warnings);\n\n return {\n valid: errors.length === 0,\n errors,\n warnings,\n };\n}\n\n/**\n * Validate operation spec\n */\nfunction validateOperationSpec(\n code: string,\n errors: string[],\n warnings: string[]\n) {\n // Check for defineCommand or defineQuery\n const hasDefine = /define(Command|Query)/.test(code);\n if (!hasDefine) {\n errors.push('Missing defineCommand or defineQuery call');\n }\n\n // Check for required meta fields\n if (!code.includes('meta:')) {\n errors.push('Missing meta section');\n }\n\n if (!code.includes('io:')) {\n errors.push('Missing io section');\n }\n\n if (!code.includes('policy:')) {\n errors.push('Missing policy section');\n }\n\n // Check for name\n if (!code.match(/name:\\s*['\"][^'\"]+['\"]/)) {\n errors.push('Missing or invalid name field');\n }\n\n // Check for version\n if (!code.match(/version:\\s*\\d+/)) {\n errors.push('Missing or invalid version field');\n }\n\n // Check for kind (defineCommand/defineQuery set it automatically, or explicit kind field)\n const hasDefineCommand = /defineCommand\\s*\\(/.test(code);\n const hasDefineQuery = /defineQuery\\s*\\(/.test(code);\n const hasExplicitKind = /kind:\\s*['\"](?:command|query)['\"]/.test(code);\n if (!hasDefineCommand && !hasDefineQuery && !hasExplicitKind) {\n errors.push(\n 'Missing kind: use defineCommand(), defineQuery(), or explicit kind field'\n );\n }\n\n // Warnings\n if (!code.includes('acceptance:')) {\n warnings.push('No acceptance scenarios defined');\n }\n\n if (!code.includes('examples:')) {\n warnings.push('No examples provided');\n }\n\n if (code.includes('TODO')) {\n warnings.push('Contains TODO items that need completion');\n }\n}\n\nfunction validateTelemetrySpec(\n code: string,\n errors: string[],\n warnings: string[]\n) {\n if (!code.match(/:\\s*TelemetrySpec\\s*=/)) {\n errors.push('Missing TelemetrySpec type annotation');\n }\n\n if (!code.match(/meta:\\s*{[\\s\\S]*name:/)) {\n errors.push('TelemetrySpec.meta is required');\n }\n\n if (!code.includes('events:')) {\n errors.push('TelemetrySpec must declare events');\n }\n\n if (!code.match(/privacy:\\s*'(public|internal|pii|sensitive)'/)) {\n warnings.push('No explicit privacy classification found');\n }\n}\n\nfunction validateExperimentSpec(\n code: string,\n errors: string[],\n warnings: string[]\n) {\n if (!code.match(/:\\s*ExperimentSpec\\s*=/)) {\n errors.push('Missing ExperimentSpec type annotation');\n }\n if (!code.includes('controlVariant')) {\n errors.push('ExperimentSpec must declare controlVariant');\n }\n if (!code.includes('variants:')) {\n errors.push('ExperimentSpec must declare variants');\n }\n if (!code.match(/allocation:\\s*{/)) {\n warnings.push('ExperimentSpec missing allocation configuration');\n }\n}\n\nfunction validateAppConfigSpec(\n code: string,\n errors: string[],\n warnings: string[]\n) {\n if (!code.match(/:\\s*AppBlueprintSpec\\s*=/)) {\n errors.push('Missing AppBlueprintSpec type annotation');\n }\n if (!code.includes('meta:')) {\n errors.push('AppBlueprintSpec must define meta');\n }\n if (!code.includes('appId')) {\n warnings.push('AppBlueprint meta missing appId assignment');\n }\n if (!code.includes('capabilities')) {\n warnings.push('App blueprint spec does not declare capabilities');\n }\n}\n\n/**\n * Validate event spec\n */\nfunction validateEventSpec(code: string, errors: string[], warnings: string[]) {\n if (!code.includes('defineEvent')) {\n errors.push('Missing defineEvent call');\n }\n\n if (!code.match(/name:\\s*['\"][^'\"]+['\"]/)) {\n errors.push('Missing or invalid name field');\n }\n\n if (!code.match(/version:\\s*\\d+/)) {\n errors.push('Missing or invalid version field');\n }\n\n if (!code.includes('payload:')) {\n errors.push('Missing payload field');\n }\n\n // Check for past tense naming convention\n const nameMatch = code.match(/name:\\s*['\"]([^'\"]+)['\"]/);\n if (nameMatch?.[1]) {\n const eventName = nameMatch[1].split('.').pop() ?? '';\n if (!eventName.match(/(ed|created|updated|deleted|completed)$/i)) {\n warnings.push(\n 'Event name should use past tense (e.g., \"created\", \"updated\")'\n );\n }\n }\n}\n\n/**\n * Validate presentation spec\n */\nfunction validatePresentationSpec(\n code: string,\n errors: string[],\n _warnings: string[]\n) {\n if (!code.match(/:\\s*PresentationSpec\\s*=/)) {\n errors.push('Missing PresentationSpec type annotation');\n }\n\n if (!code.includes('meta:')) {\n errors.push('Missing meta section');\n }\n\n if (!code.includes('content:')) {\n errors.push('Missing content section');\n }\n\n if (!code.match(/kind:\\s*['\"](?:web_component|markdown|data)['\"]/)) {\n errors.push('Missing or invalid kind field');\n }\n}\n\nfunction validateWorkflowSpec(\n code: string,\n errors: string[],\n warnings: string[]\n) {\n if (!code.match(/:\\s*WorkflowSpec\\s*=/)) {\n errors.push('Missing WorkflowSpec type annotation');\n }\n\n if (!code.includes('definition:')) {\n errors.push('Missing definition section');\n }\n\n if (!code.includes('steps:')) {\n errors.push('Workflow must declare steps');\n }\n\n if (!code.includes('transitions:')) {\n warnings.push(\n 'No transitions declared; workflow will complete after first step.'\n );\n }\n\n if (!code.match(/title:\\s*['\"][^'\"]+['\"]/)) {\n warnings.push('Missing workflow title');\n }\n\n if (!code.match(/domain:\\s*['\"][^'\"]+['\"]/)) {\n warnings.push('Missing domain field');\n }\n\n if (code.includes('TODO')) {\n warnings.push('Contains TODO items that need completion');\n }\n}\n\nfunction validateMigrationSpec(\n code: string,\n errors: string[],\n warnings: string[]\n) {\n if (!code.match(/:\\s*MigrationSpec\\s*=/)) {\n errors.push('Missing MigrationSpec type annotation');\n }\n\n if (!code.includes('plan:')) {\n errors.push('Missing plan section');\n } else {\n if (!code.includes('up:')) {\n errors.push('Migration must define at least one up step');\n }\n }\n\n if (!code.match(/name:\\s*['\"][^'\"]+['\"]/)) {\n errors.push('Missing or invalid migration name');\n }\n\n if (!code.match(/version:\\s*\\d+/)) {\n errors.push('Missing or invalid migration version');\n }\n\n if (code.includes('TODO')) {\n warnings.push('Contains TODO items that need completion');\n }\n}\n\n/**\n * Validate common fields across all spec types\n */\nfunction validateCommonFields(\n code: string,\n errors: string[],\n warnings: string[]\n) {\n // Check for SchemaModel import\n if (\n code.includes('SchemaModel') &&\n !code.includes(\"from '@lssm/lib.schema'\")\n ) {\n errors.push('Missing import for SchemaModel from @lssm/lib.schema');\n }\n\n // Check for contracts import\n if (!code.includes(\"from '@lssm/lib.contracts'\")) {\n errors.push('Missing import from @lssm/lib.contracts');\n }\n\n // Check owners format\n const ownersMatch = code.match(/owners:\\s*\\[(.*?)\\]/s);\n if (ownersMatch?.[1]) {\n const ownersContent = ownersMatch[1];\n if (!ownersContent.includes('@')) {\n warnings.push('Owners should start with @ (e.g., \"@team\")');\n }\n }\n\n // Check for stability\n if (\n !code.match(/stability:\\s*['\"](?:experimental|beta|stable|deprecated)['\"]/)\n ) {\n warnings.push('Missing or invalid stability field');\n }\n}\n\nfunction validateDataViewSpec(\n code: string,\n errors: string[],\n _warnings: string[]\n) {\n if (!code.match(/:\\s*DataViewSpec\\s*=/)) {\n errors.push('Missing DataViewSpec type annotation');\n }\n if (!code.includes('meta:')) {\n errors.push('Missing meta section');\n }\n if (!code.includes('source:')) {\n errors.push('Missing source section');\n }\n if (!code.includes('view:')) {\n errors.push('Missing view section');\n }\n if (!code.match(/kind:\\s*['\"](list|table|detail|grid)['\"]/)) {\n errors.push('Missing or invalid view.kind (list/table/detail/grid)');\n }\n if (!code.match(/fields:\\s*\\[/)) {\n // Note: _warnings unused in this case, but kept for consistency with other validators\n errors.push('No fields defined for data view');\n }\n}\n"],"mappings":";;;;AAYA,SAAgB,sBACd,MACA,UACkB;CAClB,MAAMA,SAAmB,EAAE;CAC3B,MAAMC,WAAqB,EAAE;AAI7B,KAAI,CADc,6BAA6B,KAAK,KAAK,CAEvD,QAAO,KAAK,yBAAyB;AAIvC,KACE,SAAS,SAAS,cAAc,IAChC,SAAS,SAAS,aAAa,IAC/B,SAAS,SAAS,eAAe,IACjC,SAAS,SAAS,cAAc,CAEhC,uBAAsB,MAAM,QAAQ,SAAS;AAI/C,KAAI,SAAS,SAAS,UAAU,CAC9B,mBAAkB,MAAM,QAAQ,SAAS;AAI3C,KAAI,SAAS,SAAS,iBAAiB,CACrC,0BAAyB,MAAM,QAAQ,SAAS;AAGlD,KAAI,SAAS,SAAS,aAAa,CACjC,sBAAqB,MAAM,QAAQ,SAAS;AAG9C,KAAI,SAAS,SAAS,cAAc,CAClC,sBAAqB,MAAM,QAAQ,SAAS;AAG9C,KAAI,SAAS,SAAS,cAAc,CAClC,uBAAsB,MAAM,QAAQ,SAAS;AAG/C,KAAI,SAAS,SAAS,cAAc,CAClC,uBAAsB,MAAM,QAAQ,SAAS;AAG/C,KAAI,SAAS,SAAS,eAAe,CACnC,wBAAuB,MAAM,QAAQ,SAAS;AAGhD,KAAI,SAAS,SAAS,eAAe,CACnC,uBAAsB,MAAM,QAAQ,SAAS;AAI/C,sBAAqB,MAAM,QAAQ,SAAS;AAE5C,QAAO;EACL,OAAO,OAAO,WAAW;EACzB;EACA;EACD;;;;;AAMH,SAAS,sBACP,MACA,QACA,UACA;AAGA,KAAI,CADc,wBAAwB,KAAK,KAAK,CAElD,QAAO,KAAK,4CAA4C;AAI1D,KAAI,CAAC,KAAK,SAAS,QAAQ,CACzB,QAAO,KAAK,uBAAuB;AAGrC,KAAI,CAAC,KAAK,SAAS,MAAM,CACvB,QAAO,KAAK,qBAAqB;AAGnC,KAAI,CAAC,KAAK,SAAS,UAAU,CAC3B,QAAO,KAAK,yBAAyB;AAIvC,KAAI,CAAC,KAAK,MAAM,yBAAyB,CACvC,QAAO,KAAK,gCAAgC;AAI9C,KAAI,CAAC,KAAK,MAAM,iBAAiB,CAC/B,QAAO,KAAK,mCAAmC;CAIjD,MAAM,mBAAmB,qBAAqB,KAAK,KAAK;CACxD,MAAM,iBAAiB,mBAAmB,KAAK,KAAK;CACpD,MAAM,kBAAkB,oCAAoC,KAAK,KAAK;AACtE,KAAI,CAAC,oBAAoB,CAAC,kBAAkB,CAAC,gBAC3C,QAAO,KACL,2EACD;AAIH,KAAI,CAAC,KAAK,SAAS,cAAc,CAC/B,UAAS,KAAK,kCAAkC;AAGlD,KAAI,CAAC,KAAK,SAAS,YAAY,CAC7B,UAAS,KAAK,uBAAuB;AAGvC,KAAI,KAAK,SAAS,OAAO,CACvB,UAAS,KAAK,2CAA2C;;AAI7D,SAAS,sBACP,MACA,QACA,UACA;AACA,KAAI,CAAC,KAAK,MAAM,wBAAwB,CACtC,QAAO,KAAK,wCAAwC;AAGtD,KAAI,CAAC,KAAK,MAAM,wBAAwB,CACtC,QAAO,KAAK,iCAAiC;AAG/C,KAAI,CAAC,KAAK,SAAS,UAAU,CAC3B,QAAO,KAAK,oCAAoC;AAGlD,KAAI,CAAC,KAAK,MAAM,+CAA+C,CAC7D,UAAS,KAAK,2CAA2C;;AAI7D,SAAS,uBACP,MACA,QACA,UACA;AACA,KAAI,CAAC,KAAK,MAAM,yBAAyB,CACvC,QAAO,KAAK,yCAAyC;AAEvD,KAAI,CAAC,KAAK,SAAS,iBAAiB,CAClC,QAAO,KAAK,6CAA6C;AAE3D,KAAI,CAAC,KAAK,SAAS,YAAY,CAC7B,QAAO,KAAK,uCAAuC;AAErD,KAAI,CAAC,KAAK,MAAM,kBAAkB,CAChC,UAAS,KAAK,kDAAkD;;AAIpE,SAAS,sBACP,MACA,QACA,UACA;AACA,KAAI,CAAC,KAAK,MAAM,2BAA2B,CACzC,QAAO,KAAK,2CAA2C;AAEzD,KAAI,CAAC,KAAK,SAAS,QAAQ,CACzB,QAAO,KAAK,oCAAoC;AAElD,KAAI,CAAC,KAAK,SAAS,QAAQ,CACzB,UAAS,KAAK,6CAA6C;AAE7D,KAAI,CAAC,KAAK,SAAS,eAAe,CAChC,UAAS,KAAK,mDAAmD;;;;;AAOrE,SAAS,kBAAkB,MAAc,QAAkB,UAAoB;AAC7E,KAAI,CAAC,KAAK,SAAS,cAAc,CAC/B,QAAO,KAAK,2BAA2B;AAGzC,KAAI,CAAC,KAAK,MAAM,yBAAyB,CACvC,QAAO,KAAK,gCAAgC;AAG9C,KAAI,CAAC,KAAK,MAAM,iBAAiB,CAC/B,QAAO,KAAK,mCAAmC;AAGjD,KAAI,CAAC,KAAK,SAAS,WAAW,CAC5B,QAAO,KAAK,wBAAwB;CAItC,MAAM,YAAY,KAAK,MAAM,2BAA2B;AACxD,KAAI,YAAY,IAEd;MAAI,EADc,UAAU,GAAG,MAAM,IAAI,CAAC,KAAK,IAAI,IACpC,MAAM,2CAA2C,CAC9D,UAAS,KACP,oEACD;;;;;;AAQP,SAAS,yBACP,MACA,QACA,WACA;AACA,KAAI,CAAC,KAAK,MAAM,2BAA2B,CACzC,QAAO,KAAK,2CAA2C;AAGzD,KAAI,CAAC,KAAK,SAAS,QAAQ,CACzB,QAAO,KAAK,uBAAuB;AAGrC,KAAI,CAAC,KAAK,SAAS,WAAW,CAC5B,QAAO,KAAK,0BAA0B;AAGxC,KAAI,CAAC,KAAK,MAAM,kDAAkD,CAChE,QAAO,KAAK,gCAAgC;;AAIhD,SAAS,qBACP,MACA,QACA,UACA;AACA,KAAI,CAAC,KAAK,MAAM,uBAAuB,CACrC,QAAO,KAAK,uCAAuC;AAGrD,KAAI,CAAC,KAAK,SAAS,cAAc,CAC/B,QAAO,KAAK,6BAA6B;AAG3C,KAAI,CAAC,KAAK,SAAS,SAAS,CAC1B,QAAO,KAAK,8BAA8B;AAG5C,KAAI,CAAC,KAAK,SAAS,eAAe,CAChC,UAAS,KACP,oEACD;AAGH,KAAI,CAAC,KAAK,MAAM,0BAA0B,CACxC,UAAS,KAAK,yBAAyB;AAGzC,KAAI,CAAC,KAAK,MAAM,2BAA2B,CACzC,UAAS,KAAK,uBAAuB;AAGvC,KAAI,KAAK,SAAS,OAAO,CACvB,UAAS,KAAK,2CAA2C;;AAI7D,SAAS,sBACP,MACA,QACA,UACA;AACA,KAAI,CAAC,KAAK,MAAM,wBAAwB,CACtC,QAAO,KAAK,wCAAwC;AAGtD,KAAI,CAAC,KAAK,SAAS,QAAQ,CACzB,QAAO,KAAK,uBAAuB;UAE/B,CAAC,KAAK,SAAS,MAAM,CACvB,QAAO,KAAK,6CAA6C;AAI7D,KAAI,CAAC,KAAK,MAAM,yBAAyB,CACvC,QAAO,KAAK,oCAAoC;AAGlD,KAAI,CAAC,KAAK,MAAM,iBAAiB,CAC/B,QAAO,KAAK,uCAAuC;AAGrD,KAAI,KAAK,SAAS,OAAO,CACvB,UAAS,KAAK,2CAA2C;;;;;AAO7D,SAAS,qBACP,MACA,QACA,UACA;AAEA,KACE,KAAK,SAAS,cAAc,IAC5B,CAAC,KAAK,SAAS,0BAA0B,CAEzC,QAAO,KAAK,uDAAuD;AAIrE,KAAI,CAAC,KAAK,SAAS,6BAA6B,CAC9C,QAAO,KAAK,0CAA0C;CAIxD,MAAM,cAAc,KAAK,MAAM,uBAAuB;AACtD,KAAI,cAAc,IAEhB;MAAI,CADkB,YAAY,GACf,SAAS,IAAI,CAC9B,UAAS,KAAK,+CAA6C;;AAK/D,KACE,CAAC,KAAK,MAAM,+DAA+D,CAE3E,UAAS,KAAK,qCAAqC;;AAIvD,SAAS,qBACP,MACA,QACA,WACA;AACA,KAAI,CAAC,KAAK,MAAM,uBAAuB,CACrC,QAAO,KAAK,uCAAuC;AAErD,KAAI,CAAC,KAAK,SAAS,QAAQ,CACzB,QAAO,KAAK,uBAAuB;AAErC,KAAI,CAAC,KAAK,SAAS,UAAU,CAC3B,QAAO,KAAK,yBAAyB;AAEvC,KAAI,CAAC,KAAK,SAAS,QAAQ,CACzB,QAAO,KAAK,uBAAuB;AAErC,KAAI,CAAC,KAAK,MAAM,2CAA2C,CACzD,QAAO,KAAK,wDAAwD;AAEtE,KAAI,CAAC,KAAK,MAAM,eAAe,CAE7B,QAAO,KAAK,kCAAkC"}
package/dist/index.d.ts CHANGED
@@ -3,6 +3,7 @@ import { AnalyzedOperationKind, AnalyzedSpecType, ContractGraph, ContractNode, E
3
3
  import { AIGenerationOptions, CodeGenerationContext, DEFAULT_WORKSPACE_CONFIG, GenerationResult, GenerationTarget, SpecBuildType, SpecGenerationContext, TestTarget, WorkspaceConfig } from "./types/generation-types.js";
4
4
  import { extractEmittedEvents, extractPolicyRefs, extractTestRefs, inferSpecTypeFromFilePath, scanAllSpecsFromSource, scanSpecSource } from "./analysis/spec-scan.js";
5
5
  import { isFeatureFile, scanFeatureSource } from "./analysis/feature-scan.js";
6
+ import { GroupKeyFn, GroupedItems, SpecFilter, SpecGroupingStrategies, filterFeatures, filterSpecs, getUniqueSpecDomains, getUniqueSpecOwners, getUniqueSpecTags, groupSpecs, groupSpecsToArray } from "./analysis/grouping.js";
6
7
  import { computeSemanticDiff } from "./analysis/diff/semantic.js";
7
8
  import { addContractNode, buildReverseEdges, createContractGraph, detectCycles, findMissingDependencies, toDot } from "./analysis/deps/graph.js";
8
9
  import { parseImportedSpecNames } from "./analysis/deps/parse-imports.js";
@@ -23,4 +24,4 @@ import { generateComponentTemplate, generateHandlerTemplate, generateTestTemplat
23
24
  import { capitalize, escapeString, toCamelCase, toKebabCase, toPascalCase } from "./templates/utils.js";
24
25
  import { addExampleContext, buildEventSpecPrompt, buildOperationSpecPrompt, buildPresentationSpecPrompt, getSystemPrompt } from "./ai/spec-creation.js";
25
26
  import { buildComponentPrompt, buildFormPrompt, buildHandlerPrompt, buildTestPrompt, getCodeGenSystemPrompt } from "./ai/code-generation.js";
26
- export { AIGenerationOptions, AnalyzedOperationKind, AnalyzedSpecType, AppBlueprintSpecData, AppConfigFeatureFlagData, AppConfigMappingData, AppRouteConfigData, BaseSpecData, CodeGenerationContext, ContractGraph, ContractNode, DEFAULT_WORKSPACE_CONFIG, DataViewFieldData, DataViewKind, DataViewSpecData, EventSpecData, ExperimentAllocationData, ExperimentMetricData, ExperimentSpecData, ExperimentVariantData, ExperimentVariantOverrideData, ExtractedRef, FeatureScanResult, FeatureSpecData, FormSpecData, GenerationResult, GenerationTarget, IntegrationCapabilityRefData, IntegrationCapabilityRequirementData, IntegrationCategoryData, IntegrationConfigFieldData, IntegrationConfigFieldType, IntegrationHealthCheckMethod, IntegrationOwnershipModeData, IntegrationSecretFieldData, IntegrationSpecData, KnowledgeCategoryData, KnowledgeRetentionData, KnowledgeSpaceSpecData, KnowledgeTrustLevel, MigrationSpecData, MigrationStepData, MigrationStepKind, OpKind, OperationSpecData, PresentationKind, PresentationSpecData, RandomAllocationData, RefInfo, RefType, SemanticDiffItem, SemanticDiffOptions, SemanticDiffType, SpecBuildType, SpecGenerationContext, SpecScanResult, SpecType, Stability, StepType, StickyAllocationData, TargetedAllocationData, TargetingRuleData, TelemetryAnomalyRuleData, TelemetryEventData, TelemetryPrivacy, TelemetryPropertyData, TelemetryProviderData, TelemetrySpecData, TestTarget, ValidationResult, WorkflowSpecData, WorkflowStepData, WorkflowTransitionData, WorkspaceConfig, addContractNode, addExampleContext, buildComponentPrompt, buildEventSpecPrompt, buildFormPrompt, buildHandlerPrompt, buildOperationSpecPrompt, buildPresentationSpecPrompt, buildReverseEdges, buildTestPrompt, capitalize, computeSemanticDiff, createContractGraph, detectCycles, escapeString, extractEmittedEvents, extractPolicyRefs, extractTestRefs, findMissingDependencies, generateAppBlueprintSpec, generateComponentTemplate, generateDataViewSpec, generateEventSpec, generateExperimentSpec, generateHandlerTemplate, generateIntegrationSpec, generateKnowledgeSpaceSpec, generateMigrationSpec, generateOperationSpec, generatePresentationSpec, generateTelemetrySpec, generateTestTemplate, generateWorkflowRunnerTemplate, generateWorkflowSpec, getCodeGenSystemPrompt, getSystemPrompt, inferSpecTypeFromFilePath, isFeatureFile, parseImportedSpecNames, scanAllSpecsFromSource, scanFeatureSource, scanSpecSource, toCamelCase, toDot, toKebabCase, toPascalCase, validateSpecStructure };
27
+ export { AIGenerationOptions, AnalyzedOperationKind, AnalyzedSpecType, AppBlueprintSpecData, AppConfigFeatureFlagData, AppConfigMappingData, AppRouteConfigData, BaseSpecData, CodeGenerationContext, ContractGraph, ContractNode, DEFAULT_WORKSPACE_CONFIG, DataViewFieldData, DataViewKind, DataViewSpecData, EventSpecData, ExperimentAllocationData, ExperimentMetricData, ExperimentSpecData, ExperimentVariantData, ExperimentVariantOverrideData, ExtractedRef, FeatureScanResult, FeatureSpecData, FormSpecData, GenerationResult, GenerationTarget, GroupKeyFn, GroupedItems, IntegrationCapabilityRefData, IntegrationCapabilityRequirementData, IntegrationCategoryData, IntegrationConfigFieldData, IntegrationConfigFieldType, IntegrationHealthCheckMethod, IntegrationOwnershipModeData, IntegrationSecretFieldData, IntegrationSpecData, KnowledgeCategoryData, KnowledgeRetentionData, KnowledgeSpaceSpecData, KnowledgeTrustLevel, MigrationSpecData, MigrationStepData, MigrationStepKind, OpKind, OperationSpecData, PresentationKind, PresentationSpecData, RandomAllocationData, RefInfo, RefType, SemanticDiffItem, SemanticDiffOptions, SemanticDiffType, SpecBuildType, SpecFilter, SpecGenerationContext, SpecGroupingStrategies, SpecScanResult, SpecType, Stability, StepType, StickyAllocationData, TargetedAllocationData, TargetingRuleData, TelemetryAnomalyRuleData, TelemetryEventData, TelemetryPrivacy, TelemetryPropertyData, TelemetryProviderData, TelemetrySpecData, TestTarget, ValidationResult, WorkflowSpecData, WorkflowStepData, WorkflowTransitionData, WorkspaceConfig, addContractNode, addExampleContext, buildComponentPrompt, buildEventSpecPrompt, buildFormPrompt, buildHandlerPrompt, buildOperationSpecPrompt, buildPresentationSpecPrompt, buildReverseEdges, buildTestPrompt, capitalize, computeSemanticDiff, createContractGraph, detectCycles, escapeString, extractEmittedEvents, extractPolicyRefs, extractTestRefs, filterFeatures, filterSpecs, findMissingDependencies, generateAppBlueprintSpec, generateComponentTemplate, generateDataViewSpec, generateEventSpec, generateExperimentSpec, generateHandlerTemplate, generateIntegrationSpec, generateKnowledgeSpaceSpec, generateMigrationSpec, generateOperationSpec, generatePresentationSpec, generateTelemetrySpec, generateTestTemplate, generateWorkflowRunnerTemplate, generateWorkflowSpec, getCodeGenSystemPrompt, getSystemPrompt, getUniqueSpecDomains, getUniqueSpecOwners, getUniqueSpecTags, groupSpecs, groupSpecsToArray, inferSpecTypeFromFilePath, isFeatureFile, parseImportedSpecNames, scanAllSpecsFromSource, scanFeatureSource, scanSpecSource, toCamelCase, toDot, toKebabCase, toPascalCase, validateSpecStructure };
package/dist/index.js CHANGED
@@ -1,6 +1,7 @@
1
1
  import { DEFAULT_WORKSPACE_CONFIG } from "./types/generation-types.js";
2
2
  import { extractEmittedEvents, extractPolicyRefs, extractTestRefs, inferSpecTypeFromFilePath, scanAllSpecsFromSource, scanSpecSource } from "./analysis/spec-scan.js";
3
3
  import { isFeatureFile, scanFeatureSource } from "./analysis/feature-scan.js";
4
+ import { SpecGroupingStrategies, filterFeatures, filterSpecs, getUniqueSpecDomains, getUniqueSpecOwners, getUniqueSpecTags, groupSpecs, groupSpecsToArray } from "./analysis/grouping.js";
4
5
  import { computeSemanticDiff } from "./analysis/diff/semantic.js";
5
6
  import { addContractNode, buildReverseEdges, createContractGraph, detectCycles, findMissingDependencies, toDot } from "./analysis/deps/graph.js";
6
7
  import { parseImportedSpecNames } from "./analysis/deps/parse-imports.js";
@@ -22,4 +23,4 @@ import { generateComponentTemplate, generateHandlerTemplate, generateTestTemplat
22
23
  import { addExampleContext, buildEventSpecPrompt, buildOperationSpecPrompt, buildPresentationSpecPrompt, getSystemPrompt } from "./ai/spec-creation.js";
23
24
  import { buildComponentPrompt, buildFormPrompt, buildHandlerPrompt, buildTestPrompt, getCodeGenSystemPrompt } from "./ai/code-generation.js";
24
25
 
25
- export { DEFAULT_WORKSPACE_CONFIG, addContractNode, addExampleContext, buildComponentPrompt, buildEventSpecPrompt, buildFormPrompt, buildHandlerPrompt, buildOperationSpecPrompt, buildPresentationSpecPrompt, buildReverseEdges, buildTestPrompt, capitalize, computeSemanticDiff, createContractGraph, detectCycles, escapeString, extractEmittedEvents, extractPolicyRefs, extractTestRefs, findMissingDependencies, generateAppBlueprintSpec, generateComponentTemplate, generateDataViewSpec, generateEventSpec, generateExperimentSpec, generateHandlerTemplate, generateIntegrationSpec, generateKnowledgeSpaceSpec, generateMigrationSpec, generateOperationSpec, generatePresentationSpec, generateTelemetrySpec, generateTestTemplate, generateWorkflowRunnerTemplate, generateWorkflowSpec, getCodeGenSystemPrompt, getSystemPrompt, inferSpecTypeFromFilePath, isFeatureFile, parseImportedSpecNames, scanAllSpecsFromSource, scanFeatureSource, scanSpecSource, toCamelCase, toDot, toKebabCase, toPascalCase, validateSpecStructure };
26
+ export { DEFAULT_WORKSPACE_CONFIG, SpecGroupingStrategies, addContractNode, addExampleContext, buildComponentPrompt, buildEventSpecPrompt, buildFormPrompt, buildHandlerPrompt, buildOperationSpecPrompt, buildPresentationSpecPrompt, buildReverseEdges, buildTestPrompt, capitalize, computeSemanticDiff, createContractGraph, detectCycles, escapeString, extractEmittedEvents, extractPolicyRefs, extractTestRefs, filterFeatures, filterSpecs, findMissingDependencies, generateAppBlueprintSpec, generateComponentTemplate, generateDataViewSpec, generateEventSpec, generateExperimentSpec, generateHandlerTemplate, generateIntegrationSpec, generateKnowledgeSpaceSpec, generateMigrationSpec, generateOperationSpec, generatePresentationSpec, generateTelemetrySpec, generateTestTemplate, generateWorkflowRunnerTemplate, generateWorkflowSpec, getCodeGenSystemPrompt, getSystemPrompt, getUniqueSpecDomains, getUniqueSpecOwners, getUniqueSpecTags, groupSpecs, groupSpecsToArray, inferSpecTypeFromFilePath, isFeatureFile, parseImportedSpecNames, scanAllSpecsFromSource, scanFeatureSource, scanSpecSource, toCamelCase, toDot, toKebabCase, toPascalCase, validateSpecStructure };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lssm/module.contractspec-workspace",
3
- "version": "0.0.0-canary-20251220041653",
3
+ "version": "0.0.0-canary-20251221132705",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.js",
@@ -23,13 +23,13 @@
23
23
  "test": "bun run"
24
24
  },
25
25
  "dependencies": {
26
- "@lssm/lib.contracts": "0.0.0-canary-20251220041653",
27
- "@lssm/lib.schema": "0.0.0-canary-20251220041653",
26
+ "@lssm/lib.contracts": "0.0.0-canary-20251221132705",
27
+ "@lssm/lib.schema": "0.0.0-canary-20251221132705",
28
28
  "zod": "^4.1.13"
29
29
  },
30
30
  "devDependencies": {
31
- "@lssm/tool.tsdown": "0.0.0-canary-20251220041653",
32
- "@lssm/tool.typescript": "0.0.0-canary-20251220041653",
31
+ "@lssm/tool.tsdown": "0.0.0-canary-20251221132705",
32
+ "@lssm/tool.typescript": "0.0.0-canary-20251221132705",
33
33
  "tsdown": "^0.18.1",
34
34
  "typescript": "^5.9.3"
35
35
  },