@unispechq/unispec-core 0.3.2 → 0.3.3

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 (265) hide show
  1. package/dist/cjs/src/cache/cache-factory.js +1 -1
  2. package/dist/cjs/src/cache/cache-manager.js +2 -2
  3. package/dist/cjs/src/cache/hash-utils.js +1 -1
  4. package/dist/cjs/src/cache/hashing.js +1 -1
  5. package/dist/cjs/src/cache/index.js +5 -5
  6. package/dist/cjs/src/cache/lru-cache.js +1 -1
  7. package/dist/cjs/src/diff/core.js +2 -2
  8. package/dist/cjs/src/diff/enhanced-diff.js +5 -5
  9. package/dist/cjs/src/diff/index.js +8 -8
  10. package/dist/cjs/src/errors/config-error.js +1 -1
  11. package/dist/cjs/src/errors/error-factory.js +7 -7
  12. package/dist/cjs/src/errors/index.js +8 -8
  13. package/dist/cjs/src/errors/loader-error.js +1 -1
  14. package/dist/cjs/src/errors/reference-error.js +1 -1
  15. package/dist/cjs/src/errors/schema-error.js +1 -1
  16. package/dist/cjs/src/errors/security-error.js +1 -1
  17. package/dist/cjs/src/errors/semantic-error.js +1 -1
  18. package/dist/cjs/src/index.js +13 -13
  19. package/dist/cjs/src/loader/index.js +4 -4
  20. package/dist/cjs/src/loader/security-validator.js +1 -1
  21. package/dist/cjs/src/loader/unispec-loader.js +3 -3
  22. package/dist/cjs/src/loader/yaml-loader.js +1 -1
  23. package/dist/cjs/src/normalizer/core.js +4 -4
  24. package/dist/cjs/src/normalizer/index.js +1 -1
  25. package/dist/cjs/src/optimizer/core.js +2 -2
  26. package/dist/cjs/src/optimizer/index.js +3 -3
  27. package/dist/cjs/src/optimizer/optimization-functions.js +2 -2
  28. package/dist/cjs/src/schemas/dedupe.js +1 -1
  29. package/dist/cjs/src/schemas/index.js +3 -3
  30. package/dist/cjs/src/schemas/resolver.js +1 -1
  31. package/dist/cjs/src/validator/ajv-validator.js +1 -1
  32. package/dist/cjs/src/validator/config-validator-main.js +4 -4
  33. package/dist/cjs/src/validator/config-validator.js +1 -1
  34. package/dist/cjs/src/validator/index.js +7 -7
  35. package/dist/cjs/src/validator/reference-validator.js +1 -1
  36. package/dist/cjs/src/validator/tests-validator.js +2 -2
  37. package/dist/cjs/src/validator/unispec-validator.js +5 -5
  38. package/dist/cjs/src/validator/validator-factory.js +1 -1
  39. package/dist/cjs/tests/cache/cache.test.js +4 -4
  40. package/dist/cjs/tests/cache/utils.js +1 -1
  41. package/dist/cjs/tests/diff/diff-annotators.test.js +2 -2
  42. package/dist/cjs/tests/diff/diff-comprehensive.test.js +2 -2
  43. package/dist/cjs/tests/diff/diff-extended.test.js +2 -2
  44. package/dist/cjs/tests/diff/diff.test.js +2 -2
  45. package/dist/cjs/tests/diff/utils.js +1 -1
  46. package/dist/cjs/tests/errors/errors-integration.test.js +3 -3
  47. package/dist/cjs/tests/errors/errors.test.js +2 -2
  48. package/dist/cjs/tests/errors/utils.js +1 -1
  49. package/dist/cjs/tests/loader/integration.test.js +2 -2
  50. package/dist/cjs/tests/loader/loader.test.js +2 -2
  51. package/dist/cjs/tests/normalizer/normalizer-comprehensive.test.js +2 -2
  52. package/dist/cjs/tests/normalizer/normalizer-invalid.test.js +2 -2
  53. package/dist/cjs/tests/normalizer/normalizer-valid.test.js +2 -2
  54. package/dist/cjs/tests/normalizer/utils.js +1 -1
  55. package/dist/cjs/tests/optimizer/compress-references.test.js +2 -2
  56. package/dist/cjs/tests/optimizer/deduplication.test.js +2 -2
  57. package/dist/cjs/tests/optimizer/integration.test.js +3 -3
  58. package/dist/cjs/tests/optimizer/optimization-report.test.js +2 -2
  59. package/dist/cjs/tests/optimizer/optimize-document.test.js +2 -2
  60. package/dist/cjs/tests/optimizer/orphaned-schemas.test.js +2 -2
  61. package/dist/cjs/tests/optimizer/sort-schemas.test.js +2 -2
  62. package/dist/cjs/tests/optimizer/utils.js +1 -1
  63. package/dist/cjs/tests/schemas/schemas-edge-cases.test.js +3 -3
  64. package/dist/cjs/tests/schemas/schemas.test.js +3 -3
  65. package/dist/cjs/tests/schemas/utils.js +1 -1
  66. package/dist/cjs/tests/validator/config-validator.test.js +2 -2
  67. package/dist/cjs/tests/validator/unispec-validator.test.js +2 -2
  68. package/dist/cjs/tests/validator/utils.js +1 -1
  69. package/package.json +1 -1
  70. package/dist/cache/cache-factory.d.ts +0 -31
  71. package/dist/cache/cache-factory.js +0 -65
  72. package/dist/cache/cache-manager.d.ts +0 -62
  73. package/dist/cache/cache-manager.js +0 -122
  74. package/dist/cache/constants.d.ts +0 -21
  75. package/dist/cache/constants.js +0 -22
  76. package/dist/cache/hash-utils.d.ts +0 -21
  77. package/dist/cache/hash-utils.js +0 -35
  78. package/dist/cache/hashing.d.ts +0 -19
  79. package/dist/cache/hashing.js +0 -197
  80. package/dist/cache/index.d.ts +0 -6
  81. package/dist/cache/index.js +0 -10
  82. package/dist/cache/lru-cache.d.ts +0 -56
  83. package/dist/cache/lru-cache.js +0 -161
  84. package/dist/cache/types.d.ts +0 -24
  85. package/dist/cache/types.js +0 -4
  86. package/dist/cjs/cache/cache-factory.js +0 -72
  87. package/dist/cjs/cache/cache-manager.js +0 -126
  88. package/dist/cjs/cache/constants.js +0 -25
  89. package/dist/cjs/cache/hash-utils.js +0 -41
  90. package/dist/cjs/cache/hashing.js +0 -236
  91. package/dist/cjs/cache/index.js +0 -26
  92. package/dist/cjs/cache/lru-cache.js +0 -165
  93. package/dist/cjs/cache/types.js +0 -5
  94. package/dist/cjs/diff/annotators.js +0 -159
  95. package/dist/cjs/diff/change-reports.js +0 -369
  96. package/dist/cjs/diff/core.js +0 -158
  97. package/dist/cjs/diff/enhanced-diff.js +0 -79
  98. package/dist/cjs/diff/impact-strategies-refactored.js +0 -230
  99. package/dist/cjs/diff/impact-strategies.js +0 -219
  100. package/dist/cjs/diff/index.js +0 -27
  101. package/dist/cjs/diff/metrics-calculator.js +0 -69
  102. package/dist/cjs/diff/risk-calculator.js +0 -58
  103. package/dist/cjs/diff/suggestion-generator.js +0 -78
  104. package/dist/cjs/diff/types.js +0 -11
  105. package/dist/cjs/errors/base-error.js +0 -33
  106. package/dist/cjs/errors/config-error.js +0 -11
  107. package/dist/cjs/errors/error-factory.js +0 -48
  108. package/dist/cjs/errors/index.js +0 -19
  109. package/dist/cjs/errors/loader-error.js +0 -11
  110. package/dist/cjs/errors/reference-error.js +0 -11
  111. package/dist/cjs/errors/schema-error.js +0 -11
  112. package/dist/cjs/errors/security-error.js +0 -11
  113. package/dist/cjs/errors/semantic-error.js +0 -11
  114. package/dist/cjs/generated-schemas.js +0 -2100
  115. package/dist/cjs/index.js +0 -59
  116. package/dist/cjs/loader/index.js +0 -13
  117. package/dist/cjs/loader/security-validator.js +0 -53
  118. package/dist/cjs/loader/types.js +0 -11
  119. package/dist/cjs/loader/unispec-loader.js +0 -84
  120. package/dist/cjs/loader/yaml-loader.js +0 -76
  121. package/dist/cjs/normalizer/core.js +0 -32
  122. package/dist/cjs/normalizer/graphql-normalizer.js +0 -67
  123. package/dist/cjs/normalizer/index.js +0 -7
  124. package/dist/cjs/normalizer/rest-normalizer.js +0 -51
  125. package/dist/cjs/normalizer/types.js +0 -2
  126. package/dist/cjs/normalizer/utils.js +0 -33
  127. package/dist/cjs/normalizer/websocket-normalizer.js +0 -81
  128. package/dist/cjs/optimizer/core.js +0 -115
  129. package/dist/cjs/optimizer/index.js +0 -17
  130. package/dist/cjs/optimizer/optimization-functions.js +0 -185
  131. package/dist/cjs/optimizer/types.js +0 -2
  132. package/dist/cjs/optimizer/utils.js +0 -32
  133. package/dist/cjs/schemas/dedupe.js +0 -100
  134. package/dist/cjs/schemas/index.js +0 -14
  135. package/dist/cjs/schemas/resolver.js +0 -41
  136. package/dist/cjs/schemas/utils.js +0 -53
  137. package/dist/cjs/types/index.js +0 -2
  138. package/dist/cjs/validator/ajv-validator.js +0 -82
  139. package/dist/cjs/validator/config-validator-main.js +0 -34
  140. package/dist/cjs/validator/config-validator.js +0 -17
  141. package/dist/cjs/validator/index.js +0 -23
  142. package/dist/cjs/validator/object-traversal.js +0 -112
  143. package/dist/cjs/validator/reference-validator.js +0 -233
  144. package/dist/cjs/validator/schema-references.js +0 -116
  145. package/dist/cjs/validator/semantic-validator.js +0 -328
  146. package/dist/cjs/validator/tests-validator.js +0 -16
  147. package/dist/cjs/validator/types.js +0 -2
  148. package/dist/cjs/validator/unispec-validator.js +0 -84
  149. package/dist/cjs/validator/validator-factory.js +0 -77
  150. package/dist/cjs/versions.js +0 -147
  151. package/dist/diff/annotators.d.ts +0 -4
  152. package/dist/diff/annotators.js +0 -154
  153. package/dist/diff/change-reports.d.ts +0 -37
  154. package/dist/diff/change-reports.js +0 -366
  155. package/dist/diff/core.d.ts +0 -26
  156. package/dist/diff/core.js +0 -155
  157. package/dist/diff/enhanced-diff.d.ts +0 -51
  158. package/dist/diff/enhanced-diff.js +0 -76
  159. package/dist/diff/impact-strategies-refactored.d.ts +0 -69
  160. package/dist/diff/impact-strategies-refactored.js +0 -223
  161. package/dist/diff/impact-strategies.d.ts +0 -41
  162. package/dist/diff/impact-strategies.js +0 -212
  163. package/dist/diff/index.d.ts +0 -8
  164. package/dist/diff/index.js +0 -11
  165. package/dist/diff/metrics-calculator.d.ts +0 -23
  166. package/dist/diff/metrics-calculator.js +0 -65
  167. package/dist/diff/risk-calculator.d.ts +0 -23
  168. package/dist/diff/risk-calculator.js +0 -55
  169. package/dist/diff/suggestion-generator.d.ts +0 -18
  170. package/dist/diff/suggestion-generator.js +0 -74
  171. package/dist/diff/types.d.ts +0 -24
  172. package/dist/diff/types.js +0 -8
  173. package/dist/errors/base-error.d.ts +0 -20
  174. package/dist/errors/base-error.js +0 -29
  175. package/dist/errors/config-error.d.ts +0 -4
  176. package/dist/errors/config-error.js +0 -7
  177. package/dist/errors/error-factory.d.ts +0 -22
  178. package/dist/errors/error-factory.js +0 -45
  179. package/dist/errors/index.d.ts +0 -8
  180. package/dist/errors/index.js +0 -8
  181. package/dist/errors/loader-error.d.ts +0 -4
  182. package/dist/errors/loader-error.js +0 -7
  183. package/dist/errors/reference-error.d.ts +0 -4
  184. package/dist/errors/reference-error.js +0 -7
  185. package/dist/errors/schema-error.d.ts +0 -4
  186. package/dist/errors/schema-error.js +0 -7
  187. package/dist/errors/security-error.d.ts +0 -4
  188. package/dist/errors/security-error.js +0 -7
  189. package/dist/errors/semantic-error.d.ts +0 -4
  190. package/dist/errors/semantic-error.js +0 -7
  191. package/dist/generated-schemas.d.ts +0 -2073
  192. package/dist/generated-schemas.js +0 -2097
  193. package/dist/index.cjs +0 -59
  194. package/dist/index.d.ts +0 -13
  195. package/dist/index.js +0 -43
  196. package/dist/loader/index.d.ts +0 -5
  197. package/dist/loader/index.js +0 -5
  198. package/dist/loader/security-validator.d.ts +0 -5
  199. package/dist/loader/security-validator.js +0 -50
  200. package/dist/loader/types.d.ts +0 -30
  201. package/dist/loader/types.js +0 -8
  202. package/dist/loader/unispec-loader.d.ts +0 -10
  203. package/dist/loader/unispec-loader.js +0 -81
  204. package/dist/loader/yaml-loader.d.ts +0 -10
  205. package/dist/loader/yaml-loader.js +0 -39
  206. package/dist/normalizer/core.d.ts +0 -24
  207. package/dist/normalizer/core.js +0 -29
  208. package/dist/normalizer/graphql-normalizer.d.ts +0 -8
  209. package/dist/normalizer/graphql-normalizer.js +0 -64
  210. package/dist/normalizer/index.d.ts +0 -2
  211. package/dist/normalizer/index.js +0 -3
  212. package/dist/normalizer/rest-normalizer.d.ts +0 -8
  213. package/dist/normalizer/rest-normalizer.js +0 -48
  214. package/dist/normalizer/types.d.ts +0 -7
  215. package/dist/normalizer/types.js +0 -1
  216. package/dist/normalizer/utils.d.ts +0 -14
  217. package/dist/normalizer/utils.js +0 -29
  218. package/dist/normalizer/websocket-normalizer.d.ts +0 -8
  219. package/dist/normalizer/websocket-normalizer.js +0 -78
  220. package/dist/optimizer/core.d.ts +0 -17
  221. package/dist/optimizer/core.js +0 -111
  222. package/dist/optimizer/index.d.ts +0 -4
  223. package/dist/optimizer/index.js +0 -7
  224. package/dist/optimizer/optimization-functions.d.ts +0 -32
  225. package/dist/optimizer/optimization-functions.js +0 -179
  226. package/dist/optimizer/types.d.ts +0 -28
  227. package/dist/optimizer/types.js +0 -1
  228. package/dist/optimizer/utils.d.ts +0 -7
  229. package/dist/optimizer/utils.js +0 -29
  230. package/dist/schemas/dedupe.d.ts +0 -9
  231. package/dist/schemas/dedupe.js +0 -97
  232. package/dist/schemas/index.d.ts +0 -3
  233. package/dist/schemas/index.js +0 -6
  234. package/dist/schemas/resolver.d.ts +0 -19
  235. package/dist/schemas/resolver.js +0 -37
  236. package/dist/schemas/utils.d.ts +0 -20
  237. package/dist/schemas/utils.js +0 -49
  238. package/dist/types/index.d.ts +0 -433
  239. package/dist/types/index.js +0 -1
  240. package/dist/validator/ajv-validator.d.ts +0 -15
  241. package/dist/validator/ajv-validator.js +0 -75
  242. package/dist/validator/config-validator-main.d.ts +0 -10
  243. package/dist/validator/config-validator-main.js +0 -31
  244. package/dist/validator/config-validator.d.ts +0 -5
  245. package/dist/validator/config-validator.js +0 -14
  246. package/dist/validator/index.d.ts +0 -10
  247. package/dist/validator/index.js +0 -11
  248. package/dist/validator/object-traversal.d.ts +0 -52
  249. package/dist/validator/object-traversal.js +0 -104
  250. package/dist/validator/reference-validator.d.ts +0 -31
  251. package/dist/validator/reference-validator.js +0 -230
  252. package/dist/validator/schema-references.d.ts +0 -23
  253. package/dist/validator/schema-references.js +0 -111
  254. package/dist/validator/semantic-validator.d.ts +0 -26
  255. package/dist/validator/semantic-validator.js +0 -325
  256. package/dist/validator/tests-validator.d.ts +0 -9
  257. package/dist/validator/tests-validator.js +0 -13
  258. package/dist/validator/types.d.ts +0 -29
  259. package/dist/validator/types.js +0 -1
  260. package/dist/validator/unispec-validator.d.ts +0 -15
  261. package/dist/validator/unispec-validator.js +0 -81
  262. package/dist/validator/validator-factory.d.ts +0 -10
  263. package/dist/validator/validator-factory.js +0 -73
  264. package/dist/versions.d.ts +0 -10
  265. package/dist/versions.js +0 -143
@@ -1,159 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.annotateRestChange = annotateRestChange;
4
- exports.annotateWebSocketChange = annotateWebSocketChange;
5
- exports.annotateGraphQLChange = annotateGraphQLChange;
6
- function annotateRestChange(change) {
7
- // Add defensive check for null/undefined path
8
- if (!change.path || typeof change.path !== "string") {
9
- return change;
10
- }
11
- if (!change.path.startsWith("/service/protocols/rest/routes/")) {
12
- return change;
13
- }
14
- const segments = change.path.split("/").filter(Boolean);
15
- // Expected shape: ["service", "protocols", "rest", "routes", routeName, ...]
16
- if (segments[0] !== "service" ||
17
- segments[1] !== "protocols" ||
18
- segments[2] !== "rest" ||
19
- segments[3] !== "routes") {
20
- return change;
21
- }
22
- const routeName = segments[4];
23
- if (typeof routeName === "undefined") {
24
- return change;
25
- }
26
- const annotated = {
27
- ...change,
28
- protocol: "rest",
29
- };
30
- if (change.description === "Item removed") {
31
- annotated.kind = "rest.route.removed";
32
- annotated.severity = "breaking";
33
- }
34
- else if (change.description === "Item added") {
35
- annotated.kind = "rest.route.added";
36
- annotated.severity = "non-breaking";
37
- }
38
- else if (change.path.includes("/required") &&
39
- change.description === "Value changed") {
40
- // Changing required field from false to true is breaking
41
- annotated.kind = "rest.parameter.required_changed";
42
- annotated.severity = "breaking";
43
- }
44
- return annotated;
45
- }
46
- function annotateWebSocketChange(change) {
47
- // Add defensive check for null/undefined path
48
- if (!change.path || typeof change.path !== "string") {
49
- return change;
50
- }
51
- if (!change.path.startsWith("/service/protocols/websocket/channels/")) {
52
- return change;
53
- }
54
- const segments = change.path.split("/").filter(Boolean);
55
- // Expected base: ["service","protocols","websocket","channels", channelName, ...]
56
- if (segments[0] !== "service" ||
57
- segments[1] !== "protocols" ||
58
- segments[2] !== "websocket" ||
59
- segments[3] !== "channels") {
60
- return change;
61
- }
62
- const channelName = segments[4];
63
- const next = segments[5];
64
- const annotated = {
65
- ...change,
66
- protocol: "websocket",
67
- };
68
- if (typeof channelName === "undefined") {
69
- return annotated;
70
- }
71
- // Channel-level changes: /service/protocols/websocket/channels/{channelName}
72
- if (!next) {
73
- if (change.description === "Item removed") {
74
- annotated.kind = "websocket.channel.removed";
75
- annotated.severity = "breaking";
76
- }
77
- else if (change.description === "Item added") {
78
- annotated.kind = "websocket.channel.added";
79
- annotated.severity = "non-breaking";
80
- }
81
- return annotated;
82
- }
83
- // Message-level changes: /service/protocols/websocket/channels/{channelName}/messages/{messageName}
84
- if (next === "messages") {
85
- const messageName = segments[6];
86
- if (typeof messageName === "undefined") {
87
- return annotated;
88
- }
89
- if (change.description === "Item removed") {
90
- annotated.kind = "websocket.message.removed";
91
- annotated.severity = "breaking";
92
- }
93
- else if (change.description === "Item added") {
94
- annotated.kind = "websocket.message.added";
95
- annotated.severity = "non-breaking";
96
- }
97
- }
98
- return annotated;
99
- }
100
- function annotateGraphQLChange(change) {
101
- // Add defensive check for null/undefined path
102
- if (!change.path || typeof change.path !== "string") {
103
- return change;
104
- }
105
- if (!change.path.startsWith("/service/protocols/graphql/")) {
106
- return change;
107
- }
108
- const segments = change.path.split("/").filter(Boolean);
109
- // Expected: ["service","protocols","graphql", operationType, operationName, ...]
110
- if (segments[0] !== "service" ||
111
- segments[1] !== "protocols" ||
112
- segments[2] !== "graphql") {
113
- return change;
114
- }
115
- const operationType = segments[3];
116
- const operationName = segments[4];
117
- if (!operationType || typeof operationName === "undefined") {
118
- return change;
119
- }
120
- if (operationType !== "queries" &&
121
- operationType !== "mutations" &&
122
- operationType !== "subscriptions") {
123
- return change;
124
- }
125
- const annotated = {
126
- ...change,
127
- protocol: "graphql",
128
- };
129
- // Create operation-specific annotations
130
- const singularMap = {
131
- queries: "query",
132
- mutations: "mutation",
133
- subscriptions: "subscription"
134
- };
135
- const operationKind = `graphql.${singularMap[operationType]}`;
136
- // Check for specific changes first, then general ones
137
- if (change.path.includes("/returnType") &&
138
- change.description === "Value changed") {
139
- // Changing return type is breaking
140
- annotated.kind = `${operationKind}.return_type_changed`;
141
- annotated.severity = "breaking";
142
- }
143
- else if (change.path.includes("/args") &&
144
- (change.description === "Item added" || change.description === "Item removed")) {
145
- // Argument changes
146
- const action = change.description === "Item added" ? "added" : "removed";
147
- annotated.kind = `${operationKind}.argument_${action}`;
148
- annotated.severity = action === "added" ? "non-breaking" : "breaking";
149
- }
150
- else if (change.description === "Item removed") {
151
- annotated.kind = `${operationKind}.removed`;
152
- annotated.severity = "breaking";
153
- }
154
- else if (change.description === "Item added") {
155
- annotated.kind = `${operationKind}.added`;
156
- annotated.severity = "non-breaking";
157
- }
158
- return annotated;
159
- }
@@ -1,369 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.generateChangeReport = generateChangeReport;
4
- /**
5
- * Generate a markdown report from enhanced diff result.
6
- */
7
- function generateMarkdownReport(diff, options) {
8
- const lines = [];
9
- // Title and metadata
10
- lines.push(`# ${options.title || "UniSpec Changes Report"}`);
11
- lines.push("");
12
- if (options.versionInfo) {
13
- lines.push("## Version Information");
14
- if (options.versionInfo.oldVersion) {
15
- lines.push(`- **From:** ${options.versionInfo.oldVersion}`);
16
- }
17
- if (options.versionInfo.newVersion) {
18
- lines.push(`- **To:** ${options.versionInfo.newVersion}`);
19
- }
20
- if (options.versionInfo.comparisonDate) {
21
- lines.push(`- **Date:** ${options.versionInfo.comparisonDate}`);
22
- }
23
- lines.push("");
24
- }
25
- // Executive summary
26
- lines.push("## Executive Summary");
27
- lines.push("");
28
- lines.push(`- **Total Changes:** ${diff.summary.totalChanges}`);
29
- lines.push(`- **Critical Changes:** ${diff.summary.criticalChanges}`);
30
- lines.push(`- **High Risk Changes:** ${diff.summary.highRiskChanges}`);
31
- lines.push(`- **Compatibility Score:** ${diff.metrics.overallCompatibility}%`);
32
- lines.push(`- **Migration Effort:** ${diff.metrics.migrationEffort}`);
33
- lines.push("");
34
- // Compatibility metrics
35
- lines.push("## Compatibility Metrics");
36
- lines.push("");
37
- lines.push("| Metric | Score | Status |");
38
- lines.push("|--------|-------|--------|");
39
- const overallStatus = diff.metrics.overallCompatibility >= 80
40
- ? "✅ Good"
41
- : diff.metrics.overallCompatibility >= 60
42
- ? "⚠️ Moderate"
43
- : "❌ Poor";
44
- lines.push(`| Overall Compatibility | ${diff.metrics.overallCompatibility}% | ${overallStatus} |`);
45
- const clientStatus = diff.metrics.clientCompatibilityScore >= 80
46
- ? "✅ Good"
47
- : diff.metrics.clientCompatibilityScore >= 60
48
- ? "⚠️ Moderate"
49
- : "❌ Poor";
50
- lines.push(`| Client Compatibility | ${diff.metrics.clientCompatibilityScore}% | ${clientStatus} |`);
51
- const serverStatus = diff.metrics.serverCompatibilityScore >= 80
52
- ? "✅ Good"
53
- : diff.metrics.serverCompatibilityScore >= 60
54
- ? "⚠️ Moderate"
55
- : "❌ Poor";
56
- lines.push(`| Server Compatibility | ${diff.metrics.serverCompatibilityScore}% | ${serverStatus} |`);
57
- lines.push("");
58
- // Group changes
59
- const groupedChanges = groupChanges(diff.changes, options);
60
- for (const section of groupedChanges) {
61
- lines.push(`## ${section.title}`);
62
- lines.push("");
63
- lines.push(section.summary);
64
- lines.push("");
65
- if (section.changes.length === 0) {
66
- lines.push("No changes in this category.");
67
- lines.push("");
68
- continue;
69
- }
70
- for (const change of section.changes) {
71
- lines.push(`### ${getChangeIcon(change.riskLevel)} ${getChangeTitle(change)}`);
72
- lines.push("");
73
- lines.push(`- **Path:** \`${change.path}\``);
74
- lines.push(`- **Protocol:** ${change.protocol || "Unknown"}`);
75
- lines.push(`- **Risk Level:** ${change.riskLevel.toUpperCase()}`);
76
- lines.push(`- **Impact:** ${formatImpact(change.impact)}`);
77
- lines.push("");
78
- if (change.description) {
79
- lines.push(`**Description:** ${change.description}`);
80
- lines.push("");
81
- }
82
- if (options.includeSuggestions && change.suggestions.length > 0) {
83
- lines.push("**Migration Suggestions:**");
84
- for (const suggestion of change.suggestions) {
85
- lines.push(`- ${suggestion}`);
86
- }
87
- lines.push("");
88
- }
89
- }
90
- }
91
- // Migration recommendations
92
- if (diff.summary.criticalChanges > 0 || diff.summary.highRiskChanges > 0) {
93
- lines.push("## Migration Recommendations");
94
- lines.push("");
95
- if (diff.summary.criticalChanges > 0) {
96
- lines.push("⚠️ **Critical Changes Detected**");
97
- lines.push("");
98
- lines.push("Immediate action required:");
99
- lines.push("- Review all critical changes");
100
- lines.push("- Plan migration strategy");
101
- lines.push("- Communicate changes to all stakeholders");
102
- lines.push("- Consider feature flags for gradual rollout");
103
- lines.push("");
104
- }
105
- if (diff.metrics.migrationEffort === "major") {
106
- lines.push("🔧 **Major Migration Effort**");
107
- lines.push("");
108
- lines.push("Recommended approach:");
109
- lines.push("- Break migration into phases");
110
- lines.push("- Use parallel development");
111
- lines.push("- Implement comprehensive testing");
112
- lines.push("- Provide detailed migration guides");
113
- lines.push("");
114
- }
115
- }
116
- return lines.join("\n");
117
- }
118
- /**
119
- * Generate an HTML report from enhanced diff result.
120
- */
121
- function generateHtmlReport(diff, options) {
122
- const styles = `
123
- <style>
124
- body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; margin: 40px; }
125
- .header { border-bottom: 2px solid #e1e5e9; padding-bottom: 20px; margin-bottom: 30px; }
126
- .metric { display: inline-block; margin: 10px 20px 10px 0; text-align: center; }
127
- .metric-value { font-size: 2em; font-weight: bold; display: block; }
128
- .metric-label { color: #666; font-size: 0.9em; }
129
- .critical { color: #dc3545; }
130
- .high { color: #fd7e14; }
131
- .medium { color: #ffc107; }
132
- .low { color: #28a745; }
133
- .change { border: 1px solid #e1e5e9; border-radius: 8px; padding: 20px; margin: 20px 0; }
134
- .change-header { font-weight: bold; margin-bottom: 10px; }
135
- .suggestions { background: #f8f9fa; padding: 15px; border-radius: 5px; margin-top: 10px; }
136
- .compatibility-bar { height: 20px; background: #e9ecef; border-radius: 10px; overflow: hidden; margin: 10px 0; }
137
- .compatibility-fill { height: 100%; transition: width 0.3s ease; }
138
- </style>
139
- `;
140
- const groupedChanges = groupChanges(diff.changes, options);
141
- let html = `
142
- <!DOCTYPE html>
143
- <html>
144
- <head>
145
- <title>${options.title || "UniSpec Changes Report"}</title>
146
- ${styles}
147
- </head>
148
- <body>
149
- <div class="header">
150
- <h1>${options.title || "UniSpec Changes Report"}</h1>
151
- <p>Generated on ${new Date().toLocaleDateString()}</p>
152
- </div>
153
-
154
- <div class="summary">
155
- <h2>Executive Summary</h2>
156
- <div class="metric">
157
- <span class="metric-value">${diff.summary.totalChanges}</span>
158
- <span class="metric-label">Total Changes</span>
159
- </div>
160
- <div class="metric">
161
- <span class="metric-value critical">${diff.summary.criticalChanges}</span>
162
- <span class="metric-label">Critical</span>
163
- </div>
164
- <div class="metric">
165
- <span class="metric-value high">${diff.summary.highRiskChanges}</span>
166
- <span class="metric-label">High Risk</span>
167
- </div>
168
- <div class="metric">
169
- <span class="metric-value">${diff.metrics.overallCompatibility}%</span>
170
- <span class="metric-label">Compatibility</span>
171
- </div>
172
- </div>
173
-
174
- <div class="compatibility">
175
- <h2>Compatibility Metrics</h2>
176
- <div>
177
- <strong>Overall Compatibility: ${diff.metrics.overallCompatibility}%</strong>
178
- <div class="compatibility-bar">
179
- <div class="compatibility-fill ${getCompatibilityClass(diff.metrics.overallCompatibility)}"
180
- style="width: ${diff.metrics.overallCompatibility}%"></div>
181
- </div>
182
- </div>
183
- <div>
184
- <strong>Client Compatibility: ${diff.metrics.clientCompatibilityScore}%</strong>
185
- <div class="compatibility-bar">
186
- <div class="compatibility-fill ${getCompatibilityClass(diff.metrics.clientCompatibilityScore)}"
187
- style="width: ${diff.metrics.clientCompatibilityScore}%"></div>
188
- </div>
189
- </div>
190
- <div>
191
- <strong>Server Compatibility: ${diff.metrics.serverCompatibilityScore}%</strong>
192
- <div class="compatibility-bar">
193
- <div class="compatibility-fill ${getCompatibilityClass(diff.metrics.serverCompatibilityScore)}"
194
- style="width: ${diff.metrics.serverCompatibilityScore}%"></div>
195
- </div>
196
- </div>
197
- </div>
198
- `;
199
- for (const section of groupedChanges) {
200
- html += `
201
- <div class="section">
202
- <h2>${section.title}</h2>
203
- <p>${section.summary}</p>
204
- `;
205
- for (const change of section.changes) {
206
- html += `
207
- <div class="change ${change.riskLevel}">
208
- <div class="change-header">
209
- ${getChangeIcon(change.riskLevel)} ${getChangeTitle(change)}
210
- </div>
211
- <p><strong>Path:</strong> <code>${change.path}</code></p>
212
- <p><strong>Protocol:</strong> ${change.protocol || "Unknown"}</p>
213
- <p><strong>Risk Level:</strong> <span class="${change.riskLevel}">${change.riskLevel.toUpperCase()}</span></p>
214
- <p><strong>Description:</strong> ${change.description}</p>
215
- <p><strong>Impact:</strong> ${formatImpact(change.impact)}</p>
216
- `;
217
- if (options.includeSuggestions && change.suggestions.length > 0) {
218
- html += `
219
- <div class="suggestions">
220
- <strong>Migration Suggestions:</strong>
221
- <ul>
222
- `;
223
- for (const suggestion of change.suggestions) {
224
- html += `<li>${suggestion}</li>`;
225
- }
226
- html += `
227
- </ul>
228
- </div>
229
- `;
230
- }
231
- html += `
232
- </div>
233
- `;
234
- }
235
- html += `
236
- </div>
237
- `;
238
- }
239
- html += `
240
- </body>
241
- </html>
242
- `;
243
- return html;
244
- }
245
- /**
246
- * Group changes by risk level or protocol.
247
- */
248
- function groupChanges(changes, options) {
249
- const sections = [];
250
- if (options.groupByRisk) {
251
- const riskGroups = {
252
- critical: changes.filter((c) => c.riskLevel === "critical"),
253
- high: changes.filter((c) => c.riskLevel === "high"),
254
- medium: changes.filter((c) => c.riskLevel === "medium"),
255
- low: changes.filter((c) => c.riskLevel === "low"),
256
- };
257
- for (const [risk, riskChanges] of Object.entries(riskGroups)) {
258
- sections.push({
259
- title: `${risk.charAt(0).toUpperCase() + risk.slice(1)} Risk Changes`,
260
- changes: riskChanges,
261
- summary: `${riskChanges.length} ${risk} risk changes that ${getRiskDescription(risk)}.`,
262
- });
263
- }
264
- }
265
- if (options.groupByProtocol) {
266
- const protocolGroups = {
267
- rest: changes.filter((c) => c.protocol === "rest"),
268
- graphql: changes.filter((c) => c.protocol === "graphql"),
269
- websocket: changes.filter((c) => c.protocol === "websocket"),
270
- other: changes.filter((c) => !c.protocol || !["rest", "graphql", "websocket"].includes(c.protocol)),
271
- };
272
- for (const [protocol, protocolChanges] of Object.entries(protocolGroups)) {
273
- if (protocolChanges.length > 0) {
274
- sections.push({
275
- title: `${protocol.charAt(0).toUpperCase() + protocol.slice(1)} Protocol Changes`,
276
- changes: protocolChanges,
277
- summary: `${protocolChanges.length} changes affecting ${protocol} protocol.`,
278
- });
279
- }
280
- }
281
- }
282
- // If no grouping, create a single section
283
- if (sections.length === 0) {
284
- sections.push({
285
- title: "All Changes",
286
- changes,
287
- summary: `${changes.length} total changes detected.`,
288
- });
289
- }
290
- return sections;
291
- }
292
- /**
293
- * Helper functions for formatting.
294
- */
295
- function getChangeIcon(riskLevel) {
296
- switch (riskLevel) {
297
- case "critical":
298
- return "🚨";
299
- case "high":
300
- return "⚠️";
301
- case "medium":
302
- return "⚡";
303
- case "low":
304
- return "✅";
305
- default:
306
- return "📝";
307
- }
308
- }
309
- function getChangeTitle(change) {
310
- const parts = change.path.split("/").filter(Boolean);
311
- const lastPart = parts[parts.length - 1];
312
- if (change.kind) {
313
- return `${change.kind.replace(/\./g, " ").toUpperCase()} - ${lastPart}`;
314
- }
315
- return `${change.description} - ${lastPart}`;
316
- }
317
- function formatImpact(impact) {
318
- const parts = [];
319
- if (impact.backwardCompatibility !== "unknown") {
320
- parts.push(`Compatibility: ${impact.backwardCompatibility}`);
321
- }
322
- if (impact.clientImpact !== "none") {
323
- parts.push(`Client: ${impact.clientImpact}`);
324
- }
325
- if (impact.serverImpact !== "none") {
326
- parts.push(`Server: ${impact.serverImpact}`);
327
- }
328
- return parts.join(", ");
329
- }
330
- function getRiskDescription(risk) {
331
- switch (risk) {
332
- case "critical":
333
- return "require immediate attention and breaking changes";
334
- case "high":
335
- return "involve breaking changes or significant impact";
336
- case "medium":
337
- return "require attention but are manageable";
338
- case "low":
339
- return "are minor and easily handled";
340
- default:
341
- return "have unknown impact";
342
- }
343
- }
344
- function getCompatibilityClass(score) {
345
- if (score >= 80)
346
- return "low";
347
- if (score >= 60)
348
- return "medium";
349
- return "high";
350
- }
351
- /**
352
- * Generate a comprehensive change report.
353
- *
354
- * @param diff - Enhanced diff result
355
- * @param options - Report generation options
356
- * @returns Formatted report in the specified format
357
- */
358
- function generateChangeReport(diff, options) {
359
- switch (options.format) {
360
- case "markdown":
361
- return generateMarkdownReport(diff, options);
362
- case "html":
363
- return generateHtmlReport(diff, options);
364
- case "json":
365
- return JSON.stringify(diff, null, 2);
366
- default:
367
- throw new Error(`Unsupported format: ${options.format}`);
368
- }
369
- }
@@ -1,158 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.diffUniSpec = diffUniSpec;
4
- const annotators_1 = require("./annotators.js");
5
- const types_1 = require("./types.js");
6
- /**
7
- * Check if a value is a plain object (not array, null, or other object types).
8
- *
9
- * @param value - The value to check
10
- * @returns true if the value is a plain object, false otherwise
11
- */
12
- function isPlainObject(value) {
13
- return Object.prototype.toString.call(value) === "[object Object]";
14
- }
15
- function diffValues(oldVal, newVal, basePath, out, options = {}) {
16
- if (oldVal === newVal) {
17
- return;
18
- }
19
- // Both plain objects → recurse by keys
20
- if (isPlainObject(oldVal) && isPlainObject(newVal)) {
21
- const oldKeys = new Set(Object.keys(oldVal));
22
- const newKeys = new Set(Object.keys(newVal));
23
- // Removed keys
24
- for (const key of oldKeys) {
25
- if (!newKeys.has(key)) {
26
- out.push({
27
- path: `${basePath}/${key}`,
28
- description: "Field removed",
29
- severity: "unknown",
30
- });
31
- }
32
- }
33
- // Added / changed keys
34
- for (const key of newKeys) {
35
- const childPath = `${basePath}/${key}`;
36
- if (!oldKeys.has(key)) {
37
- out.push({
38
- path: childPath,
39
- description: "Field added",
40
- severity: "unknown",
41
- });
42
- continue;
43
- }
44
- const shouldRecurse = options.deepComparison !== false;
45
- if (shouldRecurse) {
46
- diffValues(oldVal[key], newVal[key], childPath, out, options);
47
- }
48
- }
49
- return;
50
- }
51
- // Arrays
52
- if (Array.isArray(oldVal) && Array.isArray(newVal)) {
53
- // Special handling for UniSpec collections identified by "name"
54
- const _namedCollectionPaths = options.namedCollectionPaths || types_1.DEFAULT_NAMED_COLLECTION_PATHS;
55
- const isNamedCollection = (options.namedCollectionPaths || [...types_1.DEFAULT_NAMED_COLLECTION_PATHS]).includes(basePath) || basePath.endsWith("/messages"); // Handle messages in channels
56
- if (isNamedCollection) {
57
- const oldByName = new Map();
58
- const newByName = new Map();
59
- for (const item of oldVal) {
60
- if (item && typeof item === "object" && typeof item.name === "string") {
61
- oldByName.set(item.name, item);
62
- }
63
- }
64
- for (const item of newVal) {
65
- if (item && typeof item === "object" && typeof item.name === "string") {
66
- newByName.set(item.name, item);
67
- }
68
- }
69
- // Removed
70
- for (const [name, oldItem] of oldByName.entries()) {
71
- if (!newByName.has(name)) {
72
- out.push({
73
- path: `${basePath}/${name}`,
74
- description: "Item removed",
75
- severity: "unknown",
76
- });
77
- }
78
- else {
79
- const newItem = newByName.get(name);
80
- const shouldRecurse = options.deepComparison !== false;
81
- if (shouldRecurse) {
82
- diffValues(oldItem, newItem, `${basePath}/${name}`, out, options);
83
- }
84
- }
85
- }
86
- // Added
87
- for (const [name, _newItem] of newByName.entries()) {
88
- if (!oldByName.has(name)) {
89
- out.push({
90
- path: `${basePath}/${name}`,
91
- description: "Item added",
92
- severity: "unknown",
93
- });
94
- }
95
- }
96
- return;
97
- }
98
- // Regular array comparison
99
- const maxLength = Math.max(oldVal.length, newVal.length);
100
- for (let i = 0; i < maxLength; i++) {
101
- if (i >= oldVal.length) {
102
- out.push({
103
- path: `${basePath}/${i}`,
104
- description: "Item added",
105
- severity: "unknown",
106
- });
107
- }
108
- else if (i >= newVal.length) {
109
- out.push({
110
- path: `${basePath}/${i}`,
111
- description: "Item removed",
112
- severity: "unknown",
113
- });
114
- }
115
- else {
116
- const shouldRecurse = options.deepComparison !== false;
117
- if (shouldRecurse) {
118
- diffValues(oldVal[i], newVal[i], `${basePath}/${i}`, out, options);
119
- }
120
- }
121
- }
122
- }
123
- // Primitive values or different types
124
- out.push({
125
- path: basePath,
126
- description: "Value changed",
127
- severity: "unknown",
128
- });
129
- }
130
- /**
131
- * Compare two UniSpec documents and return detected changes.
132
- *
133
- * This function performs a deep comparison between two UniSpec documents
134
- * and categorizes changes by severity and protocol. Useful for:
135
- * - API change detection in CI/CD pipelines
136
- * - Breaking change analysis
137
- * - Version compatibility checks
138
- * - Audit trails for API evolution
139
- *
140
- * Features:
141
- * - Detects added/removed fields at any depth
142
- * - Special handling for named collections (routes, operations, channels)
143
- * - Severity classification: "breaking" | "non-breaking" | "unknown"
144
- * - Protocol categorization: "rest" | "graphql" | "websocket"
145
- * - Detailed change descriptions with JSON paths
146
- * - Configurable named collection paths and comparison depth
147
- *
148
- * @param oldDoc - The previous version of the UniSpec document
149
- * @param newDoc - The current version of the UniSpec document
150
- * @param options - Configuration options for diff behavior
151
- * @returns Object containing all detected changes
152
- */
153
- function diffUniSpec(oldDoc, newDoc, options = {}) {
154
- const changes = [];
155
- diffValues(oldDoc, newDoc, "", changes, options);
156
- const annotated = changes.map((change) => (0, annotators_1.annotateWebSocketChange)((0, annotators_1.annotateGraphQLChange)((0, annotators_1.annotateRestChange)(change))));
157
- return { changes: annotated };
158
- }