@contractspec/module.workspace 0.0.0-canary-20260113162409

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 (170) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +50 -0
  3. package/dist/ai/prompts/code-generation.d.ts +24 -0
  4. package/dist/ai/prompts/code-generation.d.ts.map +1 -0
  5. package/dist/ai/prompts/code-generation.js +134 -0
  6. package/dist/ai/prompts/code-generation.js.map +1 -0
  7. package/dist/ai/prompts/spec-creation.d.ts +28 -0
  8. package/dist/ai/prompts/spec-creation.d.ts.map +1 -0
  9. package/dist/ai/prompts/spec-creation.js +102 -0
  10. package/dist/ai/prompts/spec-creation.js.map +1 -0
  11. package/dist/analysis/deps/graph.d.ts +34 -0
  12. package/dist/analysis/deps/graph.d.ts.map +1 -0
  13. package/dist/analysis/deps/graph.js +85 -0
  14. package/dist/analysis/deps/graph.js.map +1 -0
  15. package/dist/analysis/deps/parse-imports.d.ts +17 -0
  16. package/dist/analysis/deps/parse-imports.d.ts.map +1 -0
  17. package/dist/analysis/deps/parse-imports.js +31 -0
  18. package/dist/analysis/deps/parse-imports.js.map +1 -0
  19. package/dist/analysis/diff/deep-diff.d.ts +33 -0
  20. package/dist/analysis/diff/deep-diff.d.ts.map +1 -0
  21. package/dist/analysis/diff/deep-diff.js +114 -0
  22. package/dist/analysis/diff/deep-diff.js.map +1 -0
  23. package/dist/analysis/diff/semantic.d.ts +11 -0
  24. package/dist/analysis/diff/semantic.d.ts.map +1 -0
  25. package/dist/analysis/diff/semantic.js +97 -0
  26. package/dist/analysis/diff/semantic.js.map +1 -0
  27. package/dist/analysis/example-scan.d.ts +15 -0
  28. package/dist/analysis/example-scan.d.ts.map +1 -0
  29. package/dist/analysis/example-scan.js +116 -0
  30. package/dist/analysis/example-scan.js.map +1 -0
  31. package/dist/analysis/feature-extractor.js +203 -0
  32. package/dist/analysis/feature-extractor.js.map +1 -0
  33. package/dist/analysis/feature-scan.d.ts +15 -0
  34. package/dist/analysis/feature-scan.d.ts.map +1 -0
  35. package/dist/analysis/feature-scan.js +56 -0
  36. package/dist/analysis/feature-scan.js.map +1 -0
  37. package/dist/analysis/grouping.d.ts +79 -0
  38. package/dist/analysis/grouping.d.ts.map +1 -0
  39. package/dist/analysis/grouping.js +115 -0
  40. package/dist/analysis/grouping.js.map +1 -0
  41. package/dist/analysis/impact/classifier.d.ts +19 -0
  42. package/dist/analysis/impact/classifier.d.ts.map +1 -0
  43. package/dist/analysis/impact/classifier.js +135 -0
  44. package/dist/analysis/impact/classifier.js.map +1 -0
  45. package/dist/analysis/impact/index.js +2 -0
  46. package/dist/analysis/impact/rules.d.ts +35 -0
  47. package/dist/analysis/impact/rules.d.ts.map +1 -0
  48. package/dist/analysis/impact/rules.js +154 -0
  49. package/dist/analysis/impact/rules.js.map +1 -0
  50. package/dist/analysis/impact/types.d.ts +95 -0
  51. package/dist/analysis/impact/types.d.ts.map +1 -0
  52. package/dist/analysis/index.js +18 -0
  53. package/dist/analysis/snapshot/index.js +2 -0
  54. package/dist/analysis/snapshot/normalizer.d.ts +36 -0
  55. package/dist/analysis/snapshot/normalizer.d.ts.map +1 -0
  56. package/dist/analysis/snapshot/normalizer.js +67 -0
  57. package/dist/analysis/snapshot/normalizer.js.map +1 -0
  58. package/dist/analysis/snapshot/snapshot.d.ts +18 -0
  59. package/dist/analysis/snapshot/snapshot.d.ts.map +1 -0
  60. package/dist/analysis/snapshot/snapshot.js +163 -0
  61. package/dist/analysis/snapshot/snapshot.js.map +1 -0
  62. package/dist/analysis/snapshot/types.d.ts +74 -0
  63. package/dist/analysis/snapshot/types.d.ts.map +1 -0
  64. package/dist/analysis/spec-parser.d.ts +11 -0
  65. package/dist/analysis/spec-parser.d.ts.map +1 -0
  66. package/dist/analysis/spec-parser.js +89 -0
  67. package/dist/analysis/spec-parser.js.map +1 -0
  68. package/dist/analysis/spec-parsing-utils.d.ts +26 -0
  69. package/dist/analysis/spec-parsing-utils.d.ts.map +1 -0
  70. package/dist/analysis/spec-parsing-utils.js +98 -0
  71. package/dist/analysis/spec-parsing-utils.js.map +1 -0
  72. package/dist/analysis/spec-scan.d.ts +20 -0
  73. package/dist/analysis/spec-scan.d.ts.map +1 -0
  74. package/dist/analysis/spec-scan.js +141 -0
  75. package/dist/analysis/spec-scan.js.map +1 -0
  76. package/dist/analysis/utils/matchers.js +77 -0
  77. package/dist/analysis/utils/matchers.js.map +1 -0
  78. package/dist/analysis/utils/variables.js +45 -0
  79. package/dist/analysis/utils/variables.js.map +1 -0
  80. package/dist/analysis/validate/index.js +1 -0
  81. package/dist/analysis/validate/spec-structure.d.ts +29 -0
  82. package/dist/analysis/validate/spec-structure.d.ts.map +1 -0
  83. package/dist/analysis/validate/spec-structure.js +455 -0
  84. package/dist/analysis/validate/spec-structure.js.map +1 -0
  85. package/dist/formatter.d.ts +42 -0
  86. package/dist/formatter.d.ts.map +1 -0
  87. package/dist/formatter.js +163 -0
  88. package/dist/formatter.js.map +1 -0
  89. package/dist/formatters/index.js +2 -0
  90. package/dist/formatters/spec-markdown.d.ts +31 -0
  91. package/dist/formatters/spec-markdown.d.ts.map +1 -0
  92. package/dist/formatters/spec-markdown.js +263 -0
  93. package/dist/formatters/spec-markdown.js.map +1 -0
  94. package/dist/formatters/spec-to-docblock.d.ts +14 -0
  95. package/dist/formatters/spec-to-docblock.d.ts.map +1 -0
  96. package/dist/formatters/spec-to-docblock.js +48 -0
  97. package/dist/formatters/spec-to-docblock.js.map +1 -0
  98. package/dist/index.d.ts +42 -0
  99. package/dist/index.js +39 -0
  100. package/dist/templates/app-config.d.ts +7 -0
  101. package/dist/templates/app-config.d.ts.map +1 -0
  102. package/dist/templates/app-config.js +107 -0
  103. package/dist/templates/app-config.js.map +1 -0
  104. package/dist/templates/data-view.d.ts +7 -0
  105. package/dist/templates/data-view.d.ts.map +1 -0
  106. package/dist/templates/data-view.js +69 -0
  107. package/dist/templates/data-view.js.map +1 -0
  108. package/dist/templates/event.d.ts +11 -0
  109. package/dist/templates/event.d.ts.map +1 -0
  110. package/dist/templates/event.js +41 -0
  111. package/dist/templates/event.js.map +1 -0
  112. package/dist/templates/experiment.d.ts +7 -0
  113. package/dist/templates/experiment.d.ts.map +1 -0
  114. package/dist/templates/experiment.js +88 -0
  115. package/dist/templates/experiment.js.map +1 -0
  116. package/dist/templates/handler.d.ts +20 -0
  117. package/dist/templates/handler.d.ts.map +1 -0
  118. package/dist/templates/handler.js +96 -0
  119. package/dist/templates/handler.js.map +1 -0
  120. package/dist/templates/integration-utils.js +105 -0
  121. package/dist/templates/integration-utils.js.map +1 -0
  122. package/dist/templates/integration.d.ts +7 -0
  123. package/dist/templates/integration.d.ts.map +1 -0
  124. package/dist/templates/integration.js +62 -0
  125. package/dist/templates/integration.js.map +1 -0
  126. package/dist/templates/knowledge.d.ts +7 -0
  127. package/dist/templates/knowledge.d.ts.map +1 -0
  128. package/dist/templates/knowledge.js +69 -0
  129. package/dist/templates/knowledge.js.map +1 -0
  130. package/dist/templates/migration.d.ts +7 -0
  131. package/dist/templates/migration.d.ts.map +1 -0
  132. package/dist/templates/migration.js +61 -0
  133. package/dist/templates/migration.js.map +1 -0
  134. package/dist/templates/operation.d.ts +11 -0
  135. package/dist/templates/operation.d.ts.map +1 -0
  136. package/dist/templates/operation.js +101 -0
  137. package/dist/templates/operation.js.map +1 -0
  138. package/dist/templates/presentation.d.ts +11 -0
  139. package/dist/templates/presentation.d.ts.map +1 -0
  140. package/dist/templates/presentation.js +79 -0
  141. package/dist/templates/presentation.js.map +1 -0
  142. package/dist/templates/telemetry.d.ts +7 -0
  143. package/dist/templates/telemetry.d.ts.map +1 -0
  144. package/dist/templates/telemetry.js +90 -0
  145. package/dist/templates/telemetry.js.map +1 -0
  146. package/dist/templates/utils.d.ts +27 -0
  147. package/dist/templates/utils.d.ts.map +1 -0
  148. package/dist/templates/utils.js +39 -0
  149. package/dist/templates/utils.js.map +1 -0
  150. package/dist/templates/workflow-runner.d.ts +16 -0
  151. package/dist/templates/workflow-runner.d.ts.map +1 -0
  152. package/dist/templates/workflow-runner.js +49 -0
  153. package/dist/templates/workflow-runner.js.map +1 -0
  154. package/dist/templates/workflow.d.ts +11 -0
  155. package/dist/templates/workflow.d.ts.map +1 -0
  156. package/dist/templates/workflow.js +68 -0
  157. package/dist/templates/workflow.js.map +1 -0
  158. package/dist/types/analysis-types.d.ts +199 -0
  159. package/dist/types/analysis-types.d.ts.map +1 -0
  160. package/dist/types/generation-types.d.ts +87 -0
  161. package/dist/types/generation-types.d.ts.map +1 -0
  162. package/dist/types/generation-types.js +21 -0
  163. package/dist/types/generation-types.js.map +1 -0
  164. package/dist/types/llm-types.d.ts +138 -0
  165. package/dist/types/llm-types.d.ts.map +1 -0
  166. package/dist/types/rulesync-types.d.ts +24 -0
  167. package/dist/types/rulesync-types.d.ts.map +1 -0
  168. package/dist/types/spec-types.d.ts +343 -0
  169. package/dist/types/spec-types.d.ts.map +1 -0
  170. package/package.json +63 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"feature-extractor.js","names":[],"sources":["../../src/analysis/feature-extractor.ts"],"sourcesContent":["import {\n Project,\n Node,\n SyntaxKind,\n type ObjectLiteralExpression,\n} from 'ts-morph';\nimport type { RefInfo } from '../types/analysis-types';\n\n/**\n * Extract specs referenced in a feature.\n * Uses ts-morph for robust parsing of object structures.\n */\nexport function extractFeatureRefs(code: string): {\n operations: RefInfo[];\n events: RefInfo[];\n presentations: RefInfo[];\n experiments: RefInfo[];\n capabilities: { provides: RefInfo[]; requires: RefInfo[] };\n opToPresentationLinks: { op: RefInfo; pres: RefInfo }[];\n presentationsTargets: {\n key: string;\n version: string;\n targets: Record<string, unknown>[];\n }[];\n} {\n const result = {\n operations: [] as RefInfo[],\n events: [] as RefInfo[],\n presentations: [] as RefInfo[],\n experiments: [] as RefInfo[],\n capabilities: {\n provides: [] as RefInfo[],\n requires: [] as RefInfo[],\n },\n opToPresentationLinks: [] as { op: RefInfo; pres: RefInfo }[],\n presentationsTargets: [] as {\n key: string;\n version: string;\n targets: Record<string, unknown>[];\n }[],\n };\n\n const project = new Project({ useInMemoryFileSystem: true });\n const sourceFile = project.createSourceFile('feature.ts', code);\n\n // Helper to extract ref array from an object property\n const extractRefs = (\n obj: ObjectLiteralExpression,\n propName: string,\n allowKeyOnly = false\n ): RefInfo[] => {\n const refs: RefInfo[] = [];\n const prop = obj.getProperty(propName);\n\n if (prop && Node.isPropertyAssignment(prop)) {\n const init = prop.getInitializer();\n if (init && Node.isArrayLiteralExpression(init)) {\n for (const elem of init.getElements()) {\n if (Node.isObjectLiteralExpression(elem)) {\n const keyText = getTextFromProp(elem, 'key');\n const verText = getTextFromProp(elem, 'version');\n\n if (keyText && verText) {\n refs.push({ key: keyText, version: verText });\n } else if (keyText && allowKeyOnly) {\n refs.push({ key: keyText, version: '1.0.0' });\n }\n }\n }\n }\n }\n return refs;\n };\n\n // Find defineFeature call\n const callExpressions = sourceFile.getDescendantsOfKind(\n SyntaxKind.CallExpression\n );\n\n for (const call of callExpressions) {\n if (\n ['defineFeature', 'defineAppConfig', 'defineAppBlueprint'].includes(\n call.getExpression().getText()\n )\n ) {\n const args = call.getArguments();\n if (args.length > 0 && Node.isObjectLiteralExpression(args[0])) {\n const obj = args[0];\n\n // Extract direct lists\n result.operations.push(...extractRefs(obj, 'operations'));\n result.events.push(...extractRefs(obj, 'events'));\n result.presentations.push(...extractRefs(obj, 'presentations'));\n result.experiments.push(...extractRefs(obj, 'experiments'));\n\n // Capabilities\n const capsProp = obj.getProperty('capabilities');\n if (capsProp && Node.isPropertyAssignment(capsProp)) {\n const capsObj = capsProp.getInitializer();\n if (capsObj && Node.isObjectLiteralExpression(capsObj)) {\n result.capabilities.provides.push(\n ...extractRefs(capsObj, 'provides')\n );\n result.capabilities.requires.push(\n ...extractRefs(capsObj, 'requires', true)\n );\n }\n }\n\n // Op to Presentation Links\n // explicit 'opToPresentation' field\n const linksProp = obj.getProperty('opToPresentation');\n if (linksProp && Node.isPropertyAssignment(linksProp)) {\n const linksArr = linksProp.getInitializer();\n if (linksArr && Node.isArrayLiteralExpression(linksArr)) {\n linksArr.getElements().forEach((link) => {\n if (Node.isObjectLiteralExpression(link)) {\n const opProp = link.getProperty('op');\n const presProp = link.getProperty('pres');\n\n let opRef: RefInfo | undefined;\n let presRef: RefInfo | undefined;\n\n if (opProp && Node.isPropertyAssignment(opProp)) {\n const val = opProp.getInitializer();\n if (val && Node.isObjectLiteralExpression(val)) {\n const key = getTextFromProp(val, 'key');\n const version = getTextFromProp(val, 'version');\n if (key && version) opRef = { key, version };\n }\n }\n if (presProp && Node.isPropertyAssignment(presProp)) {\n const val = presProp.getInitializer();\n if (val && Node.isObjectLiteralExpression(val)) {\n const key = getTextFromProp(val, 'key');\n const version = getTextFromProp(val, 'version');\n if (key && version) presRef = { key, version };\n }\n }\n\n if (opRef && presRef) {\n result.opToPresentationLinks.push({\n op: opRef,\n pres: presRef,\n });\n }\n }\n });\n }\n }\n\n // Also support 'interactions' style for forward compatibility\n extractLinks(obj, result.opToPresentationLinks);\n\n // Presentation Targets\n // explicit 'presentationsTargets' field\n const targetsProp = obj.getProperty('presentationsTargets');\n if (targetsProp && Node.isPropertyAssignment(targetsProp)) {\n const targetsArr = targetsProp.getInitializer();\n if (targetsArr && Node.isArrayLiteralExpression(targetsArr)) {\n targetsArr.getElements().forEach((targetBlock) => {\n if (Node.isObjectLiteralExpression(targetBlock)) {\n const key = getTextFromProp(targetBlock, 'key');\n const version = getTextFromProp(targetBlock, 'version');\n const targetsList = getTargetsList(targetBlock, 'targets');\n\n if (key && version && targetsList) {\n result.presentationsTargets.push({\n key,\n version,\n targets: targetsList as Record<string, unknown>[],\n });\n }\n }\n });\n }\n }\n\n // Also support inline targets in presentations list\n extractPresentationTargets(obj, result.presentationsTargets);\n }\n }\n }\n\n return result;\n}\n\nfunction extractLinks(\n obj: ObjectLiteralExpression,\n opToPresentationLinks: { op: RefInfo; pres: RefInfo }[]\n): void {\n const interactionsProp = obj.getProperty('interactions');\n if (interactionsProp && Node.isPropertyAssignment(interactionsProp)) {\n const interactionsArr = interactionsProp.getInitializer();\n if (interactionsArr && Node.isArrayLiteralExpression(interactionsArr)) {\n interactionsArr.getElements().forEach((interaction) => {\n if (Node.isObjectLiteralExpression(interaction)) {\n // Check: trigger: { type: 'presentation', presentation: { key, version } }\n // Check: action: { type: 'operation', operation: { key, version } }\n\n const triggerProp = interaction.getProperty('trigger');\n const triggerObj =\n triggerProp &&\n Node.isPropertyAssignment(triggerProp) &&\n triggerProp.getInitializer();\n\n const actionProp = interaction.getProperty('action');\n const actionObj =\n actionProp &&\n Node.isPropertyAssignment(actionProp) &&\n actionProp.getInitializer();\n\n if (\n triggerObj &&\n Node.isObjectLiteralExpression(triggerObj) &&\n actionObj &&\n Node.isObjectLiteralExpression(actionObj)\n ) {\n const triggerType = getTextFromProp(triggerObj, 'type');\n const actionType = getTextFromProp(actionObj, 'type');\n\n if (triggerType === 'presentation' && actionType === 'operation') {\n const presRef = extractNestedRef(triggerObj, 'presentation');\n const opRef = extractNestedRef(actionObj, 'operation');\n\n if (presRef && opRef) {\n opToPresentationLinks.push({ op: opRef, pres: presRef });\n }\n }\n }\n }\n });\n }\n }\n}\n\nfunction extractNestedRef(\n obj: ObjectLiteralExpression,\n propName: string\n): RefInfo | undefined {\n const prop = obj.getProperty(propName);\n if (prop && Node.isPropertyAssignment(prop)) {\n const val = prop.getInitializer();\n if (val && Node.isObjectLiteralExpression(val)) {\n const key = getTextFromProp(val, 'key');\n const version = getTextFromProp(val, 'version');\n if (key && version) return { key, version };\n }\n }\n return undefined;\n}\n\nfunction extractPresentationTargets(\n obj: ObjectLiteralExpression,\n presentationsTargets: {\n key: string;\n version: string;\n targets: Record<string, unknown>[];\n }[]\n): void {\n const presentationsProp = obj.getProperty('presentations');\n if (presentationsProp && Node.isPropertyAssignment(presentationsProp)) {\n const presentationsArr = presentationsProp.getInitializer();\n if (presentationsArr && Node.isArrayLiteralExpression(presentationsArr)) {\n for (const elem of presentationsArr.getElements()) {\n if (Node.isObjectLiteralExpression(elem)) {\n const keyText = getTextFromProp(elem, 'key');\n const verText = getTextFromProp(elem, 'version');\n\n // Use targets extraction from shared utility or local helper\n const targetsList = getTargetsList(elem, 'targets');\n\n if (keyText && verText && targetsList) {\n presentationsTargets.push({\n key: keyText,\n version: verText,\n targets: targetsList as Record<string, unknown>[],\n });\n }\n }\n }\n }\n }\n}\n\nfunction getTextFromProp(\n obj: ObjectLiteralExpression,\n propName: string\n): string | undefined {\n const prop = obj.getProperty(propName);\n if (prop && Node.isPropertyAssignment(prop)) {\n const init = prop.getInitializer();\n if (init && Node.isStringLiteral(init)) {\n return init.getLiteralText();\n }\n }\n return undefined;\n}\n\nfunction getTargetsList(\n obj: ObjectLiteralExpression,\n propName: string\n): unknown[] | undefined {\n const prop = obj.getProperty(propName);\n if (prop && Node.isPropertyAssignment(prop)) {\n const init = prop.getInitializer();\n if (init && Node.isArrayLiteralExpression(init)) {\n // Just extract strings for now, or raw text if they are identifiers/enums\n return init.getElements().map((e) => {\n if (Node.isStringLiteral(e)) return e.getLiteralText();\n // Fallback: create a dummy record to match the Expected type in the rest of the app\n return { target: e.getText() } as unknown;\n });\n }\n }\n return undefined;\n}\n"],"mappings":"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"}
@@ -0,0 +1,15 @@
1
+ import { FeatureScanResult } from "../types/analysis-types.js";
2
+
3
+ //#region src/analysis/feature-scan.d.ts
4
+
5
+ /**
6
+ * Check if a file is a feature file based on naming conventions.
7
+ */
8
+ declare function isFeatureFile(filePath: string): boolean;
9
+ /**
10
+ * Scan a feature source file to extract metadata.
11
+ */
12
+ declare function scanFeatureSource(code: string, filePath: string): FeatureScanResult;
13
+ //#endregion
14
+ export { isFeatureFile, scanFeatureSource };
15
+ //# sourceMappingURL=feature-scan.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"feature-scan.d.ts","names":[],"sources":["../../src/analysis/feature-scan.ts"],"sourcesContent":[],"mappings":";;;;;;AAWA;AAOgB,iBAPA,aAAA,CAUb,QAAA,EAAA,MAAA,CAAiB,EAAA,OAAA;;;;iBAHJ,iBAAA,kCAGb"}
@@ -0,0 +1,56 @@
1
+ import { isStability, matchStringArrayField, matchStringField } from "./utils/matchers.js";
2
+ import { extractFeatureRefs } from "./feature-extractor.js";
3
+
4
+ //#region src/analysis/feature-scan.ts
5
+ /**
6
+ * Check if a file is a feature file based on naming conventions.
7
+ */
8
+ function isFeatureFile(filePath) {
9
+ return filePath.includes(".feature.") && filePath.endsWith(".ts");
10
+ }
11
+ /**
12
+ * Scan a feature source file to extract metadata.
13
+ */
14
+ function scanFeatureSource(code, filePath) {
15
+ const key = matchStringField(code, "key") ?? extractKeyFromFilePath(filePath);
16
+ const version = matchStringField(code, "version") ?? "1.0.0";
17
+ const title = matchStringField(code, "title") ?? void 0;
18
+ const description = matchStringField(code, "description") ?? void 0;
19
+ const goal = matchStringField(code, "goal") ?? void 0;
20
+ const context = matchStringField(code, "context") ?? void 0;
21
+ const stabilityRaw = matchStringField(code, "stability");
22
+ const stability = isStability(stabilityRaw) ? stabilityRaw : void 0;
23
+ const owners = matchStringArrayField(code, "owners");
24
+ const tags = matchStringArrayField(code, "tags");
25
+ const refs = extractFeatureRefs(code);
26
+ return {
27
+ filePath,
28
+ key,
29
+ version,
30
+ title,
31
+ description,
32
+ goal,
33
+ context,
34
+ stability,
35
+ owners,
36
+ tags,
37
+ operations: refs.operations,
38
+ events: refs.events,
39
+ presentations: refs.presentations,
40
+ experiments: refs.experiments,
41
+ capabilities: refs.capabilities,
42
+ opToPresentationLinks: refs.opToPresentationLinks,
43
+ presentationsTargets: refs.presentationsTargets,
44
+ sourceBlock: code
45
+ };
46
+ }
47
+ /**
48
+ * Extract key from file path as fallback.
49
+ */
50
+ function extractKeyFromFilePath(filePath) {
51
+ return (filePath.split("/").pop() ?? filePath).replace(/\.feature\.[jt]s$/, "").replace(/[^a-zA-Z0-9-]/g, "-");
52
+ }
53
+
54
+ //#endregion
55
+ export { isFeatureFile, scanFeatureSource };
56
+ //# sourceMappingURL=feature-scan.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"feature-scan.js","names":[],"sources":["../../src/analysis/feature-scan.ts"],"sourcesContent":["import {\n isStability,\n matchStringArrayField,\n matchStringField,\n} from './utils/matchers';\nimport type { FeatureScanResult } from '../types/analysis-types';\nimport { extractFeatureRefs } from './feature-extractor';\n\n/**\n * Check if a file is a feature file based on naming conventions.\n */\nexport function isFeatureFile(filePath: string): boolean {\n return filePath.includes('.feature.') && filePath.endsWith('.ts');\n}\n\n/**\n * Scan a feature source file to extract metadata.\n */\nexport function scanFeatureSource(\n code: string,\n filePath: string\n): FeatureScanResult {\n const key = matchStringField(code, 'key') ?? extractKeyFromFilePath(filePath);\n const versionRaw = matchStringField(code, 'version');\n const version = versionRaw ?? '1.0.0'; // Default version\n const title = matchStringField(code, 'title') ?? undefined;\n const description = matchStringField(code, 'description') ?? undefined;\n const goal = matchStringField(code, 'goal') ?? undefined;\n const context = matchStringField(code, 'context') ?? undefined;\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 // Parse structure using ts-morph to extract nested refs\n const refs = extractFeatureRefs(code);\n\n return {\n filePath,\n key,\n version,\n title,\n description,\n goal,\n context,\n stability,\n owners,\n tags,\n operations: refs.operations,\n events: refs.events,\n presentations: refs.presentations,\n experiments: refs.experiments,\n capabilities: refs.capabilities,\n opToPresentationLinks: refs.opToPresentationLinks,\n presentationsTargets: refs.presentationsTargets,\n sourceBlock: code,\n };\n}\n\n/**\n * Extract key from file path as fallback.\n */\nfunction extractKeyFromFilePath(filePath: string): string {\n const fileName = filePath.split('/').pop() ?? filePath;\n return fileName\n .replace(/\\.feature\\.[jt]s$/, '')\n .replace(/[^a-zA-Z0-9-]/g, '-');\n}\n"],"mappings":";;;;;;;AAWA,SAAgB,cAAc,UAA2B;AACvD,QAAO,SAAS,SAAS,YAAY,IAAI,SAAS,SAAS,MAAM;;;;;AAMnE,SAAgB,kBACd,MACA,UACmB;CACnB,MAAM,MAAM,iBAAiB,MAAM,MAAM,IAAI,uBAAuB,SAAS;CAE7E,MAAM,UADa,iBAAiB,MAAM,UAAU,IACtB;CAC9B,MAAM,QAAQ,iBAAiB,MAAM,QAAQ,IAAI;CACjD,MAAM,cAAc,iBAAiB,MAAM,cAAc,IAAI;CAC7D,MAAM,OAAO,iBAAiB,MAAM,OAAO,IAAI;CAC/C,MAAM,UAAU,iBAAiB,MAAM,UAAU,IAAI;CACrD,MAAM,eAAe,iBAAiB,MAAM,YAAY;CACxD,MAAM,YAAY,YAAY,aAAa,GAAG,eAAe;CAC7D,MAAM,SAAS,sBAAsB,MAAM,SAAS;CACpD,MAAM,OAAO,sBAAsB,MAAM,OAAO;CAGhD,MAAM,OAAO,mBAAmB,KAAK;AAErC,QAAO;EACL;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA,YAAY,KAAK;EACjB,QAAQ,KAAK;EACb,eAAe,KAAK;EACpB,aAAa,KAAK;EAClB,cAAc,KAAK;EACnB,uBAAuB,KAAK;EAC5B,sBAAsB,KAAK;EAC3B,aAAa;EACd;;;;;AAMH,SAAS,uBAAuB,UAA0B;AAExD,SADiB,SAAS,MAAM,IAAI,CAAC,KAAK,IAAI,UAE3C,QAAQ,qBAAqB,GAAG,CAChC,QAAQ,kBAAkB,IAAI"}
@@ -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":";;;;;AAmCA;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;;;;AAGe,KArGH,UAqGG,CAAA,CAAA,CAAA,GAAA,CAAA,IAAA,EArGoB,CAqGpB,EAAA,GAAA,MAAA;;;AAmBf;AACS,UApHQ,YAoHR,CAAA,CAAA,CAAA,CAAA;EACW,GAAA,EAAA,MAAA;EAAX,KAAA,EAnHA,CAmHA,EAAA;;;;AAWT;AAagB,cArIH,sBAqI8B,EAAA;EAa3B;EAWA,KAAA,EAAA,CAAA,IAAA,EA3JA,cA2Jc,EAAA,GAAA,MAAA;EAClB;EACF,OAAA,EAAA,CAAA,IAAA,EA1JQ,cA0JR,EAAA,GAAA,MAAA;EACP;EAAiB,QAAA,EAAA,CAAA,IAAA,EAxJD,cAwJC,EAAA,GAAA,MAAA;;sBA/IE;;qBAGD;;sBAGC;;;;;iBAUN,WAAA,QACP,0BACC,aACP;;;;iBAiDa,qBACP,YACA,WAAW,KACjB,YAAY;;;;iBAmBC,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 key = item.key ?? "";
10
+ if (key.includes(".")) return key.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 key = spec.key ?? "";
38
+ const pattern = filter.namePattern.replace(/\*/g, ".*").replace(/\?/g, ".");
39
+ if (!new RegExp(`^${pattern}$`, "i").test(key)) 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 {\n FeatureScanResult,\n SpecScanResult,\n} 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 key = item.key ?? '';\n if (key.includes('.')) {\n return key.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 key = spec.key ?? '';\n const pattern = filter.namePattern\n .replace(/\\*/g, '.*')\n .replace(/\\?/g, '.');\n const regex = new RegExp(`^${pattern}$`, 'i');\n if (!regex.test(key)) return false;\n }\n\n return true;\n });\n}\n\n/**\n * Group specs by key function.\n */\nexport function groupSpecs<T>(\n items: T[],\n keyFn: GroupKeyFn<T>\n): 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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"}
@@ -0,0 +1,19 @@
1
+ import { SemanticDiffItem } from "../../types/analysis-types.js";
2
+ import { SpecSnapshot } from "../snapshot/types.js";
3
+ import { ClassifyOptions, ImpactResult } from "./types.js";
4
+
5
+ //#region src/analysis/impact/classifier.d.ts
6
+
7
+ /**
8
+ * Classify the impact of changes between base and head snapshots.
9
+ *
10
+ * @param baseSpecs - Specs from the base (baseline) version
11
+ * @param headSpecs - Specs from the head (current) version
12
+ * @param diffs - Semantic diff items from comparison
13
+ * @param options - Classification options
14
+ * @returns Classified impact result
15
+ */
16
+ declare function classifyImpact(baseSpecs: SpecSnapshot[], headSpecs: SpecSnapshot[], diffs: SemanticDiffItem[], options?: ClassifyOptions): ImpactResult;
17
+ //#endregion
18
+ export { classifyImpact };
19
+ //# sourceMappingURL=classifier.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"classifier.d.ts","names":[],"sources":["../../../src/analysis/impact/classifier.ts"],"sourcesContent":[],"mappings":";;;;;;;;;;;;;;;iBA0BgB,cAAA,YACH,2BACA,uBACJ,8BACE,kBACR"}
@@ -0,0 +1,135 @@
1
+ import { DEFAULT_RULES, findMatchingRule } from "./rules.js";
2
+
3
+ //#region src/analysis/impact/classifier.ts
4
+ /**
5
+ * Classify the impact of changes between base and head snapshots.
6
+ *
7
+ * @param baseSpecs - Specs from the base (baseline) version
8
+ * @param headSpecs - Specs from the head (current) version
9
+ * @param diffs - Semantic diff items from comparison
10
+ * @param options - Classification options
11
+ * @returns Classified impact result
12
+ */
13
+ function classifyImpact(baseSpecs, headSpecs, diffs, options = {}) {
14
+ const rules = options.customRules ?? DEFAULT_RULES;
15
+ const deltas = [];
16
+ const baseMap = new Map(baseSpecs.map((s) => [`${s.key}@${s.version}`, s]));
17
+ const headMap = new Map(headSpecs.map((s) => [`${s.key}@${s.version}`, s]));
18
+ const addedSpecs = [];
19
+ for (const spec of headSpecs) {
20
+ const lookupKey = `${spec.key}@${spec.version}`;
21
+ if (!baseMap.has(lookupKey)) addedSpecs.push({
22
+ key: spec.key,
23
+ version: spec.version,
24
+ type: spec.type
25
+ });
26
+ }
27
+ const removedSpecs = [];
28
+ for (const spec of baseSpecs) {
29
+ const lookupKey = `${spec.key}@${spec.version}`;
30
+ if (!headMap.has(lookupKey)) {
31
+ removedSpecs.push({
32
+ key: spec.key,
33
+ version: spec.version,
34
+ type: spec.type
35
+ });
36
+ deltas.push({
37
+ specKey: spec.key,
38
+ specVersion: spec.version,
39
+ specType: spec.type,
40
+ path: `spec.${spec.key}`,
41
+ severity: "breaking",
42
+ rule: "endpoint-removed",
43
+ description: `${spec.type === "operation" ? "Operation" : "Event"} '${spec.key}' was removed`
44
+ });
45
+ }
46
+ }
47
+ for (const diff of diffs) {
48
+ const matchingRule = findMatchingRule({
49
+ path: diff.path,
50
+ description: diff.description,
51
+ type: diff.type
52
+ }, rules);
53
+ const specInfo = findSpecInfo(extractSpecKey(diff.path, baseSpecs, headSpecs), baseSpecs, headSpecs);
54
+ deltas.push({
55
+ specKey: specInfo?.key ?? "unknown",
56
+ specVersion: specInfo?.version ?? "1.0.0",
57
+ specType: specInfo?.type ?? "operation",
58
+ path: diff.path,
59
+ severity: matchingRule?.severity ?? mapDiffTypeToSeverity(diff.type),
60
+ rule: matchingRule?.id ?? "unknown",
61
+ description: diff.description,
62
+ oldValue: diff.oldValue,
63
+ newValue: diff.newValue
64
+ });
65
+ }
66
+ for (const spec of addedSpecs) deltas.push({
67
+ specKey: spec.key,
68
+ specVersion: spec.version,
69
+ specType: spec.type,
70
+ path: `spec.${spec.key}`,
71
+ severity: "non_breaking",
72
+ rule: "endpoint-added",
73
+ description: `${spec.type === "operation" ? "Operation" : "Event"} '${spec.key}' was added`
74
+ });
75
+ const summary = calculateSummary(deltas, addedSpecs, removedSpecs);
76
+ const hasBreaking = summary.breaking > 0 || summary.removed > 0;
77
+ const hasNonBreaking = summary.nonBreaking > 0 || summary.added > 0;
78
+ return {
79
+ status: determineStatus(hasBreaking, hasNonBreaking),
80
+ hasBreaking,
81
+ hasNonBreaking,
82
+ summary,
83
+ deltas,
84
+ addedSpecs,
85
+ removedSpecs,
86
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
87
+ };
88
+ }
89
+ /**
90
+ * Calculate summary counts from deltas.
91
+ */
92
+ function calculateSummary(deltas, addedSpecs, removedSpecs) {
93
+ return {
94
+ breaking: deltas.filter((d) => d.severity === "breaking").length,
95
+ nonBreaking: deltas.filter((d) => d.severity === "non_breaking").length,
96
+ info: deltas.filter((d) => d.severity === "info").length,
97
+ added: addedSpecs.length,
98
+ removed: removedSpecs.length
99
+ };
100
+ }
101
+ /**
102
+ * Determine overall status from flags.
103
+ */
104
+ function determineStatus(hasBreaking, hasNonBreaking) {
105
+ if (hasBreaking) return "breaking";
106
+ if (hasNonBreaking) return "non-breaking";
107
+ return "no-impact";
108
+ }
109
+ /**
110
+ * Map semantic diff type to impact severity.
111
+ */
112
+ function mapDiffTypeToSeverity(type) {
113
+ switch (type) {
114
+ case "breaking": return "breaking";
115
+ case "removed": return "breaking";
116
+ case "added": return "non_breaking";
117
+ case "changed": return "info";
118
+ default: return "info";
119
+ }
120
+ }
121
+ /**
122
+ * Extract spec key from a diff path (heuristic).
123
+ */
124
+ function extractSpecKey(_path, _baseSpecs, _headSpecs) {}
125
+ /**
126
+ * Find spec info from key.
127
+ */
128
+ function findSpecInfo(key, baseSpecs, headSpecs) {
129
+ if (!key) return headSpecs[0] ?? baseSpecs[0];
130
+ return headSpecs.find((s) => s.key === key) ?? baseSpecs.find((s) => s.key === key);
131
+ }
132
+
133
+ //#endregion
134
+ export { classifyImpact };
135
+ //# sourceMappingURL=classifier.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"classifier.js","names":[],"sources":["../../../src/analysis/impact/classifier.ts"],"sourcesContent":["/**\n * Impact classifier.\n *\n * Classifies contract changes as breaking, non-breaking, or info.\n */\n\nimport type { SemanticDiffItem } from '../../types/analysis-types';\nimport type { SpecSnapshot } from '../snapshot/types';\nimport { DEFAULT_RULES, findMatchingRule } from './rules';\nimport type {\n ClassifyOptions,\n ImpactDelta,\n ImpactResult,\n ImpactStatus,\n ImpactSummary,\n} from './types';\n\n/**\n * Classify the impact of changes between base and head snapshots.\n *\n * @param baseSpecs - Specs from the base (baseline) version\n * @param headSpecs - Specs from the head (current) version\n * @param diffs - Semantic diff items from comparison\n * @param options - Classification options\n * @returns Classified impact result\n */\nexport function classifyImpact(\n baseSpecs: SpecSnapshot[],\n headSpecs: SpecSnapshot[],\n diffs: SemanticDiffItem[],\n options: ClassifyOptions = {}\n): ImpactResult {\n const rules = options.customRules ?? DEFAULT_RULES;\n const deltas: ImpactDelta[] = [];\n\n // Create lookup maps\n const baseMap = new Map(baseSpecs.map((s) => [`${s.key}@${s.version}`, s]));\n const headMap = new Map(headSpecs.map((s) => [`${s.key}@${s.version}`, s]));\n\n // Detect added specs\n const addedSpecs: ImpactResult['addedSpecs'] = [];\n for (const spec of headSpecs) {\n const lookupKey = `${spec.key}@${spec.version}`;\n if (!baseMap.has(lookupKey)) {\n addedSpecs.push({\n key: spec.key,\n version: spec.version,\n type: spec.type,\n });\n }\n }\n\n // Detect removed specs\n const removedSpecs: ImpactResult['removedSpecs'] = [];\n for (const spec of baseSpecs) {\n const lookupKey = `${spec.key}@${spec.version}`;\n if (!headMap.has(lookupKey)) {\n removedSpecs.push({\n key: spec.key,\n version: spec.version,\n type: spec.type,\n });\n\n // Removed spec is always breaking\n deltas.push({\n specKey: spec.key,\n specVersion: spec.version,\n specType: spec.type,\n path: `spec.${spec.key}`,\n severity: 'breaking',\n rule: 'endpoint-removed',\n description: `${spec.type === 'operation' ? 'Operation' : 'Event'} '${spec.key}' was removed`,\n });\n }\n }\n\n // Classify diffs\n for (const diff of diffs) {\n const matchingRule = findMatchingRule(\n { path: diff.path, description: diff.description, type: diff.type },\n rules\n );\n\n // Extract spec key from path (heuristic)\n const specKey = extractSpecKey(diff.path, baseSpecs, headSpecs);\n const specInfo = findSpecInfo(specKey, baseSpecs, headSpecs);\n\n deltas.push({\n specKey: specInfo?.key ?? 'unknown',\n specVersion: specInfo?.version ?? '1.0.0',\n specType: specInfo?.type ?? 'operation',\n path: diff.path,\n severity: matchingRule?.severity ?? mapDiffTypeToSeverity(diff.type),\n rule: matchingRule?.id ?? 'unknown',\n description: diff.description,\n oldValue: diff.oldValue,\n newValue: diff.newValue,\n });\n }\n\n // Add added specs as non-breaking changes\n for (const spec of addedSpecs) {\n deltas.push({\n specKey: spec.key,\n specVersion: spec.version,\n specType: spec.type,\n path: `spec.${spec.key}`,\n severity: 'non_breaking',\n rule: 'endpoint-added',\n description: `${spec.type === 'operation' ? 'Operation' : 'Event'} '${spec.key}' was added`,\n });\n }\n\n // Calculate summary\n const summary = calculateSummary(deltas, addedSpecs, removedSpecs);\n\n // Determine status\n const hasBreaking = summary.breaking > 0 || summary.removed > 0;\n const hasNonBreaking = summary.nonBreaking > 0 || summary.added > 0;\n const status = determineStatus(hasBreaking, hasNonBreaking);\n\n return {\n status,\n hasBreaking,\n hasNonBreaking,\n summary,\n deltas,\n addedSpecs,\n removedSpecs,\n timestamp: new Date().toISOString(),\n };\n}\n\n/**\n * Calculate summary counts from deltas.\n */\nfunction calculateSummary(\n deltas: ImpactDelta[],\n addedSpecs: ImpactResult['addedSpecs'],\n removedSpecs: ImpactResult['removedSpecs']\n): ImpactSummary {\n return {\n breaking: deltas.filter((d) => d.severity === 'breaking').length,\n nonBreaking: deltas.filter((d) => d.severity === 'non_breaking').length,\n info: deltas.filter((d) => d.severity === 'info').length,\n added: addedSpecs.length,\n removed: removedSpecs.length,\n };\n}\n\n/**\n * Determine overall status from flags.\n */\nfunction determineStatus(\n hasBreaking: boolean,\n hasNonBreaking: boolean\n): ImpactStatus {\n if (hasBreaking) return 'breaking';\n if (hasNonBreaking) return 'non-breaking';\n return 'no-impact';\n}\n\n/**\n * Map semantic diff type to impact severity.\n */\nfunction mapDiffTypeToSeverity(type: string): ImpactDelta['severity'] {\n switch (type) {\n case 'breaking':\n return 'breaking';\n case 'removed':\n return 'breaking';\n case 'added':\n return 'non_breaking';\n case 'changed':\n return 'info';\n default:\n return 'info';\n }\n}\n\n/**\n * Extract spec key from a diff path (heuristic).\n */\nfunction extractSpecKey(\n _path: string,\n _baseSpecs: SpecSnapshot[],\n _headSpecs: SpecSnapshot[]\n): string | undefined {\n // This is a simplified heuristic; in practice would need more context\n return undefined;\n}\n\n/**\n * Find spec info from key.\n */\nfunction findSpecInfo(\n key: string | undefined,\n baseSpecs: SpecSnapshot[],\n headSpecs: SpecSnapshot[]\n): SpecSnapshot | undefined {\n if (!key) return headSpecs[0] ?? baseSpecs[0];\n return (\n headSpecs.find((s) => s.key === key) ?? baseSpecs.find((s) => s.key === key)\n );\n}\n"],"mappings":";;;;;;;;;;;;AA0BA,SAAgB,eACd,WACA,WACA,OACA,UAA2B,EAAE,EACf;CACd,MAAM,QAAQ,QAAQ,eAAe;CACrC,MAAM,SAAwB,EAAE;CAGhC,MAAM,UAAU,IAAI,IAAI,UAAU,KAAK,MAAM,CAAC,GAAG,EAAE,IAAI,GAAG,EAAE,WAAW,EAAE,CAAC,CAAC;CAC3E,MAAM,UAAU,IAAI,IAAI,UAAU,KAAK,MAAM,CAAC,GAAG,EAAE,IAAI,GAAG,EAAE,WAAW,EAAE,CAAC,CAAC;CAG3E,MAAM,aAAyC,EAAE;AACjD,MAAK,MAAM,QAAQ,WAAW;EAC5B,MAAM,YAAY,GAAG,KAAK,IAAI,GAAG,KAAK;AACtC,MAAI,CAAC,QAAQ,IAAI,UAAU,CACzB,YAAW,KAAK;GACd,KAAK,KAAK;GACV,SAAS,KAAK;GACd,MAAM,KAAK;GACZ,CAAC;;CAKN,MAAM,eAA6C,EAAE;AACrD,MAAK,MAAM,QAAQ,WAAW;EAC5B,MAAM,YAAY,GAAG,KAAK,IAAI,GAAG,KAAK;AACtC,MAAI,CAAC,QAAQ,IAAI,UAAU,EAAE;AAC3B,gBAAa,KAAK;IAChB,KAAK,KAAK;IACV,SAAS,KAAK;IACd,MAAM,KAAK;IACZ,CAAC;AAGF,UAAO,KAAK;IACV,SAAS,KAAK;IACd,aAAa,KAAK;IAClB,UAAU,KAAK;IACf,MAAM,QAAQ,KAAK;IACnB,UAAU;IACV,MAAM;IACN,aAAa,GAAG,KAAK,SAAS,cAAc,cAAc,QAAQ,IAAI,KAAK,IAAI;IAChF,CAAC;;;AAKN,MAAK,MAAM,QAAQ,OAAO;EACxB,MAAM,eAAe,iBACnB;GAAE,MAAM,KAAK;GAAM,aAAa,KAAK;GAAa,MAAM,KAAK;GAAM,EACnE,MACD;EAID,MAAM,WAAW,aADD,eAAe,KAAK,MAAM,WAAW,UAAU,EACxB,WAAW,UAAU;AAE5D,SAAO,KAAK;GACV,SAAS,UAAU,OAAO;GAC1B,aAAa,UAAU,WAAW;GAClC,UAAU,UAAU,QAAQ;GAC5B,MAAM,KAAK;GACX,UAAU,cAAc,YAAY,sBAAsB,KAAK,KAAK;GACpE,MAAM,cAAc,MAAM;GAC1B,aAAa,KAAK;GAClB,UAAU,KAAK;GACf,UAAU,KAAK;GAChB,CAAC;;AAIJ,MAAK,MAAM,QAAQ,WACjB,QAAO,KAAK;EACV,SAAS,KAAK;EACd,aAAa,KAAK;EAClB,UAAU,KAAK;EACf,MAAM,QAAQ,KAAK;EACnB,UAAU;EACV,MAAM;EACN,aAAa,GAAG,KAAK,SAAS,cAAc,cAAc,QAAQ,IAAI,KAAK,IAAI;EAChF,CAAC;CAIJ,MAAM,UAAU,iBAAiB,QAAQ,YAAY,aAAa;CAGlE,MAAM,cAAc,QAAQ,WAAW,KAAK,QAAQ,UAAU;CAC9D,MAAM,iBAAiB,QAAQ,cAAc,KAAK,QAAQ,QAAQ;AAGlE,QAAO;EACL,QAHa,gBAAgB,aAAa,eAAe;EAIzD;EACA;EACA;EACA;EACA;EACA;EACA,4BAAW,IAAI,MAAM,EAAC,aAAa;EACpC;;;;;AAMH,SAAS,iBACP,QACA,YACA,cACe;AACf,QAAO;EACL,UAAU,OAAO,QAAQ,MAAM,EAAE,aAAa,WAAW,CAAC;EAC1D,aAAa,OAAO,QAAQ,MAAM,EAAE,aAAa,eAAe,CAAC;EACjE,MAAM,OAAO,QAAQ,MAAM,EAAE,aAAa,OAAO,CAAC;EAClD,OAAO,WAAW;EAClB,SAAS,aAAa;EACvB;;;;;AAMH,SAAS,gBACP,aACA,gBACc;AACd,KAAI,YAAa,QAAO;AACxB,KAAI,eAAgB,QAAO;AAC3B,QAAO;;;;;AAMT,SAAS,sBAAsB,MAAuC;AACpE,SAAQ,MAAR;EACE,KAAK,WACH,QAAO;EACT,KAAK,UACH,QAAO;EACT,KAAK,QACH,QAAO;EACT,KAAK,UACH,QAAO;EACT,QACE,QAAO;;;;;;AAOb,SAAS,eACP,OACA,YACA,YACoB;;;;AAQtB,SAAS,aACP,KACA,WACA,WAC0B;AAC1B,KAAI,CAAC,IAAK,QAAO,UAAU,MAAM,UAAU;AAC3C,QACE,UAAU,MAAM,MAAM,EAAE,QAAQ,IAAI,IAAI,UAAU,MAAM,MAAM,EAAE,QAAQ,IAAI"}
@@ -0,0 +1,2 @@
1
+ import { BREAKING_RULES, DEFAULT_RULES, INFO_RULES, NON_BREAKING_RULES, findMatchingRule, getRulesBySeverity } from "./rules.js";
2
+ import { classifyImpact } from "./classifier.js";
@@ -0,0 +1,35 @@
1
+ import { ImpactRule, ImpactSeverity } from "./types.js";
2
+
3
+ //#region src/analysis/impact/rules.d.ts
4
+
5
+ /**
6
+ * Default breaking change rules.
7
+ */
8
+ declare const BREAKING_RULES: ImpactRule[];
9
+ /**
10
+ * Non-breaking change rules.
11
+ */
12
+ declare const NON_BREAKING_RULES: ImpactRule[];
13
+ /**
14
+ * Info-level change rules.
15
+ */
16
+ declare const INFO_RULES: ImpactRule[];
17
+ /**
18
+ * All default rules in priority order (breaking > non_breaking > info).
19
+ */
20
+ declare const DEFAULT_RULES: ImpactRule[];
21
+ /**
22
+ * Get rules by severity.
23
+ */
24
+ declare function getRulesBySeverity(severity: ImpactSeverity): ImpactRule[];
25
+ /**
26
+ * Find matching rule for a delta.
27
+ */
28
+ declare function findMatchingRule(delta: {
29
+ path: string;
30
+ description: string;
31
+ type: string;
32
+ }, rules?: ImpactRule[]): ImpactRule | undefined;
33
+ //#endregion
34
+ export { BREAKING_RULES, DEFAULT_RULES, INFO_RULES, NON_BREAKING_RULES, findMatchingRule, getRulesBySeverity };
35
+ //# sourceMappingURL=rules.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rules.d.ts","names":[],"sources":["../../../src/analysis/impact/rules.ts"],"sourcesContent":[],"mappings":";;;;AAqKA;AASA;AAOA;cA1Ka,gBAAgB;;;;cAiFhB,oBAAoB;;;;cA2CpB,YAAY;;;;cA8BZ,eAAe;;;;iBASZ,kBAAA,WAA6B,iBAAiB;;;;iBAO9C,gBAAA;;;;WAEP,eACN"}