@contractspec/module.workspace 1.57.0 → 1.58.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (248) hide show
  1. package/dist/ai/code-generation.d.ts +25 -0
  2. package/dist/ai/code-generation.d.ts.map +1 -0
  3. package/dist/ai/index.d.ts +5 -0
  4. package/dist/ai/index.d.ts.map +1 -0
  5. package/dist/ai/prompts/code-generation.d.ts +5 -8
  6. package/dist/ai/prompts/code-generation.d.ts.map +1 -1
  7. package/dist/ai/prompts/index.d.ts +3 -0
  8. package/dist/ai/prompts/index.d.ts.map +1 -0
  9. package/dist/ai/prompts/spec-creation.d.ts +7 -11
  10. package/dist/ai/prompts/spec-creation.d.ts.map +1 -1
  11. package/dist/ai/spec-creation.d.ts +27 -0
  12. package/dist/ai/spec-creation.d.ts.map +1 -0
  13. package/dist/analysis/deps/graph.d.ts +14 -13
  14. package/dist/analysis/deps/graph.d.ts.map +1 -1
  15. package/dist/analysis/deps/index.d.ts +6 -0
  16. package/dist/analysis/deps/index.d.ts.map +1 -0
  17. package/dist/analysis/deps/parse-imports.d.ts +1 -4
  18. package/dist/analysis/deps/parse-imports.d.ts.map +1 -1
  19. package/dist/analysis/diff/deep-diff.d.ts +17 -15
  20. package/dist/analysis/diff/deep-diff.d.ts.map +1 -1
  21. package/dist/analysis/diff/deep-diff.test.d.ts +5 -0
  22. package/dist/analysis/diff/deep-diff.test.d.ts.map +1 -0
  23. package/dist/analysis/diff/index.d.ts +6 -0
  24. package/dist/analysis/diff/index.d.ts.map +1 -0
  25. package/dist/analysis/diff/semantic.d.ts +6 -6
  26. package/dist/analysis/diff/semantic.d.ts.map +1 -1
  27. package/dist/analysis/example-scan.d.ts +3 -7
  28. package/dist/analysis/example-scan.d.ts.map +1 -1
  29. package/dist/analysis/example-scan.test.d.ts +7 -0
  30. package/dist/analysis/example-scan.test.d.ts.map +1 -0
  31. package/dist/analysis/feature-extractor.d.ts +25 -0
  32. package/dist/analysis/feature-extractor.d.ts.map +1 -0
  33. package/dist/analysis/feature-scan.d.ts +3 -7
  34. package/dist/analysis/feature-scan.d.ts.map +1 -1
  35. package/dist/analysis/feature-scan.test.d.ts +2 -0
  36. package/dist/analysis/feature-scan.test.d.ts.map +1 -0
  37. package/dist/analysis/grouping.d.ts +41 -35
  38. package/dist/analysis/grouping.d.ts.map +1 -1
  39. package/dist/analysis/impact/classifier.d.ts +9 -8
  40. package/dist/analysis/impact/classifier.d.ts.map +1 -1
  41. package/dist/analysis/impact/classifier.test.d.ts +5 -0
  42. package/dist/analysis/impact/classifier.test.d.ts.map +1 -0
  43. package/dist/analysis/impact/index.d.ts +9 -0
  44. package/dist/analysis/impact/index.d.ts.map +1 -0
  45. package/dist/analysis/impact/rules.d.ts +15 -14
  46. package/dist/analysis/impact/rules.d.ts.map +1 -1
  47. package/dist/analysis/impact/types.d.ts +73 -76
  48. package/dist/analysis/impact/types.d.ts.map +1 -1
  49. package/dist/analysis/index.d.ts +14 -0
  50. package/dist/analysis/index.d.ts.map +1 -0
  51. package/dist/analysis/snapshot/index.d.ts +9 -0
  52. package/dist/analysis/snapshot/index.d.ts.map +1 -0
  53. package/dist/analysis/snapshot/normalizer.d.ts +7 -10
  54. package/dist/analysis/snapshot/normalizer.d.ts.map +1 -1
  55. package/dist/analysis/snapshot/snapshot.d.ts +10 -8
  56. package/dist/analysis/snapshot/snapshot.d.ts.map +1 -1
  57. package/dist/analysis/snapshot/snapshot.test.d.ts +5 -0
  58. package/dist/analysis/snapshot/snapshot.test.d.ts.map +1 -0
  59. package/dist/analysis/snapshot/types.d.ts +58 -56
  60. package/dist/analysis/snapshot/types.d.ts.map +1 -1
  61. package/dist/analysis/spec-parser.d.ts +8 -6
  62. package/dist/analysis/spec-parser.d.ts.map +1 -1
  63. package/dist/analysis/spec-parsing-utils.d.ts +20 -10
  64. package/dist/analysis/spec-parsing-utils.d.ts.map +1 -1
  65. package/dist/analysis/spec-scan.d.ts +13 -12
  66. package/dist/analysis/spec-scan.d.ts.map +1 -1
  67. package/dist/analysis/spec-scan.test.d.ts +2 -0
  68. package/dist/analysis/spec-scan.test.d.ts.map +1 -0
  69. package/dist/analysis/utils/matchers.d.ts +39 -0
  70. package/dist/analysis/utils/matchers.d.ts.map +1 -0
  71. package/dist/analysis/utils/variables.d.ts +15 -0
  72. package/dist/analysis/utils/variables.d.ts.map +1 -0
  73. package/dist/analysis/validate/index.d.ts +5 -0
  74. package/dist/analysis/validate/index.d.ts.map +1 -0
  75. package/dist/analysis/validate/spec-structure.d.ts +15 -14
  76. package/dist/analysis/validate/spec-structure.d.ts.map +1 -1
  77. package/dist/analysis/validate/spec-structure.test.d.ts +2 -0
  78. package/dist/analysis/validate/spec-structure.test.d.ts.map +1 -0
  79. package/dist/formatter.d.ts +28 -27
  80. package/dist/formatter.d.ts.map +1 -1
  81. package/dist/formatters/index.d.ts +8 -0
  82. package/dist/formatters/index.d.ts.map +1 -0
  83. package/dist/formatters/spec-markdown.d.ts +13 -11
  84. package/dist/formatters/spec-markdown.d.ts.map +1 -1
  85. package/dist/formatters/spec-markdown.test.d.ts +5 -0
  86. package/dist/formatters/spec-markdown.test.d.ts.map +1 -0
  87. package/dist/formatters/spec-to-docblock.d.ts +4 -8
  88. package/dist/formatters/spec-to-docblock.d.ts.map +1 -1
  89. package/dist/index.d.ts +13 -42
  90. package/dist/index.d.ts.map +1 -0
  91. package/dist/index.js +4302 -38
  92. package/dist/node/index.js +4301 -0
  93. package/dist/templates/app-config.d.ts +2 -6
  94. package/dist/templates/app-config.d.ts.map +1 -1
  95. package/dist/templates/app-config.test.d.ts +2 -0
  96. package/dist/templates/app-config.test.d.ts.map +1 -0
  97. package/dist/templates/data-view.d.ts +2 -6
  98. package/dist/templates/data-view.d.ts.map +1 -1
  99. package/dist/templates/data-view.test.d.ts +2 -0
  100. package/dist/templates/data-view.test.d.ts.map +1 -0
  101. package/dist/templates/event.d.ts +6 -6
  102. package/dist/templates/event.d.ts.map +1 -1
  103. package/dist/templates/event.test.d.ts +2 -0
  104. package/dist/templates/event.test.d.ts.map +1 -0
  105. package/dist/templates/experiment.d.ts +2 -6
  106. package/dist/templates/experiment.d.ts.map +1 -1
  107. package/dist/templates/experiment.test.d.ts +2 -0
  108. package/dist/templates/experiment.test.d.ts.map +1 -0
  109. package/dist/templates/handler.d.ts +3 -6
  110. package/dist/templates/handler.d.ts.map +1 -1
  111. package/dist/templates/handler.test.d.ts +2 -0
  112. package/dist/templates/handler.test.d.ts.map +1 -0
  113. package/dist/templates/index.d.ts +18 -0
  114. package/dist/templates/index.d.ts.map +1 -0
  115. package/dist/templates/integration-utils.d.ts +13 -0
  116. package/dist/templates/integration-utils.d.ts.map +1 -0
  117. package/dist/templates/integration-utils.test.d.ts +2 -0
  118. package/dist/templates/integration-utils.test.d.ts.map +1 -0
  119. package/dist/templates/integration.d.ts +2 -6
  120. package/dist/templates/integration.d.ts.map +1 -1
  121. package/dist/templates/integration.test.d.ts +2 -0
  122. package/dist/templates/integration.test.d.ts.map +1 -0
  123. package/dist/templates/knowledge.d.ts +2 -6
  124. package/dist/templates/knowledge.d.ts.map +1 -1
  125. package/dist/templates/knowledge.test.d.ts +2 -0
  126. package/dist/templates/knowledge.test.d.ts.map +1 -0
  127. package/dist/templates/migration.d.ts +2 -6
  128. package/dist/templates/migration.d.ts.map +1 -1
  129. package/dist/templates/migration.test.d.ts +2 -0
  130. package/dist/templates/migration.test.d.ts.map +1 -0
  131. package/dist/templates/operation.d.ts +6 -6
  132. package/dist/templates/operation.d.ts.map +1 -1
  133. package/dist/templates/operation.test.d.ts +2 -0
  134. package/dist/templates/operation.test.d.ts.map +1 -0
  135. package/dist/templates/presentation.d.ts +6 -6
  136. package/dist/templates/presentation.d.ts.map +1 -1
  137. package/dist/templates/presentation.test.d.ts +2 -0
  138. package/dist/templates/presentation.test.d.ts.map +1 -0
  139. package/dist/templates/telemetry.d.ts +2 -6
  140. package/dist/templates/telemetry.d.ts.map +1 -1
  141. package/dist/templates/telemetry.test.d.ts +2 -0
  142. package/dist/templates/telemetry.test.d.ts.map +1 -0
  143. package/dist/templates/utils.d.ts +5 -8
  144. package/dist/templates/utils.d.ts.map +1 -1
  145. package/dist/templates/utils.test.d.ts +2 -0
  146. package/dist/templates/utils.test.d.ts.map +1 -0
  147. package/dist/templates/workflow-runner.d.ts +6 -13
  148. package/dist/templates/workflow-runner.d.ts.map +1 -1
  149. package/dist/templates/workflow-runner.test.d.ts +2 -0
  150. package/dist/templates/workflow-runner.test.d.ts.map +1 -0
  151. package/dist/templates/workflow.d.ts +6 -6
  152. package/dist/templates/workflow.d.ts.map +1 -1
  153. package/dist/templates/workflow.test.d.ts +2 -0
  154. package/dist/templates/workflow.test.d.ts.map +1 -0
  155. package/dist/types/analysis-types.d.ts +135 -136
  156. package/dist/types/analysis-types.d.ts.map +1 -1
  157. package/dist/types/generation-types.d.ts +36 -37
  158. package/dist/types/generation-types.d.ts.map +1 -1
  159. package/dist/types/index.d.ts +9 -0
  160. package/dist/types/index.d.ts.map +1 -0
  161. package/dist/types/llm-types.d.ts +97 -96
  162. package/dist/types/llm-types.d.ts.map +1 -1
  163. package/dist/types/rulesync-types.d.ts +17 -18
  164. package/dist/types/rulesync-types.d.ts.map +1 -1
  165. package/dist/types/spec-types.d.ts +329 -329
  166. package/dist/types/spec-types.d.ts.map +1 -1
  167. package/package.json +20 -16
  168. package/dist/ai/prompts/code-generation.js +0 -134
  169. package/dist/ai/prompts/code-generation.js.map +0 -1
  170. package/dist/ai/prompts/spec-creation.js +0 -102
  171. package/dist/ai/prompts/spec-creation.js.map +0 -1
  172. package/dist/analysis/deps/graph.js +0 -85
  173. package/dist/analysis/deps/graph.js.map +0 -1
  174. package/dist/analysis/deps/parse-imports.js +0 -31
  175. package/dist/analysis/deps/parse-imports.js.map +0 -1
  176. package/dist/analysis/diff/deep-diff.js +0 -114
  177. package/dist/analysis/diff/deep-diff.js.map +0 -1
  178. package/dist/analysis/diff/semantic.js +0 -97
  179. package/dist/analysis/diff/semantic.js.map +0 -1
  180. package/dist/analysis/example-scan.js +0 -116
  181. package/dist/analysis/example-scan.js.map +0 -1
  182. package/dist/analysis/feature-extractor.js +0 -203
  183. package/dist/analysis/feature-extractor.js.map +0 -1
  184. package/dist/analysis/feature-scan.js +0 -56
  185. package/dist/analysis/feature-scan.js.map +0 -1
  186. package/dist/analysis/grouping.js +0 -115
  187. package/dist/analysis/grouping.js.map +0 -1
  188. package/dist/analysis/impact/classifier.js +0 -135
  189. package/dist/analysis/impact/classifier.js.map +0 -1
  190. package/dist/analysis/impact/index.js +0 -2
  191. package/dist/analysis/impact/rules.js +0 -154
  192. package/dist/analysis/impact/rules.js.map +0 -1
  193. package/dist/analysis/index.js +0 -18
  194. package/dist/analysis/snapshot/index.js +0 -2
  195. package/dist/analysis/snapshot/normalizer.js +0 -67
  196. package/dist/analysis/snapshot/normalizer.js.map +0 -1
  197. package/dist/analysis/snapshot/snapshot.js +0 -163
  198. package/dist/analysis/snapshot/snapshot.js.map +0 -1
  199. package/dist/analysis/spec-parser.js +0 -89
  200. package/dist/analysis/spec-parser.js.map +0 -1
  201. package/dist/analysis/spec-parsing-utils.js +0 -98
  202. package/dist/analysis/spec-parsing-utils.js.map +0 -1
  203. package/dist/analysis/spec-scan.js +0 -157
  204. package/dist/analysis/spec-scan.js.map +0 -1
  205. package/dist/analysis/utils/matchers.js +0 -77
  206. package/dist/analysis/utils/matchers.js.map +0 -1
  207. package/dist/analysis/utils/variables.js +0 -45
  208. package/dist/analysis/utils/variables.js.map +0 -1
  209. package/dist/analysis/validate/index.js +0 -1
  210. package/dist/analysis/validate/spec-structure.js +0 -475
  211. package/dist/analysis/validate/spec-structure.js.map +0 -1
  212. package/dist/formatter.js +0 -163
  213. package/dist/formatter.js.map +0 -1
  214. package/dist/formatters/index.js +0 -2
  215. package/dist/formatters/spec-markdown.js +0 -263
  216. package/dist/formatters/spec-markdown.js.map +0 -1
  217. package/dist/formatters/spec-to-docblock.js +0 -48
  218. package/dist/formatters/spec-to-docblock.js.map +0 -1
  219. package/dist/templates/app-config.js +0 -107
  220. package/dist/templates/app-config.js.map +0 -1
  221. package/dist/templates/data-view.js +0 -69
  222. package/dist/templates/data-view.js.map +0 -1
  223. package/dist/templates/event.js +0 -41
  224. package/dist/templates/event.js.map +0 -1
  225. package/dist/templates/experiment.js +0 -88
  226. package/dist/templates/experiment.js.map +0 -1
  227. package/dist/templates/handler.js +0 -96
  228. package/dist/templates/handler.js.map +0 -1
  229. package/dist/templates/integration-utils.js +0 -102
  230. package/dist/templates/integration-utils.js.map +0 -1
  231. package/dist/templates/integration.js +0 -62
  232. package/dist/templates/integration.js.map +0 -1
  233. package/dist/templates/knowledge.js +0 -68
  234. package/dist/templates/knowledge.js.map +0 -1
  235. package/dist/templates/migration.js +0 -60
  236. package/dist/templates/migration.js.map +0 -1
  237. package/dist/templates/operation.js +0 -101
  238. package/dist/templates/operation.js.map +0 -1
  239. package/dist/templates/presentation.js +0 -79
  240. package/dist/templates/presentation.js.map +0 -1
  241. package/dist/templates/telemetry.js +0 -90
  242. package/dist/templates/telemetry.js.map +0 -1
  243. package/dist/templates/utils.js +0 -39
  244. package/dist/templates/utils.js.map +0 -1
  245. package/dist/templates/workflow-runner.js +0 -49
  246. package/dist/templates/workflow-runner.js.map +0 -1
  247. package/dist/templates/workflow.js +0 -68
  248. package/dist/templates/workflow.js.map +0 -1
@@ -1,115 +0,0 @@
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]) => ({
63
- key,
64
- items
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
@@ -1 +0,0 @@
1
- {"version":3,"file":"grouping.js","names":[],"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":";;;;AA2CA,MAAa,yBAAyB;CAEpC,QAAQ,SAAiC,KAAK,OAAO,MAAM;CAG3D,UAAU,SAAiC,KAAK,SAAS,MAAM;CAG/D,WAAW,SAAiC;EAC1C,MAAM,MAAM,KAAK,OAAO;AACxB,MAAI,IAAI,SAAS,IAAI,CACnB,QAAO,IAAI,MAAM,IAAI,CAAC,MAAM;AAE9B,SAAO;;CAIT,cAAc,SAAiC,KAAK,aAAa;CAGjE,aAAa,SAAiC,KAAK;CAGnD,cAAc,SAAiC;AAG7C,SAFc,KAAK,SAAS,MAAM,IAAI,CAEzB,MAAM,GAAG,GAAG,CAAC,KAAK,IAAI,IAAI;;CAE1C;;;;AAKD,SAAgB,YACd,OACA,QACkB;AAClB,QAAO,MAAM,QAAQ,SAAS;AAE5B,MAAI,OAAO,MAAM,QAIf;OAAI,CAHmB,OAAO,KAAK,MAAM,QACvC,KAAK,MAAM,SAAS,IAAI,CACzB,CACoB,QAAO;;AAI9B,MAAI,OAAO,QAAQ,QAIjB;OAAI,CAHqB,OAAO,OAAO,MAAM,UAC3C,KAAK,QAAQ,SAAS,MAAM,CAC7B,CACsB,QAAO;;AAIhC,MAAI,OAAO,WAAW,QACpB;OAAI,CAAC,OAAO,UAAU,SAAS,KAAK,aAAa,SAAS,CACxD,QAAO;;AAKX,MAAI,OAAO,UAAU,QACnB;OAAI,CAAC,OAAO,SAAS,SAAS,KAAK,SAAS,CAC1C,QAAO;;AAKX,MAAI,OAAO,aAAa;GACtB,MAAM,MAAM,KAAK,OAAO;GACxB,MAAM,UAAU,OAAO,YACpB,QAAQ,OAAO,KAAK,CACpB,QAAQ,OAAO,IAAI;AAEtB,OAAI,CADU,IAAI,OAAO,IAAI,QAAQ,IAAI,IAAI,CAClC,KAAK,IAAI,CAAE,QAAO;;AAG/B,SAAO;GACP;;;;;AAMJ,SAAgB,WACd,OACA,OACkB;CAClB,MAAM,yBAAS,IAAI,KAAkB;AAErC,MAAK,MAAM,QAAQ,OAAO;EACxB,MAAM,MAAM,MAAM,KAAK;EACvB,MAAM,WAAW,OAAO,IAAI,IAAI;AAChC,MAAI,SACF,UAAS,KAAK,KAAK;MAEnB,QAAO,IAAI,KAAK,CAAC,KAAK,CAAC;;AAI3B,QAAO;;;;;AAMT,SAAgB,kBACd,OACA,OACmB;CACnB,MAAM,MAAM,WAAW,OAAO,MAAM;AACpC,QAAO,MAAM,KAAK,IAAI,SAAS,CAAC,CAC7B,KAAK,CAAC,KAAK,YAAY;EAAE;EAAK;EAAO,EAAE,CACvC,MAAM,GAAG,MAAM,EAAE,IAAI,cAAc,EAAE,IAAI,CAAC;;;;;AAM/C,SAAgB,kBAAkB,OAAmC;CACnE,MAAM,uBAAO,IAAI,KAAa;AAC9B,MAAK,MAAM,QAAQ,MACjB,MAAK,MAAM,OAAO,KAAK,QAAQ,EAAE,CAC/B,MAAK,IAAI,IAAI;AAGjB,QAAO,MAAM,KAAK,KAAK,CAAC,MAAM;;;;;AAMhC,SAAgB,oBAAoB,OAAmC;CACrE,MAAM,yBAAS,IAAI,KAAa;AAChC,MAAK,MAAM,QAAQ,MACjB,MAAK,MAAM,SAAS,KAAK,UAAU,EAAE,CACnC,QAAO,IAAI,MAAM;AAGrB,QAAO,MAAM,KAAK,OAAO,CAAC,MAAM;;;;;AAMlC,SAAgB,qBAAqB,OAAmC;CACtE,MAAM,0BAAU,IAAI,KAAa;AACjC,MAAK,MAAM,QAAQ,MACjB,SAAQ,IAAI,uBAAuB,SAAS,KAAK,CAAC;AAEpD,QAAO,MAAM,KAAK,QAAQ,CAAC,MAAM;;;;;AAMnC,SAAgB,eACd,UACA,QACqB;AACrB,QAAO,SAAS,QAAQ,YAAY;AAElC,MAAI,OAAO,MAAM,QAIf;OAAI,CAHmB,OAAO,KAAK,MAAM,QACvC,QAAQ,MAAM,SAAS,IAAI,CAC5B,CACoB,QAAO;;AAI9B,MAAI,OAAO,QAAQ,QAIjB;OAAI,CAHqB,OAAO,OAAO,MAAM,UAC3C,QAAQ,QAAQ,SAAS,MAAM,CAChC,CACsB,QAAO;;AAIhC,MAAI,OAAO,WAAW,QACpB;OAAI,CAAC,OAAO,UAAU,SAAS,QAAQ,aAAa,SAAS,CAC3D,QAAO;;AAKX,MAAI,OAAO,aAAa;GACtB,MAAM,UAAU,OAAO,YACpB,QAAQ,OAAO,KAAK,CACpB,QAAQ,OAAO,IAAI;AAEtB,OAAI,CADU,IAAI,OAAO,IAAI,QAAQ,IAAI,IAAI,CAClC,KAAK,QAAQ,IAAI,CAAE,QAAO;;AAGvC,SAAO;GACP"}
@@ -1,135 +0,0 @@
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
@@ -1 +0,0 @@
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":"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"}
@@ -1,2 +0,0 @@
1
- import { BREAKING_RULES, DEFAULT_RULES, INFO_RULES, NON_BREAKING_RULES, findMatchingRule, getRulesBySeverity } from "./rules.js";
2
- import { classifyImpact } from "./classifier.js";
@@ -1,154 +0,0 @@
1
- //#region src/analysis/impact/rules.ts
2
- /**
3
- * Default breaking change rules.
4
- */
5
- const BREAKING_RULES = [
6
- {
7
- id: "endpoint-removed",
8
- description: "Endpoint/operation was removed",
9
- severity: "breaking",
10
- matches: (delta) => delta.path.includes("spec.") && delta.type === "removed"
11
- },
12
- {
13
- id: "field-removed",
14
- description: "Field was removed from response",
15
- severity: "breaking",
16
- matches: (delta) => delta.path.includes(".output.") && delta.description.includes("removed")
17
- },
18
- {
19
- id: "field-type-changed",
20
- description: "Field type was changed",
21
- severity: "breaking",
22
- matches: (delta) => delta.path.includes(".type") && delta.description.includes("type changed")
23
- },
24
- {
25
- id: "field-made-required",
26
- description: "Optional field became required",
27
- severity: "breaking",
28
- matches: (delta) => delta.path.includes(".required") && delta.description.includes("optional to required")
29
- },
30
- {
31
- id: "enum-value-removed",
32
- description: "Enum value was removed",
33
- severity: "breaking",
34
- matches: (delta) => delta.path.includes(".enumValues") && delta.description.includes("removed")
35
- },
36
- {
37
- id: "nullable-removed",
38
- description: "Field is no longer nullable",
39
- severity: "breaking",
40
- matches: (delta) => delta.path.includes(".nullable") && delta.description.includes("no longer nullable")
41
- },
42
- {
43
- id: "method-changed",
44
- description: "HTTP method was changed",
45
- severity: "breaking",
46
- matches: (delta) => delta.path.includes(".http.method") || delta.path.includes(".method")
47
- },
48
- {
49
- id: "path-changed",
50
- description: "HTTP path was changed",
51
- severity: "breaking",
52
- matches: (delta) => delta.path.includes(".http.path") || delta.path.includes(".path")
53
- },
54
- {
55
- id: "required-field-added-to-input",
56
- description: "Required field was added to input",
57
- severity: "breaking",
58
- matches: (delta) => delta.path.includes(".input.") && delta.description.includes("Required field")
59
- },
60
- {
61
- id: "event-payload-field-removed",
62
- description: "Event payload field was removed",
63
- severity: "breaking",
64
- matches: (delta) => delta.path.includes(".payload.") && delta.description.includes("removed")
65
- }
66
- ];
67
- /**
68
- * Non-breaking change rules.
69
- */
70
- const NON_BREAKING_RULES = [
71
- {
72
- id: "optional-field-added",
73
- description: "Optional field was added",
74
- severity: "non_breaking",
75
- matches: (delta) => delta.description.includes("Optional field") && delta.description.includes("added")
76
- },
77
- {
78
- id: "endpoint-added",
79
- description: "New endpoint/operation was added",
80
- severity: "non_breaking",
81
- matches: (delta) => delta.path.includes("spec.") && delta.type === "added"
82
- },
83
- {
84
- id: "enum-value-added",
85
- description: "Enum value was added",
86
- severity: "non_breaking",
87
- matches: (delta) => delta.path.includes(".enumValues") && delta.description.includes("added")
88
- },
89
- {
90
- id: "field-made-optional",
91
- description: "Required field became optional",
92
- severity: "non_breaking",
93
- matches: (delta) => delta.path.includes(".required") && delta.description.includes("required to optional")
94
- },
95
- {
96
- id: "nullable-added",
97
- description: "Field is now nullable",
98
- severity: "non_breaking",
99
- matches: (delta) => delta.path.includes(".nullable") && delta.description.includes("now nullable")
100
- }
101
- ];
102
- /**
103
- * Info-level change rules.
104
- */
105
- const INFO_RULES = [
106
- {
107
- id: "stability-changed",
108
- description: "Stability level was changed",
109
- severity: "info",
110
- matches: (delta) => delta.path.includes(".stability")
111
- },
112
- {
113
- id: "description-changed",
114
- description: "Description was changed",
115
- severity: "info",
116
- matches: (delta) => delta.path.includes(".description")
117
- },
118
- {
119
- id: "owners-changed",
120
- description: "Owners were changed",
121
- severity: "info",
122
- matches: (delta) => delta.path.includes(".owners")
123
- },
124
- {
125
- id: "tags-changed",
126
- description: "Tags were changed",
127
- severity: "info",
128
- matches: (delta) => delta.path.includes(".tags")
129
- }
130
- ];
131
- /**
132
- * All default rules in priority order (breaking > non_breaking > info).
133
- */
134
- const DEFAULT_RULES = [
135
- ...BREAKING_RULES,
136
- ...NON_BREAKING_RULES,
137
- ...INFO_RULES
138
- ];
139
- /**
140
- * Get rules by severity.
141
- */
142
- function getRulesBySeverity(severity) {
143
- return DEFAULT_RULES.filter((rule) => rule.severity === severity);
144
- }
145
- /**
146
- * Find matching rule for a delta.
147
- */
148
- function findMatchingRule(delta, rules = DEFAULT_RULES) {
149
- return rules.find((rule) => rule.matches(delta));
150
- }
151
-
152
- //#endregion
153
- export { BREAKING_RULES, DEFAULT_RULES, INFO_RULES, NON_BREAKING_RULES, findMatchingRule, getRulesBySeverity };
154
- //# sourceMappingURL=rules.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"rules.js","names":[],"sources":["../../../src/analysis/impact/rules.ts"],"sourcesContent":["/**\n * Impact classification rules.\n *\n * Defines rules for classifying changes as breaking or non-breaking.\n */\n\nimport type { ImpactRule, ImpactSeverity } from './types';\n\n/**\n * Default breaking change rules.\n */\nexport const BREAKING_RULES: ImpactRule[] = [\n {\n id: 'endpoint-removed',\n description: 'Endpoint/operation was removed',\n severity: 'breaking',\n matches: (delta) =>\n delta.path.includes('spec.') && delta.type === 'removed',\n },\n {\n id: 'field-removed',\n description: 'Field was removed from response',\n severity: 'breaking',\n matches: (delta) =>\n delta.path.includes('.output.') && delta.description.includes('removed'),\n },\n {\n id: 'field-type-changed',\n description: 'Field type was changed',\n severity: 'breaking',\n matches: (delta) =>\n delta.path.includes('.type') &&\n delta.description.includes('type changed'),\n },\n {\n id: 'field-made-required',\n description: 'Optional field became required',\n severity: 'breaking',\n matches: (delta) =>\n delta.path.includes('.required') &&\n delta.description.includes('optional to required'),\n },\n {\n id: 'enum-value-removed',\n description: 'Enum value was removed',\n severity: 'breaking',\n matches: (delta) =>\n delta.path.includes('.enumValues') &&\n delta.description.includes('removed'),\n },\n {\n id: 'nullable-removed',\n description: 'Field is no longer nullable',\n severity: 'breaking',\n matches: (delta) =>\n delta.path.includes('.nullable') &&\n delta.description.includes('no longer nullable'),\n },\n {\n id: 'method-changed',\n description: 'HTTP method was changed',\n severity: 'breaking',\n matches: (delta) =>\n delta.path.includes('.http.method') || delta.path.includes('.method'),\n },\n {\n id: 'path-changed',\n description: 'HTTP path was changed',\n severity: 'breaking',\n matches: (delta) =>\n delta.path.includes('.http.path') || delta.path.includes('.path'),\n },\n {\n id: 'required-field-added-to-input',\n description: 'Required field was added to input',\n severity: 'breaking',\n matches: (delta) =>\n delta.path.includes('.input.') &&\n delta.description.includes('Required field'),\n },\n {\n id: 'event-payload-field-removed',\n description: 'Event payload field was removed',\n severity: 'breaking',\n matches: (delta) =>\n delta.path.includes('.payload.') && delta.description.includes('removed'),\n },\n];\n\n/**\n * Non-breaking change rules.\n */\nexport const NON_BREAKING_RULES: ImpactRule[] = [\n {\n id: 'optional-field-added',\n description: 'Optional field was added',\n severity: 'non_breaking',\n matches: (delta) =>\n delta.description.includes('Optional field') &&\n delta.description.includes('added'),\n },\n {\n id: 'endpoint-added',\n description: 'New endpoint/operation was added',\n severity: 'non_breaking',\n matches: (delta) => delta.path.includes('spec.') && delta.type === 'added',\n },\n {\n id: 'enum-value-added',\n description: 'Enum value was added',\n severity: 'non_breaking',\n matches: (delta) =>\n delta.path.includes('.enumValues') && delta.description.includes('added'),\n },\n {\n id: 'field-made-optional',\n description: 'Required field became optional',\n severity: 'non_breaking',\n matches: (delta) =>\n delta.path.includes('.required') &&\n delta.description.includes('required to optional'),\n },\n {\n id: 'nullable-added',\n description: 'Field is now nullable',\n severity: 'non_breaking',\n matches: (delta) =>\n delta.path.includes('.nullable') &&\n delta.description.includes('now nullable'),\n },\n];\n\n/**\n * Info-level change rules.\n */\nexport const INFO_RULES: ImpactRule[] = [\n {\n id: 'stability-changed',\n description: 'Stability level was changed',\n severity: 'info',\n matches: (delta) => delta.path.includes('.stability'),\n },\n {\n id: 'description-changed',\n description: 'Description was changed',\n severity: 'info',\n matches: (delta) => delta.path.includes('.description'),\n },\n {\n id: 'owners-changed',\n description: 'Owners were changed',\n severity: 'info',\n matches: (delta) => delta.path.includes('.owners'),\n },\n {\n id: 'tags-changed',\n description: 'Tags were changed',\n severity: 'info',\n matches: (delta) => delta.path.includes('.tags'),\n },\n];\n\n/**\n * All default rules in priority order (breaking > non_breaking > info).\n */\nexport const DEFAULT_RULES: ImpactRule[] = [\n ...BREAKING_RULES,\n ...NON_BREAKING_RULES,\n ...INFO_RULES,\n];\n\n/**\n * Get rules by severity.\n */\nexport function getRulesBySeverity(severity: ImpactSeverity): ImpactRule[] {\n return DEFAULT_RULES.filter((rule) => rule.severity === severity);\n}\n\n/**\n * Find matching rule for a delta.\n */\nexport function findMatchingRule(\n delta: { path: string; description: string; type: string },\n rules: ImpactRule[] = DEFAULT_RULES\n): ImpactRule | undefined {\n return rules.find((rule) => rule.matches(delta));\n}\n"],"mappings":";;;;AAWA,MAAa,iBAA+B;CAC1C;EACE,IAAI;EACJ,aAAa;EACb,UAAU;EACV,UAAU,UACR,MAAM,KAAK,SAAS,QAAQ,IAAI,MAAM,SAAS;EAClD;CACD;EACE,IAAI;EACJ,aAAa;EACb,UAAU;EACV,UAAU,UACR,MAAM,KAAK,SAAS,WAAW,IAAI,MAAM,YAAY,SAAS,UAAU;EAC3E;CACD;EACE,IAAI;EACJ,aAAa;EACb,UAAU;EACV,UAAU,UACR,MAAM,KAAK,SAAS,QAAQ,IAC5B,MAAM,YAAY,SAAS,eAAe;EAC7C;CACD;EACE,IAAI;EACJ,aAAa;EACb,UAAU;EACV,UAAU,UACR,MAAM,KAAK,SAAS,YAAY,IAChC,MAAM,YAAY,SAAS,uBAAuB;EACrD;CACD;EACE,IAAI;EACJ,aAAa;EACb,UAAU;EACV,UAAU,UACR,MAAM,KAAK,SAAS,cAAc,IAClC,MAAM,YAAY,SAAS,UAAU;EACxC;CACD;EACE,IAAI;EACJ,aAAa;EACb,UAAU;EACV,UAAU,UACR,MAAM,KAAK,SAAS,YAAY,IAChC,MAAM,YAAY,SAAS,qBAAqB;EACnD;CACD;EACE,IAAI;EACJ,aAAa;EACb,UAAU;EACV,UAAU,UACR,MAAM,KAAK,SAAS,eAAe,IAAI,MAAM,KAAK,SAAS,UAAU;EACxE;CACD;EACE,IAAI;EACJ,aAAa;EACb,UAAU;EACV,UAAU,UACR,MAAM,KAAK,SAAS,aAAa,IAAI,MAAM,KAAK,SAAS,QAAQ;EACpE;CACD;EACE,IAAI;EACJ,aAAa;EACb,UAAU;EACV,UAAU,UACR,MAAM,KAAK,SAAS,UAAU,IAC9B,MAAM,YAAY,SAAS,iBAAiB;EAC/C;CACD;EACE,IAAI;EACJ,aAAa;EACb,UAAU;EACV,UAAU,UACR,MAAM,KAAK,SAAS,YAAY,IAAI,MAAM,YAAY,SAAS,UAAU;EAC5E;CACF;;;;AAKD,MAAa,qBAAmC;CAC9C;EACE,IAAI;EACJ,aAAa;EACb,UAAU;EACV,UAAU,UACR,MAAM,YAAY,SAAS,iBAAiB,IAC5C,MAAM,YAAY,SAAS,QAAQ;EACtC;CACD;EACE,IAAI;EACJ,aAAa;EACb,UAAU;EACV,UAAU,UAAU,MAAM,KAAK,SAAS,QAAQ,IAAI,MAAM,SAAS;EACpE;CACD;EACE,IAAI;EACJ,aAAa;EACb,UAAU;EACV,UAAU,UACR,MAAM,KAAK,SAAS,cAAc,IAAI,MAAM,YAAY,SAAS,QAAQ;EAC5E;CACD;EACE,IAAI;EACJ,aAAa;EACb,UAAU;EACV,UAAU,UACR,MAAM,KAAK,SAAS,YAAY,IAChC,MAAM,YAAY,SAAS,uBAAuB;EACrD;CACD;EACE,IAAI;EACJ,aAAa;EACb,UAAU;EACV,UAAU,UACR,MAAM,KAAK,SAAS,YAAY,IAChC,MAAM,YAAY,SAAS,eAAe;EAC7C;CACF;;;;AAKD,MAAa,aAA2B;CACtC;EACE,IAAI;EACJ,aAAa;EACb,UAAU;EACV,UAAU,UAAU,MAAM,KAAK,SAAS,aAAa;EACtD;CACD;EACE,IAAI;EACJ,aAAa;EACb,UAAU;EACV,UAAU,UAAU,MAAM,KAAK,SAAS,eAAe;EACxD;CACD;EACE,IAAI;EACJ,aAAa;EACb,UAAU;EACV,UAAU,UAAU,MAAM,KAAK,SAAS,UAAU;EACnD;CACD;EACE,IAAI;EACJ,aAAa;EACb,UAAU;EACV,UAAU,UAAU,MAAM,KAAK,SAAS,QAAQ;EACjD;CACF;;;;AAKD,MAAa,gBAA8B;CACzC,GAAG;CACH,GAAG;CACH,GAAG;CACJ;;;;AAKD,SAAgB,mBAAmB,UAAwC;AACzE,QAAO,cAAc,QAAQ,SAAS,KAAK,aAAa,SAAS;;;;;AAMnE,SAAgB,iBACd,OACA,QAAsB,eACE;AACxB,QAAO,MAAM,MAAM,SAAS,KAAK,QAAQ,MAAM,CAAC"}
@@ -1,18 +0,0 @@
1
- import { extractTestCoverage, extractTestTarget } from "./spec-parsing-utils.js";
2
- import { inferSpecTypeFromCodeBlock, inferSpecTypeFromFilePath, scanAllSpecsFromSource, scanSpecSource } from "./spec-scan.js";
3
- import { isFeatureFile, scanFeatureSource } from "./feature-scan.js";
4
- import { isExampleFile, scanExampleSource } from "./example-scan.js";
5
- import { SpecGroupingStrategies, filterFeatures, filterSpecs, getUniqueSpecDomains, getUniqueSpecOwners, getUniqueSpecTags, groupSpecs, groupSpecsToArray } from "./grouping.js";
6
- import { computeSemanticDiff } from "./diff/semantic.js";
7
- import { computeFieldDiff, computeFieldsDiff, computeIoDiff, isBreakingChange } from "./diff/deep-diff.js";
8
- import { addContractNode, buildReverseEdges, createContractGraph, detectCycles, findMissingDependencies, toDot } from "./deps/graph.js";
9
- import { parseImportedSpecNames } from "./deps/parse-imports.js";
10
- import { validateSpecStructure } from "./validate/spec-structure.js";
11
- import "./validate/index.js";
12
- import { computeHash, normalizeValue, sortFields, sortSpecs, toCanonicalJson } from "./snapshot/normalizer.js";
13
- import { generateSnapshot } from "./snapshot/snapshot.js";
14
- import "./snapshot/index.js";
15
- import { BREAKING_RULES, DEFAULT_RULES, INFO_RULES, NON_BREAKING_RULES, findMatchingRule, getRulesBySeverity } from "./impact/rules.js";
16
- import { classifyImpact } from "./impact/classifier.js";
17
- import "./impact/index.js";
18
- import { loadSpecFromSource } from "./spec-parser.js";
@@ -1,2 +0,0 @@
1
- import { computeHash, normalizeValue, sortFields, sortSpecs, toCanonicalJson } from "./normalizer.js";
2
- import { generateSnapshot } from "./snapshot.js";
@@ -1,67 +0,0 @@
1
- import { createHash } from "crypto";
2
- import { compareVersions } from "compare-versions";
3
-
4
- //#region src/analysis/snapshot/normalizer.ts
5
- /**
6
- * JSON normalization utilities for deterministic snapshots.
7
- *
8
- * Ensures that snapshots are stable across ordering, whitespace,
9
- * and other non-semantic differences.
10
- */
11
- /**
12
- * Normalize a value for deterministic JSON serialization.
13
- * - Sorts object keys alphabetically
14
- * - Removes undefined values
15
- * - Preserves null values
16
- */
17
- function normalizeValue(value) {
18
- if (value === null || value === void 0) return value === null ? null : void 0;
19
- if (Array.isArray(value)) return value.map(normalizeValue);
20
- if (typeof value === "object") {
21
- const obj = value;
22
- const sortedKeys = Object.keys(obj).sort();
23
- const normalized = {};
24
- for (const key of sortedKeys) {
25
- const normalizedValue = normalizeValue(obj[key]);
26
- if (normalizedValue !== void 0) normalized[key] = normalizedValue;
27
- }
28
- return normalized;
29
- }
30
- return value;
31
- }
32
- /**
33
- * Serialize a value to deterministic JSON string.
34
- */
35
- function toCanonicalJson(value) {
36
- return JSON.stringify(normalizeValue(value), null, 0);
37
- }
38
- /**
39
- * Compute a SHA-256 hash of canonical JSON representation.
40
- */
41
- function computeHash(value) {
42
- const canonical = toCanonicalJson(value);
43
- return createHash("sha256").update(canonical).digest("hex").slice(0, 16);
44
- }
45
- /**
46
- * Sort specs by key and version for deterministic ordering.
47
- */
48
- function sortSpecs(specs) {
49
- return [...specs].sort((a, b) => {
50
- const keyCompare = a.key.localeCompare(b.key);
51
- if (keyCompare !== 0) return keyCompare;
52
- return compareVersions(a.version, b.version);
53
- });
54
- }
55
- /**
56
- * Sort field snapshots by name for deterministic ordering.
57
- */
58
- function sortFields(fields) {
59
- const sorted = {};
60
- const keys = Object.keys(fields).sort();
61
- for (const key of keys) sorted[key] = fields[key];
62
- return sorted;
63
- }
64
-
65
- //#endregion
66
- export { computeHash, normalizeValue, sortFields, sortSpecs, toCanonicalJson };
67
- //# sourceMappingURL=normalizer.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"normalizer.js","names":[],"sources":["../../../src/analysis/snapshot/normalizer.ts"],"sourcesContent":["/**\n * JSON normalization utilities for deterministic snapshots.\n *\n * Ensures that snapshots are stable across ordering, whitespace,\n * and other non-semantic differences.\n */\n\nimport { createHash } from 'crypto';\nimport { compareVersions } from 'compare-versions';\n\n/**\n * Normalize a value for deterministic JSON serialization.\n * - Sorts object keys alphabetically\n * - Removes undefined values\n * - Preserves null values\n */\nexport function normalizeValue(value: unknown): unknown {\n if (value === null || value === undefined) {\n return value === null ? null : undefined;\n }\n\n if (Array.isArray(value)) {\n return value.map(normalizeValue);\n }\n\n if (typeof value === 'object') {\n const obj = value as Record<string, unknown>;\n const sortedKeys = Object.keys(obj).sort();\n const normalized: Record<string, unknown> = {};\n\n for (const key of sortedKeys) {\n const normalizedValue = normalizeValue(obj[key]);\n // Only include defined values\n if (normalizedValue !== undefined) {\n normalized[key] = normalizedValue;\n }\n }\n\n return normalized;\n }\n\n return value;\n}\n\n/**\n * Serialize a value to deterministic JSON string.\n */\nexport function toCanonicalJson(value: unknown): string {\n return JSON.stringify(normalizeValue(value), null, 0);\n}\n\n/**\n * Compute a SHA-256 hash of canonical JSON representation.\n */\nexport function computeHash(value: unknown): string {\n const canonical = toCanonicalJson(value);\n return createHash('sha256').update(canonical).digest('hex').slice(0, 16);\n}\n\n/**\n * Sort specs by key and version for deterministic ordering.\n */\nexport function sortSpecs<T extends { key: string; version: string }>(\n specs: T[]\n): T[] {\n return [...specs].sort((a, b) => {\n const keyCompare = a.key.localeCompare(b.key);\n if (keyCompare !== 0) return keyCompare;\n return compareVersions(a.version, b.version);\n });\n}\n\n/**\n * Sort field snapshots by name for deterministic ordering.\n */\nexport function sortFields(\n fields: Record<string, unknown>\n): Record<string, unknown> {\n const sorted: Record<string, unknown> = {};\n const keys = Object.keys(fields).sort();\n for (const key of keys) {\n sorted[key] = fields[key];\n }\n return sorted;\n}\n"],"mappings":";;;;;;;;;;;;;;;;AAgBA,SAAgB,eAAe,OAAyB;AACtD,KAAI,UAAU,QAAQ,UAAU,OAC9B,QAAO,UAAU,OAAO,OAAO;AAGjC,KAAI,MAAM,QAAQ,MAAM,CACtB,QAAO,MAAM,IAAI,eAAe;AAGlC,KAAI,OAAO,UAAU,UAAU;EAC7B,MAAM,MAAM;EACZ,MAAM,aAAa,OAAO,KAAK,IAAI,CAAC,MAAM;EAC1C,MAAM,aAAsC,EAAE;AAE9C,OAAK,MAAM,OAAO,YAAY;GAC5B,MAAM,kBAAkB,eAAe,IAAI,KAAK;AAEhD,OAAI,oBAAoB,OACtB,YAAW,OAAO;;AAItB,SAAO;;AAGT,QAAO;;;;;AAMT,SAAgB,gBAAgB,OAAwB;AACtD,QAAO,KAAK,UAAU,eAAe,MAAM,EAAE,MAAM,EAAE;;;;;AAMvD,SAAgB,YAAY,OAAwB;CAClD,MAAM,YAAY,gBAAgB,MAAM;AACxC,QAAO,WAAW,SAAS,CAAC,OAAO,UAAU,CAAC,OAAO,MAAM,CAAC,MAAM,GAAG,GAAG;;;;;AAM1E,SAAgB,UACd,OACK;AACL,QAAO,CAAC,GAAG,MAAM,CAAC,MAAM,GAAG,MAAM;EAC/B,MAAM,aAAa,EAAE,IAAI,cAAc,EAAE,IAAI;AAC7C,MAAI,eAAe,EAAG,QAAO;AAC7B,SAAO,gBAAgB,EAAE,SAAS,EAAE,QAAQ;GAC5C;;;;;AAMJ,SAAgB,WACd,QACyB;CACzB,MAAM,SAAkC,EAAE;CAC1C,MAAM,OAAO,OAAO,KAAK,OAAO,CAAC,MAAM;AACvC,MAAK,MAAM,OAAO,KAChB,QAAO,OAAO,OAAO;AAEvB,QAAO"}