@unispechq/unispec-core 0.3.0 → 0.3.2

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 (306) hide show
  1. package/dist/cjs/diff/annotators.js +36 -9
  2. package/dist/cjs/src/cache/cache-factory.js +72 -0
  3. package/dist/cjs/src/cache/cache-manager.js +128 -0
  4. package/dist/cjs/src/cache/constants.js +25 -0
  5. package/dist/cjs/src/cache/hash-utils.js +19 -0
  6. package/dist/cjs/src/cache/hashing.js +230 -0
  7. package/dist/cjs/src/cache/index.js +24 -0
  8. package/dist/cjs/src/cache/lru-cache.js +144 -0
  9. package/dist/cjs/src/cache/types.js +5 -0
  10. package/dist/cjs/src/diff/annotators.js +160 -0
  11. package/dist/cjs/src/diff/change-reports.js +369 -0
  12. package/dist/cjs/src/diff/core.js +158 -0
  13. package/dist/cjs/src/diff/enhanced-diff.js +65 -0
  14. package/dist/cjs/src/diff/impact-strategies-refactored.js +230 -0
  15. package/dist/cjs/src/diff/impact-strategies.js +219 -0
  16. package/dist/cjs/src/diff/index.js +27 -0
  17. package/dist/cjs/src/diff/metrics-calculator.js +69 -0
  18. package/dist/cjs/src/diff/risk-calculator.js +58 -0
  19. package/dist/cjs/src/diff/suggestion-generator.js +78 -0
  20. package/dist/cjs/src/diff/types.js +11 -0
  21. package/dist/cjs/src/errors/base-error.js +33 -0
  22. package/dist/cjs/src/errors/config-error.js +11 -0
  23. package/dist/cjs/src/errors/error-factory.js +48 -0
  24. package/dist/cjs/src/errors/index.js +19 -0
  25. package/dist/cjs/src/errors/loader-error.js +11 -0
  26. package/dist/cjs/src/errors/reference-error.js +11 -0
  27. package/dist/cjs/src/errors/schema-error.js +11 -0
  28. package/dist/cjs/src/errors/security-error.js +11 -0
  29. package/dist/cjs/src/errors/semantic-error.js +11 -0
  30. package/dist/cjs/src/generated-schemas.js +2100 -0
  31. package/dist/cjs/src/index.js +59 -0
  32. package/dist/cjs/src/loader/index.js +13 -0
  33. package/dist/cjs/src/loader/security-validator.js +53 -0
  34. package/dist/cjs/src/loader/types.js +11 -0
  35. package/dist/cjs/src/loader/unispec-loader.js +84 -0
  36. package/dist/cjs/src/loader/yaml-loader.js +76 -0
  37. package/dist/cjs/src/normalizer/core.js +37 -0
  38. package/dist/cjs/src/normalizer/graphql-normalizer.js +67 -0
  39. package/dist/cjs/src/normalizer/index.js +7 -0
  40. package/dist/cjs/src/normalizer/rest-normalizer.js +51 -0
  41. package/dist/cjs/src/normalizer/types.js +2 -0
  42. package/dist/cjs/src/normalizer/utils.js +49 -0
  43. package/dist/cjs/src/normalizer/websocket-normalizer.js +81 -0
  44. package/dist/cjs/src/optimizer/core.js +140 -0
  45. package/dist/cjs/src/optimizer/index.js +17 -0
  46. package/dist/cjs/src/optimizer/optimization-functions.js +185 -0
  47. package/dist/cjs/src/optimizer/types.js +2 -0
  48. package/dist/cjs/src/optimizer/utils.js +32 -0
  49. package/dist/cjs/src/schemas/dedupe.js +113 -0
  50. package/dist/cjs/src/schemas/index.js +14 -0
  51. package/dist/cjs/src/schemas/resolver.js +42 -0
  52. package/dist/cjs/src/schemas/utils.js +53 -0
  53. package/dist/cjs/src/types/index.js +2 -0
  54. package/dist/cjs/src/validator/ajv-validator.js +82 -0
  55. package/dist/cjs/src/validator/config-validator-main.js +34 -0
  56. package/dist/cjs/src/validator/config-validator.js +17 -0
  57. package/dist/cjs/src/validator/index.js +23 -0
  58. package/dist/cjs/src/validator/object-traversal.js +112 -0
  59. package/dist/cjs/src/validator/reference-validator.js +233 -0
  60. package/dist/cjs/src/validator/schema-references.js +116 -0
  61. package/dist/cjs/src/validator/semantic-validator.js +328 -0
  62. package/dist/cjs/src/validator/tests-validator.js +16 -0
  63. package/dist/cjs/src/validator/types.js +2 -0
  64. package/dist/cjs/src/validator/unispec-validator.js +80 -0
  65. package/dist/cjs/src/validator/validator-factory.js +77 -0
  66. package/dist/cjs/src/versions.js +147 -0
  67. package/dist/cjs/tests/cache/cache.test.js +274 -0
  68. package/dist/cjs/tests/cache/utils.js +32 -0
  69. package/dist/cjs/tests/concurrency-normalizer-optimizer.test.js +1 -0
  70. package/dist/cjs/tests/diff/diff-annotators.test.js +280 -0
  71. package/dist/cjs/tests/diff/diff-comprehensive.test.js +262 -0
  72. package/dist/cjs/tests/diff/diff-extended.test.js +235 -0
  73. package/dist/cjs/tests/diff/diff.test.js +189 -0
  74. package/dist/cjs/tests/diff/utils.js +8 -0
  75. package/dist/cjs/tests/errors/errors-integration.test.js +173 -0
  76. package/dist/cjs/tests/errors/errors.test.js +280 -0
  77. package/dist/cjs/tests/errors/utils.js +7 -0
  78. package/dist/cjs/tests/loader/integration.test.js +216 -0
  79. package/dist/cjs/tests/loader/loader.test.js +341 -0
  80. package/dist/cjs/tests/normalizer/normalizer-comprehensive.test.js +648 -0
  81. package/dist/cjs/tests/normalizer/normalizer-invalid.test.js +258 -0
  82. package/dist/cjs/tests/normalizer/normalizer-valid.test.js +238 -0
  83. package/dist/cjs/tests/normalizer/utils.js +47 -0
  84. package/dist/cjs/tests/optimizer/compress-references.test.js +304 -0
  85. package/dist/cjs/tests/optimizer/deduplication.test.js +132 -0
  86. package/dist/cjs/tests/optimizer/integration.test.js +131 -0
  87. package/dist/cjs/tests/optimizer/optimization-report.test.js +222 -0
  88. package/dist/cjs/tests/optimizer/optimize-document.test.js +187 -0
  89. package/dist/cjs/tests/optimizer/orphaned-schemas.test.js +194 -0
  90. package/dist/cjs/tests/optimizer/sort-schemas.test.js +131 -0
  91. package/dist/cjs/tests/optimizer/utils.js +209 -0
  92. package/dist/cjs/tests/schemas/schemas-edge-cases.test.js +223 -0
  93. package/dist/cjs/tests/schemas/schemas.test.js +400 -0
  94. package/dist/cjs/tests/schemas/utils.js +7 -0
  95. package/dist/cjs/tests/utils.js +131 -0
  96. package/dist/cjs/tests/validator/config-validator.test.js +78 -0
  97. package/dist/cjs/tests/validator/debug-config.js +1 -0
  98. package/dist/cjs/tests/validator/debug-missing-service.js +1 -0
  99. package/dist/cjs/tests/validator/debug-other-configs.js +1 -0
  100. package/dist/cjs/tests/validator/debug-references.js +1 -0
  101. package/dist/cjs/tests/validator/unispec-validator.test.js +103 -0
  102. package/dist/cjs/tests/validator/utils.js +25 -0
  103. package/dist/diff/annotators.js +36 -9
  104. package/dist/src/cache/cache-factory.d.ts +31 -0
  105. package/dist/src/cache/cache-factory.js +65 -0
  106. package/dist/src/cache/cache-manager.d.ts +62 -0
  107. package/dist/src/cache/cache-manager.js +124 -0
  108. package/dist/src/cache/constants.d.ts +21 -0
  109. package/dist/src/cache/constants.js +22 -0
  110. package/dist/src/cache/hash-utils.d.ts +11 -0
  111. package/dist/src/cache/hash-utils.js +15 -0
  112. package/dist/src/cache/hashing.d.ts +28 -0
  113. package/dist/src/cache/hashing.js +193 -0
  114. package/dist/src/cache/index.d.ts +6 -0
  115. package/dist/src/cache/index.js +10 -0
  116. package/dist/src/cache/lru-cache.d.ts +44 -0
  117. package/dist/src/cache/lru-cache.js +140 -0
  118. package/dist/src/cache/types.d.ts +24 -0
  119. package/dist/src/cache/types.js +4 -0
  120. package/dist/src/diff/annotators.d.ts +4 -0
  121. package/dist/src/diff/annotators.js +155 -0
  122. package/dist/src/diff/change-reports.d.ts +37 -0
  123. package/dist/src/diff/change-reports.js +366 -0
  124. package/dist/src/diff/core.d.ts +26 -0
  125. package/dist/src/diff/core.js +155 -0
  126. package/dist/src/diff/enhanced-diff.d.ts +51 -0
  127. package/dist/src/diff/enhanced-diff.js +62 -0
  128. package/dist/src/diff/impact-strategies-refactored.d.ts +69 -0
  129. package/dist/src/diff/impact-strategies-refactored.js +223 -0
  130. package/dist/src/diff/impact-strategies.d.ts +41 -0
  131. package/dist/src/diff/impact-strategies.js +212 -0
  132. package/dist/src/diff/index.d.ts +8 -0
  133. package/dist/src/diff/index.js +11 -0
  134. package/dist/src/diff/metrics-calculator.d.ts +23 -0
  135. package/dist/src/diff/metrics-calculator.js +65 -0
  136. package/dist/src/diff/risk-calculator.d.ts +23 -0
  137. package/dist/src/diff/risk-calculator.js +55 -0
  138. package/dist/src/diff/suggestion-generator.d.ts +18 -0
  139. package/dist/src/diff/suggestion-generator.js +74 -0
  140. package/dist/src/diff/types.d.ts +24 -0
  141. package/dist/src/diff/types.js +8 -0
  142. package/dist/src/errors/base-error.d.ts +20 -0
  143. package/dist/src/errors/base-error.js +29 -0
  144. package/dist/src/errors/config-error.d.ts +4 -0
  145. package/dist/src/errors/config-error.js +7 -0
  146. package/dist/src/errors/error-factory.d.ts +22 -0
  147. package/dist/src/errors/error-factory.js +45 -0
  148. package/dist/src/errors/index.d.ts +8 -0
  149. package/dist/src/errors/index.js +8 -0
  150. package/dist/src/errors/loader-error.d.ts +4 -0
  151. package/dist/src/errors/loader-error.js +7 -0
  152. package/dist/src/errors/reference-error.d.ts +4 -0
  153. package/dist/src/errors/reference-error.js +7 -0
  154. package/dist/src/errors/schema-error.d.ts +4 -0
  155. package/dist/src/errors/schema-error.js +7 -0
  156. package/dist/src/errors/security-error.d.ts +4 -0
  157. package/dist/src/errors/security-error.js +7 -0
  158. package/dist/src/errors/semantic-error.d.ts +4 -0
  159. package/dist/src/errors/semantic-error.js +7 -0
  160. package/dist/src/generated-schemas.d.ts +2073 -0
  161. package/dist/src/generated-schemas.js +2097 -0
  162. package/dist/src/index.d.ts +13 -0
  163. package/dist/src/index.js +43 -0
  164. package/dist/src/loader/index.d.ts +5 -0
  165. package/dist/src/loader/index.js +5 -0
  166. package/dist/src/loader/security-validator.d.ts +5 -0
  167. package/dist/src/loader/security-validator.js +50 -0
  168. package/dist/src/loader/types.d.ts +30 -0
  169. package/dist/src/loader/types.js +8 -0
  170. package/dist/src/loader/unispec-loader.d.ts +10 -0
  171. package/dist/src/loader/unispec-loader.js +81 -0
  172. package/dist/src/loader/yaml-loader.d.ts +10 -0
  173. package/dist/src/loader/yaml-loader.js +39 -0
  174. package/dist/src/normalizer/core.d.ts +24 -0
  175. package/dist/src/normalizer/core.js +34 -0
  176. package/dist/src/normalizer/graphql-normalizer.d.ts +8 -0
  177. package/dist/src/normalizer/graphql-normalizer.js +64 -0
  178. package/dist/src/normalizer/index.d.ts +2 -0
  179. package/dist/src/normalizer/index.js +3 -0
  180. package/dist/src/normalizer/rest-normalizer.d.ts +8 -0
  181. package/dist/src/normalizer/rest-normalizer.js +48 -0
  182. package/dist/src/normalizer/types.d.ts +7 -0
  183. package/dist/src/normalizer/types.js +1 -0
  184. package/dist/src/normalizer/utils.d.ts +17 -0
  185. package/dist/src/normalizer/utils.js +45 -0
  186. package/dist/src/normalizer/websocket-normalizer.d.ts +8 -0
  187. package/dist/src/normalizer/websocket-normalizer.js +78 -0
  188. package/dist/src/optimizer/core.d.ts +17 -0
  189. package/dist/src/optimizer/core.js +136 -0
  190. package/dist/src/optimizer/index.d.ts +4 -0
  191. package/dist/src/optimizer/index.js +7 -0
  192. package/dist/src/optimizer/optimization-functions.d.ts +32 -0
  193. package/dist/src/optimizer/optimization-functions.js +179 -0
  194. package/dist/src/optimizer/types.d.ts +28 -0
  195. package/dist/src/optimizer/types.js +1 -0
  196. package/dist/src/optimizer/utils.d.ts +7 -0
  197. package/dist/src/optimizer/utils.js +29 -0
  198. package/dist/src/schemas/dedupe.d.ts +9 -0
  199. package/dist/src/schemas/dedupe.js +110 -0
  200. package/dist/src/schemas/index.d.ts +3 -0
  201. package/dist/src/schemas/index.js +6 -0
  202. package/dist/src/schemas/resolver.d.ts +19 -0
  203. package/dist/src/schemas/resolver.js +38 -0
  204. package/dist/src/schemas/utils.d.ts +20 -0
  205. package/dist/src/schemas/utils.js +49 -0
  206. package/dist/src/types/index.d.ts +434 -0
  207. package/dist/src/types/index.js +1 -0
  208. package/dist/src/validator/ajv-validator.d.ts +15 -0
  209. package/dist/src/validator/ajv-validator.js +75 -0
  210. package/dist/src/validator/config-validator-main.d.ts +10 -0
  211. package/dist/src/validator/config-validator-main.js +31 -0
  212. package/dist/src/validator/config-validator.d.ts +5 -0
  213. package/dist/src/validator/config-validator.js +14 -0
  214. package/dist/src/validator/index.d.ts +10 -0
  215. package/dist/src/validator/index.js +11 -0
  216. package/dist/src/validator/object-traversal.d.ts +52 -0
  217. package/dist/src/validator/object-traversal.js +104 -0
  218. package/dist/src/validator/reference-validator.d.ts +31 -0
  219. package/dist/src/validator/reference-validator.js +230 -0
  220. package/dist/src/validator/schema-references.d.ts +23 -0
  221. package/dist/src/validator/schema-references.js +111 -0
  222. package/dist/src/validator/semantic-validator.d.ts +26 -0
  223. package/dist/src/validator/semantic-validator.js +325 -0
  224. package/dist/src/validator/tests-validator.d.ts +9 -0
  225. package/dist/src/validator/tests-validator.js +13 -0
  226. package/dist/src/validator/types.d.ts +29 -0
  227. package/dist/src/validator/types.js +1 -0
  228. package/dist/src/validator/unispec-validator.d.ts +15 -0
  229. package/dist/src/validator/unispec-validator.js +77 -0
  230. package/dist/src/validator/validator-factory.d.ts +10 -0
  231. package/dist/src/validator/validator-factory.js +73 -0
  232. package/dist/src/versions.d.ts +10 -0
  233. package/dist/src/versions.js +143 -0
  234. package/dist/tests/cache/cache.test.d.ts +1 -0
  235. package/dist/tests/cache/cache.test.js +269 -0
  236. package/dist/tests/cache/utils.d.ts +4 -0
  237. package/dist/tests/cache/utils.js +24 -0
  238. package/dist/tests/concurrency-normalizer-optimizer.test.d.ts +0 -0
  239. package/dist/tests/concurrency-normalizer-optimizer.test.js +1 -0
  240. package/dist/tests/diff/diff-annotators.test.d.ts +1 -0
  241. package/dist/tests/diff/diff-annotators.test.js +275 -0
  242. package/dist/tests/diff/diff-comprehensive.test.d.ts +1 -0
  243. package/dist/tests/diff/diff-comprehensive.test.js +257 -0
  244. package/dist/tests/diff/diff-extended.test.d.ts +1 -0
  245. package/dist/tests/diff/diff-extended.test.js +230 -0
  246. package/dist/tests/diff/diff.test.d.ts +1 -0
  247. package/dist/tests/diff/diff.test.js +184 -0
  248. package/dist/tests/diff/utils.d.ts +2 -0
  249. package/dist/tests/diff/utils.js +3 -0
  250. package/dist/tests/errors/errors-integration.test.d.ts +1 -0
  251. package/dist/tests/errors/errors-integration.test.js +168 -0
  252. package/dist/tests/errors/errors.test.d.ts +1 -0
  253. package/dist/tests/errors/errors.test.js +275 -0
  254. package/dist/tests/errors/utils.d.ts +2 -0
  255. package/dist/tests/errors/utils.js +3 -0
  256. package/dist/tests/loader/integration.test.d.ts +1 -0
  257. package/dist/tests/loader/integration.test.js +211 -0
  258. package/dist/tests/loader/loader.test.d.ts +1 -0
  259. package/dist/tests/loader/loader.test.js +336 -0
  260. package/dist/tests/normalizer/normalizer-comprehensive.test.d.ts +1 -0
  261. package/dist/tests/normalizer/normalizer-comprehensive.test.js +643 -0
  262. package/dist/tests/normalizer/normalizer-invalid.test.d.ts +1 -0
  263. package/dist/tests/normalizer/normalizer-invalid.test.js +253 -0
  264. package/dist/tests/normalizer/normalizer-valid.test.d.ts +1 -0
  265. package/dist/tests/normalizer/normalizer-valid.test.js +233 -0
  266. package/dist/tests/normalizer/utils.d.ts +18 -0
  267. package/dist/tests/normalizer/utils.js +36 -0
  268. package/dist/tests/optimizer/compress-references.test.d.ts +1 -0
  269. package/dist/tests/optimizer/compress-references.test.js +299 -0
  270. package/dist/tests/optimizer/deduplication.test.d.ts +1 -0
  271. package/dist/tests/optimizer/deduplication.test.js +127 -0
  272. package/dist/tests/optimizer/integration.test.d.ts +1 -0
  273. package/dist/tests/optimizer/integration.test.js +126 -0
  274. package/dist/tests/optimizer/optimization-report.test.d.ts +1 -0
  275. package/dist/tests/optimizer/optimization-report.test.js +217 -0
  276. package/dist/tests/optimizer/optimize-document.test.d.ts +1 -0
  277. package/dist/tests/optimizer/optimize-document.test.js +182 -0
  278. package/dist/tests/optimizer/orphaned-schemas.test.d.ts +1 -0
  279. package/dist/tests/optimizer/orphaned-schemas.test.js +189 -0
  280. package/dist/tests/optimizer/sort-schemas.test.d.ts +1 -0
  281. package/dist/tests/optimizer/sort-schemas.test.js +126 -0
  282. package/dist/tests/optimizer/utils.d.ts +8 -0
  283. package/dist/tests/optimizer/utils.js +199 -0
  284. package/dist/tests/schemas/schemas-edge-cases.test.d.ts +1 -0
  285. package/dist/tests/schemas/schemas-edge-cases.test.js +218 -0
  286. package/dist/tests/schemas/schemas.test.d.ts +1 -0
  287. package/dist/tests/schemas/schemas.test.js +395 -0
  288. package/dist/tests/schemas/utils.d.ts +2 -0
  289. package/dist/tests/schemas/utils.js +3 -0
  290. package/dist/tests/utils.d.ts +10 -0
  291. package/dist/tests/utils.js +118 -0
  292. package/dist/tests/validator/config-validator.test.d.ts +1 -0
  293. package/dist/tests/validator/config-validator.test.js +73 -0
  294. package/dist/tests/validator/debug-config.d.ts +0 -0
  295. package/dist/tests/validator/debug-config.js +1 -0
  296. package/dist/tests/validator/debug-missing-service.d.ts +0 -0
  297. package/dist/tests/validator/debug-missing-service.js +1 -0
  298. package/dist/tests/validator/debug-other-configs.d.ts +0 -0
  299. package/dist/tests/validator/debug-other-configs.js +1 -0
  300. package/dist/tests/validator/debug-references.d.ts +0 -0
  301. package/dist/tests/validator/debug-references.js +1 -0
  302. package/dist/tests/validator/unispec-validator.test.d.ts +1 -0
  303. package/dist/tests/validator/unispec-validator.test.js +98 -0
  304. package/dist/tests/validator/utils.d.ts +6 -0
  305. package/dist/tests/validator/utils.js +20 -0
  306. package/package.json +4 -3
@@ -0,0 +1,648 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const node_assert_1 = __importDefault(require("node:assert"));
7
+ const node_path_1 = __importDefault(require("node:path"));
8
+ const node_test_1 = require("node:test");
9
+ const normalizer_1 = require("../../src/normalizer/index.js");
10
+ const utils_1 = require("./utils.js");
11
+ (0, node_test_1.describe)("normalizer module - comprehensive protocol tests", () => {
12
+ (0, node_test_1.describe)("REST protocol normalization", () => {
13
+ (0, node_test_1.it)("should sort REST routes by name when available", () => {
14
+ const doc = (0, utils_1.createTestDocument)({
15
+ service: {
16
+ name: "test-service",
17
+ protocols: {
18
+ rest: {
19
+ routes: [
20
+ {
21
+ name: "zebraRoute",
22
+ method: "GET",
23
+ path: "/zebra",
24
+ responses: { "200": { description: "OK" } },
25
+ },
26
+ {
27
+ name: "alphaRoute",
28
+ method: "POST",
29
+ path: "/alpha",
30
+ responses: { "201": { description: "Created" } },
31
+ },
32
+ {
33
+ name: "betaRoute",
34
+ method: "PUT",
35
+ path: "/beta",
36
+ responses: { "200": { description: "OK" } },
37
+ },
38
+ ],
39
+ },
40
+ },
41
+ },
42
+ });
43
+ const normalized = (0, normalizer_1.normalizeUniSpec)(doc);
44
+ // Routes should be sorted by name
45
+ const routes = normalized.service?.protocols?.rest?.routes || [];
46
+ const routeNames = routes.map((r) => r.name).filter(Boolean);
47
+ node_assert_1.default.deepStrictEqual(routeNames, [
48
+ "alphaRoute",
49
+ "betaRoute",
50
+ "zebraRoute",
51
+ ]);
52
+ });
53
+ (0, node_test_1.it)("should sort REST routes by path+method when name is missing", () => {
54
+ const doc = (0, utils_1.createTestDocument)({
55
+ service: {
56
+ name: "test-service",
57
+ protocols: {
58
+ rest: {
59
+ routes: [
60
+ {
61
+ name: "",
62
+ method: "GET",
63
+ path: "/users",
64
+ responses: { "200": { description: "OK" } },
65
+ },
66
+ {
67
+ name: "",
68
+ method: "POST",
69
+ path: "/users",
70
+ responses: { "201": { description: "Created" } },
71
+ },
72
+ {
73
+ name: "",
74
+ method: "DELETE",
75
+ path: "/users/{id}",
76
+ responses: { "204": { description: "No Content" } },
77
+ },
78
+ ],
79
+ },
80
+ },
81
+ },
82
+ });
83
+ const normalized = (0, normalizer_1.normalizeUniSpec)(doc);
84
+ // Routes should be sorted by path+method
85
+ const routes = normalized.service?.protocols?.rest?.routes || [];
86
+ const routeKeys = routes.map((r) => r.name ? `${r.path} ${r.method}` : `${r.path} ${r.method}`);
87
+ node_assert_1.default.deepStrictEqual(routeKeys, [
88
+ "/users GET",
89
+ "/users POST",
90
+ "/users/{id} DELETE",
91
+ ]);
92
+ });
93
+ (0, node_test_1.it)("should handle mixed named and unnamed routes", () => {
94
+ const doc = (0, utils_1.createTestDocument)({
95
+ service: {
96
+ name: "test-service",
97
+ protocols: {
98
+ rest: {
99
+ routes: [
100
+ {
101
+ name: "getUser",
102
+ method: "GET",
103
+ path: "/users/{id}",
104
+ responses: { "200": { description: "OK" } },
105
+ },
106
+ {
107
+ name: "",
108
+ method: "POST",
109
+ path: "/users",
110
+ responses: { "201": { description: "Created" } },
111
+ },
112
+ {
113
+ name: "deleteUser",
114
+ method: "DELETE",
115
+ path: "/users/{id}",
116
+ responses: { "204": { description: "No Content" } },
117
+ },
118
+ ],
119
+ },
120
+ },
121
+ },
122
+ });
123
+ const normalized = (0, normalizer_1.normalizeUniSpec)(doc);
124
+ // Routes should be sorted by name or path+method
125
+ const routes = normalized.service?.protocols?.rest?.routes || [];
126
+ const routeKeys = routes.map((r) => r.name || `${r.path} ${r.method}`);
127
+ node_assert_1.default.deepStrictEqual(routeKeys, [
128
+ "/users POST",
129
+ "deleteUser",
130
+ "getUser",
131
+ ]);
132
+ });
133
+ });
134
+ (0, node_test_1.describe)("GraphQL protocol normalization", () => {
135
+ (0, node_test_1.it)("should sort GraphQL queries by name", () => {
136
+ const doc = (0, utils_1.createTestDocument)({
137
+ service: {
138
+ name: "test-service",
139
+ protocols: {
140
+ graphql: {
141
+ queries: [
142
+ {
143
+ name: "zebraQuery",
144
+ description: "Get zebra",
145
+ args: [],
146
+ returnType: "Zebra",
147
+ },
148
+ {
149
+ name: "alphaQuery",
150
+ description: "Get alpha",
151
+ args: [],
152
+ returnType: "Alpha",
153
+ },
154
+ {
155
+ name: "betaQuery",
156
+ description: "Get beta",
157
+ args: [],
158
+ returnType: "Beta",
159
+ },
160
+ ],
161
+ mutations: [],
162
+ subscriptions: [],
163
+ schema: "type Query { alphaQuery: Alpha betaQuery: Beta zebraQuery: Zebra }",
164
+ },
165
+ },
166
+ },
167
+ });
168
+ const normalized = (0, normalizer_1.normalizeUniSpec)(doc);
169
+ // Queries should be sorted by name
170
+ const queries = normalized.service?.protocols?.graphql?.queries || [];
171
+ const queryNames = queries.map((q) => q.name).filter(Boolean);
172
+ node_assert_1.default.deepStrictEqual(queryNames, [
173
+ "alphaQuery",
174
+ "betaQuery",
175
+ "zebraQuery",
176
+ ]);
177
+ });
178
+ (0, node_test_1.it)("should sort GraphQL mutations by name", () => {
179
+ const doc = (0, utils_1.createTestDocument)({
180
+ service: {
181
+ name: "test-service",
182
+ protocols: {
183
+ graphql: {
184
+ queries: [],
185
+ mutations: [
186
+ {
187
+ name: "zebraMutation",
188
+ description: "Create zebra",
189
+ args: [],
190
+ returnType: "Zebra",
191
+ },
192
+ {
193
+ name: "alphaMutation",
194
+ description: "Create alpha",
195
+ args: [],
196
+ returnType: "Alpha",
197
+ },
198
+ ],
199
+ subscriptions: [],
200
+ schema: "type Mutation { alphaMutation: Alpha zebraMutation: Zebra }",
201
+ },
202
+ },
203
+ },
204
+ });
205
+ const normalized = (0, normalizer_1.normalizeUniSpec)(doc);
206
+ // Mutations should be sorted by name
207
+ const mutations = normalized.service?.protocols?.graphql?.mutations || [];
208
+ const mutationNames = mutations.map((m) => m.name).filter(Boolean);
209
+ node_assert_1.default.deepStrictEqual(mutationNames, ["alphaMutation", "zebraMutation"]);
210
+ });
211
+ (0, node_test_1.it)("should sort GraphQL subscriptions by name", () => {
212
+ const doc = (0, utils_1.createTestDocument)({
213
+ service: {
214
+ name: "test-service",
215
+ protocols: {
216
+ graphql: {
217
+ queries: [],
218
+ mutations: [],
219
+ subscriptions: [
220
+ {
221
+ name: "zebraSubscription",
222
+ description: "Subscribe to zebra",
223
+ args: [],
224
+ returnType: "Zebra",
225
+ },
226
+ {
227
+ name: "alphaSubscription",
228
+ description: "Subscribe to alpha",
229
+ args: [],
230
+ returnType: "Alpha",
231
+ },
232
+ ],
233
+ schema: "type Subscription { alphaSubscription: Alpha zebraSubscription: Zebra }",
234
+ },
235
+ },
236
+ },
237
+ });
238
+ const normalized = (0, normalizer_1.normalizeUniSpec)(doc);
239
+ // Subscriptions should be sorted by name
240
+ const subscriptions = normalized.service?.protocols?.graphql?.subscriptions || [];
241
+ const subscriptionNames = subscriptions
242
+ .map((s) => s.name)
243
+ .filter(Boolean);
244
+ node_assert_1.default.deepStrictEqual(subscriptionNames, [
245
+ "alphaSubscription",
246
+ "zebraSubscription",
247
+ ]);
248
+ });
249
+ (0, node_test_1.it)("should handle operations with missing names", () => {
250
+ const doc = (0, utils_1.createTestDocument)({
251
+ service: {
252
+ name: "test-service",
253
+ protocols: {
254
+ graphql: {
255
+ queries: [
256
+ {
257
+ name: "",
258
+ description: "Query without name",
259
+ args: [],
260
+ returnType: "String",
261
+ },
262
+ {
263
+ name: "namedQuery",
264
+ description: "Named query",
265
+ args: [],
266
+ returnType: "String",
267
+ },
268
+ ],
269
+ mutations: [],
270
+ subscriptions: [],
271
+ schema: "type Query { namedQuery: String }",
272
+ },
273
+ },
274
+ },
275
+ });
276
+ const normalized = (0, normalizer_1.normalizeUniSpec)(doc);
277
+ // Should handle missing names gracefully
278
+ const queries = normalized.service?.protocols?.graphql?.queries || [];
279
+ node_assert_1.default.strictEqual(queries.length, 2);
280
+ // Named query should come after unnamed (empty string sorts first)
281
+ const queryNames = queries.map((q) => q.name || "");
282
+ node_assert_1.default.deepStrictEqual(queryNames, ["", "namedQuery"]);
283
+ });
284
+ });
285
+ (0, node_test_1.describe)("WebSocket protocol normalization", () => {
286
+ (0, node_test_1.it)("should sort WebSocket channels by name", () => {
287
+ const doc = (0, utils_1.createTestDocument)({
288
+ service: {
289
+ name: "test-service",
290
+ protocols: {
291
+ websocket: {
292
+ channels: [
293
+ {
294
+ name: "zebraChannel",
295
+ description: "Zebra channel",
296
+ messages: [],
297
+ },
298
+ {
299
+ name: "alphaChannel",
300
+ description: "Alpha channel",
301
+ messages: [],
302
+ },
303
+ {
304
+ name: "betaChannel",
305
+ description: "Beta channel",
306
+ messages: [],
307
+ },
308
+ ],
309
+ },
310
+ },
311
+ },
312
+ });
313
+ const normalized = (0, normalizer_1.normalizeUniSpec)(doc);
314
+ // Channels should be sorted by name
315
+ const channels = normalized.service?.protocols?.websocket?.channels || [];
316
+ const channelNames = channels.map((c) => c.name).filter(Boolean);
317
+ node_assert_1.default.deepStrictEqual(channelNames, [
318
+ "alphaChannel",
319
+ "betaChannel",
320
+ "zebraChannel",
321
+ ]);
322
+ });
323
+ (0, node_test_1.it)("should sort messages within each channel by name", () => {
324
+ const doc = (0, utils_1.createTestDocument)({
325
+ service: {
326
+ name: "test-service",
327
+ protocols: {
328
+ websocket: {
329
+ channels: [
330
+ {
331
+ name: "notifications",
332
+ description: "Notification channel",
333
+ messages: [
334
+ {
335
+ name: "zebraMessage",
336
+ description: "Zebra notification",
337
+ direction: "subscribe",
338
+ },
339
+ {
340
+ name: "alphaMessage",
341
+ description: "Alpha notification",
342
+ direction: "publish",
343
+ },
344
+ {
345
+ name: "betaMessage",
346
+ description: "Beta notification",
347
+ direction: "both",
348
+ },
349
+ ],
350
+ },
351
+ ],
352
+ },
353
+ },
354
+ },
355
+ });
356
+ const normalized = (0, normalizer_1.normalizeUniSpec)(doc);
357
+ // Messages should be sorted by name within the channel
358
+ const channel = normalized.service?.protocols?.websocket?.channels?.find((c) => c.name === "notifications");
359
+ node_assert_1.default.ok(channel);
360
+ const messages = channel?.messages || [];
361
+ const messageNames = messages.map((m) => m.name).filter(Boolean);
362
+ node_assert_1.default.deepStrictEqual(messageNames, [
363
+ "alphaMessage",
364
+ "betaMessage",
365
+ "zebraMessage",
366
+ ]);
367
+ });
368
+ (0, node_test_1.it)("should handle channels with missing names", () => {
369
+ const doc = (0, utils_1.createTestDocument)({
370
+ service: {
371
+ name: "test-service",
372
+ protocols: {
373
+ websocket: {
374
+ channels: [
375
+ {
376
+ name: "",
377
+ description: "Channel without name",
378
+ messages: [],
379
+ },
380
+ {
381
+ name: "namedChannel",
382
+ description: "Named channel",
383
+ messages: [],
384
+ },
385
+ ],
386
+ },
387
+ },
388
+ },
389
+ });
390
+ const normalized = (0, normalizer_1.normalizeUniSpec)(doc);
391
+ // Should handle missing names gracefully
392
+ const channels = normalized.service?.protocols?.websocket?.channels || [];
393
+ node_assert_1.default.strictEqual(channels.length, 2);
394
+ // Named channel should come after unnamed
395
+ const channelNames = channels.map((c) => c.name || "");
396
+ node_assert_1.default.deepStrictEqual(channelNames, ["", "namedChannel"]);
397
+ });
398
+ (0, node_test_1.it)("should handle messages with missing names", () => {
399
+ const doc = (0, utils_1.createTestDocument)({
400
+ service: {
401
+ name: "test-service",
402
+ protocols: {
403
+ websocket: {
404
+ channels: [
405
+ {
406
+ name: "testChannel",
407
+ description: "Test channel",
408
+ messages: [
409
+ {
410
+ name: "",
411
+ description: "Message without name",
412
+ direction: "subscribe",
413
+ },
414
+ {
415
+ name: "namedMessage",
416
+ description: "Named message",
417
+ direction: "publish",
418
+ },
419
+ ],
420
+ },
421
+ ],
422
+ },
423
+ },
424
+ },
425
+ });
426
+ const normalized = (0, normalizer_1.normalizeUniSpec)(doc);
427
+ // Should handle missing names gracefully
428
+ const channel = normalized.service?.protocols?.websocket?.channels?.find((c) => c.name === "testChannel");
429
+ node_assert_1.default.ok(channel);
430
+ const messages = channel?.messages || [];
431
+ node_assert_1.default.strictEqual(messages.length, 2);
432
+ // Named message should come after unnamed
433
+ const messageNames = messages.map((m) => m.name || "");
434
+ node_assert_1.default.deepStrictEqual(messageNames, ["", "namedMessage"]);
435
+ });
436
+ });
437
+ (0, node_test_1.describe)("Mixed protocol normalization", () => {
438
+ (0, node_test_1.it)("should normalize all protocols simultaneously", () => {
439
+ const doc = (0, utils_1.createTestDocument)({
440
+ service: {
441
+ name: "test-service",
442
+ protocols: {
443
+ rest: {
444
+ routes: [
445
+ {
446
+ name: "zebraRoute",
447
+ method: "GET",
448
+ path: "/zebra",
449
+ responses: { "200": { description: "OK" } },
450
+ },
451
+ {
452
+ name: "alphaRoute",
453
+ method: "POST",
454
+ path: "/alpha",
455
+ responses: { "201": { description: "Created" } },
456
+ },
457
+ ],
458
+ },
459
+ graphql: {
460
+ queries: [
461
+ {
462
+ name: "zebraQuery",
463
+ description: "Get zebra",
464
+ args: [],
465
+ returnType: "Zebra",
466
+ },
467
+ {
468
+ name: "alphaQuery",
469
+ description: "Get alpha",
470
+ args: [],
471
+ returnType: "Alpha",
472
+ },
473
+ ],
474
+ mutations: [],
475
+ subscriptions: [],
476
+ schema: "type Query { alphaQuery: Alpha zebraQuery: Zebra }",
477
+ },
478
+ websocket: {
479
+ channels: [
480
+ {
481
+ name: "zebraChannel",
482
+ description: "Zebra channel",
483
+ messages: [],
484
+ },
485
+ {
486
+ name: "alphaChannel",
487
+ description: "Alpha channel",
488
+ messages: [],
489
+ },
490
+ ],
491
+ },
492
+ },
493
+ },
494
+ });
495
+ const normalized = (0, normalizer_1.normalizeUniSpec)(doc);
496
+ // All protocols should be normalized
497
+ const protocols = normalized.service?.protocols;
498
+ // Check REST routes
499
+ const restRoutes = protocols?.rest?.routes || [];
500
+ const restRouteNames = restRoutes.map((r) => r.name).filter(Boolean);
501
+ node_assert_1.default.deepStrictEqual(restRouteNames, ["alphaRoute", "zebraRoute"]);
502
+ // Check GraphQL queries
503
+ const graphqlQueries = protocols?.graphql?.queries || [];
504
+ const graphqlQueryNames = graphqlQueries
505
+ .map((q) => q.name)
506
+ .filter(Boolean);
507
+ node_assert_1.default.deepStrictEqual(graphqlQueryNames, ["alphaQuery", "zebraQuery"]);
508
+ // Check WebSocket channels
509
+ const wsChannels = protocols?.websocket?.channels || [];
510
+ const wsChannelNames = wsChannels.map((c) => c.name).filter(Boolean);
511
+ node_assert_1.default.deepStrictEqual(wsChannelNames, ["alphaChannel", "zebraChannel"]);
512
+ });
513
+ });
514
+ (0, node_test_1.describe)("Structural normalization", () => {
515
+ (0, node_test_1.it)("should sort all object keys lexicographically", () => {
516
+ const doc = {
517
+ unispecVersion: "1.0.0",
518
+ service: {
519
+ name: "test-service",
520
+ description: "Test service for sorting",
521
+ protocols: {
522
+ rest: {
523
+ routes: [
524
+ {
525
+ name: "zRoute",
526
+ method: "GET",
527
+ path: "/z",
528
+ responses: { "200": { description: "OK" } },
529
+ },
530
+ {
531
+ name: "aRoute",
532
+ method: "POST",
533
+ path: "/a",
534
+ responses: { "201": { description: "Created" } },
535
+ },
536
+ ],
537
+ },
538
+ },
539
+ },
540
+ };
541
+ const normalized = (0, normalizer_1.normalizeUniSpec)(doc);
542
+ // Top-level keys should be sorted
543
+ const docKeys = Object.keys(normalized);
544
+ node_assert_1.default.deepStrictEqual(docKeys, ["service", "unispecVersion"]);
545
+ // Service keys should be sorted lexicographically
546
+ const serviceKeys = Object.keys(normalized.service || {});
547
+ const expectedServiceKeys = ["description", "name", "protocols"];
548
+ node_assert_1.default.deepStrictEqual(serviceKeys, expectedServiceKeys);
549
+ // Protocol keys should be sorted
550
+ const protocolKeys = Object.keys(normalized.service?.protocols || {});
551
+ node_assert_1.default.deepStrictEqual(protocolKeys, ["rest"]);
552
+ });
553
+ (0, node_test_1.it)("should handle nested objects recursively", () => {
554
+ const doc = (0, utils_1.createTestDocument)({
555
+ service: {
556
+ name: "test-service",
557
+ protocols: {
558
+ rest: {
559
+ routes: [
560
+ {
561
+ name: "testRoute",
562
+ method: "POST",
563
+ path: "/test",
564
+ requestBody: {
565
+ description: "Request body",
566
+ content: {
567
+ "application/json": {
568
+ schemaRef: "ZSchema",
569
+ },
570
+ "text/plain": {
571
+ schemaRef: "ASchema",
572
+ },
573
+ },
574
+ },
575
+ responses: {
576
+ "201": {
577
+ description: "Created",
578
+ },
579
+ "200": {
580
+ description: "OK",
581
+ },
582
+ },
583
+ },
584
+ ],
585
+ },
586
+ },
587
+ },
588
+ });
589
+ const normalized = (0, normalizer_1.normalizeUniSpec)(doc);
590
+ // Nested object keys should be sorted
591
+ const route = normalized.service?.protocols?.rest?.routes?.[0];
592
+ node_assert_1.default.ok(route);
593
+ // requestBody keys should be sorted lexicographically
594
+ const requestBodyKeys = Object.keys(route.requestBody || {});
595
+ node_assert_1.default.deepStrictEqual(requestBodyKeys, ["content", "description"]);
596
+ // response keys should be sorted
597
+ const responseKeys = Object.keys(route.responses || {});
598
+ node_assert_1.default.deepStrictEqual(responseKeys, ["200", "201"]);
599
+ });
600
+ });
601
+ (0, node_test_1.describe)("Real-world examples", () => {
602
+ (0, node_test_1.it)("should handle complex real-world example from mixed-complete.json", () => {
603
+ const doc = (0, utils_1.loadExample)(node_path_1.default.join(utils_1.examplesDir, "valid", "spec", "mixed-complete.json"));
604
+ // Should not crash
605
+ const normalized = (0, normalizer_1.normalizeUniSpec)(doc);
606
+ node_assert_1.default.ok(normalized);
607
+ // Should have sorted structure
608
+ const docKeys = Object.keys(normalized);
609
+ node_assert_1.default.deepStrictEqual(docKeys, ["service", "unispecVersion"]);
610
+ // Should normalize all protocols present
611
+ const protocols = normalized.service?.protocols;
612
+ if (protocols?.rest?.routes) {
613
+ const routeKeys = protocols.rest.routes.map((r) => r.name || `${r.path} ${r.method}`);
614
+ const sortedRouteKeys = [...routeKeys].sort();
615
+ node_assert_1.default.deepStrictEqual(routeKeys, sortedRouteKeys);
616
+ }
617
+ if (protocols?.graphql) {
618
+ ["queries", "mutations", "subscriptions"].forEach((operationType) => {
619
+ const operations = protocols[operationType];
620
+ if (Array.isArray(operations)) {
621
+ const names = operations
622
+ .map((op) => op.name || "")
623
+ .filter(Boolean);
624
+ const sortedNames = [...names].sort();
625
+ node_assert_1.default.deepStrictEqual(names, sortedNames);
626
+ }
627
+ });
628
+ }
629
+ if (protocols?.websocket?.channels) {
630
+ const channelNames = protocols.websocket.channels
631
+ .map((c) => c.name || "")
632
+ .filter(Boolean);
633
+ const sortedChannelNames = [...channelNames].sort();
634
+ node_assert_1.default.deepStrictEqual(channelNames, sortedChannelNames);
635
+ // Check message sorting within channels
636
+ protocols.websocket.channels.forEach((channel) => {
637
+ if (channel.messages) {
638
+ const messageNames = channel.messages
639
+ .map((m) => m.name || "")
640
+ .filter(Boolean);
641
+ const sortedMessageNames = [...messageNames].sort();
642
+ node_assert_1.default.deepStrictEqual(messageNames, sortedMessageNames);
643
+ }
644
+ });
645
+ }
646
+ });
647
+ });
648
+ });