@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,217 @@
1
+ import assert from "node:assert";
2
+ import { describe, it } from "node:test";
3
+ import { generateOptimizationReport } from "../../src/optimizer/core.js";
4
+ import { createDocumentWithDuplicateSchemas, createDocumentWithOrphanedSchemas, getExamplePath, loadExample, } from "./utils.js";
5
+ describe("generateOptimizationReport", () => {
6
+ it("should generate report for document with duplicates", () => {
7
+ const doc = createDocumentWithDuplicateSchemas();
8
+ const report = generateOptimizationReport(doc);
9
+ // Check properties exist using if statements
10
+ if (!report.recommendations) {
11
+ throw new Error("recommendations should exist");
12
+ }
13
+ if (!report.potentialSavings) {
14
+ throw new Error("potentialSavings should exist");
15
+ }
16
+ // Should recommend deduplication
17
+ const dedupRecommendation = report.recommendations.find((rec) => rec.includes("Deduplicate"));
18
+ if (!dedupRecommendation) {
19
+ throw new Error("Should recommend deduplication");
20
+ }
21
+ // The test document has 3 schemas: User (referenced), Person (duplicate of User), Profile (orphaned)
22
+ // So we expect 1 duplicate removal + 1 orphaned removal = 2 total reduction
23
+ assert.strictEqual(report.potentialSavings.schemaReduction, 2);
24
+ });
25
+ it("should generate report for document with orphaned schemas", () => {
26
+ const doc = createDocumentWithOrphanedSchemas();
27
+ const report = generateOptimizationReport(doc);
28
+ // Should recommend removing orphaned schemas
29
+ const orphanedRecommendation = report.recommendations.find((rec) => rec.includes("orphaned"));
30
+ assert.ok(orphanedRecommendation);
31
+ assert.strictEqual(report.potentialSavings.schemaReduction, 2);
32
+ });
33
+ it("should generate report for document with both duplicates and orphaned schemas", () => {
34
+ const doc = createDocumentWithDuplicateSchemas();
35
+ // Add orphaned schema
36
+ if (doc.service?.schemas) {
37
+ doc.service.schemas.OrphanedSchema = {
38
+ description: "Orphaned schema",
39
+ jsonSchema: { type: "string" },
40
+ };
41
+ }
42
+ const report = generateOptimizationReport(doc);
43
+ // Should recommend both optimizations
44
+ const dedupRecommendation = report.recommendations.find((rec) => rec.includes("Deduplicate"));
45
+ const orphanedRecommendation = report.recommendations.find((rec) => rec.includes("orphaned"));
46
+ assert.ok(dedupRecommendation);
47
+ assert.ok(orphanedRecommendation);
48
+ // Now we have: 1 duplicate + 2 orphaned (Profile + OrphanedSchema) = 3 total reduction
49
+ assert.strictEqual(report.potentialSavings.schemaReduction, 3);
50
+ });
51
+ it("should generate empty report for optimized document", () => {
52
+ const doc = {
53
+ unispecVersion: "1.0.0",
54
+ service: {
55
+ name: "test",
56
+ schemas: {
57
+ TestSchema: {
58
+ name: "TestSchema",
59
+ description: "Test schema",
60
+ jsonSchema: { type: "string" },
61
+ },
62
+ },
63
+ protocols: {
64
+ rest: {
65
+ routes: [
66
+ {
67
+ name: "testRoute",
68
+ method: "GET",
69
+ path: "/test",
70
+ responses: {
71
+ "200": {
72
+ description: "Success",
73
+ content: {
74
+ "application/json": {
75
+ schemaRef: "TestSchema",
76
+ },
77
+ },
78
+ },
79
+ },
80
+ },
81
+ ],
82
+ },
83
+ },
84
+ },
85
+ };
86
+ const report = generateOptimizationReport(doc);
87
+ assert.strictEqual(report.recommendations.length, 0);
88
+ assert.strictEqual(report.potentialSavings.schemaReduction, 0);
89
+ // This schema is referenced, so no compression savings
90
+ assert.strictEqual(report.potentialSavings.referenceCompression, 0);
91
+ });
92
+ it("should handle documents without schemas", () => {
93
+ const doc = { unispecVersion: "1.0.0", service: { name: "test" } };
94
+ const report = generateOptimizationReport(doc);
95
+ assert.strictEqual(report.recommendations.length, 0);
96
+ assert.strictEqual(report.potentialSavings.schemaReduction, 0);
97
+ assert.strictEqual(report.potentialSavings.referenceCompression, 0);
98
+ });
99
+ it("should handle documents without service", () => {
100
+ const doc = { unispecVersion: "1.0.0", service: { name: "test" } };
101
+ const report = generateOptimizationReport(doc);
102
+ assert.strictEqual(report.recommendations.length, 0);
103
+ assert.strictEqual(report.potentialSavings.schemaReduction, 0);
104
+ assert.strictEqual(report.potentialSavings.referenceCompression, 0);
105
+ });
106
+ it("should estimate reference compression savings", () => {
107
+ const doc = {
108
+ unispecVersion: "1.0.0",
109
+ service: {
110
+ name: "test",
111
+ schemas: {
112
+ TestSchema: {
113
+ name: "TestSchema",
114
+ description: "Test schema",
115
+ jsonSchema: { type: "string" },
116
+ },
117
+ },
118
+ protocols: {
119
+ rest: {
120
+ routes: [
121
+ {
122
+ name: "testRoute",
123
+ method: "GET",
124
+ path: "/test",
125
+ responses: {
126
+ "200": {
127
+ description: "Success",
128
+ content: {
129
+ "application/json": {
130
+ schemaRef: "#/service/schemas/TestSchema",
131
+ },
132
+ },
133
+ },
134
+ },
135
+ },
136
+ ],
137
+ },
138
+ },
139
+ },
140
+ };
141
+ const report = generateOptimizationReport(doc);
142
+ // Should estimate reference compression (only 1 reference, so no compression)
143
+ assert.strictEqual(report.potentialSavings.referenceCompression, 0);
144
+ });
145
+ it("should work with real examples from node_modules", () => {
146
+ const doc = loadExample(getExamplePath("valid", "spec", "mixed-complete.json"));
147
+ const report = generateOptimizationReport(JSON.parse(JSON.stringify(doc)));
148
+ // mixed-complete.json is well-structured, so minimal recommendations
149
+ assert.ok(report.recommendations.length >= 0);
150
+ assert.ok(report.potentialSavings.schemaReduction >= 0);
151
+ assert.ok(report.potentialSavings.referenceCompression >= 0);
152
+ });
153
+ it("should handle complex documents with multiple optimization opportunities", () => {
154
+ const doc = createDocumentWithDuplicateSchemas();
155
+ // Add multiple orphaned schemas
156
+ if (doc.service?.schemas) {
157
+ doc.service.schemas.Orphaned1 = {
158
+ description: "Orphaned schema 1",
159
+ jsonSchema: { type: "string" },
160
+ };
161
+ doc.service.schemas.Orphaned2 = {
162
+ description: "Orphaned schema 2",
163
+ jsonSchema: { type: "number" },
164
+ };
165
+ doc.service.schemas.Orphaned3 = {
166
+ description: "Orphaned schema 3",
167
+ jsonSchema: { type: "boolean" },
168
+ };
169
+ }
170
+ const report = generateOptimizationReport(doc);
171
+ // Should recommend multiple optimizations
172
+ assert.ok(report.recommendations.length >= 2);
173
+ // Should account for all potential savings
174
+ assert.ok(report.potentialSavings.schemaReduction >= 3); // 1 duplicate + 2+ orphaned
175
+ });
176
+ it("should not recommend removing schemas that are referenced", () => {
177
+ const doc = {
178
+ unispecVersion: "1.0.0",
179
+ service: {
180
+ name: "test",
181
+ schemas: {
182
+ ReferencedSchema: {
183
+ name: "ReferencedSchema",
184
+ description: "Referenced schema",
185
+ jsonSchema: { type: "string" },
186
+ },
187
+ },
188
+ protocols: {
189
+ rest: {
190
+ routes: [
191
+ {
192
+ name: "testRoute",
193
+ method: "GET",
194
+ path: "/test",
195
+ responses: {
196
+ "200": {
197
+ description: "Success",
198
+ content: {
199
+ "application/json": {
200
+ schemaRef: "ReferencedSchema",
201
+ },
202
+ },
203
+ },
204
+ },
205
+ },
206
+ ],
207
+ },
208
+ },
209
+ },
210
+ };
211
+ const report = generateOptimizationReport(doc);
212
+ // Should not recommend removing the referenced schema
213
+ const removeRecommendation = report.recommendations.find((rec) => rec.includes("Remove"));
214
+ assert.strictEqual(removeRecommendation, undefined);
215
+ assert.strictEqual(report.potentialSavings.schemaReduction, 0);
216
+ });
217
+ });
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,182 @@
1
+ import assert from "node:assert";
2
+ import { describe, it } from "node:test";
3
+ import { optimizeDocument } from "../../src/optimizer/core.js";
4
+ import { createDocumentWithDuplicateSchemas, createDocumentWithLongReferences, createDocumentWithOrphanedSchemas, createDocumentWithUnsortedSchemas, getExamplePath, loadExample, } from "./utils.js";
5
+ describe("optimizeDocument", () => {
6
+ it("should apply all optimizations by default", () => {
7
+ const doc = createDocumentWithDuplicateSchemas();
8
+ const result = optimizeDocument(doc);
9
+ assert.ok(result.document);
10
+ assert.ok(result.statistics);
11
+ assert.ok(Array.isArray(result.changes));
12
+ // Should have removed duplicate schemas
13
+ assert.strictEqual(result.statistics.originalSchemas, 3);
14
+ assert.strictEqual(result.statistics.optimizedSchemas, 2);
15
+ assert.strictEqual(result.statistics.removedSchemas, 1);
16
+ assert.strictEqual(result.statistics.deduplicatedSchemas, 1);
17
+ // Should have changes recorded
18
+ assert.ok(result.changes.length > 0);
19
+ });
20
+ it("should apply only specified optimizations", () => {
21
+ const doc = createDocumentWithDuplicateSchemas();
22
+ const result = optimizeDocument(doc, {
23
+ dedupeSchemas: true,
24
+ sortSchemas: false,
25
+ removeUnusedSchemas: false,
26
+ compressReferences: false,
27
+ });
28
+ // Should only deduplicate
29
+ assert.strictEqual(result.statistics.removedSchemas, 1);
30
+ assert.strictEqual(result.statistics.deduplicatedSchemas, 1);
31
+ // Check that changes mention deduplication
32
+ const deduplicationChange = result.changes.find((change) => change.includes("Deduplicated"));
33
+ assert.ok(deduplicationChange);
34
+ });
35
+ it("should handle documents without schemas", () => {
36
+ const doc = { unispecVersion: "1.0.0", service: { name: "test" } };
37
+ const result = optimizeDocument(doc);
38
+ assert.strictEqual(result.statistics.originalSchemas, 0);
39
+ assert.strictEqual(result.statistics.optimizedSchemas, 0);
40
+ assert.strictEqual(result.statistics.removedSchemas, 0);
41
+ assert.strictEqual(result.statistics.deduplicatedSchemas, 0);
42
+ assert.strictEqual(result.changes.length, 0);
43
+ });
44
+ it("should handle documents without service", () => {
45
+ const doc = { unispecVersion: "1.0.0", service: { name: "test" } };
46
+ const result = optimizeDocument(doc);
47
+ assert.strictEqual(result.statistics.originalSchemas, 0);
48
+ assert.strictEqual(result.statistics.optimizedSchemas, 0);
49
+ assert.strictEqual(result.statistics.removedSchemas, 0);
50
+ assert.strictEqual(result.statistics.deduplicatedSchemas, 0);
51
+ assert.strictEqual(result.changes.length, 0);
52
+ });
53
+ it("should remove orphaned schemas when enabled", () => {
54
+ const doc = createDocumentWithOrphanedSchemas();
55
+ const result = optimizeDocument(doc, {
56
+ removeUnusedSchemas: true,
57
+ dedupeSchemas: false,
58
+ sortSchemas: false,
59
+ compressReferences: false,
60
+ });
61
+ assert.strictEqual(result.statistics.originalSchemas, 3);
62
+ assert.strictEqual(result.statistics.optimizedSchemas, 1);
63
+ assert.strictEqual(result.statistics.removedSchemas, 2);
64
+ // Should have changes about orphaned schema removal
65
+ const orphanedChange = result.changes.find((change) => change.includes("orphaned"));
66
+ assert.ok(orphanedChange);
67
+ });
68
+ it("should sort schemas when enabled", () => {
69
+ const doc = createDocumentWithUnsortedSchemas();
70
+ const result = optimizeDocument(doc, {
71
+ sortSchemas: true,
72
+ dedupeSchemas: false,
73
+ removeUnusedSchemas: false,
74
+ compressReferences: false,
75
+ });
76
+ assert.strictEqual(result.statistics.originalSchemas, 3);
77
+ assert.strictEqual(result.statistics.optimizedSchemas, 3);
78
+ // Check schemas are sorted
79
+ const schemaNames = Object.keys(result.document.service?.schemas || {});
80
+ assert.deepStrictEqual(schemaNames, ["Alpha", "Beta", "Zebra"]);
81
+ // Should have changes about sorting
82
+ const sortChange = result.changes.find((change) => change.includes("Sorted"));
83
+ assert.ok(sortChange);
84
+ });
85
+ it("should compress references when enabled", () => {
86
+ const doc = createDocumentWithLongReferences();
87
+ const result = optimizeDocument(doc, {
88
+ compressReferences: true,
89
+ dedupeSchemas: false,
90
+ sortSchemas: false,
91
+ removeUnusedSchemas: false,
92
+ });
93
+ assert.strictEqual(result.statistics.originalSchemas, 1);
94
+ assert.strictEqual(result.statistics.optimizedSchemas, 1);
95
+ // Should have changes about compression
96
+ const compressionChange = result.changes.find((change) => change.includes("Compressed"));
97
+ assert.ok(compressionChange);
98
+ });
99
+ it("should create a deep copy of the document", () => {
100
+ const doc = createDocumentWithDuplicateSchemas();
101
+ const originalSchemas = doc.service?.schemas;
102
+ const result = optimizeDocument(doc);
103
+ // Original document should be unchanged
104
+ assert.strictEqual(doc.service?.schemas, originalSchemas);
105
+ assert.strictEqual(Object.keys(doc.service?.schemas || {}).length, 3);
106
+ // Result document should be different object
107
+ assert.notStrictEqual(result.document, doc);
108
+ assert.notStrictEqual(result.document.service, doc.service);
109
+ assert.notStrictEqual(result.document.service?.schemas, doc.service?.schemas);
110
+ });
111
+ it("should work with complex documents", () => {
112
+ const doc = loadExample(getExamplePath("valid", "spec", "mixed-complete.json"));
113
+ const result = optimizeDocument(JSON.parse(JSON.stringify(doc)));
114
+ assert.ok(result.document);
115
+ assert.ok(result.statistics);
116
+ assert.ok(Array.isArray(result.changes));
117
+ // mixed-complete.json has 2 schemas that are referenced
118
+ assert.strictEqual(result.statistics.originalSchemas, 2);
119
+ assert.strictEqual(result.statistics.optimizedSchemas, 2);
120
+ assert.strictEqual(result.statistics.removedSchemas, 0);
121
+ assert.strictEqual(result.statistics.deduplicatedSchemas, 0);
122
+ });
123
+ it("should handle all optimizations together", () => {
124
+ // Create a simpler document with all optimization opportunities
125
+ const doc = createDocumentWithDuplicateSchemas();
126
+ // Add one orphaned schema
127
+ if (doc.service?.schemas) {
128
+ doc.service.schemas.OrphanedSchema = {
129
+ description: "Orphaned schema",
130
+ jsonSchema: { type: "string" },
131
+ };
132
+ }
133
+ // Add long reference
134
+ if (doc.service?.protocols?.rest?.routes?.[0]) {
135
+ doc.service.protocols.rest.routes[0].responses = {
136
+ "200": {
137
+ description: "Success",
138
+ content: {
139
+ "application/json": {
140
+ schemaRef: "#/service/schemas/Person",
141
+ },
142
+ },
143
+ },
144
+ };
145
+ }
146
+ const result = optimizeDocument(doc, {
147
+ dedupeSchemas: true,
148
+ sortSchemas: true,
149
+ removeUnusedSchemas: true,
150
+ compressReferences: true,
151
+ });
152
+ assert.strictEqual(result.statistics.originalSchemas, 4);
153
+ assert.strictEqual(result.statistics.optimizedSchemas, 1); // Person (Profile is orphaned)
154
+ assert.strictEqual(result.statistics.removedSchemas, 3); // User + Profile + OrphanedSchema
155
+ assert.strictEqual(result.statistics.deduplicatedSchemas, 1); // User removed as duplicate
156
+ // Should have multiple changes
157
+ assert.ok(result.changes.length >= 2);
158
+ // Check final state
159
+ const finalSchemaNames = Object.keys(result.document.service?.schemas || {});
160
+ assert.deepStrictEqual(finalSchemaNames, ["Person"]); // Only Person remains
161
+ // Check reference was compressed
162
+ const route = result.document.service?.protocols?.rest?.routes?.[0];
163
+ const schemaRef = route?.responses?.["200"]?.content?.["application/json"]?.schemaRef;
164
+ assert.strictEqual(schemaRef, "Person");
165
+ });
166
+ it("should handle empty options", () => {
167
+ const doc = createDocumentWithDuplicateSchemas();
168
+ const result = optimizeDocument(doc, {});
169
+ // Should apply default optimizations (all enabled except removeUnusedSchemas)
170
+ assert.ok(result.statistics.removedSchemas >= 1); // At least deduplication
171
+ });
172
+ it("should preserve document structure", () => {
173
+ const doc = loadExample(getExamplePath("valid", "spec", "mixed-complete.json"));
174
+ const result = optimizeDocument(JSON.parse(JSON.stringify(doc)));
175
+ // Should preserve all required fields
176
+ assert.strictEqual(result.document.unispecVersion, doc.unispecVersion);
177
+ assert.strictEqual(result.document.service?.name, doc.service?.name);
178
+ assert.strictEqual(result.document.service?.description, doc.service?.description);
179
+ assert.ok(result.document.service?.protocols);
180
+ assert.ok(result.document.service?.schemas);
181
+ });
182
+ });
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,189 @@
1
+ import assert from "node:assert";
2
+ import { describe, it } from "node:test";
3
+ import { removeOrphanedSchemas } from "../../src/optimizer/optimization-functions.js";
4
+ import { createDocumentWithOrphanedSchemas, getExamplePath, loadExample, } from "./utils.js";
5
+ describe("removeOrphanedSchemas", () => {
6
+ it("should remove orphaned schemas", () => {
7
+ const doc = createDocumentWithOrphanedSchemas();
8
+ const removed = removeOrphanedSchemas(doc);
9
+ assert.strictEqual(removed.length, 2);
10
+ assert.ok(removed.includes("OrphanedSchema1"));
11
+ assert.ok(removed.includes("OrphanedSchema2"));
12
+ // Check that only used schema remains
13
+ const remainingSchemas = Object.keys(doc.service?.schemas || {});
14
+ assert.strictEqual(remainingSchemas.length, 1);
15
+ assert.ok(remainingSchemas.includes("UsedSchema"));
16
+ });
17
+ it("should keep all schemas when all are referenced", () => {
18
+ const doc = createDocumentWithOrphanedSchemas();
19
+ // Add references to all schemas
20
+ if (doc.service?.protocols?.rest?.routes?.[0]) {
21
+ doc.service.protocols.rest.routes[0].responses = {
22
+ "200": {
23
+ description: "Success",
24
+ content: {
25
+ "application/json": {
26
+ schemaRef: "UsedSchema",
27
+ },
28
+ },
29
+ },
30
+ "201": {
31
+ description: "Created",
32
+ content: {
33
+ "application/json": {
34
+ schemaRef: "OrphanedSchema1",
35
+ },
36
+ },
37
+ },
38
+ "202": {
39
+ description: "Accepted",
40
+ content: {
41
+ "application/json": {
42
+ schemaRef: "OrphanedSchema2",
43
+ },
44
+ },
45
+ },
46
+ };
47
+ }
48
+ const removed = removeOrphanedSchemas(doc);
49
+ assert.strictEqual(removed.length, 0);
50
+ assert.strictEqual(Object.keys(doc.service?.schemas || {}).length, 3);
51
+ });
52
+ it("should handle documents without schemas", () => {
53
+ const doc = { unispecVersion: "1.0.0", service: { name: "test" } };
54
+ const removed = removeOrphanedSchemas(doc);
55
+ assert.strictEqual(removed.length, 0);
56
+ });
57
+ it("should handle documents without service", () => {
58
+ const doc = { unispecVersion: "1.0.0", service: { name: "test" } };
59
+ const removed = removeOrphanedSchemas(doc);
60
+ assert.strictEqual(removed.length, 0);
61
+ });
62
+ it("should handle empty schemas object", () => {
63
+ const doc = {
64
+ unispecVersion: "1.0.0",
65
+ service: { name: "test", schemas: {} },
66
+ };
67
+ const removed = removeOrphanedSchemas(doc);
68
+ assert.strictEqual(removed.length, 0);
69
+ });
70
+ it("should find references in nested structures", () => {
71
+ const doc = {
72
+ unispecVersion: "1.0.0",
73
+ service: {
74
+ name: "test",
75
+ schemas: {
76
+ ReferencedSchema: {
77
+ name: "ReferencedSchema",
78
+ description: "Referenced schema",
79
+ jsonSchema: { type: "string" },
80
+ },
81
+ UnreferencedSchema: {
82
+ name: "UnreferencedSchema",
83
+ description: "Unreferenced schema",
84
+ jsonSchema: { type: "number" },
85
+ },
86
+ },
87
+ protocols: {
88
+ rest: {
89
+ routes: [
90
+ {
91
+ name: "testRoute",
92
+ method: "GET",
93
+ path: "/test",
94
+ responses: {
95
+ "200": {
96
+ description: "Success",
97
+ content: {
98
+ "application/json": {
99
+ schemaRef: "ReferencedSchema",
100
+ },
101
+ },
102
+ },
103
+ },
104
+ },
105
+ ],
106
+ },
107
+ graphql: {
108
+ queries: [
109
+ {
110
+ name: "testQuery",
111
+ returnType: "ReferencedSchema",
112
+ },
113
+ ],
114
+ },
115
+ websocket: {
116
+ channels: [
117
+ {
118
+ name: "testChannel",
119
+ messages: [
120
+ {
121
+ name: "testMessage",
122
+ schemaRef: "ReferencedSchema",
123
+ },
124
+ ],
125
+ },
126
+ ],
127
+ },
128
+ },
129
+ },
130
+ };
131
+ const removed = removeOrphanedSchemas(doc);
132
+ assert.strictEqual(removed.length, 1);
133
+ assert.ok(removed.includes("UnreferencedSchema"));
134
+ assert.ok(doc.service?.schemas?.ReferencedSchema);
135
+ assert.ok(!doc.service?.schemas?.UnreferencedSchema);
136
+ });
137
+ it("should work with real examples from node_modules", () => {
138
+ const doc = loadExample(getExamplePath("valid", "spec", "mixed-complete.json"));
139
+ const removed = removeOrphanedSchemas(JSON.parse(JSON.stringify(doc)));
140
+ // mixed-complete.json has schemas that are referenced, so nothing should be removed
141
+ assert.strictEqual(removed.length, 0);
142
+ });
143
+ it("should handle schema references in different formats", () => {
144
+ const doc = {
145
+ unispecVersion: "1.0.0",
146
+ service: {
147
+ name: "test",
148
+ schemas: {
149
+ TestSchema: {
150
+ name: "TestSchema",
151
+ description: "Test schema",
152
+ jsonSchema: { type: "string" },
153
+ },
154
+ OrphanedSchema: {
155
+ name: "OrphanedSchema",
156
+ description: "Orphaned schema",
157
+ jsonSchema: { type: "number" },
158
+ },
159
+ },
160
+ protocols: {
161
+ rest: {
162
+ routes: [
163
+ {
164
+ name: "testRoute",
165
+ method: "GET",
166
+ path: "/test",
167
+ responses: {
168
+ "200": {
169
+ description: "Success",
170
+ content: {
171
+ "application/json": {
172
+ schemaRef: "#/service/schemas/TestSchema", // JSON Pointer format
173
+ },
174
+ },
175
+ },
176
+ },
177
+ },
178
+ ],
179
+ },
180
+ },
181
+ },
182
+ };
183
+ const removed = removeOrphanedSchemas(doc);
184
+ assert.strictEqual(removed.length, 1);
185
+ assert.ok(removed.includes("OrphanedSchema"));
186
+ assert.ok(doc.service?.schemas?.TestSchema);
187
+ assert.ok(!doc.service?.schemas?.OrphanedSchema);
188
+ });
189
+ });
@@ -0,0 +1 @@
1
+ export {};