@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,126 @@
1
+ import assert from "node:assert";
2
+ import { describe, it } from "node:test";
3
+ import { sortSchemas } from "../../src/optimizer/optimization-functions.js";
4
+ import { createDocumentWithUnsortedSchemas, getExamplePath, loadExample, } from "./utils.js";
5
+ describe("sortSchemas", () => {
6
+ it("should sort schemas alphabetically", () => {
7
+ const doc = createDocumentWithUnsortedSchemas();
8
+ const sortedCount = sortSchemas(doc);
9
+ assert.strictEqual(sortedCount, 3);
10
+ const schemaNames = Object.keys(doc.service?.schemas || {});
11
+ assert.deepStrictEqual(schemaNames, ["Alpha", "Beta", "Zebra"]);
12
+ });
13
+ it("should return 0 for empty schemas", () => {
14
+ const doc = {
15
+ unispecVersion: "1.0.0",
16
+ service: { name: "test", schemas: {} },
17
+ };
18
+ const sortedCount = sortSchemas(doc);
19
+ assert.strictEqual(sortedCount, 0);
20
+ });
21
+ it("should handle documents without schemas", () => {
22
+ const doc = { unispecVersion: "1.0.0", service: { name: "test" } };
23
+ const sortedCount = sortSchemas(doc);
24
+ assert.strictEqual(sortedCount, 0);
25
+ });
26
+ it("should handle documents without service", () => {
27
+ const doc = { unispecVersion: "1.0.0", service: { name: "test" } };
28
+ const sortedCount = sortSchemas(doc);
29
+ assert.strictEqual(sortedCount, 0);
30
+ });
31
+ it("should preserve schema content while sorting", () => {
32
+ const doc = createDocumentWithUnsortedSchemas();
33
+ sortSchemas(doc);
34
+ const schemas = doc.service?.schemas || {};
35
+ assert.strictEqual(schemas.Alpha.description, "Alpha schema");
36
+ assert.strictEqual(schemas.Beta.description, "Beta schema");
37
+ assert.strictEqual(schemas.Zebra.description, "Zebra schema");
38
+ });
39
+ it("should handle already sorted schemas", () => {
40
+ const doc = {
41
+ unispecVersion: "1.0.0",
42
+ service: {
43
+ name: "test",
44
+ schemas: {
45
+ Alpha: {
46
+ name: "Alpha",
47
+ description: "Alpha schema",
48
+ jsonSchema: { type: "string" },
49
+ },
50
+ Beta: {
51
+ name: "Beta",
52
+ description: "Beta schema",
53
+ jsonSchema: { type: "number" },
54
+ },
55
+ Zebra: {
56
+ name: "Zebra",
57
+ description: "Zebra schema",
58
+ jsonSchema: { type: "boolean" },
59
+ },
60
+ },
61
+ },
62
+ };
63
+ const sortedCount = sortSchemas(doc);
64
+ assert.strictEqual(sortedCount, 3);
65
+ const schemaNames = Object.keys(doc.service?.schemas || {});
66
+ assert.deepStrictEqual(schemaNames, ["Alpha", "Beta", "Zebra"]);
67
+ });
68
+ it("should handle single schema", () => {
69
+ const doc = {
70
+ unispecVersion: "1.0.0",
71
+ service: {
72
+ name: "test",
73
+ schemas: {
74
+ SingleSchema: {
75
+ name: "SingleSchema",
76
+ description: "Single schema",
77
+ jsonSchema: { type: "string" },
78
+ },
79
+ },
80
+ },
81
+ };
82
+ const sortedCount = sortSchemas(doc);
83
+ assert.strictEqual(sortedCount, 1);
84
+ const schemaNames = Object.keys(doc.service?.schemas || {});
85
+ assert.deepStrictEqual(schemaNames, ["SingleSchema"]);
86
+ });
87
+ it("should work with real examples from node_modules", () => {
88
+ const doc = loadExample(getExamplePath("valid", "spec", "mixed-complete.json"));
89
+ const sortedCount = sortSchemas(JSON.parse(JSON.stringify(doc)));
90
+ // mixed-complete.json has 2 schemas
91
+ assert.strictEqual(sortedCount, 2);
92
+ // Check that schemas are sorted
93
+ const schemaNames = Object.keys(doc.service?.schemas || {});
94
+ const sortedSchemaNames = [...schemaNames].sort();
95
+ assert.deepStrictEqual(schemaNames, sortedSchemaNames);
96
+ });
97
+ it("should handle case-sensitive sorting", () => {
98
+ const doc = {
99
+ unispecVersion: "1.0.0",
100
+ service: {
101
+ name: "test",
102
+ schemas: {
103
+ zebra: {
104
+ name: "zebra",
105
+ description: "lowercase zebra",
106
+ jsonSchema: { type: "string" },
107
+ },
108
+ Alpha: {
109
+ name: "Alpha",
110
+ description: "uppercase Alpha",
111
+ jsonSchema: { type: "string" },
112
+ },
113
+ beta: {
114
+ name: "beta",
115
+ description: "lowercase beta",
116
+ jsonSchema: { type: "string" },
117
+ },
118
+ },
119
+ },
120
+ };
121
+ sortSchemas(doc);
122
+ const schemaNames = Object.keys(doc.service?.schemas || {});
123
+ // Default JavaScript sort is case-sensitive: uppercase letters come before lowercase
124
+ assert.deepStrictEqual(schemaNames, ["Alpha", "beta", "zebra"]);
125
+ });
126
+ });
@@ -0,0 +1,8 @@
1
+ import { createTestDocument, examplesDir, getExamplePath, loadExample } from "../../tests/utils.js";
2
+ import type { UniSpecDocument, UniSpecSchemaDefinition } from "../../src/types/index.js";
3
+ export declare function createTestDocumentWithSchemas(schemas?: Record<string, UniSpecSchemaDefinition>, overrides?: Partial<UniSpecDocument>): UniSpecDocument;
4
+ export declare function createDocumentWithDuplicateSchemas(): UniSpecDocument;
5
+ export declare function createDocumentWithOrphanedSchemas(): UniSpecDocument;
6
+ export declare function createDocumentWithUnsortedSchemas(): UniSpecDocument;
7
+ export declare function createDocumentWithLongReferences(): UniSpecDocument;
8
+ export { createTestDocument, examplesDir, getExamplePath, loadExample };
@@ -0,0 +1,199 @@
1
+ import { createTestDocument, examplesDir, getExamplePath, loadExample, } from "../../tests/utils.js";
2
+ // Optimizer-specific utilities
3
+ export function createTestDocumentWithSchemas(schemas = {}, overrides = {}) {
4
+ const base = {
5
+ unispecVersion: "1.0.0",
6
+ service: {
7
+ name: "test-service",
8
+ description: "Test service description",
9
+ protocols: {
10
+ rest: {
11
+ routes: [
12
+ {
13
+ name: "testRoute",
14
+ summary: "Test route",
15
+ path: "/test",
16
+ method: "GET",
17
+ responses: {
18
+ "200": { description: "Success" },
19
+ },
20
+ },
21
+ ],
22
+ },
23
+ },
24
+ schemas,
25
+ },
26
+ };
27
+ // Deep merge overrides
28
+ return mergeDeep(base, overrides);
29
+ }
30
+ export function createDocumentWithDuplicateSchemas() {
31
+ const duplicateSchema = {
32
+ type: "object",
33
+ properties: {
34
+ id: { type: "string" },
35
+ name: { type: "string" },
36
+ },
37
+ required: ["id", "name"],
38
+ };
39
+ return createTestDocumentWithSchemas({
40
+ User: {
41
+ description: "User schema",
42
+ jsonSchema: duplicateSchema,
43
+ },
44
+ Person: {
45
+ description: "Person schema",
46
+ jsonSchema: duplicateSchema,
47
+ },
48
+ Profile: {
49
+ description: "Profile schema",
50
+ jsonSchema: {
51
+ type: "object",
52
+ properties: {
53
+ profileId: { type: "string" },
54
+ title: { type: "string" },
55
+ },
56
+ required: ["profileId", "title"],
57
+ },
58
+ },
59
+ }, {
60
+ service: {
61
+ name: "test-service",
62
+ protocols: {
63
+ rest: {
64
+ routes: [
65
+ {
66
+ name: "userRoute",
67
+ method: "GET",
68
+ path: "/users",
69
+ responses: {
70
+ "200": {
71
+ description: "Success",
72
+ content: {
73
+ "application/json": {
74
+ schemaRef: "User",
75
+ },
76
+ },
77
+ },
78
+ },
79
+ },
80
+ ],
81
+ },
82
+ },
83
+ },
84
+ });
85
+ }
86
+ export function createDocumentWithOrphanedSchemas() {
87
+ return createTestDocumentWithSchemas({
88
+ UsedSchema: {
89
+ description: "Used schema",
90
+ jsonSchema: {
91
+ type: "object",
92
+ properties: { id: { type: "string" } },
93
+ required: ["id"],
94
+ },
95
+ },
96
+ OrphanedSchema1: {
97
+ description: "Orphaned schema 1",
98
+ jsonSchema: { type: "string" },
99
+ },
100
+ OrphanedSchema2: {
101
+ description: "Orphaned schema 2",
102
+ jsonSchema: { type: "number" },
103
+ },
104
+ }, {
105
+ service: {
106
+ name: "test-service",
107
+ protocols: {
108
+ rest: {
109
+ routes: [
110
+ {
111
+ name: "testRoute",
112
+ summary: "Test route",
113
+ path: "/test",
114
+ method: "GET",
115
+ responses: {
116
+ "200": {
117
+ description: "Success",
118
+ content: {
119
+ "application/json": {
120
+ schemaRef: "UsedSchema",
121
+ },
122
+ },
123
+ },
124
+ },
125
+ },
126
+ ],
127
+ },
128
+ },
129
+ },
130
+ });
131
+ }
132
+ export function createDocumentWithUnsortedSchemas() {
133
+ return createTestDocumentWithSchemas({
134
+ Zebra: {
135
+ description: "Zebra schema",
136
+ jsonSchema: { type: "string" },
137
+ },
138
+ Alpha: {
139
+ description: "Alpha schema",
140
+ jsonSchema: { type: "number" },
141
+ },
142
+ Beta: {
143
+ description: "Beta schema",
144
+ jsonSchema: { type: "boolean" },
145
+ },
146
+ });
147
+ }
148
+ export function createDocumentWithLongReferences() {
149
+ return createTestDocumentWithSchemas({
150
+ TestSchema: {
151
+ description: "Test schema",
152
+ jsonSchema: { type: "string" },
153
+ },
154
+ }, {
155
+ service: {
156
+ name: "test-service",
157
+ protocols: {
158
+ rest: {
159
+ routes: [
160
+ {
161
+ name: "testRoute",
162
+ summary: "Test route",
163
+ path: "/test",
164
+ method: "GET",
165
+ responses: {
166
+ "200": {
167
+ description: "Success",
168
+ content: {
169
+ "application/json": {
170
+ schemaRef: "#/service/schemas/TestSchema",
171
+ },
172
+ },
173
+ },
174
+ },
175
+ },
176
+ ],
177
+ },
178
+ },
179
+ },
180
+ });
181
+ }
182
+ function mergeDeep(target, source) {
183
+ const result = { ...target };
184
+ for (const key in source) {
185
+ if (source[key] !== undefined) {
186
+ if (typeof source[key] === "object" &&
187
+ source[key] !== null &&
188
+ !Array.isArray(source[key])) {
189
+ result[key] = mergeDeep(result[key], source[key]);
190
+ }
191
+ else {
192
+ result[key] = source[key];
193
+ }
194
+ }
195
+ }
196
+ return result;
197
+ }
198
+ // Re-export commonly used functions from tests/utils for convenience
199
+ export { createTestDocument, examplesDir, getExamplePath, loadExample };
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,218 @@
1
+ import assert from "node:assert";
2
+ import { describe, it } from "node:test";
3
+ import { dedupeSchemas, resolveSchemaRef } from "../../src/schemas/index.js";
4
+ import { normalizeSchemaRef } from "../../src/schemas/utils.js";
5
+ import { getExamplePath, loadExample } from "./utils.js";
6
+ describe("schemas module - edge cases and invalid examples", () => {
7
+ describe("resolveSchemaRef with invalid examples", () => {
8
+ it("should handle documents without schemas gracefully", () => {
9
+ const doc = loadExample(getExamplePath("invalid", "spec", "missing-unispec-version.json"));
10
+ // Should not throw even with invalid documents
11
+ const schema = resolveSchemaRef(doc, "AnySchema");
12
+ assert.strictEqual(schema, undefined);
13
+ });
14
+ it("should handle malformed schema references", () => {
15
+ const doc = loadExample(getExamplePath("valid", "spec", "mixed-complete.json"));
16
+ // Test various malformed references
17
+ assert.strictEqual(resolveSchemaRef(doc, ""), undefined);
18
+ assert.strictEqual(resolveSchemaRef(doc, " "), undefined);
19
+ assert.strictEqual(resolveSchemaRef(doc, "#"), undefined);
20
+ assert.strictEqual(resolveSchemaRef(doc, "#/"), undefined);
21
+ assert.strictEqual(resolveSchemaRef(doc, "#/service/schemas/"), undefined);
22
+ });
23
+ it("should handle deeply nested JSON pointers", () => {
24
+ const doc = loadExample(getExamplePath("valid", "spec", "mixed-complete.json"));
25
+ // Test deeply nested pointer that doesn't exist
26
+ const schema = resolveSchemaRef(doc, "#/service/schemas/nonexistent/deep/nested/schema");
27
+ assert.strictEqual(schema, undefined);
28
+ });
29
+ it("should handle special characters in schema names", () => {
30
+ // biome-ignore lint/suspicious/noExplicitAny: Testing edge cases with special characters
31
+ const doc = {
32
+ service: {
33
+ schemas: {
34
+ "schema-with-dashes": { jsonSchema: { type: "string" } },
35
+ "schema_with_underscores": { jsonSchema: { type: "number" } },
36
+ "schema.with.dots": { jsonSchema: { type: "boolean" } },
37
+ "schema with spaces": { jsonSchema: { type: "array" } },
38
+ },
39
+ },
40
+ };
41
+ assert.ok(resolveSchemaRef(doc, "schema-with-dashes"));
42
+ assert.ok(resolveSchemaRef(doc, "schema_with_underscores"));
43
+ assert.ok(resolveSchemaRef(doc, "schema.with.dots"));
44
+ assert.ok(resolveSchemaRef(doc, "schema with spaces"));
45
+ });
46
+ });
47
+ describe("dedupeSchemas with complex scenarios", () => {
48
+ it("should handle circular references safely", () => {
49
+ // biome-ignore lint/suspicious/noExplicitAny: Testing edge cases with circular references
50
+ const doc = {
51
+ service: {
52
+ schemas: {
53
+ SchemaA: {
54
+ jsonSchema: {
55
+ type: "object",
56
+ properties: {
57
+ ref: { $ref: "#/service/schemas/SchemaB" },
58
+ },
59
+ },
60
+ },
61
+ SchemaB: {
62
+ jsonSchema: {
63
+ type: "object",
64
+ properties: {
65
+ ref: { $ref: "#/service/schemas/SchemaA" },
66
+ },
67
+ },
68
+ },
69
+ },
70
+ },
71
+ };
72
+ // Should not throw with circular references
73
+ dedupeSchemas(doc);
74
+ // Both schemas should remain since they have different content
75
+ assert.strictEqual(Object.keys(doc.service.schemas).length, 2);
76
+ });
77
+ it("should preserve schema order during deduplication", () => {
78
+ // biome-ignore lint/suspicious/noExplicitAny: Testing edge cases with schema ordering
79
+ const doc = {
80
+ service: {
81
+ schemas: {
82
+ ThirdSchema: { jsonSchema: { type: "string" } },
83
+ FirstSchema: { jsonSchema: { type: "string" } },
84
+ SecondSchema: { jsonSchema: { type: "number" } },
85
+ },
86
+ },
87
+ };
88
+ dedupeSchemas(doc);
89
+ // Should deduplicate one of the string schemas and keep the number schema
90
+ const remainingSchemas = Object.keys(doc.service.schemas);
91
+ assert.strictEqual(remainingSchemas.length, 2);
92
+ assert.ok(remainingSchemas.includes("SecondSchema")); // Number schema should remain
93
+ assert.ok(remainingSchemas.includes("FirstSchema") ||
94
+ remainingSchemas.includes("ThirdSchema")); // One string schema should remain
95
+ });
96
+ it("should handle null and undefined schemas", () => {
97
+ // biome-ignore lint/suspicious/noExplicitAny: Testing edge cases with null/undefined schemas
98
+ const doc = {
99
+ service: {
100
+ schemas: {
101
+ NullSchema: null,
102
+ UndefinedSchema: undefined,
103
+ ValidSchema: { jsonSchema: { type: "string" } },
104
+ },
105
+ },
106
+ };
107
+ // Should not throw
108
+ dedupeSchemas(doc);
109
+ // Should handle null/undefined gracefully
110
+ assert.ok(doc.service.schemas.ValidSchema);
111
+ });
112
+ it("should handle empty schema objects", () => {
113
+ // biome-ignore lint/suspicious/noExplicitAny: Testing edge cases with empty schemas
114
+ const doc = {
115
+ service: {
116
+ schemas: {
117
+ EmptySchema1: {},
118
+ EmptySchema2: {},
119
+ ValidSchema: { jsonSchema: { type: "string" } },
120
+ },
121
+ },
122
+ };
123
+ dedupeSchemas(doc);
124
+ // Should deduplicate empty schemas
125
+ const schemas = Object.keys(doc.service.schemas);
126
+ assert.strictEqual(schemas.length, 2);
127
+ assert.ok(schemas.includes("ValidSchema"));
128
+ assert.ok(schemas.includes("EmptySchema1") || schemas.includes("EmptySchema2"));
129
+ });
130
+ });
131
+ describe("normalizeSchemaRef edge cases", () => {
132
+ it("should handle Unicode characters", () => {
133
+ assert.strictEqual(normalizeSchemaRef("Схема"), "Схема");
134
+ assert.strictEqual(normalizeSchemaRef("#/service/schemas/Схема"), "Схема");
135
+ assert.strictEqual(normalizeSchemaRef("#Схема"), "Схема");
136
+ });
137
+ it("should handle very long schema names", () => {
138
+ const longName = "a".repeat(1000);
139
+ assert.strictEqual(normalizeSchemaRef(longName), longName);
140
+ assert.strictEqual(normalizeSchemaRef(`#/service/schemas/${longName}`), longName);
141
+ });
142
+ it("should handle JSON pointer encoding", () => {
143
+ assert.strictEqual(normalizeSchemaRef("#/service/schemas/schema~0name"), "schema~0name");
144
+ assert.strictEqual(normalizeSchemaRef("#/service/schemas/schema~1name"), "schema~1name");
145
+ assert.strictEqual(normalizeSchemaRef("#/service/schemas/schema/name"), "name");
146
+ });
147
+ });
148
+ describe("integration with invalid examples", () => {
149
+ it("should not crash on completely invalid documents", () => {
150
+ const invalidDocs = [
151
+ { invalid: "structure" },
152
+ {},
153
+ { service: null },
154
+ { service: { schemas: "not an object" } },
155
+ ];
156
+ for (const doc of invalidDocs) {
157
+ // Should not throw
158
+ assert.doesNotThrow(() => {
159
+ // biome-ignore lint/suspicious/noExplicitAny: Testing invalid document structures
160
+ resolveSchemaRef(doc, "test");
161
+ // biome-ignore lint/suspicious/noExplicitAny: Testing invalid document structures
162
+ dedupeSchemas(doc);
163
+ });
164
+ }
165
+ // null and undefined should be handled by resolveSchemaRef
166
+ // biome-ignore lint/suspicious/noExplicitAny: Testing null input handling
167
+ assert.strictEqual(resolveSchemaRef(null, "test"), undefined);
168
+ // biome-ignore lint/suspicious/noExplicitAny: Testing undefined input handling
169
+ assert.strictEqual(resolveSchemaRef(undefined, "test"), undefined);
170
+ });
171
+ it("should handle documents with missing required fields", () => {
172
+ const doc = loadExample(getExamplePath("invalid", "spec", "missing-service-name.json"));
173
+ // Should handle gracefully
174
+ const schema = resolveSchemaRef(doc, "anything");
175
+ assert.strictEqual(schema, undefined);
176
+ // Should not throw
177
+ assert.doesNotThrow(() => dedupeSchemas(doc));
178
+ });
179
+ });
180
+ describe("performance and memory", () => {
181
+ it("should handle large numbers of schemas efficiently", () => {
182
+ // biome-ignore lint/suspicious/noExplicitAny: Testing performance with large schema sets
183
+ const doc = {
184
+ service: {
185
+ schemas: {},
186
+ },
187
+ };
188
+ // Create 100 schemas with some duplicates (10 unique types, each repeated 10 times)
189
+ const schemaTypes = [
190
+ { type: "string" },
191
+ { type: "number" },
192
+ { type: "boolean" },
193
+ { type: "array" },
194
+ { type: "object" },
195
+ { type: "null" },
196
+ { type: "string", format: "date" },
197
+ { type: "string", format: "email" },
198
+ { type: "integer" },
199
+ { type: "number", format: "float" },
200
+ ];
201
+ let schemaIndex = 0;
202
+ for (let i = 0; i < 10; i++) {
203
+ for (let j = 0; j < schemaTypes.length; j++) {
204
+ doc.service.schemas[`Schema${schemaIndex++}`] = {
205
+ jsonSchema: schemaTypes[j],
206
+ };
207
+ }
208
+ }
209
+ const startTime = Date.now();
210
+ dedupeSchemas(doc);
211
+ const endTime = Date.now();
212
+ // Should complete in reasonable time (less than 1 second)
213
+ assert.ok(endTime - startTime < 1000);
214
+ // Should deduplicate to only 10 unique schemas
215
+ assert.strictEqual(Object.keys(doc.service.schemas).length, 10);
216
+ });
217
+ });
218
+ });
@@ -0,0 +1 @@
1
+ export {};