adorn-api 1.0.23 → 1.0.24

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 (382) hide show
  1. package/.eslintignore +3 -0
  2. package/.eslintrc.cjs +30 -0
  3. package/README.md +375 -531
  4. package/dist/core/express-adapter.d.ts +27 -0
  5. package/dist/core/express-adapter.d.ts.map +1 -0
  6. package/dist/core/express-adapter.js +146 -0
  7. package/dist/core/express-adapter.js.map +1 -0
  8. package/dist/core/http-error.d.ts +7 -0
  9. package/dist/core/http-error.d.ts.map +1 -0
  10. package/dist/core/http-error.js +14 -0
  11. package/dist/core/http-error.js.map +1 -0
  12. package/dist/decorators/controller.decorator.d.ts +2 -0
  13. package/dist/decorators/controller.decorator.d.ts.map +1 -0
  14. package/dist/decorators/controller.decorator.js +26 -0
  15. package/dist/decorators/controller.decorator.js.map +1 -0
  16. package/dist/decorators/create.decorator.d.ts +8 -0
  17. package/dist/decorators/create.decorator.d.ts.map +1 -0
  18. package/dist/decorators/create.decorator.js +67 -0
  19. package/dist/decorators/create.decorator.js.map +1 -0
  20. package/dist/decorators/http-method.decorator.d.ts +16 -0
  21. package/dist/decorators/http-method.decorator.d.ts.map +1 -0
  22. package/dist/decorators/http-method.decorator.js +117 -0
  23. package/dist/decorators/http-method.decorator.js.map +1 -0
  24. package/dist/decorators/http-params.d.ts +17 -0
  25. package/dist/decorators/http-params.d.ts.map +1 -0
  26. package/dist/decorators/http-params.js +26 -0
  27. package/dist/decorators/http-params.js.map +1 -0
  28. package/dist/decorators/index.d.ts +10 -5
  29. package/dist/decorators/index.d.ts.map +1 -1
  30. package/dist/decorators/index.js +14 -0
  31. package/dist/decorators/index.js.map +1 -0
  32. package/dist/decorators/list.decorator.d.ts +18 -0
  33. package/dist/decorators/list.decorator.d.ts.map +1 -0
  34. package/dist/decorators/list.decorator.js +99 -0
  35. package/dist/decorators/list.decorator.js.map +1 -0
  36. package/dist/decorators/middleware.decorator.d.ts +4 -0
  37. package/dist/decorators/middleware.decorator.d.ts.map +1 -0
  38. package/dist/decorators/middleware.decorator.js +34 -0
  39. package/dist/decorators/middleware.decorator.js.map +1 -0
  40. package/dist/decorators/response.decorator.d.ts +8 -0
  41. package/dist/decorators/response.decorator.d.ts.map +1 -0
  42. package/dist/decorators/response.decorator.js +44 -0
  43. package/dist/decorators/response.decorator.js.map +1 -0
  44. package/dist/decorators/route-options.d.ts +14 -0
  45. package/dist/decorators/route-options.d.ts.map +1 -0
  46. package/dist/decorators/route-options.js +22 -0
  47. package/dist/decorators/route-options.js.map +1 -0
  48. package/dist/decorators/schema.decorator.d.ts +82 -0
  49. package/dist/decorators/schema.decorator.d.ts.map +1 -0
  50. package/dist/decorators/schema.decorator.js +123 -0
  51. package/dist/decorators/schema.decorator.js.map +1 -0
  52. package/dist/decorators/update.decorator.d.ts +8 -0
  53. package/dist/decorators/update.decorator.d.ts.map +1 -0
  54. package/dist/decorators/update.decorator.js +63 -0
  55. package/dist/decorators/update.decorator.js.map +1 -0
  56. package/dist/index.d.ts +11 -13
  57. package/dist/index.d.ts.map +1 -1
  58. package/dist/index.js +18 -637
  59. package/dist/index.js.map +1 -1
  60. package/dist/metadata/metadata-storage.d.ts +38 -0
  61. package/dist/metadata/metadata-storage.d.ts.map +1 -0
  62. package/dist/metadata/metadata-storage.js +102 -0
  63. package/dist/metadata/metadata-storage.js.map +1 -0
  64. package/dist/metal-orm-integration/dto-helper.d.ts +5 -0
  65. package/dist/metal-orm-integration/dto-helper.d.ts.map +1 -0
  66. package/dist/metal-orm-integration/dto-helper.js +48 -0
  67. package/dist/metal-orm-integration/dto-helper.js.map +1 -0
  68. package/dist/metal-orm-integration/dto-response.decorator.d.ts +4 -0
  69. package/dist/metal-orm-integration/dto-response.decorator.d.ts.map +1 -0
  70. package/dist/metal-orm-integration/dto-response.decorator.js +69 -0
  71. package/dist/metal-orm-integration/dto-response.decorator.js.map +1 -0
  72. package/dist/metal-orm-integration/entity-schema-builder.d.ts +20 -0
  73. package/dist/metal-orm-integration/entity-schema-builder.d.ts.map +1 -0
  74. package/dist/metal-orm-integration/entity-schema-builder.js +356 -0
  75. package/dist/metal-orm-integration/entity-schema-builder.js.map +1 -0
  76. package/dist/metal-orm-integration/index.d.ts +5 -0
  77. package/dist/metal-orm-integration/index.d.ts.map +1 -0
  78. package/dist/metal-orm-integration/index.js +5 -0
  79. package/dist/metal-orm-integration/index.js.map +1 -0
  80. package/dist/metal-orm-integration/schema-modifier.d.ts +11 -0
  81. package/dist/metal-orm-integration/schema-modifier.d.ts.map +1 -0
  82. package/dist/metal-orm-integration/schema-modifier.js +62 -0
  83. package/dist/metal-orm-integration/schema-modifier.js.map +1 -0
  84. package/dist/openapi/index.d.ts +4 -0
  85. package/dist/openapi/index.d.ts.map +1 -0
  86. package/dist/openapi/index.js +4 -0
  87. package/dist/openapi/index.js.map +1 -0
  88. package/dist/openapi/openapi-generator.d.ts +22 -0
  89. package/dist/openapi/openapi-generator.d.ts.map +1 -0
  90. package/dist/openapi/openapi-generator.js +428 -0
  91. package/dist/openapi/openapi-generator.js.map +1 -0
  92. package/dist/openapi/swagger-ui.d.ts +11 -0
  93. package/dist/openapi/swagger-ui.d.ts.map +1 -0
  94. package/dist/openapi/swagger-ui.js +20 -0
  95. package/dist/openapi/swagger-ui.js.map +1 -0
  96. package/dist/openapi/zod-to-openapi.d.ts +4 -0
  97. package/dist/openapi/zod-to-openapi.d.ts.map +1 -0
  98. package/dist/openapi/zod-to-openapi.js +184 -0
  99. package/dist/openapi/zod-to-openapi.js.map +1 -0
  100. package/dist/types/common.d.ts +4 -0
  101. package/dist/types/common.d.ts.map +1 -0
  102. package/dist/types/common.js +2 -0
  103. package/dist/types/common.js.map +1 -0
  104. package/dist/types/controller.d.ts +14 -0
  105. package/dist/types/controller.d.ts.map +1 -0
  106. package/dist/types/controller.js +2 -0
  107. package/dist/types/controller.js.map +1 -0
  108. package/dist/types/metadata.d.ts +48 -0
  109. package/dist/types/metadata.d.ts.map +1 -0
  110. package/dist/types/metadata.js +2 -0
  111. package/dist/types/metadata.js.map +1 -0
  112. package/dist/types/openapi.d.ts +30 -0
  113. package/dist/types/openapi.d.ts.map +1 -0
  114. package/dist/types/openapi.js +2 -0
  115. package/dist/types/openapi.js.map +1 -0
  116. package/dist/validation/zod-adapter.d.ts +15 -0
  117. package/dist/validation/zod-adapter.d.ts.map +1 -0
  118. package/dist/validation/zod-adapter.js +61 -0
  119. package/dist/validation/zod-adapter.js.map +1 -0
  120. package/examples/basic/app.ts +15 -0
  121. package/examples/basic/index.ts +6 -0
  122. package/examples/basic/user.controller.ts +35 -0
  123. package/examples/basic/user.dtos.ts +23 -0
  124. package/examples/metal-orm-sqlite/app.ts +18 -0
  125. package/examples/metal-orm-sqlite/db.ts +90 -0
  126. package/examples/metal-orm-sqlite/index.ts +6 -0
  127. package/examples/metal-orm-sqlite/post.controller.ts +209 -0
  128. package/examples/metal-orm-sqlite/post.dtos.ts +78 -0
  129. package/examples/metal-orm-sqlite/post.entity.ts +24 -0
  130. package/examples/metal-orm-sqlite/user.controller.helpers.ts +305 -0
  131. package/examples/metal-orm-sqlite/user.controller.ts +231 -0
  132. package/examples/metal-orm-sqlite/user.dtos.ts +88 -0
  133. package/examples/metal-orm-sqlite/user.entity.ts +21 -0
  134. package/examples/metal-orm-sqlite-music/album.controller.ts +278 -0
  135. package/examples/metal-orm-sqlite-music/album.dtos.ts +85 -0
  136. package/examples/metal-orm-sqlite-music/album.entity.ts +28 -0
  137. package/examples/metal-orm-sqlite-music/app.ts +19 -0
  138. package/examples/metal-orm-sqlite-music/artist.controller.ts +272 -0
  139. package/examples/metal-orm-sqlite-music/artist.dtos.ts +68 -0
  140. package/examples/metal-orm-sqlite-music/artist.entity.ts +27 -0
  141. package/examples/metal-orm-sqlite-music/db.ts +148 -0
  142. package/examples/metal-orm-sqlite-music/index.ts +6 -0
  143. package/examples/metal-orm-sqlite-music/track.controller.ts +221 -0
  144. package/examples/metal-orm-sqlite-music/track.dtos.ts +82 -0
  145. package/examples/metal-orm-sqlite-music/track.entity.ts +27 -0
  146. package/examples/openapi/health.controller.ts +11 -0
  147. package/examples/openapi/health.dto.ts +7 -0
  148. package/examples/openapi/index.ts +12 -0
  149. package/examples/restful/app.ts +15 -0
  150. package/examples/restful/index.ts +9 -0
  151. package/examples/restful/task.controller.ts +118 -0
  152. package/examples/restful/task.dtos.ts +66 -0
  153. package/examples/restful/task.store.ts +95 -0
  154. package/examples/tsconfig.json +8 -0
  155. package/examples/utils/start-server.ts +56 -0
  156. package/package.json +33 -97
  157. package/scripts/run-example.js +29 -0
  158. package/src/adapter/express.ts +589 -0
  159. package/src/adapter/metal-orm/convention-overrides.ts +115 -0
  160. package/src/adapter/metal-orm/crud-dtos.ts +141 -0
  161. package/src/adapter/metal-orm/dto.ts +20 -0
  162. package/src/adapter/metal-orm/error-dtos.ts +51 -0
  163. package/src/adapter/metal-orm/field-builder.ts +185 -0
  164. package/src/adapter/metal-orm/filters.ts +52 -0
  165. package/src/adapter/metal-orm/index.ts +66 -0
  166. package/src/adapter/metal-orm/paged-dtos.ts +94 -0
  167. package/src/adapter/metal-orm/pagination.ts +28 -0
  168. package/src/adapter/metal-orm/types.ts +250 -0
  169. package/src/adapter/metal-orm/utils.ts +36 -0
  170. package/src/adapter/metal-orm.test.ts +439 -0
  171. package/src/core/__tests__/coerce.test.ts +39 -0
  172. package/src/core/__tests__/dto-compose.test.ts +68 -0
  173. package/src/core/__tests__/schema-builder.test.ts +82 -0
  174. package/src/core/coerce.ts +190 -0
  175. package/src/core/decorators.ts +645 -0
  176. package/src/core/errors.ts +55 -0
  177. package/src/core/metadata.ts +110 -0
  178. package/src/core/openapi.ts +282 -0
  179. package/src/core/schema-builder.ts +287 -0
  180. package/src/core/schema.ts +400 -0
  181. package/src/core/types.ts +14 -0
  182. package/src/e2e/http-error.e2e.test.ts +52 -0
  183. package/src/e2e/sqlite-metal-orm.e2e.test.ts +174 -0
  184. package/src/e2e/sqlite.e2e.test.ts +126 -0
  185. package/src/index.ts +8 -0
  186. package/tsconfig.eslint.json +7 -0
  187. package/tsconfig.json +18 -0
  188. package/vitest.config.ts +8 -0
  189. package/dist/adapter/express/auth.d.ts +0 -13
  190. package/dist/adapter/express/auth.d.ts.map +0 -1
  191. package/dist/adapter/express/bootstrap.d.ts +0 -40
  192. package/dist/adapter/express/bootstrap.d.ts.map +0 -1
  193. package/dist/adapter/express/coercion.d.ts +0 -102
  194. package/dist/adapter/express/coercion.d.ts.map +0 -1
  195. package/dist/adapter/express/index.d.ts +0 -6
  196. package/dist/adapter/express/index.d.ts.map +0 -1
  197. package/dist/adapter/express/merge.d.ts +0 -45
  198. package/dist/adapter/express/merge.d.ts.map +0 -1
  199. package/dist/adapter/express/openapi.d.ts +0 -66
  200. package/dist/adapter/express/openapi.d.ts.map +0 -1
  201. package/dist/adapter/express/router.d.ts +0 -10
  202. package/dist/adapter/express/router.d.ts.map +0 -1
  203. package/dist/adapter/express/swagger.d.ts +0 -18
  204. package/dist/adapter/express/swagger.d.ts.map +0 -1
  205. package/dist/adapter/express/types.d.ts +0 -110
  206. package/dist/adapter/express/types.d.ts.map +0 -1
  207. package/dist/adapter/express/validation.d.ts +0 -27
  208. package/dist/adapter/express/validation.d.ts.map +0 -1
  209. package/dist/cli/progress.d.ts +0 -124
  210. package/dist/cli/progress.d.ts.map +0 -1
  211. package/dist/cli.cjs +0 -4622
  212. package/dist/cli.cjs.map +0 -1
  213. package/dist/cli.d.ts +0 -3
  214. package/dist/cli.d.ts.map +0 -1
  215. package/dist/cli.js +0 -4603
  216. package/dist/cli.js.map +0 -1
  217. package/dist/compiler/analyze/index.d.ts +0 -5
  218. package/dist/compiler/analyze/index.d.ts.map +0 -1
  219. package/dist/compiler/analyze/scanControllers.d.ts +0 -88
  220. package/dist/compiler/analyze/scanControllers.d.ts.map +0 -1
  221. package/dist/compiler/cache/isStale.d.ts +0 -46
  222. package/dist/compiler/cache/isStale.d.ts.map +0 -1
  223. package/dist/compiler/cache/loadArtifacts.d.ts +0 -149
  224. package/dist/compiler/cache/loadArtifacts.d.ts.map +0 -1
  225. package/dist/compiler/cache/schema.d.ts +0 -32
  226. package/dist/compiler/cache/schema.d.ts.map +0 -1
  227. package/dist/compiler/cache/writeCache.d.ts +0 -14
  228. package/dist/compiler/cache/writeCache.d.ts.map +0 -1
  229. package/dist/compiler/gems.d.ts +0 -75
  230. package/dist/compiler/gems.d.ts.map +0 -1
  231. package/dist/compiler/generator/index.d.ts +0 -7
  232. package/dist/compiler/generator/index.d.ts.map +0 -1
  233. package/dist/compiler/generator/manifest.d.ts +0 -23
  234. package/dist/compiler/generator/manifest.d.ts.map +0 -1
  235. package/dist/compiler/generator/openapi.d.ts +0 -118
  236. package/dist/compiler/generator/openapi.d.ts.map +0 -1
  237. package/dist/compiler/graph/builder.d.ts +0 -24
  238. package/dist/compiler/graph/builder.d.ts.map +0 -1
  239. package/dist/compiler/graph/index.d.ts +0 -7
  240. package/dist/compiler/graph/index.d.ts.map +0 -1
  241. package/dist/compiler/graph/schemaGraph.d.ts +0 -67
  242. package/dist/compiler/graph/schemaGraph.d.ts.map +0 -1
  243. package/dist/compiler/graph/types.d.ts +0 -203
  244. package/dist/compiler/graph/types.d.ts.map +0 -1
  245. package/dist/compiler/index.d.ts +0 -12
  246. package/dist/compiler/index.d.ts.map +0 -1
  247. package/dist/compiler/ir/index.d.ts +0 -7
  248. package/dist/compiler/ir/index.d.ts.map +0 -1
  249. package/dist/compiler/ir/pipeline.d.ts +0 -82
  250. package/dist/compiler/ir/pipeline.d.ts.map +0 -1
  251. package/dist/compiler/ir/stages.d.ts +0 -40
  252. package/dist/compiler/ir/stages.d.ts.map +0 -1
  253. package/dist/compiler/ir/visitor.d.ts +0 -98
  254. package/dist/compiler/ir/visitor.d.ts.map +0 -1
  255. package/dist/compiler/manifest/emit.d.ts +0 -21
  256. package/dist/compiler/manifest/emit.d.ts.map +0 -1
  257. package/dist/compiler/manifest/format.d.ts +0 -119
  258. package/dist/compiler/manifest/format.d.ts.map +0 -1
  259. package/dist/compiler/manifest/index.d.ts +0 -6
  260. package/dist/compiler/manifest/index.d.ts.map +0 -1
  261. package/dist/compiler/runner/createProgram.d.ts +0 -24
  262. package/dist/compiler/runner/createProgram.d.ts.map +0 -1
  263. package/dist/compiler/runner/index.d.ts +0 -5
  264. package/dist/compiler/runner/index.d.ts.map +0 -1
  265. package/dist/compiler/schema/extractAnnotations.d.ts +0 -57
  266. package/dist/compiler/schema/extractAnnotations.d.ts.map +0 -1
  267. package/dist/compiler/schema/index.d.ts +0 -10
  268. package/dist/compiler/schema/index.d.ts.map +0 -1
  269. package/dist/compiler/schema/intersectionHandler.d.ts +0 -44
  270. package/dist/compiler/schema/intersectionHandler.d.ts.map +0 -1
  271. package/dist/compiler/schema/objectHandler.d.ts +0 -146
  272. package/dist/compiler/schema/objectHandler.d.ts.map +0 -1
  273. package/dist/compiler/schema/openapi.d.ts +0 -71
  274. package/dist/compiler/schema/openapi.d.ts.map +0 -1
  275. package/dist/compiler/schema/parameters.d.ts +0 -90
  276. package/dist/compiler/schema/parameters.d.ts.map +0 -1
  277. package/dist/compiler/schema/partitioner.d.ts +0 -85
  278. package/dist/compiler/schema/partitioner.d.ts.map +0 -1
  279. package/dist/compiler/schema/primitives.d.ts +0 -68
  280. package/dist/compiler/schema/primitives.d.ts.map +0 -1
  281. package/dist/compiler/schema/queryBuilderAnalyzer.d.ts +0 -95
  282. package/dist/compiler/schema/queryBuilderAnalyzer.d.ts.map +0 -1
  283. package/dist/compiler/schema/queryBuilderSchemaBuilder.d.ts +0 -13
  284. package/dist/compiler/schema/queryBuilderSchemaBuilder.d.ts.map +0 -1
  285. package/dist/compiler/schema/serviceCallAnalyzer.d.ts +0 -102
  286. package/dist/compiler/schema/serviceCallAnalyzer.d.ts.map +0 -1
  287. package/dist/compiler/schema/splitOpenapi.d.ts +0 -46
  288. package/dist/compiler/schema/splitOpenapi.d.ts.map +0 -1
  289. package/dist/compiler/schema/typeToJsonSchema.d.ts +0 -26
  290. package/dist/compiler/schema/typeToJsonSchema.d.ts.map +0 -1
  291. package/dist/compiler/schema/types.d.ts +0 -70
  292. package/dist/compiler/schema/types.d.ts.map +0 -1
  293. package/dist/compiler/schema/unionHandler.d.ts +0 -70
  294. package/dist/compiler/schema/unionHandler.d.ts.map +0 -1
  295. package/dist/compiler/transform/dedup.d.ts +0 -35
  296. package/dist/compiler/transform/dedup.d.ts.map +0 -1
  297. package/dist/compiler/transform/flatten.d.ts +0 -50
  298. package/dist/compiler/transform/flatten.d.ts.map +0 -1
  299. package/dist/compiler/transform/index.d.ts +0 -7
  300. package/dist/compiler/transform/index.d.ts.map +0 -1
  301. package/dist/compiler/transform/inline.d.ts +0 -46
  302. package/dist/compiler/transform/inline.d.ts.map +0 -1
  303. package/dist/compiler/validation/emitPrecompiledValidators.d.ts +0 -62
  304. package/dist/compiler/validation/emitPrecompiledValidators.d.ts.map +0 -1
  305. package/dist/compiler/validation/index.d.ts +0 -5
  306. package/dist/compiler/validation/index.d.ts.map +0 -1
  307. package/dist/decorators/Auth.d.ts +0 -22
  308. package/dist/decorators/Auth.d.ts.map +0 -1
  309. package/dist/decorators/Controller.d.ts +0 -17
  310. package/dist/decorators/Controller.d.ts.map +0 -1
  311. package/dist/decorators/Public.d.ts +0 -15
  312. package/dist/decorators/Public.d.ts.map +0 -1
  313. package/dist/decorators/Use.d.ts +0 -23
  314. package/dist/decorators/Use.d.ts.map +0 -1
  315. package/dist/decorators/methods.d.ts +0 -26
  316. package/dist/decorators/methods.d.ts.map +0 -1
  317. package/dist/express.cjs +0 -1186
  318. package/dist/express.cjs.map +0 -1
  319. package/dist/express.d.ts +0 -8
  320. package/dist/express.d.ts.map +0 -1
  321. package/dist/express.js +0 -1150
  322. package/dist/express.js.map +0 -1
  323. package/dist/http.d.ts +0 -33
  324. package/dist/http.d.ts.map +0 -1
  325. package/dist/index.cjs +0 -724
  326. package/dist/index.cjs.map +0 -1
  327. package/dist/metal/applyListQuery.d.ts +0 -100
  328. package/dist/metal/applyListQuery.d.ts.map +0 -1
  329. package/dist/metal/index.cjs +0 -278
  330. package/dist/metal/index.cjs.map +0 -1
  331. package/dist/metal/index.d.ts +0 -15
  332. package/dist/metal/index.d.ts.map +0 -1
  333. package/dist/metal/index.js +0 -243
  334. package/dist/metal/index.js.map +0 -1
  335. package/dist/metal/listQuery.d.ts +0 -26
  336. package/dist/metal/listQuery.d.ts.map +0 -1
  337. package/dist/metal/queryOptions.d.ts +0 -16
  338. package/dist/metal/queryOptions.d.ts.map +0 -1
  339. package/dist/metal/readMetalBag.d.ts +0 -69
  340. package/dist/metal/readMetalBag.d.ts.map +0 -1
  341. package/dist/metal/registerMetalEntities.d.ts +0 -26
  342. package/dist/metal/registerMetalEntities.d.ts.map +0 -1
  343. package/dist/metal/schemaFromEntity.d.ts +0 -41
  344. package/dist/metal/schemaFromEntity.d.ts.map +0 -1
  345. package/dist/metal/searchWhere.d.ts +0 -97
  346. package/dist/metal/searchWhere.d.ts.map +0 -1
  347. package/dist/metal/symbolMetadata.d.ts +0 -8
  348. package/dist/metal/symbolMetadata.d.ts.map +0 -1
  349. package/dist/runtime/auth/runtime.d.ts +0 -183
  350. package/dist/runtime/auth/runtime.d.ts.map +0 -1
  351. package/dist/runtime/metadata/bucket.d.ts +0 -2
  352. package/dist/runtime/metadata/bucket.d.ts.map +0 -1
  353. package/dist/runtime/metadata/key.d.ts +0 -2
  354. package/dist/runtime/metadata/key.d.ts.map +0 -1
  355. package/dist/runtime/metadata/read.d.ts +0 -2
  356. package/dist/runtime/metadata/read.d.ts.map +0 -1
  357. package/dist/runtime/metadata/types.d.ts +0 -95
  358. package/dist/runtime/metadata/types.d.ts.map +0 -1
  359. package/dist/runtime/polyfill.d.ts +0 -2
  360. package/dist/runtime/polyfill.d.ts.map +0 -1
  361. package/dist/runtime/upload.d.ts +0 -44
  362. package/dist/runtime/upload.d.ts.map +0 -1
  363. package/dist/runtime/validation/ajv.d.ts +0 -120
  364. package/dist/runtime/validation/ajv.d.ts.map +0 -1
  365. package/dist/runtime/validation/index.d.ts +0 -11
  366. package/dist/runtime/validation/index.d.ts.map +0 -1
  367. package/dist/schema/decorators.d.ts +0 -37
  368. package/dist/schema/decorators.d.ts.map +0 -1
  369. package/dist/schema/index.cjs +0 -214
  370. package/dist/schema/index.cjs.map +0 -1
  371. package/dist/schema/index.d.ts +0 -2
  372. package/dist/schema/index.d.ts.map +0 -1
  373. package/dist/schema/index.js +0 -163
  374. package/dist/schema/index.js.map +0 -1
  375. package/dist/scripts/adorn-example.cjs +0 -404
  376. package/dist/scripts/adorn-example.cjs.map +0 -1
  377. package/dist/utils/operationId.d.ts +0 -2
  378. package/dist/utils/operationId.d.ts.map +0 -1
  379. package/dist/utils/path.d.ts +0 -2
  380. package/dist/utils/path.d.ts.map +0 -1
  381. package/dist/utils/port.d.ts +0 -9
  382. package/dist/utils/port.d.ts.map +0 -1
package/dist/cli.cjs DELETED
@@ -1,4622 +0,0 @@
1
- #!/usr/bin/env node
2
- "use strict";
3
- var __create = Object.create;
4
- var __defProp = Object.defineProperty;
5
- var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
6
- var __getOwnPropNames = Object.getOwnPropertyNames;
7
- var __getProtoOf = Object.getPrototypeOf;
8
- var __hasOwnProp = Object.prototype.hasOwnProperty;
9
- var __copyProps = (to, from, except, desc) => {
10
- if (from && typeof from === "object" || typeof from === "function") {
11
- for (let key of __getOwnPropNames(from))
12
- if (!__hasOwnProp.call(to, key) && key !== except)
13
- __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
14
- }
15
- return to;
16
- };
17
- var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
18
- // If the importer is in node compatibility mode or this is not an ESM
19
- // file that has been converted to a CommonJS file using a Babel-
20
- // compatible transform (i.e. "__esModule" has not been set), then set
21
- // "default" to the CommonJS "module.exports" for node compatibility.
22
- isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
23
- mod
24
- ));
25
-
26
- // src/cli.ts
27
- var import_node_fs6 = require("fs");
28
- var import_node_path6 = require("path");
29
- var import_node_url = require("url");
30
-
31
- // src/compiler/runner/createProgram.ts
32
- var import_typescript = __toESM(require("typescript"), 1);
33
- var import_node_fs = require("fs");
34
- var import_node_path = require("path");
35
- function createProgramFromConfig(tsconfigPath) {
36
- const absolutePath = (0, import_node_path.resolve)(tsconfigPath);
37
- const configDir = (0, import_node_path.dirname)(absolutePath);
38
- const configFile = import_typescript.default.readConfigFile(absolutePath, (path4) => (0, import_node_fs.readFileSync)(path4, "utf-8"));
39
- if (configFile.error) {
40
- throw new Error(`Failed to read tsconfig: ${import_typescript.default.flattenDiagnosticMessageText(configFile.error.messageText, "\n")}`);
41
- }
42
- const parsed = import_typescript.default.parseJsonConfigFileContent(configFile.config, import_typescript.default.sys, configDir);
43
- if (parsed.errors.length > 0) {
44
- const messages = parsed.errors.map((e) => import_typescript.default.flattenDiagnosticMessageText(e.messageText, "\n"));
45
- throw new Error(`Failed to parse tsconfig:
46
- ${messages.join("\n")}`);
47
- }
48
- const program = import_typescript.default.createProgram(parsed.fileNames, parsed.options);
49
- const checker = program.getTypeChecker();
50
- const sourceFiles = program.getSourceFiles().filter(
51
- (sf) => !sf.isDeclarationFile && !sf.fileName.includes("node_modules")
52
- );
53
- return { program, checker, sourceFiles };
54
- }
55
-
56
- // src/compiler/analyze/scanControllers.ts
57
- var import_typescript2 = __toESM(require("typescript"), 1);
58
-
59
- // src/utils/operationId.ts
60
- function defaultOperationId(controllerName, methodName) {
61
- return `${controllerName}_${methodName}`;
62
- }
63
-
64
- // src/compiler/analyze/scanControllers.ts
65
- function scanControllers(sourceFiles, checker) {
66
- const controllers = [];
67
- for (const sourceFile of sourceFiles) {
68
- import_typescript2.default.forEachChild(sourceFile, (node) => {
69
- if (import_typescript2.default.isClassDeclaration(node) && node.name) {
70
- const controller = analyzeClass(node, sourceFile, checker);
71
- if (controller) {
72
- controllers.push(controller);
73
- }
74
- }
75
- });
76
- }
77
- return controllers;
78
- }
79
- function analyzeClass(node, sourceFile, checker) {
80
- if (!node.name) return null;
81
- const controllerDecorator = findDecorator(node, "Controller");
82
- if (!controllerDecorator) return null;
83
- const basePath = extractDecoratorStringArg(controllerDecorator) ?? "/";
84
- const className = node.name.text;
85
- const consumes = extractClassConsumes(node, checker);
86
- const produces = extractClassProduces(node, checker);
87
- const operations = [];
88
- for (const member of node.members) {
89
- if (import_typescript2.default.isMethodDeclaration(member) && member.name) {
90
- const operation = analyzeMethod(member, className, checker);
91
- if (operation) {
92
- operations.push(operation);
93
- }
94
- }
95
- }
96
- if (operations.length === 0) return null;
97
- return {
98
- className,
99
- basePath,
100
- sourceFile,
101
- classDeclaration: node,
102
- operations,
103
- consumes,
104
- produces
105
- };
106
- }
107
- function extractClassConsumes(node, _checker) {
108
- const decorator = findDecorator(node, "Consumes");
109
- if (!decorator) return void 0;
110
- const callExpr = decorator.expression;
111
- if (!import_typescript2.default.isCallExpression(callExpr)) return void 0;
112
- const args = callExpr.arguments;
113
- if (args.length === 0) return void 0;
114
- const firstArg = args[0];
115
- if (import_typescript2.default.isStringLiteral(firstArg)) {
116
- return [firstArg.text];
117
- }
118
- if (import_typescript2.default.isArrayLiteralExpression(firstArg)) {
119
- return firstArg.elements.filter(import_typescript2.default.isStringLiteral).map((e) => e.text);
120
- }
121
- return void 0;
122
- }
123
- function extractClassProduces(node, _checker) {
124
- const decorator = findDecorator(node, "Produces");
125
- if (!decorator) return void 0;
126
- const callExpr = decorator.expression;
127
- if (!import_typescript2.default.isCallExpression(callExpr)) return void 0;
128
- const args = callExpr.arguments;
129
- if (args.length === 0) return void 0;
130
- const firstArg = args[0];
131
- if (import_typescript2.default.isStringLiteral(firstArg)) {
132
- return [firstArg.text];
133
- }
134
- if (import_typescript2.default.isArrayLiteralExpression(firstArg)) {
135
- return firstArg.elements.filter(import_typescript2.default.isStringLiteral).map((e) => e.text);
136
- }
137
- return void 0;
138
- }
139
- function analyzeMethod(node, className, checker) {
140
- const methodName = import_typescript2.default.isIdentifier(node.name) ? node.name.text : null;
141
- if (!methodName) return null;
142
- const httpMethods = ["Get", "Post", "Put", "Patch", "Delete"];
143
- let httpMethod = null;
144
- let path4 = "/";
145
- for (const method of httpMethods) {
146
- const decorator = findDecorator(node, method);
147
- if (decorator) {
148
- httpMethod = method.toUpperCase();
149
- path4 = extractDecoratorStringArg(decorator) ?? "/";
150
- break;
151
- }
152
- }
153
- if (!httpMethod) return null;
154
- const signature = checker.getSignatureFromDeclaration(node);
155
- if (!signature) return null;
156
- let returnType = checker.getReturnTypeOfSignature(signature);
157
- returnType = unwrapPromise(returnType, checker);
158
- const returnTypeNode = unwrapPromiseTypeNode(node.type);
159
- const parameters = [];
160
- for (let i = 0; i < node.parameters.length; i++) {
161
- const param = node.parameters[i];
162
- const paramName = import_typescript2.default.isIdentifier(param.name) ? param.name.text : `arg${i}`;
163
- const paramType = checker.getTypeAtLocation(param);
164
- const isOptional = !!param.questionToken || !!param.initializer;
165
- parameters.push({
166
- name: paramName,
167
- index: i,
168
- type: paramType,
169
- isOptional,
170
- paramNode: param
171
- });
172
- }
173
- const pathParamNames = extractPathParams(path4);
174
- const pathParamIndices = matchPathParamsToIndices(pathParamNames, parameters);
175
- const { bodyParamIndex, queryParamIndices, queryObjectParamIndex, headerObjectParamIndex, cookieObjectParamIndex, bodyContentType } = classifyParameters(parameters, httpMethod, pathParamIndices, checker);
176
- return {
177
- methodName,
178
- httpMethod,
179
- path: path4,
180
- operationId: defaultOperationId(className, methodName),
181
- methodDeclaration: node,
182
- returnType,
183
- returnTypeNode,
184
- parameters,
185
- pathParamIndices,
186
- bodyParamIndex,
187
- queryParamIndices,
188
- queryObjectParamIndex,
189
- headerObjectParamIndex,
190
- cookieObjectParamIndex,
191
- bodyContentType
192
- };
193
- }
194
- function extractPathParams(path4) {
195
- const matches = path4.match(/:([^/]+)/g);
196
- if (!matches) return [];
197
- return matches.map((m) => m.slice(1));
198
- }
199
- function matchPathParamsToIndices(pathParamNames, parameters) {
200
- const indices = [];
201
- for (const name of pathParamNames) {
202
- const param = parameters.find((p) => p.name === name);
203
- if (param) {
204
- indices.push(param.index);
205
- }
206
- }
207
- return indices;
208
- }
209
- function classifyParameters(parameters, httpMethod, pathParamIndices, checker) {
210
- const usedIndices = new Set(pathParamIndices);
211
- const queryParamIndices = [];
212
- let bodyParamIndex = null;
213
- let queryObjectParamIndex = null;
214
- let headerObjectParamIndex = null;
215
- let cookieObjectParamIndex = null;
216
- const isBodyMethod = ["POST", "PUT", "PATCH"].includes(httpMethod);
217
- for (let i = 0; i < parameters.length; i++) {
218
- const param = parameters[i];
219
- if (usedIndices.has(i)) continue;
220
- const nonNullableType = checker.getNonNullableType(param.type);
221
- const typeStr = getTypeName(param.type) || getTypeName(nonNullableType);
222
- if (typeStr === "Body") {
223
- bodyParamIndex = i;
224
- usedIndices.add(i);
225
- continue;
226
- }
227
- if (typeStr === "Query") {
228
- queryObjectParamIndex = i;
229
- usedIndices.add(i);
230
- continue;
231
- }
232
- if (typeStr === "Headers") {
233
- headerObjectParamIndex = i;
234
- usedIndices.add(i);
235
- continue;
236
- }
237
- if (typeStr === "Cookies") {
238
- cookieObjectParamIndex = i;
239
- usedIndices.add(i);
240
- continue;
241
- }
242
- if (isBodyMethod && bodyParamIndex === null) {
243
- bodyParamIndex = i;
244
- usedIndices.add(i);
245
- continue;
246
- }
247
- const isObj = isObjectType(nonNullableType, checker);
248
- if (isObj && queryObjectParamIndex === null && !isBodyMethod) {
249
- queryObjectParamIndex = i;
250
- usedIndices.add(i);
251
- continue;
252
- }
253
- queryParamIndices.push(i);
254
- usedIndices.add(i);
255
- }
256
- return {
257
- bodyParamIndex,
258
- queryParamIndices,
259
- queryObjectParamIndex,
260
- headerObjectParamIndex,
261
- cookieObjectParamIndex,
262
- bodyContentType: void 0
263
- };
264
- }
265
- function isObjectType(type, checker) {
266
- const objectFlags = (type.flags & import_typescript2.default.TypeFlags.Object) !== 0;
267
- const intersectionFlags = (type.flags & import_typescript2.default.TypeFlags.Intersection) !== 0;
268
- if (!objectFlags && !intersectionFlags) return false;
269
- const symbol = type.getSymbol();
270
- if (symbol?.getName() === "__object") return true;
271
- const properties = checker.getPropertiesOfType(type);
272
- if (properties.length > 0) return true;
273
- const callSigs = type.getCallSignatures?.();
274
- if (callSigs && callSigs.length > 0) return false;
275
- return true;
276
- }
277
- function getTypeName(type) {
278
- const aliasSymbol = type.aliasSymbol ?? type.aliasSymbol;
279
- if (aliasSymbol) return aliasSymbol.getName();
280
- const symbol = type.getSymbol();
281
- return symbol?.getName() ?? "";
282
- }
283
- function findDecorator(node, name) {
284
- const decorators = import_typescript2.default.getDecorators(node);
285
- if (!decorators) return null;
286
- for (const decorator of decorators) {
287
- if (import_typescript2.default.isCallExpression(decorator.expression)) {
288
- const expr = decorator.expression.expression;
289
- if (import_typescript2.default.isIdentifier(expr) && expr.text === name) {
290
- return decorator;
291
- }
292
- }
293
- }
294
- return null;
295
- }
296
- function extractDecoratorStringArg(decorator) {
297
- if (import_typescript2.default.isCallExpression(decorator.expression)) {
298
- const arg = decorator.expression.arguments[0];
299
- if (arg && import_typescript2.default.isStringLiteral(arg)) {
300
- return arg.text;
301
- }
302
- }
303
- return null;
304
- }
305
- function unwrapPromise(type, _checker) {
306
- const symbol = type.getSymbol();
307
- if (symbol?.getName() === "Promise") {
308
- const typeArgs = type.typeArguments;
309
- if (typeArgs && typeArgs.length > 0) {
310
- return typeArgs[0];
311
- }
312
- }
313
- return type;
314
- }
315
- function unwrapPromiseTypeNode(typeNode) {
316
- if (!typeNode) return void 0;
317
- if (import_typescript2.default.isTypeReferenceNode(typeNode)) {
318
- if (import_typescript2.default.isIdentifier(typeNode.typeName) && typeNode.typeName.text === "Promise") {
319
- return typeNode.typeArguments?.[0] ?? typeNode;
320
- }
321
- }
322
- return typeNode;
323
- }
324
-
325
- // src/compiler/schema/openapi.ts
326
- var import_typescript12 = __toESM(require("typescript"), 1);
327
-
328
- // src/compiler/schema/typeToJsonSchema.ts
329
- var import_typescript7 = __toESM(require("typescript"), 1);
330
-
331
- // src/compiler/schema/primitives.ts
332
- var import_typescript3 = __toESM(require("typescript"), 1);
333
- function handlePrimitiveType(type, ctx, typeNode) {
334
- const { checker, propertyName } = ctx;
335
- if (type.flags & import_typescript3.default.TypeFlags.Undefined) {
336
- return {};
337
- }
338
- if (type.flags & import_typescript3.default.TypeFlags.Null) {
339
- return { type: "null" };
340
- }
341
- if (isDateType(type, checker)) {
342
- return { type: "string", format: "date-time" };
343
- }
344
- if (type.flags & import_typescript3.default.TypeFlags.String) {
345
- return { type: "string" };
346
- }
347
- if (type.flags & import_typescript3.default.TypeFlags.Number) {
348
- return normalizeNumericType(type, checker, typeNode, propertyName);
349
- }
350
- if (type.flags & import_typescript3.default.TypeFlags.Boolean) {
351
- return { type: "boolean" };
352
- }
353
- if (type.flags & import_typescript3.default.TypeFlags.BigInt) {
354
- return {
355
- type: "string",
356
- format: "int64",
357
- pattern: "^-?\\d+$"
358
- };
359
- }
360
- if (type.flags & import_typescript3.default.TypeFlags.StringLiteral) {
361
- const value = type.value;
362
- return { type: "string", enum: [value] };
363
- }
364
- if (type.flags & import_typescript3.default.TypeFlags.NumberLiteral) {
365
- const value = type.value;
366
- return { type: "number", enum: [value] };
367
- }
368
- if (type.flags & import_typescript3.default.TypeFlags.BooleanLiteral) {
369
- const intrinsic = type.intrinsicName;
370
- return { type: "boolean", enum: [intrinsic === "true"] };
371
- }
372
- return null;
373
- }
374
- function isDateType(type, checker) {
375
- const symbol = type.getSymbol();
376
- const aliasSymbol = type.aliasSymbol;
377
- if (aliasSymbol && aliasSymbol.flags & import_typescript3.default.SymbolFlags.Alias) {
378
- const aliased = checker.getAliasedSymbol(aliasSymbol);
379
- return aliased?.getName() === "Date";
380
- }
381
- if (symbol && symbol.flags & import_typescript3.default.SymbolFlags.Alias) {
382
- const aliased = checker.getAliasedSymbol(symbol);
383
- return aliased?.getName() === "Date";
384
- }
385
- return symbol?.getName() === "Date";
386
- }
387
- function normalizeNumericType(type, checker, typeNode, propertyName) {
388
- const typeName = getExplicitTypeNameFromNode(typeNode) ?? null;
389
- const symbol = getEffectiveSymbol(type, checker);
390
- const symbolName = symbol?.getName() ?? null;
391
- if (shouldBeIntegerType(typeName) || shouldBeIntegerType(symbolName) || shouldBeIntegerType(propertyName ?? null)) {
392
- return { type: "integer" };
393
- }
394
- return { type: "number" };
395
- }
396
- function shouldBeIntegerType(typeName) {
397
- if (!typeName) return false;
398
- const lower = typeName.toLowerCase();
399
- return lower === "id" || lower.endsWith("id") || lower === "primarykey" || lower === "pk" || lower === "page" || lower === "pagesize" || lower === "totalitems" || lower === "limit" || lower === "offset";
400
- }
401
- function getExplicitTypeNameFromNode(typeNode) {
402
- if (!typeNode) return null;
403
- if (import_typescript3.default.isTypeReferenceNode(typeNode)) {
404
- if (import_typescript3.default.isIdentifier(typeNode.typeName)) {
405
- return typeNode.typeName.text;
406
- }
407
- }
408
- if (import_typescript3.default.isTypeAliasDeclaration(typeNode.parent)) {
409
- if (import_typescript3.default.isIdentifier(typeNode.parent.name)) {
410
- return typeNode.parent.name.text;
411
- }
412
- }
413
- return null;
414
- }
415
- function getEffectiveSymbol(type, checker) {
416
- const aliasSymbol = type.aliasSymbol ?? type.aliasSymbol;
417
- if (aliasSymbol && aliasSymbol.flags & import_typescript3.default.SymbolFlags.Alias) {
418
- return checker.getAliasedSymbol(aliasSymbol);
419
- }
420
- return type.getSymbol() ?? null;
421
- }
422
-
423
- // src/compiler/schema/unionHandler.ts
424
- var import_typescript4 = __toESM(require("typescript"), 1);
425
- function handleUnion(type, ctx, typeNode) {
426
- return buildNamedSchema(type, ctx, typeNode, () => {
427
- const types = type.types;
428
- const nullType = types.find((t) => t.flags & import_typescript4.default.TypeFlags.Null);
429
- const undefinedType = types.find((t) => t.flags & import_typescript4.default.TypeFlags.Undefined);
430
- const otherTypes = types.filter((t) => !(t.flags & import_typescript4.default.TypeFlags.Null) && !(t.flags & import_typescript4.default.TypeFlags.Undefined));
431
- const allStringLiterals = otherTypes.every((t) => t.flags & import_typescript4.default.TypeFlags.StringLiteral);
432
- if (allStringLiterals && otherTypes.length > 0) {
433
- const enumValues = otherTypes.map((t) => t.value);
434
- const schema = { type: "string", enum: enumValues };
435
- if (nullType) {
436
- schema.type = ["string", "null"];
437
- }
438
- return schema;
439
- }
440
- const allBooleanLiterals = otherTypes.length > 0 && otherTypes.every((t) => t.flags & import_typescript4.default.TypeFlags.BooleanLiteral);
441
- if (allBooleanLiterals) {
442
- const schema = { type: "boolean" };
443
- if (nullType || undefinedType) {
444
- schema.type = ["boolean", "null"];
445
- }
446
- return schema;
447
- }
448
- if (otherTypes.length === 1 && nullType) {
449
- const innerSchema = typeToJsonSchema(otherTypes[0], ctx);
450
- if (typeof innerSchema.type === "string") {
451
- innerSchema.type = [innerSchema.type, "null"];
452
- }
453
- return innerSchema;
454
- }
455
- if (otherTypes.length > 1) {
456
- const branches = otherTypes.map((t) => typeToJsonSchema(t, ctx));
457
- const hasNull = !!nullType;
458
- const result = {};
459
- if (hasNull) {
460
- result.anyOf = [...branches, { type: "null" }];
461
- } else {
462
- result.anyOf = branches;
463
- }
464
- const discriminatorResult = detectDiscriminatedUnion(otherTypes, ctx, branches);
465
- if (discriminatorResult) {
466
- result.oneOf = branches;
467
- result.discriminator = discriminatorResult;
468
- }
469
- return result;
470
- }
471
- if (otherTypes.length === 1) {
472
- return typeToJsonSchema(otherTypes[0], ctx);
473
- }
474
- return {};
475
- });
476
- }
477
- function detectDiscriminatedUnion(types, ctx, _branches) {
478
- if (types.length < 2) return null;
479
- const candidates = findCommonPropertyNames(ctx.checker, types);
480
- for (const propName of candidates) {
481
- const requiredInAll = types.every((t) => isRequiredProperty(ctx.checker, t, propName));
482
- if (!requiredInAll) continue;
483
- const literalSets = types.map((t) => getPropertyLiteralValues(ctx.checker, t, propName));
484
- if (literalSets.some((s) => s === null)) continue;
485
- const allSets = literalSets;
486
- if (!areSetsDisjoint(allSets)) continue;
487
- const mapping = {};
488
- for (let i = 0; i < types.length; i++) {
489
- const branchName = getBranchSchemaName(types[i], ctx);
490
- for (const val of allSets[i]) {
491
- mapping[val] = `#/components/schemas/${branchName}`;
492
- }
493
- }
494
- return { propertyName: propName, mapping };
495
- }
496
- return null;
497
- }
498
- function findCommonPropertyNames(checker, types) {
499
- if (types.length === 0) return [];
500
- const firstProps = types[0].getProperties().map((s) => s.getName());
501
- return firstProps.filter(
502
- (name) => types.every((m) => !!checker.getPropertyOfType(m, name))
503
- );
504
- }
505
- function isRequiredProperty(checker, type, propName) {
506
- const sym = checker.getPropertyOfType(type, propName);
507
- if (!sym) return false;
508
- if (sym.flags & import_typescript4.default.SymbolFlags.Optional) return false;
509
- const propType = checker.getTypeOfSymbol(sym);
510
- if (propType.isUnion?.()) {
511
- const hasUndefined = propType.types.some((t) => (t.flags & import_typescript4.default.TypeFlags.Undefined) !== 0);
512
- if (hasUndefined) return false;
513
- }
514
- return true;
515
- }
516
- function getPropertyLiteralValues(checker, type, propName) {
517
- const sym = checker.getPropertyOfType(type, propName);
518
- if (!sym) return null;
519
- const propType = checker.getTypeOfSymbol(sym);
520
- if (propType.isStringLiteral?.()) {
521
- return /* @__PURE__ */ new Set([propType.value]);
522
- }
523
- if (propType.isUnion?.()) {
524
- const values = /* @__PURE__ */ new Set();
525
- for (const m of propType.types) {
526
- if (!m.isStringLiteral?.()) return null;
527
- values.add(m.value);
528
- }
529
- return values;
530
- }
531
- return null;
532
- }
533
- function areSetsDisjoint(sets) {
534
- const seen = /* @__PURE__ */ new Set();
535
- for (const s of sets) {
536
- for (const v of s) {
537
- if (seen.has(v)) return false;
538
- seen.add(v);
539
- }
540
- }
541
- return true;
542
- }
543
- function getBranchSchemaName(type, ctx) {
544
- const symbol = type.getSymbol();
545
- if (symbol) {
546
- return symbol.getName();
547
- }
548
- const aliasSymbol = type.aliasSymbol;
549
- if (aliasSymbol) {
550
- return aliasSymbol.getName();
551
- }
552
- return `Anonymous_${ctx.typeNameStack.length}`;
553
- }
554
- function buildNamedSchema(type, ctx, typeNode, build) {
555
- const name = getSchemaName(type, typeNode);
556
- if (!name) {
557
- return build();
558
- }
559
- const { components, typeStack } = ctx;
560
- if (components.has(name) || typeStack.has(type)) {
561
- return { $ref: `#/components/schemas/${name}` };
562
- }
563
- typeStack.add(type);
564
- const schema = build();
565
- typeStack.delete(type);
566
- if (!components.has(name)) {
567
- components.set(name, schema);
568
- }
569
- return { $ref: `#/components/schemas/${name}` };
570
- }
571
- function getSchemaName(type, typeNode) {
572
- const aliasSymbol = type.aliasSymbol ?? type.aliasSymbol;
573
- const aliasName = aliasSymbol?.getName();
574
- if (aliasName && aliasName !== "__type") {
575
- return aliasName;
576
- }
577
- const symbol = type.getSymbol();
578
- const symbolName = symbol?.getName?.();
579
- if (symbolName && symbolName !== "__type") {
580
- return symbolName;
581
- }
582
- const nodeName = getExplicitTypeNameFromNode2(typeNode);
583
- if (nodeName && nodeName !== "__type") {
584
- return nodeName;
585
- }
586
- return null;
587
- }
588
- function getExplicitTypeNameFromNode2(typeNode) {
589
- if (!typeNode) return null;
590
- if (import_typescript4.default.isTypeReferenceNode(typeNode)) {
591
- if (import_typescript4.default.isIdentifier(typeNode.typeName)) {
592
- return typeNode.typeName.text;
593
- }
594
- }
595
- if (import_typescript4.default.isTypeAliasDeclaration(typeNode.parent)) {
596
- if (import_typescript4.default.isIdentifier(typeNode.parent.name)) {
597
- return typeNode.parent.name.text;
598
- }
599
- }
600
- return null;
601
- }
602
-
603
- // src/compiler/schema/intersectionHandler.ts
604
- var import_typescript5 = __toESM(require("typescript"), 1);
605
- function handleIntersection(type, ctx, typeNode) {
606
- return buildNamedSchema2(type, ctx, typeNode, () => {
607
- const types = type.types;
608
- const brandCollapsed = tryCollapseBrandedIntersection(types, ctx, typeNode);
609
- if (brandCollapsed) {
610
- return brandCollapsed;
611
- }
612
- const allOf = [];
613
- for (const t of types) {
614
- const schema = typeToJsonSchema(t, ctx);
615
- if (Object.keys(schema).length > 0) {
616
- if (isEmptyObjectSchema(schema)) {
617
- continue;
618
- }
619
- allOf.push(schema);
620
- }
621
- }
622
- if (allOf.length === 0) {
623
- return {};
624
- }
625
- if (allOf.length === 1) {
626
- return allOf[0];
627
- }
628
- return { allOf };
629
- });
630
- }
631
- function tryCollapseBrandedIntersection(types, ctx, _typeNode) {
632
- const { checker } = ctx;
633
- const parts = [...types];
634
- const prim = parts.find(isPrimitiveLike);
635
- if (!prim) return null;
636
- const rest = parts.filter((p) => p !== prim);
637
- if (rest.every((r) => isBrandObject(checker, r, ctx))) {
638
- return typeToJsonSchema(prim, ctx);
639
- }
640
- return null;
641
- }
642
- function isPrimitiveLike(t) {
643
- return (t.flags & (import_typescript5.default.TypeFlags.String | import_typescript5.default.TypeFlags.Number | import_typescript5.default.TypeFlags.Boolean | import_typescript5.default.TypeFlags.BigInt)) !== 0 || (t.flags & import_typescript5.default.TypeFlags.StringLiteral) !== 0 || (t.flags & import_typescript5.default.TypeFlags.NumberLiteral) !== 0;
644
- }
645
- function isBrandObject(checker, t, _ctx) {
646
- if (!(t.flags & import_typescript5.default.TypeFlags.Object)) return false;
647
- const props = t.getProperties();
648
- if (props.length === 0) return false;
649
- const allowed = /* @__PURE__ */ new Set(["__brand", "__type", "__tag", "brand"]);
650
- for (const p of props) {
651
- if (!allowed.has(p.getName())) return false;
652
- }
653
- const callSigs = t.getCallSignatures?.();
654
- if (callSigs && callSigs.length > 0) return false;
655
- const constructSigs = t.getConstructSignatures?.();
656
- if (constructSigs && constructSigs.length > 0) return false;
657
- return true;
658
- }
659
- function isEmptyObjectSchema(schema) {
660
- if (schema.type !== "object") {
661
- return false;
662
- }
663
- if (!schema.properties || Object.keys(schema.properties).length === 0) {
664
- if (!schema.additionalProperties) {
665
- return true;
666
- }
667
- }
668
- return false;
669
- }
670
- function buildNamedSchema2(type, ctx, typeNode, build) {
671
- const name = getSchemaName2(type, typeNode);
672
- if (!name) {
673
- return build();
674
- }
675
- const { components, typeStack } = ctx;
676
- if (components.has(name) || typeStack.has(type)) {
677
- return { $ref: `#/components/schemas/${name}` };
678
- }
679
- typeStack.add(type);
680
- const schema = build();
681
- typeStack.delete(type);
682
- if (!components.has(name)) {
683
- components.set(name, schema);
684
- }
685
- return { $ref: `#/components/schemas/${name}` };
686
- }
687
- function getSchemaName2(type, typeNode) {
688
- const aliasSymbol = type.aliasSymbol ?? type.aliasSymbol;
689
- const aliasName = aliasSymbol?.getName();
690
- if (aliasName && aliasName !== "__type") {
691
- return aliasName;
692
- }
693
- const symbol = type.getSymbol();
694
- const symbolName = symbol?.getName?.();
695
- if (symbolName && symbolName !== "__type") {
696
- return symbolName;
697
- }
698
- const nodeName = getExplicitTypeNameFromNode3(typeNode);
699
- if (nodeName && nodeName !== "__type") {
700
- return nodeName;
701
- }
702
- return null;
703
- }
704
- function getExplicitTypeNameFromNode3(typeNode) {
705
- if (!typeNode) return null;
706
- if (import_typescript5.default.isTypeReferenceNode(typeNode)) {
707
- if (import_typescript5.default.isIdentifier(typeNode.typeName)) {
708
- return typeNode.typeName.text;
709
- }
710
- }
711
- if (import_typescript5.default.isTypeAliasDeclaration(typeNode.parent)) {
712
- if (import_typescript5.default.isIdentifier(typeNode.parent.name)) {
713
- return typeNode.parent.name.text;
714
- }
715
- }
716
- return null;
717
- }
718
-
719
- // src/compiler/schema/objectHandler.ts
720
- var import_typescript6 = __toESM(require("typescript"), 1);
721
- function getTypeParameterName(type) {
722
- if (type.flags & import_typescript6.default.TypeFlags.TypeParameter) {
723
- const typeParam = type;
724
- return typeParam.symbol?.getName() ?? null;
725
- }
726
- return null;
727
- }
728
- function getTypeArguments(type) {
729
- const typeRef = type;
730
- const args = typeRef.typeArguments;
731
- if (!args) return void 0;
732
- return Array.from(args);
733
- }
734
- function createTypeParameterSubstitutions(type, typeNode, checker) {
735
- const typeArgs = getTypeArguments(type);
736
- if (!typeArgs || typeArgs.length === 0) {
737
- return void 0;
738
- }
739
- if (!typeNode || !import_typescript6.default.isTypeReferenceNode(typeNode)) {
740
- return void 0;
741
- }
742
- const typeParams = typeNode.typeArguments;
743
- if (!typeParams || typeParams.length !== typeArgs.length) {
744
- return void 0;
745
- }
746
- const substitutions = /* @__PURE__ */ new Map();
747
- for (let i = 0; i < typeParams.length; i++) {
748
- const typeParamNode = typeParams[i];
749
- const typeArg = typeArgs[i];
750
- if (import_typescript6.default.isIdentifier(typeParamNode)) {
751
- substitutions.set(typeParamNode.text, typeArg);
752
- }
753
- }
754
- return substitutions.size > 0 ? substitutions : void 0;
755
- }
756
- function resolveTypeParameter(type, substitutions, _checker) {
757
- if (!substitutions) return null;
758
- const paramName = getTypeParameterName(type);
759
- if (!paramName) return null;
760
- const resolved = substitutions.get(paramName);
761
- return resolved ?? null;
762
- }
763
- function handleObjectType(type, ctx, typeNode) {
764
- const { checker, components, typeStack } = ctx;
765
- const symbol = type.getSymbol();
766
- const typeName = symbol?.getName?.() ?? getTypeNameFromNode(typeNode, ctx);
767
- if (isMetalOrmWrapperType(type, checker)) {
768
- return handleMetalOrmWrapper(type, ctx);
769
- }
770
- if (typeName && typeName !== "__type") {
771
- const isMetalOrmGeneric = METAL_ORM_WRAPPER_NAMES.some(
772
- (name) => typeName === name || typeName.endsWith("Api")
773
- );
774
- if (isMetalOrmGeneric) {
775
- return {};
776
- }
777
- if (typeStack.has(type)) {
778
- return { $ref: `#/components/schemas/${typeName}` };
779
- }
780
- typeStack.add(type);
781
- }
782
- const typeParamSubstitutions = createTypeParameterSubstitutions(type, typeNode, checker);
783
- const buildCtx = typeParamSubstitutions ? { ...ctx, typeParameterSubstitutions: typeParamSubstitutions } : ctx;
784
- const schema = buildObjectSchema(type, buildCtx, typeNode);
785
- if (typeName && typeName !== "__type") {
786
- typeStack.delete(type);
787
- const existing = components.get(typeName);
788
- if (!existing) {
789
- components.set(typeName, schema);
790
- } else {
791
- const merged = mergeSchemasIfNeeded(existing, schema);
792
- if (merged !== existing) {
793
- components.set(typeName, merged);
794
- }
795
- }
796
- return { $ref: `#/components/schemas/${typeName}` };
797
- }
798
- typeStack.delete(type);
799
- return schema;
800
- }
801
- function buildObjectSchema(type, ctx, _typeNode) {
802
- const { checker, mode, typeParameterSubstitutions } = ctx;
803
- const properties = {};
804
- const required = [];
805
- const props = checker.getPropertiesOfType(type);
806
- for (const prop of props) {
807
- const propName = prop.getName();
808
- if (isIteratorOrSymbolProperty(propName)) {
809
- continue;
810
- }
811
- let propType = checker.getTypeOfSymbol(prop);
812
- if (isMethodLike(propType)) {
813
- continue;
814
- }
815
- const resolvedType = resolveTypeParameter(propType, typeParameterSubstitutions, checker);
816
- if (resolvedType) {
817
- propType = resolvedType;
818
- }
819
- const isOptional = !!(prop.flags & import_typescript6.default.SymbolFlags.Optional);
820
- const isRelation = isMetalOrmWrapperType(propType, checker);
821
- const propCtx = { ...ctx, propertyName: propName };
822
- properties[propName] = typeToJsonSchema(propType, propCtx);
823
- const shouldRequire = mode === "response" ? !isRelation && !isOptional : !isOptional;
824
- if (shouldRequire) {
825
- required.push(propName);
826
- }
827
- }
828
- const schema = {
829
- type: "object",
830
- properties
831
- };
832
- if (required.length > 0) {
833
- schema.required = required;
834
- }
835
- if (isRecordType(type, checker)) {
836
- const valueType = getRecordValueType(type, checker);
837
- if (valueType) {
838
- const resolvedValueType = resolveTypeParameter(valueType, typeParameterSubstitutions, checker);
839
- schema.additionalProperties = typeToJsonSchema(resolvedValueType ?? valueType, ctx);
840
- }
841
- }
842
- return schema;
843
- }
844
- function isRecordType(type, _checker) {
845
- const symbol = type.getSymbol();
846
- if (!symbol) return false;
847
- const name = symbol.getName();
848
- if (name === "Record") return true;
849
- return false;
850
- }
851
- function getRecordValueType(type, _checker) {
852
- const symbol = type.getSymbol();
853
- if (!symbol) return null;
854
- const name = symbol.getName();
855
- if (name === "Record") {
856
- const typeRef = type;
857
- const typeArgs = typeRef.typeArguments;
858
- if (typeArgs && typeArgs.length >= 2) {
859
- return typeArgs[1];
860
- }
861
- }
862
- return null;
863
- }
864
- function isMetalOrmWrapperType(type, checker) {
865
- return !!findMetalOrmWrapper(type, checker);
866
- }
867
- function isMethodLike(type) {
868
- const callSigs = type.getCallSignatures?.();
869
- return !!(callSigs && callSigs.length > 0);
870
- }
871
- function isIteratorOrSymbolProperty(propName) {
872
- return propName.startsWith("__@") || propName.startsWith("[") || propName === Symbol.iterator.toString();
873
- }
874
- function getTypeNameFromNode(typeNode, _ctx) {
875
- const explicitName = getExplicitTypeNameFromNode4(typeNode);
876
- if (explicitName) return explicitName;
877
- return "Anonymous_${ctx.typeNameStack.length}";
878
- }
879
- function getExplicitTypeNameFromNode4(typeNode) {
880
- if (!typeNode) return null;
881
- if (import_typescript6.default.isTypeReferenceNode(typeNode)) {
882
- if (import_typescript6.default.isIdentifier(typeNode.typeName)) {
883
- return typeNode.typeName.text;
884
- }
885
- }
886
- if (import_typescript6.default.isTypeAliasDeclaration(typeNode.parent)) {
887
- if (import_typescript6.default.isIdentifier(typeNode.parent.name)) {
888
- return typeNode.parent.name.text;
889
- }
890
- }
891
- return null;
892
- }
893
- function mergeSchemasIfNeeded(existing, newSchema) {
894
- if (existing.type === "array" && newSchema.type === "array") {
895
- return mergeArraySchemas(existing, newSchema);
896
- }
897
- const result = { ...existing };
898
- for (const [key, newValue] of Object.entries(newSchema)) {
899
- if (key === "properties" && newValue) {
900
- result.properties = mergePropertiesIfNeeded(existing.properties || {}, newValue);
901
- } else if (key === "required" && newValue) {
902
- result.required = mergeRequiredFields(existing.required || [], newValue);
903
- } else if (!deepEqual(existing[key], newValue)) {
904
- result[key] = newValue;
905
- }
906
- }
907
- return result;
908
- }
909
- function mergePropertiesIfNeeded(existing, newProps) {
910
- const result = { ...existing };
911
- for (const [propName, newPropSchema] of Object.entries(newProps)) {
912
- const existingProp = existing[propName];
913
- if (!existingProp) {
914
- result[propName] = newPropSchema;
915
- } else if (deepEqual(existingProp, newPropSchema)) {
916
- continue;
917
- } else {
918
- result[propName] = mergePropertySchemas(existingProp, newPropSchema);
919
- }
920
- }
921
- return result;
922
- }
923
- function mergePropertySchemas(schema1, schema2) {
924
- if (deepEqual(schema1, schema2)) {
925
- return schema1;
926
- }
927
- if (schema1.type === "array" && schema2.type === "array") {
928
- return mergeArraySchemas(schema1, schema2);
929
- }
930
- const existingOneOf = schema1.oneOf || schema1.anyOf;
931
- const newOneOf = schema2.oneOf || schema2.anyOf;
932
- if (existingOneOf) {
933
- const mergedOneOf = [...existingOneOf];
934
- if (newOneOf) {
935
- for (const newItem of newOneOf) {
936
- if (!mergedOneOf.some((item) => deepEqual(item, newItem))) {
937
- mergedOneOf.push(newItem);
938
- }
939
- }
940
- } else if (!mergedOneOf.some((item) => deepEqual(item, schema2))) {
941
- mergedOneOf.push(schema2);
942
- }
943
- return { ...schema1, oneOf: mergedOneOf };
944
- }
945
- return {
946
- oneOf: [schema1, schema2]
947
- };
948
- }
949
- function mergeArraySchemas(schema1, schema2) {
950
- const result = { type: "array" };
951
- if (schema1.uniqueItems || schema2.uniqueItems) {
952
- result.uniqueItems = true;
953
- }
954
- if (schema1.items && schema2.items) {
955
- result.items = mergePropertySchemas(schema1.items, schema2.items);
956
- } else if (schema1.items) {
957
- result.items = schema1.items;
958
- } else if (schema2.items) {
959
- result.items = schema2.items;
960
- }
961
- return result;
962
- }
963
- function mergeRequiredFields(existing, newFields) {
964
- const merged = /* @__PURE__ */ new Set([...existing, ...newFields]);
965
- return Array.from(merged);
966
- }
967
- function deepEqual(a, b) {
968
- if (a === b) return true;
969
- if (a == null || b == null) return false;
970
- if (typeof a !== typeof b) return false;
971
- if (typeof a === "object") {
972
- const aKeys = Object.keys(a);
973
- const bKeys = Object.keys(b);
974
- if (aKeys.length !== bKeys.length) return false;
975
- for (const key of aKeys) {
976
- if (!bKeys.includes(key)) return false;
977
- if (!deepEqual(a[key], b[key])) return false;
978
- }
979
- return true;
980
- }
981
- return false;
982
- }
983
- var METAL_ORM_WRAPPER_NAMES = ["HasManyCollection", "ManyToManyCollection", "BelongsToReference", "HasOneReference"];
984
- function findMetalOrmWrapper(type, checker) {
985
- if (type.isIntersection()) {
986
- let wrapperInfo = null;
987
- let hasReadonlyArray = false;
988
- for (const constituent of type.types) {
989
- const result = findWrapperInType(constituent, checker);
990
- if (result) {
991
- wrapperInfo = result;
992
- }
993
- if (!(constituent.flags & import_typescript6.default.TypeFlags.Object)) continue;
994
- const symbol = constituent.getSymbol();
995
- if (symbol?.getName() === "ReadonlyArray") {
996
- hasReadonlyArray = true;
997
- }
998
- }
999
- if (wrapperInfo) {
1000
- return { ...wrapperInfo, isReadonlyArray: hasReadonlyArray };
1001
- }
1002
- return null;
1003
- }
1004
- return findWrapperInType(type, checker);
1005
- }
1006
- function findWrapperInType(type, checker) {
1007
- const aliasSymbol = type.aliasSymbol ?? type.aliasSymbol;
1008
- const symbol = type.getSymbol();
1009
- const effectiveSymbol = aliasSymbol && aliasSymbol.flags & import_typescript6.default.SymbolFlags.Alias ? checker.getAliasedSymbol(aliasSymbol) : symbol;
1010
- if (!effectiveSymbol) return null;
1011
- const name = effectiveSymbol.getName();
1012
- if (!METAL_ORM_WRAPPER_NAMES.includes(name)) return null;
1013
- const typeRef = type;
1014
- const typeArgs = typeRef.typeArguments || [];
1015
- return {
1016
- wrapperName: name,
1017
- targetTypeArgs: typeArgs,
1018
- isReadonlyArray: false
1019
- };
1020
- }
1021
- function getWrapperTypeName(type, _checker) {
1022
- const symbol = type.getSymbol();
1023
- if (!symbol) return null;
1024
- const name = symbol.getName();
1025
- return METAL_ORM_WRAPPER_NAMES.includes(name) ? name : null;
1026
- }
1027
- function handleMetalOrmWrapper(type, ctx) {
1028
- const typeRef = type;
1029
- const typeArgs = typeRef.typeArguments;
1030
- const targetType = typeArgs?.[0] ?? null;
1031
- const wrapperName = getWrapperTypeName(type, ctx.checker);
1032
- if (!wrapperName) return {};
1033
- const wrapperRel = { wrapper: wrapperName };
1034
- if (!targetType) {
1035
- return { "x-metal-orm-rel": wrapperRel };
1036
- }
1037
- if (wrapperName === "HasManyCollection" || wrapperName === "ManyToManyCollection") {
1038
- if (ctx.typeStack.has(targetType)) {
1039
- const items2 = {
1040
- type: "object",
1041
- properties: {
1042
- id: { type: "integer" }
1043
- },
1044
- required: ["id"]
1045
- };
1046
- if (wrapperName === "ManyToManyCollection" && typeArgs?.[1]) {
1047
- wrapperRel.pivot = ctx.checker.typeToString(typeArgs[1]);
1048
- }
1049
- return {
1050
- type: "array",
1051
- items: items2,
1052
- "x-metal-orm-rel": wrapperRel
1053
- };
1054
- }
1055
- const items = targetType ? typeToJsonSchema(targetType, ctx) : {};
1056
- if (wrapperName === "ManyToManyCollection" && typeArgs?.[1]) {
1057
- wrapperRel.pivot = ctx.checker.typeToString(typeArgs[1]);
1058
- }
1059
- return {
1060
- type: "array",
1061
- items,
1062
- "x-metal-orm-rel": wrapperRel
1063
- };
1064
- }
1065
- if (wrapperName === "BelongsToReference" || wrapperName === "HasOneReference") {
1066
- return handleBelongsToReference(targetType, ctx, wrapperRel);
1067
- }
1068
- const targetSchema = typeToJsonSchema(targetType, ctx);
1069
- return {
1070
- ...targetSchema,
1071
- "x-metal-orm-rel": wrapperRel
1072
- };
1073
- }
1074
- function handleBelongsToReference(targetType, ctx, wrapperRel) {
1075
- const { components, typeStack } = ctx;
1076
- const targetSymbol = targetType.getSymbol();
1077
- const typeName = targetSymbol?.getName();
1078
- if (!typeName) {
1079
- return {
1080
- type: "object",
1081
- properties: {},
1082
- "x-metal-orm-rel": wrapperRel
1083
- };
1084
- }
1085
- const refSchemaName = `${typeName}Ref`;
1086
- if (components.has(refSchemaName)) {
1087
- return {
1088
- $ref: `#/components/schemas/${refSchemaName}`,
1089
- "x-metal-orm-rel": wrapperRel
1090
- };
1091
- }
1092
- if (typeStack.has(targetType)) {
1093
- const circularRefSchema = {
1094
- type: "object",
1095
- properties: {
1096
- id: { type: "integer" }
1097
- },
1098
- required: ["id"]
1099
- };
1100
- components.set(refSchemaName, circularRefSchema);
1101
- return {
1102
- $ref: `#/components/schemas/${refSchemaName}`,
1103
- "x-metal-orm-rel": wrapperRel
1104
- };
1105
- }
1106
- const refSchema = buildRefSchema(targetType, ctx);
1107
- components.set(refSchemaName, refSchema);
1108
- return {
1109
- $ref: `#/components/schemas/${refSchemaName}`,
1110
- "x-metal-orm-rel": wrapperRel
1111
- };
1112
- }
1113
- function buildRefSchema(type, ctx) {
1114
- const { checker } = ctx;
1115
- if (!(type.flags & import_typescript6.default.TypeFlags.Object)) {
1116
- return { type: "object", properties: {} };
1117
- }
1118
- const objectType = type;
1119
- const properties = {};
1120
- const required = [];
1121
- const props = checker.getPropertiesOfType(objectType);
1122
- for (const prop of props) {
1123
- const propName = prop.getName();
1124
- if (isIteratorOrSymbolProperty(propName)) {
1125
- continue;
1126
- }
1127
- const propType = checker.getTypeOfSymbol(prop);
1128
- if (isMethodLike(propType)) {
1129
- continue;
1130
- }
1131
- const isOptional = !!(prop.flags & import_typescript6.default.SymbolFlags.Optional);
1132
- const isRelation = isMetalOrmWrapperType(propType, checker);
1133
- if (isRelation) {
1134
- continue;
1135
- }
1136
- const propCtx = { ...ctx, propertyName: propName };
1137
- properties[propName] = typeToJsonSchema(propType, propCtx);
1138
- if (!isOptional) {
1139
- required.push(propName);
1140
- }
1141
- }
1142
- const schema = {
1143
- type: "object",
1144
- properties
1145
- };
1146
- if (required.length > 0) {
1147
- schema.required = required;
1148
- }
1149
- return schema;
1150
- }
1151
-
1152
- // src/compiler/schema/typeToJsonSchema.ts
1153
- function typeToJsonSchema(type, ctx, typeNode) {
1154
- const primitiveResult = handlePrimitiveType(type, ctx, typeNode);
1155
- if (primitiveResult) {
1156
- return primitiveResult;
1157
- }
1158
- if (type.isUnion()) {
1159
- return handleUnion(type, ctx, typeNode);
1160
- }
1161
- if (type.isIntersection()) {
1162
- return handleIntersection(type, ctx, typeNode);
1163
- }
1164
- if (ctx.checker.isArrayType(type)) {
1165
- const typeArgs = type.typeArguments;
1166
- const itemType = typeArgs?.[0];
1167
- const items = itemType ? typeToJsonSchema(itemType, ctx) : {};
1168
- return {
1169
- type: "array",
1170
- items,
1171
- uniqueItems: isSetType(type, ctx.checker) ? true : void 0
1172
- };
1173
- }
1174
- if (type.flags & import_typescript7.default.TypeFlags.Object) {
1175
- const objectType = type;
1176
- if (isMetalOrmWrapperType(type, ctx.checker)) {
1177
- return handleMetalOrmWrapper(objectType, ctx);
1178
- }
1179
- return handleObjectType(objectType, ctx, typeNode);
1180
- }
1181
- return {};
1182
- }
1183
- function isSetType(type, _checker) {
1184
- const symbol = type.getSymbol();
1185
- if (!symbol) return false;
1186
- const name = symbol.getName();
1187
- if (name === "Set") return true;
1188
- return false;
1189
- }
1190
-
1191
- // src/compiler/schema/extractAnnotations.ts
1192
- var import_typescript8 = __toESM(require("typescript"), 1);
1193
- function extractPropertySchemaFragments(checker, prop) {
1194
- if (!import_typescript8.default.canHaveDecorators(prop)) return [];
1195
- const decs = import_typescript8.default.getDecorators(prop);
1196
- if (!decs || decs.length === 0) return [];
1197
- const frags = [];
1198
- for (const d of decs) {
1199
- const expr = d.expression;
1200
- let callee;
1201
- let args;
1202
- if (import_typescript8.default.isCallExpression(expr)) {
1203
- callee = expr.expression;
1204
- args = expr.arguments;
1205
- } else {
1206
- callee = expr;
1207
- args = import_typescript8.default.factory.createNodeArray([]);
1208
- }
1209
- const sym = checker.getSymbolAtLocation(callee);
1210
- if (!sym) continue;
1211
- const resolved = resolveImportedDecorator(checker, sym);
1212
- if (!resolved || resolved.module !== "adorn-api/schema") continue;
1213
- const name = resolved.name;
1214
- if (name === "Schema") {
1215
- const obj = args[0];
1216
- if (obj && import_typescript8.default.isObjectLiteralExpression(obj)) {
1217
- const frag = objectLiteralToJson(obj);
1218
- if (frag) frags.push(frag);
1219
- }
1220
- continue;
1221
- }
1222
- if (name === "Min" && isNumberLiteral(args[0])) {
1223
- frags.push({ minimum: Number(args[0].text) });
1224
- } else if (name === "Max" && isNumberLiteral(args[0])) {
1225
- frags.push({ maximum: Number(args[0].text) });
1226
- } else if (name === "ExclusiveMin" && isNumberLiteral(args[0])) {
1227
- frags.push({ exclusiveMinimum: Number(args[0].text) });
1228
- } else if (name === "ExclusiveMax" && isNumberLiteral(args[0])) {
1229
- frags.push({ exclusiveMaximum: Number(args[0].text) });
1230
- } else if (name === "MinLength" && isNumberLiteral(args[0])) {
1231
- frags.push({ minLength: Number(args[0].text) });
1232
- } else if (name === "MaxLength" && isNumberLiteral(args[0])) {
1233
- frags.push({ maxLength: Number(args[0].text) });
1234
- } else if (name === "Format" && isStringLiteral(args[0])) {
1235
- frags.push({ format: args[0].text });
1236
- } else if (name === "Pattern") {
1237
- const arg = args[0];
1238
- if (arg && import_typescript8.default.isRegularExpressionLiteral(arg)) {
1239
- frags.push({ pattern: extractRegexPattern(arg.text) });
1240
- } else if (isStringLiteral(arg)) {
1241
- frags.push({ pattern: arg.text });
1242
- }
1243
- } else if (name === "MinItems" && isNumberLiteral(args[0])) {
1244
- frags.push({ minItems: Number(args[0].text) });
1245
- } else if (name === "MaxItems" && isNumberLiteral(args[0])) {
1246
- frags.push({ maxItems: Number(args[0].text) });
1247
- } else if (name === "MinProperties" && isNumberLiteral(args[0])) {
1248
- frags.push({ minProperties: Number(args[0].text) });
1249
- } else if (name === "MaxProperties" && isNumberLiteral(args[0])) {
1250
- frags.push({ maxProperties: Number(args[0].text) });
1251
- } else if (name === "MultipleOf" && isNumberLiteral(args[0])) {
1252
- frags.push({ multipleOf: Number(args[0].text) });
1253
- } else if (name === "Example") {
1254
- frags.push({ example: literalToJson(args[0]) });
1255
- } else if (name === "Examples" && import_typescript8.default.isArrayLiteralExpression(args[0])) {
1256
- frags.push({ examples: args[0].elements.map((e) => literalToJson(e)) });
1257
- } else if (name === "Description" && isStringLiteral(args[0])) {
1258
- frags.push({ description: args[0].text });
1259
- } else if (name === "Enum" && import_typescript8.default.isArrayLiteralExpression(args[0])) {
1260
- frags.push({ enum: args[0].elements.map((e) => literalToJson(e)) });
1261
- } else if (name === "Const") {
1262
- frags.push({ const: literalToJson(args[0]) });
1263
- } else if (name === "Default") {
1264
- frags.push({ default: literalToJson(args[0]) });
1265
- } else if (name === "AdditionalProperties") {
1266
- const arg = args[0];
1267
- if (arg && (arg.kind === import_typescript8.default.SyntaxKind.FalseKeyword || arg.kind === import_typescript8.default.SyntaxKind.TrueKeyword)) {
1268
- frags.push({ additionalProperties: arg.kind === import_typescript8.default.SyntaxKind.TrueKeyword });
1269
- } else if (arg && import_typescript8.default.isObjectLiteralExpression(arg)) {
1270
- const obj = objectLiteralToJson(arg);
1271
- if (obj) frags.push({ additionalProperties: obj });
1272
- }
1273
- } else if (name === "Closed") {
1274
- frags.push({ additionalProperties: false });
1275
- } else if (name === "ClosedUnevaluated") {
1276
- frags.push({ unevaluatedProperties: false });
1277
- }
1278
- }
1279
- return frags;
1280
- }
1281
- function resolveImportedDecorator(checker, sym) {
1282
- const target = sym.flags & import_typescript8.default.SymbolFlags.Alias ? checker.getAliasedSymbol(sym) : sym;
1283
- const name = target.getName();
1284
- const decl = target.declarations?.[0];
1285
- if (!decl) return null;
1286
- const fileName = decl.getSourceFile().fileName.replace(/\\/g, "/");
1287
- if (fileName.includes("/node_modules/adorn-api/") || fileName.includes("/src/schema/")) {
1288
- return { module: "adorn-api/schema", name };
1289
- }
1290
- return null;
1291
- }
1292
- function isNumberLiteral(node) {
1293
- return !!node && import_typescript8.default.isNumericLiteral(node);
1294
- }
1295
- function isStringLiteral(node) {
1296
- return !!node && import_typescript8.default.isStringLiteral(node);
1297
- }
1298
- function extractRegexPattern(text) {
1299
- const match = text.match(/^\/(.+)\/[gimsuy]*$/);
1300
- return match ? match[1] : text;
1301
- }
1302
- function objectLiteralToJson(obj) {
1303
- const out = {};
1304
- for (const prop of obj.properties) {
1305
- if (!import_typescript8.default.isPropertyAssignment(prop)) continue;
1306
- const name = prop.name;
1307
- let key;
1308
- if (import_typescript8.default.isIdentifier(name)) {
1309
- key = name.text;
1310
- } else if (import_typescript8.default.isStringLiteral(name)) {
1311
- key = name.text;
1312
- } else {
1313
- continue;
1314
- }
1315
- out[key] = literalToJson(prop.initializer);
1316
- }
1317
- return out;
1318
- }
1319
- function literalToJson(node) {
1320
- if (import_typescript8.default.isStringLiteral(node)) return node.text;
1321
- if (import_typescript8.default.isNumericLiteral(node)) return Number(node.text);
1322
- if (node.kind === import_typescript8.default.SyntaxKind.TrueKeyword) return true;
1323
- if (node.kind === import_typescript8.default.SyntaxKind.FalseKeyword) return false;
1324
- if (node.kind === import_typescript8.default.SyntaxKind.NullKeyword) return null;
1325
- if (import_typescript8.default.isObjectLiteralExpression(node)) return objectLiteralToJson(node);
1326
- if (import_typescript8.default.isArrayLiteralExpression(node)) return node.elements.map((e) => literalToJson(e));
1327
- return void 0;
1328
- }
1329
- function mergeFragments(base, ...frags) {
1330
- const result = { ...base };
1331
- for (const frag of frags) {
1332
- Object.assign(result, frag);
1333
- }
1334
- return result;
1335
- }
1336
-
1337
- // src/compiler/schema/parameters.ts
1338
- function buildPathParameters(operation, ctx, parameters) {
1339
- for (const paramIndex of operation.pathParamIndices) {
1340
- const param = operation.parameters[paramIndex];
1341
- if (param) {
1342
- let paramSchema = typeToJsonSchema(param.type, ctx);
1343
- if (param.paramNode) {
1344
- const frags = extractPropertySchemaFragments(ctx.checker, param.paramNode);
1345
- if (frags.length > 0) {
1346
- paramSchema = mergeFragments(paramSchema, ...frags);
1347
- }
1348
- }
1349
- const schema = paramSchema.$ref ? { $ref: paramSchema.$ref } : paramSchema;
1350
- const paramName = param.name.toLowerCase();
1351
- const isIdParam = paramName === "id" || paramName.endsWith("id");
1352
- if (!schema.$ref && schema.type === "number" && isIdParam) {
1353
- schema.type = "integer";
1354
- if (!schema.minimum) {
1355
- schema.minimum = 1;
1356
- }
1357
- }
1358
- parameters.push({
1359
- name: param.name,
1360
- in: "path",
1361
- required: !param.isOptional,
1362
- schema
1363
- });
1364
- }
1365
- }
1366
- }
1367
- function buildQueryParameters(operation, ctx, parameters) {
1368
- if (operation.queryObjectParamIndex !== null) {
1369
- const queryParam = operation.parameters[operation.queryObjectParamIndex];
1370
- if (!queryParam) return;
1371
- const querySchema = typeToJsonSchema(queryParam.type, ctx);
1372
- const { properties: queryObjProps, required: queryRequired } = resolveAndCollectObjectProps(querySchema, ctx.components);
1373
- for (const [propName, propSchema] of Object.entries(queryObjProps)) {
1374
- const isRequired = queryRequired.includes(propName);
1375
- const isDeepObject = isDeepObjectSchema(propSchema, ctx);
1376
- const isObjectLike = isObjectLikeSchema(propSchema, ctx);
1377
- const serialization = determineQuerySerialization(propSchema.type);
1378
- const exampleValue = generateExampleValue(propSchema, propName);
1379
- if (isDeepObject) {
1380
- parameters.push({
1381
- name: propName,
1382
- in: "query",
1383
- required: isRequired,
1384
- style: "deepObject",
1385
- explode: true,
1386
- schema: propSchema.$ref ? { $ref: propSchema.$ref } : propSchema
1387
- });
1388
- } else if (isObjectLike) {
1389
- const schemaRef = propSchema.$ref || "#/components/schemas/InlineQueryParam";
1390
- parameters.push({
1391
- name: propName,
1392
- in: "query",
1393
- required: isRequired,
1394
- schema: { type: "string" },
1395
- description: `JSON-encoded object. ${exampleValue}`,
1396
- examples: {
1397
- default: { value: parseExampleValue(exampleValue) }
1398
- },
1399
- "x-adorn-jsonSchemaRef": schemaRef
1400
- });
1401
- } else {
1402
- const paramDef = {
1403
- name: propName,
1404
- in: "query",
1405
- required: isRequired,
1406
- schema: propSchema.$ref ? { $ref: propSchema.$ref } : propSchema
1407
- };
1408
- if (propName === "page") {
1409
- paramDef.schema = { type: "integer", default: 1, minimum: 1 };
1410
- } else if (propName === "pageSize") {
1411
- paramDef.schema = { type: "integer", default: 10, minimum: 1 };
1412
- } else if (propName === "totalItems") {
1413
- paramDef.schema = { type: "integer", minimum: 0 };
1414
- } else if (propName === "sort") {
1415
- paramDef.schema = {
1416
- oneOf: [
1417
- { type: "string" },
1418
- { type: "array", items: { type: "string" } }
1419
- ]
1420
- };
1421
- } else if (propName === "q") {
1422
- paramDef.schema = { type: "string" };
1423
- } else if (propName === "hasComments") {
1424
- paramDef.schema = { type: "boolean" };
1425
- }
1426
- if (Object.keys(serialization).length > 0) {
1427
- Object.assign(paramDef, serialization);
1428
- }
1429
- parameters.push(paramDef);
1430
- }
1431
- }
1432
- }
1433
- for (const paramIndex of operation.queryParamIndices) {
1434
- const param = operation.parameters[paramIndex];
1435
- if (param) {
1436
- let paramSchema = typeToJsonSchema(param.type, ctx);
1437
- if (param.paramNode) {
1438
- const frags = extractPropertySchemaFragments(ctx.checker, param.paramNode);
1439
- if (frags.length > 0) {
1440
- paramSchema = mergeFragments(paramSchema, ...frags);
1441
- }
1442
- }
1443
- const isDeepObject = isDeepObjectSchema(paramSchema, ctx);
1444
- const isObjectLike = isObjectLikeSchema(paramSchema, ctx);
1445
- if (isDeepObject) {
1446
- parameters.push({
1447
- name: param.name,
1448
- in: "query",
1449
- required: !param.isOptional,
1450
- style: "deepObject",
1451
- explode: true,
1452
- schema: paramSchema.$ref ? { $ref: paramSchema.$ref } : paramSchema
1453
- });
1454
- } else if (isObjectLike) {
1455
- const schemaRef = paramSchema.$ref || "#/components/schemas/InlineQueryParam";
1456
- const exampleValue = generateExampleValue(paramSchema, param.name);
1457
- parameters.push({
1458
- name: param.name,
1459
- in: "query",
1460
- required: !param.isOptional,
1461
- schema: { type: "string" },
1462
- description: `JSON-encoded object. ${exampleValue}`,
1463
- examples: {
1464
- default: { value: parseExampleValue(exampleValue) }
1465
- },
1466
- "x-adorn-jsonSchemaRef": schemaRef
1467
- });
1468
- } else {
1469
- const serialization = determineQuerySerialization(paramSchema.type);
1470
- parameters.push({
1471
- name: param.name,
1472
- in: "query",
1473
- required: !param.isOptional,
1474
- schema: paramSchema.$ref ? { $ref: paramSchema.$ref } : paramSchema,
1475
- ...Object.keys(serialization).length > 0 ? serialization : {}
1476
- });
1477
- }
1478
- }
1479
- }
1480
- }
1481
- function buildHeaderParameters(operation, ctx, parameters) {
1482
- if (operation.headerObjectParamIndex === null) return;
1483
- const headerParam = operation.parameters[operation.headerObjectParamIndex];
1484
- if (!headerParam) return;
1485
- const headerSchema = typeToJsonSchema(headerParam.type, ctx);
1486
- if (!headerSchema.properties) return;
1487
- const headerObjProps = headerSchema.properties;
1488
- for (const [propName, propSchema] of Object.entries(headerObjProps)) {
1489
- const isRequired = headerSchema.required?.includes(propName) ?? false;
1490
- parameters.push({
1491
- name: propName,
1492
- in: "header",
1493
- required: isRequired,
1494
- schema: propSchema
1495
- });
1496
- }
1497
- }
1498
- function buildCookieParameters(operation, ctx, parameters) {
1499
- if (operation.cookieObjectParamIndex === null) return;
1500
- const cookieParam = operation.parameters[operation.cookieObjectParamIndex];
1501
- if (!cookieParam) return;
1502
- const cookieSchema = typeToJsonSchema(cookieParam.type, ctx);
1503
- if (!cookieSchema.properties) return;
1504
- const cookieObjProps = cookieSchema.properties;
1505
- for (const [propName, propSchema] of Object.entries(cookieObjProps)) {
1506
- const isRequired = cookieSchema.required?.includes(propName) ?? false;
1507
- parameters.push({
1508
- name: propName,
1509
- in: "cookie",
1510
- required: isRequired,
1511
- schema: propSchema,
1512
- style: "form",
1513
- explode: true
1514
- });
1515
- }
1516
- }
1517
- function determineQuerySerialization(schemaType) {
1518
- const typeArray = Array.isArray(schemaType) ? schemaType : schemaType ? [schemaType] : [];
1519
- const isArray = typeArray.includes("array");
1520
- if (isArray) {
1521
- return { style: "form", explode: true };
1522
- }
1523
- return {};
1524
- }
1525
- function generateExampleValue(schema, propName) {
1526
- const resolved = resolveSchemaRef(schema, /* @__PURE__ */ new Map());
1527
- if (resolved.type === "object" && resolved.properties) {
1528
- const example = {};
1529
- for (const [key, prop] of Object.entries(resolved.properties)) {
1530
- const propResolved = resolveSchemaRef(prop, /* @__PURE__ */ new Map());
1531
- if (propResolved.type === "string") {
1532
- example[key] = "value";
1533
- } else if (propResolved.type === "number" || propResolved.type === "integer") {
1534
- example[key] = 1;
1535
- } else if (propResolved.type === "boolean") {
1536
- example[key] = true;
1537
- } else if (Array.isArray(propResolved.type) && propResolved.type.includes("null")) {
1538
- example[key] = null;
1539
- } else if (propResolved.enum) {
1540
- example[key] = propResolved.enum[0];
1541
- } else {
1542
- example[key] = "value";
1543
- }
1544
- }
1545
- return `Example: ${propName}=${JSON.stringify(example)}`;
1546
- }
1547
- return `Example: ${propName}=${JSON.stringify({ key: "value" })}`;
1548
- }
1549
- function parseExampleValue(description) {
1550
- const match = description.match(/Example:\s*\w+=(\{[^}]+\})/);
1551
- if (match) {
1552
- return match[1];
1553
- }
1554
- return JSON.stringify({ key: "value" });
1555
- }
1556
- function isDeepObjectSchema(schema, ctx) {
1557
- const resolved = resolveSchemaRef(schema, ctx.components);
1558
- if (resolved.type === "array") {
1559
- return false;
1560
- }
1561
- if (resolved.type === "object" || resolved.properties || resolved.additionalProperties) {
1562
- return true;
1563
- }
1564
- if (resolved.allOf) {
1565
- for (const branch of resolved.allOf) {
1566
- if (isDeepObjectSchema(branch, ctx)) {
1567
- return true;
1568
- }
1569
- }
1570
- }
1571
- return false;
1572
- }
1573
- function isObjectLikeSchema(schema, ctx) {
1574
- const resolved = resolveSchemaRef(schema, ctx.components);
1575
- if (resolved.type === "object" || resolved.properties || resolved.additionalProperties) {
1576
- return true;
1577
- }
1578
- if (resolved.allOf) {
1579
- for (const branch of resolved.allOf) {
1580
- if (isObjectLikeSchema(branch, ctx)) {
1581
- return true;
1582
- }
1583
- }
1584
- }
1585
- if (resolved.type === "array" && resolved.items) {
1586
- const itemsSchema = resolveSchemaRef(resolved.items, ctx.components);
1587
- return isObjectLikeSchema(itemsSchema, ctx);
1588
- }
1589
- return false;
1590
- }
1591
- function resolveSchemaRef(schema, components) {
1592
- const ref = schema.$ref;
1593
- if (typeof ref !== "string" || !ref.startsWith("#/components/schemas/")) {
1594
- return schema;
1595
- }
1596
- const name = ref.replace("#/components/schemas/", "");
1597
- const next = components.get(name);
1598
- if (!next) return schema;
1599
- return resolveSchemaRef(next, components);
1600
- }
1601
- function resolveAndCollectObjectProps(schema, components) {
1602
- const resolved = resolveSchemaRef(schema, components);
1603
- const properties = {};
1604
- const required = [];
1605
- const processSchema = (s) => {
1606
- const current = resolveSchemaRef(s, components);
1607
- if (current.properties) {
1608
- for (const [key, val] of Object.entries(current.properties)) {
1609
- if (!properties[key]) {
1610
- properties[key] = val;
1611
- }
1612
- }
1613
- }
1614
- if (current.required) {
1615
- for (const req of current.required) {
1616
- if (!required.includes(req)) {
1617
- required.push(req);
1618
- }
1619
- }
1620
- }
1621
- if (current.allOf) {
1622
- for (const branch of current.allOf) {
1623
- processSchema(branch);
1624
- }
1625
- }
1626
- };
1627
- processSchema(resolved);
1628
- return { properties, required };
1629
- }
1630
-
1631
- // src/compiler/schema/queryBuilderAnalyzer.ts
1632
- var import_typescript10 = __toESM(require("typescript"), 1);
1633
-
1634
- // src/compiler/schema/serviceCallAnalyzer.ts
1635
- var import_typescript9 = __toESM(require("typescript"), 1);
1636
- var ServiceCallAnalyzer = class {
1637
- checker;
1638
- program;
1639
- cache = /* @__PURE__ */ new Map();
1640
- analyzedMethods = /* @__PURE__ */ new Set();
1641
- constructor(checker, program) {
1642
- this.checker = checker;
1643
- this.program = program;
1644
- }
1645
- /**
1646
- * Analyzes a controller method for query builder patterns, following service calls
1647
- */
1648
- analyzeControllerMethod(methodDeclaration, options = {}) {
1649
- const cacheKey = this.getMethodCacheKey(methodDeclaration);
1650
- if (this.cache.has(cacheKey)) {
1651
- return this.cache.get(cacheKey) ?? null;
1652
- }
1653
- const maxDepth = options.maxDepth ?? 3;
1654
- const schema = this.analyzeMethodWithServiceCalls(methodDeclaration, 0, maxDepth, options);
1655
- this.cache.set(cacheKey, schema);
1656
- return schema;
1657
- }
1658
- /**
1659
- * Recursively analyzes method with service call traversal
1660
- */
1661
- analyzeMethodWithServiceCalls(methodDeclaration, currentDepth, maxDepth, options) {
1662
- if (currentDepth >= maxDepth) {
1663
- return null;
1664
- }
1665
- const methodKey = this.getMethodCacheKey(methodDeclaration);
1666
- if (this.analyzedMethods.has(methodKey)) {
1667
- return null;
1668
- }
1669
- this.analyzedMethods.add(methodKey);
1670
- const directSchema = analyzeQueryBuilderForSchema(methodDeclaration, this.checker);
1671
- if (directSchema) {
1672
- return directSchema;
1673
- }
1674
- const serviceCalls = this.findServiceCalls(methodDeclaration);
1675
- for (const serviceCall of serviceCalls) {
1676
- const serviceSchema = this.analyzeServiceMethod(serviceCall, currentDepth, maxDepth, options);
1677
- if (serviceSchema) {
1678
- return serviceSchema;
1679
- }
1680
- }
1681
- return null;
1682
- }
1683
- /**
1684
- * Analyzes a service method for query builder patterns
1685
- */
1686
- analyzeServiceMethod(serviceCall, currentDepth, maxDepth, options) {
1687
- const directSchema = analyzeQueryBuilderForSchema(serviceCall.methodDeclaration, this.checker);
1688
- if (directSchema) {
1689
- return directSchema;
1690
- }
1691
- if (options.analyzeHelpers) {
1692
- return this.analyzeMethodWithServiceCalls(
1693
- serviceCall.methodDeclaration,
1694
- currentDepth + 1,
1695
- maxDepth,
1696
- options
1697
- );
1698
- }
1699
- return null;
1700
- }
1701
- /**
1702
- * Finds service calls in a method body
1703
- */
1704
- findServiceCalls(methodDeclaration) {
1705
- const serviceCalls = [];
1706
- const body = methodDeclaration.body;
1707
- if (!body) {
1708
- return serviceCalls;
1709
- }
1710
- const visitor = (node) => {
1711
- if (import_typescript9.default.isCallExpression(node)) {
1712
- const serviceCall = this.resolveServiceCall(node);
1713
- if (serviceCall) {
1714
- serviceCalls.push(serviceCall);
1715
- }
1716
- }
1717
- import_typescript9.default.forEachChild(node, visitor);
1718
- };
1719
- import_typescript9.default.forEachChild(body, visitor);
1720
- return serviceCalls;
1721
- }
1722
- /**
1723
- * Resolves a call expression to a service method
1724
- */
1725
- resolveServiceCall(callExpression) {
1726
- if (import_typescript9.default.isPropertyAccessExpression(callExpression.expression)) {
1727
- const propAccess = callExpression.expression;
1728
- const methodName = propAccess.name.text;
1729
- const objectType = this.checker.getTypeAtLocation(propAccess.expression);
1730
- const objectSymbol = objectType.getSymbol();
1731
- if (objectSymbol) {
1732
- const classDeclaration = this.findClassDeclaration(objectSymbol);
1733
- if (classDeclaration) {
1734
- const methodDeclaration = this.findMethodDeclaration(classDeclaration, methodName);
1735
- if (methodDeclaration) {
1736
- return {
1737
- serviceName: classDeclaration.name?.text || "Unknown",
1738
- methodName,
1739
- filePath: classDeclaration.getSourceFile().fileName,
1740
- classDeclaration,
1741
- methodDeclaration
1742
- };
1743
- }
1744
- }
1745
- }
1746
- }
1747
- if (import_typescript9.default.isPropertyAccessExpression(callExpression.expression)) {
1748
- const propAccess = callExpression.expression;
1749
- const methodName = propAccess.name.text;
1750
- if (import_typescript9.default.isIdentifier(propAccess.expression)) {
1751
- const className = propAccess.expression.text;
1752
- const classSymbol = this.checker.getSymbolAtLocation(propAccess.expression);
1753
- if (classSymbol) {
1754
- const classDeclaration = this.findClassDeclaration(classSymbol);
1755
- if (classDeclaration && classDeclaration.name?.text === className) {
1756
- const methodDeclaration = this.findMethodDeclaration(classDeclaration, methodName);
1757
- if (methodDeclaration) {
1758
- return {
1759
- serviceName: className,
1760
- methodName,
1761
- filePath: classDeclaration.getSourceFile().fileName,
1762
- classDeclaration,
1763
- methodDeclaration
1764
- };
1765
- }
1766
- }
1767
- }
1768
- }
1769
- }
1770
- return null;
1771
- }
1772
- /**
1773
- * Finds class declaration from a symbol
1774
- */
1775
- findClassDeclaration(symbol) {
1776
- const declarations = symbol.getDeclarations();
1777
- if (!declarations) return null;
1778
- for (const declaration of declarations) {
1779
- if (import_typescript9.default.isClassDeclaration(declaration)) {
1780
- return declaration;
1781
- }
1782
- }
1783
- return null;
1784
- }
1785
- /**
1786
- * Finds method declaration in a class
1787
- */
1788
- findMethodDeclaration(classDeclaration, methodName) {
1789
- for (const member of classDeclaration.members) {
1790
- if (import_typescript9.default.isMethodDeclaration(member) && member.name) {
1791
- if (import_typescript9.default.isIdentifier(member.name) && member.name.text === methodName) {
1792
- return member;
1793
- }
1794
- }
1795
- }
1796
- return null;
1797
- }
1798
- /**
1799
- * Generates cache key for a method
1800
- */
1801
- getMethodCacheKey(methodDeclaration) {
1802
- const sourceFile = methodDeclaration.getSourceFile();
1803
- const className = this.getClassName(methodDeclaration);
1804
- const methodName = methodDeclaration.name?.getText() || "unknown";
1805
- const line = sourceFile.getLineAndCharacterOfPosition(methodDeclaration.getStart()).line;
1806
- return `${sourceFile.fileName}:${className}:${methodName}:${line}`;
1807
- }
1808
- /**
1809
- * Gets class name from method declaration
1810
- */
1811
- getClassName(methodDeclaration) {
1812
- let node = methodDeclaration;
1813
- while (node) {
1814
- if (import_typescript9.default.isClassDeclaration(node)) {
1815
- return node.name?.text || "Unknown";
1816
- }
1817
- node = node.parent;
1818
- }
1819
- return "Unknown";
1820
- }
1821
- /**
1822
- * Clears the analysis cache
1823
- */
1824
- clearCache() {
1825
- this.cache.clear();
1826
- this.analyzedMethods.clear();
1827
- }
1828
- /**
1829
- * Gets cache statistics
1830
- */
1831
- getCacheStats() {
1832
- return {
1833
- cached: this.cache.size,
1834
- analyzed: this.analyzedMethods.size
1835
- };
1836
- }
1837
- };
1838
- function analyzeControllerWithServiceCalls(methodDeclaration, checker, program, options = {}) {
1839
- if (!program) {
1840
- return null;
1841
- }
1842
- const analyzer = new ServiceCallAnalyzer(checker, program);
1843
- return analyzer.analyzeControllerMethod(methodDeclaration, options);
1844
- }
1845
-
1846
- // src/compiler/schema/queryBuilderAnalyzer.ts
1847
- function analyzeQueryBuilderForSchema(methodDeclaration, checker, options = {}) {
1848
- const body = methodDeclaration.body;
1849
- if (!body) {
1850
- return null;
1851
- }
1852
- const trackedSchema = analyzeWithVariableTracking(body, checker, options);
1853
- if (trackedSchema) {
1854
- return trackedSchema;
1855
- }
1856
- const returnStatement = findReturnStatement(body);
1857
- if (!returnStatement) {
1858
- return null;
1859
- }
1860
- const callChain = analyzeReturnExpression(returnStatement.expression);
1861
- if (!callChain) {
1862
- return null;
1863
- }
1864
- return parseQueryBuilderChain(callChain, checker, options);
1865
- }
1866
- function analyzeQueryBuilderWithServiceCalls(methodDeclaration, checker, program, options = {}, operationInfo) {
1867
- let schema = analyzeQueryBuilderForSchema(methodDeclaration, checker, options);
1868
- if (!schema && program) {
1869
- try {
1870
- schema = analyzeControllerWithServiceCalls(methodDeclaration, checker, program, {
1871
- maxDepth: options.maxDepth,
1872
- analyzeHelpers: options.analyzeHelpers
1873
- });
1874
- } catch (error) {
1875
- console.warn("Service call analysis failed:", error);
1876
- }
1877
- }
1878
- return {
1879
- detected: schema !== null,
1880
- schema,
1881
- ...operationInfo
1882
- };
1883
- }
1884
- function analyzeWithVariableTracking(body, checker, options) {
1885
- let queryBuilderVar = null;
1886
- let entityName = null;
1887
- const selectedFields = /* @__PURE__ */ new Set();
1888
- const includes = {};
1889
- let isPaged = false;
1890
- let hasReturn = false;
1891
- for (const statement of body.statements) {
1892
- if (import_typescript10.default.isReturnStatement(statement)) {
1893
- hasReturn = true;
1894
- const returnExpr = statement.expression;
1895
- if (returnExpr && import_typescript10.default.isCallExpression(returnExpr)) {
1896
- const callExpr = returnExpr;
1897
- if (import_typescript10.default.isIdentifier(callExpr.expression) && queryBuilderVar) {
1898
- const varName = callExpr.expression.text;
1899
- if (varName === queryBuilderVar) {
1900
- const methodName = callExpr.expression.text;
1901
- if (methodName === "executePaged") {
1902
- isPaged = true;
1903
- }
1904
- }
1905
- }
1906
- if (import_typescript10.default.isPropertyAccessExpression(callExpr.expression) && queryBuilderVar) {
1907
- const propAccess = callExpr.expression;
1908
- if (import_typescript10.default.isIdentifier(propAccess.expression) && propAccess.expression.text === queryBuilderVar) {
1909
- const methodName = propAccess.name.text;
1910
- if (methodName === "executePaged") {
1911
- isPaged = true;
1912
- }
1913
- }
1914
- }
1915
- }
1916
- continue;
1917
- }
1918
- if (!import_typescript10.default.isExpressionStatement(statement)) {
1919
- if (import_typescript10.default.isVariableStatement(statement)) {
1920
- for (const declaration of statement.declarationList.declarations) {
1921
- if (!import_typescript10.default.isIdentifier(declaration.name)) continue;
1922
- const varName = declaration.name.text;
1923
- const initializer = declaration.initializer;
1924
- if (!initializer || !import_typescript10.default.isCallExpression(initializer)) continue;
1925
- const opInfo = extractChainedOperation(initializer);
1926
- if (opInfo && (opInfo.operation === "selectFromEntity" || opInfo.operation === "selectFrom")) {
1927
- queryBuilderVar = varName;
1928
- if (opInfo.entityName) {
1929
- entityName = opInfo.entityName;
1930
- }
1931
- }
1932
- }
1933
- }
1934
- continue;
1935
- }
1936
- const expr = statement.expression;
1937
- if (import_typescript10.default.isBinaryExpression(expr) && expr.operatorToken.kind === import_typescript10.default.SyntaxKind.EqualsToken) {
1938
- if (!import_typescript10.default.isIdentifier(expr.left)) {
1939
- continue;
1940
- }
1941
- const varName = expr.left.text;
1942
- const rightSide = expr.right;
1943
- if (import_typescript10.default.isCallExpression(rightSide)) {
1944
- const opInfo = extractChainedOperation(rightSide);
1945
- if (opInfo) {
1946
- if (opInfo.operation === "selectFromEntity" || opInfo.operation === "selectFrom") {
1947
- queryBuilderVar = varName;
1948
- if (opInfo.entityName) {
1949
- entityName = opInfo.entityName;
1950
- }
1951
- }
1952
- if ((opInfo.operation === "select" || opInfo.operation === "include") && queryBuilderVar === varName) {
1953
- if (opInfo.operation === "select") {
1954
- for (const field of opInfo.fields || []) {
1955
- selectedFields.add(field);
1956
- }
1957
- } else if (opInfo.operation === "include" && opInfo.includeArg) {
1958
- const parsedIncludes = parseIncludeObjectLiteral(opInfo.includeArg);
1959
- if (parsedIncludes) {
1960
- for (const [relName, relSchema] of Object.entries(parsedIncludes)) {
1961
- includes[relName] = relSchema;
1962
- }
1963
- }
1964
- }
1965
- }
1966
- }
1967
- }
1968
- }
1969
- }
1970
- if (!hasReturn || !queryBuilderVar || !entityName) {
1971
- return null;
1972
- }
1973
- return {
1974
- entityName,
1975
- selectedFields: Array.from(selectedFields),
1976
- includes,
1977
- isPaged
1978
- };
1979
- }
1980
- function extractChainedOperation(callExpr) {
1981
- if (import_typescript10.default.isIdentifier(callExpr.expression)) {
1982
- const methodName2 = callExpr.expression.text;
1983
- if (methodName2 === "selectFromEntity" || methodName2 === "selectFrom") {
1984
- const entityArg = callExpr.arguments[0];
1985
- let entityName = null;
1986
- if (import_typescript10.default.isIdentifier(entityArg)) {
1987
- entityName = entityArg.text;
1988
- } else if (import_typescript10.default.isPropertyAccessExpression(entityArg)) {
1989
- entityName = entityArg.name.text;
1990
- }
1991
- return {
1992
- operation: methodName2 === "selectFromEntity" ? "selectFromEntity" : "selectFrom",
1993
- fields: null,
1994
- includeArg: null,
1995
- entityName
1996
- };
1997
- }
1998
- }
1999
- if (!import_typescript10.default.isPropertyAccessExpression(callExpr.expression)) {
2000
- return null;
2001
- }
2002
- const propAccess = callExpr.expression;
2003
- const methodName = propAccess.name.text;
2004
- if (methodName === "select") {
2005
- const fields = [];
2006
- for (const arg of callExpr.arguments) {
2007
- if (import_typescript10.default.isStringLiteral(arg)) {
2008
- fields.push(arg.text);
2009
- }
2010
- }
2011
- return {
2012
- operation: "select",
2013
- fields,
2014
- includeArg: null,
2015
- entityName: null
2016
- };
2017
- }
2018
- if (methodName === "include") {
2019
- return {
2020
- operation: "include",
2021
- fields: null,
2022
- includeArg: callExpr.arguments[0] || null,
2023
- entityName: null
2024
- };
2025
- }
2026
- return null;
2027
- }
2028
- function parseIncludeObjectLiteral(arg) {
2029
- if (!import_typescript10.default.isObjectLiteralExpression(arg)) {
2030
- return null;
2031
- }
2032
- const includes = {};
2033
- for (const prop of arg.properties) {
2034
- if (!import_typescript10.default.isPropertyAssignment(prop) || !import_typescript10.default.isIdentifier(prop.name)) {
2035
- continue;
2036
- }
2037
- const relationName = prop.name.text;
2038
- const value = prop.initializer;
2039
- if (value.kind === import_typescript10.default.SyntaxKind.TrueKeyword) {
2040
- includes[relationName] = true;
2041
- } else if (import_typescript10.default.isObjectLiteralExpression(value)) {
2042
- const nestedSchema = parseNestedInclude(value, 0);
2043
- if (nestedSchema) {
2044
- includes[relationName] = nestedSchema;
2045
- }
2046
- }
2047
- }
2048
- return includes;
2049
- }
2050
- function parseNestedInclude(obj, depth) {
2051
- const selectedFields = [];
2052
- const includes = {};
2053
- for (const prop of obj.properties) {
2054
- if (!import_typescript10.default.isPropertyAssignment(prop) || !import_typescript10.default.isIdentifier(prop.name)) {
2055
- continue;
2056
- }
2057
- const propName = prop.name.text;
2058
- const value = prop.initializer;
2059
- if (propName === "select" && import_typescript10.default.isArrayLiteralExpression(value)) {
2060
- for (const element of value.elements) {
2061
- if (import_typescript10.default.isStringLiteral(element)) {
2062
- selectedFields.push(element.text);
2063
- }
2064
- }
2065
- } else if (propName === "include" && import_typescript10.default.isObjectLiteralExpression(value)) {
2066
- const nestedIncludes = parseIncludeObjectLiteral(value);
2067
- if (nestedIncludes) {
2068
- for (const [relName, relSchema] of Object.entries(nestedIncludes)) {
2069
- includes[relName] = relSchema;
2070
- }
2071
- }
2072
- }
2073
- }
2074
- return {
2075
- entityName: "",
2076
- selectedFields,
2077
- includes,
2078
- isPaged: false
2079
- };
2080
- }
2081
- function getMethodName(expression) {
2082
- if (import_typescript10.default.isIdentifier(expression)) {
2083
- return expression.text;
2084
- }
2085
- if (import_typescript10.default.isPropertyAccessExpression(expression)) {
2086
- return expression.name.text;
2087
- }
2088
- return null;
2089
- }
2090
- function findReturnStatement(body) {
2091
- let returnStatement = null;
2092
- for (const statement of body.statements) {
2093
- if (import_typescript10.default.isReturnStatement(statement)) {
2094
- if (returnStatement !== null) {
2095
- return null;
2096
- }
2097
- returnStatement = statement;
2098
- }
2099
- }
2100
- return returnStatement;
2101
- }
2102
- function analyzeReturnExpression(expression) {
2103
- if (!expression) {
2104
- return null;
2105
- }
2106
- if (import_typescript10.default.isCallExpression(expression)) {
2107
- return buildCallChain(expression, null);
2108
- }
2109
- return null;
2110
- }
2111
- function buildCallChain(node, parent) {
2112
- if (import_typescript10.default.isCallExpression(node)) {
2113
- const callNode = {
2114
- expression: node.expression,
2115
- methodName: getMethodName(node.expression),
2116
- arguments: node.arguments,
2117
- parent
2118
- };
2119
- if (import_typescript10.default.isPropertyAccessExpression(node.expression)) {
2120
- return buildCallChain(node.expression.expression, callNode);
2121
- }
2122
- return callNode;
2123
- }
2124
- return parent;
2125
- }
2126
- function parseQueryBuilderChain(chain, checker, options) {
2127
- if (!chain) {
2128
- return null;
2129
- }
2130
- const rootNode = findSelectFromEntityCall(chain);
2131
- if (!rootNode) {
2132
- return null;
2133
- }
2134
- const entityName = extractEntityName(rootNode, checker);
2135
- if (!entityName) {
2136
- return null;
2137
- }
2138
- const selectedFields = /* @__PURE__ */ new Set();
2139
- const includes = {};
2140
- let isPaged = false;
2141
- let currentNode = chain;
2142
- while (currentNode) {
2143
- const methodName = currentNode.methodName;
2144
- if (methodName === "select") {
2145
- for (const arg of currentNode.arguments) {
2146
- if (import_typescript10.default.isStringLiteral(arg)) {
2147
- selectedFields.add(arg.text);
2148
- }
2149
- }
2150
- } else if (methodName === "include") {
2151
- parseIncludeArgument(currentNode.arguments[0], includes, checker, options, 0);
2152
- } else if (methodName === "executePaged") {
2153
- isPaged = true;
2154
- }
2155
- currentNode = currentNode.parent;
2156
- }
2157
- return {
2158
- entityName,
2159
- selectedFields: Array.from(selectedFields),
2160
- includes,
2161
- isPaged
2162
- };
2163
- }
2164
- function findSelectFromEntityCall(chain) {
2165
- let currentNode = chain;
2166
- let lastNode = null;
2167
- while (currentNode) {
2168
- if (currentNode.methodName === "selectFromEntity" || currentNode.methodName === "selectFrom") {
2169
- return currentNode;
2170
- }
2171
- lastNode = currentNode;
2172
- currentNode = currentNode.parent;
2173
- }
2174
- return lastNode;
2175
- }
2176
- function extractEntityName(callNode, checker) {
2177
- if (callNode.arguments.length === 0) {
2178
- return null;
2179
- }
2180
- const entityArg = callNode.arguments[0];
2181
- if (import_typescript10.default.isIdentifier(entityArg)) {
2182
- return entityArg.text;
2183
- }
2184
- if (import_typescript10.default.isPropertyAccessExpression(entityArg)) {
2185
- return entityArg.name.text;
2186
- }
2187
- return null;
2188
- }
2189
- function parseIncludeArgument(arg, includes, checker, options, depth) {
2190
- if (!arg) {
2191
- return;
2192
- }
2193
- if (import_typescript10.default.isObjectLiteralExpression(arg)) {
2194
- for (const prop of arg.properties) {
2195
- if (!import_typescript10.default.isPropertyAssignment(prop) || !import_typescript10.default.isIdentifier(prop.name)) {
2196
- continue;
2197
- }
2198
- const relationName = prop.name.text;
2199
- const value = prop.initializer;
2200
- if (value.kind === import_typescript10.default.SyntaxKind.TrueKeyword) {
2201
- includes[relationName] = true;
2202
- } else if (import_typescript10.default.isObjectLiteralExpression(value)) {
2203
- const maxDepth = options.maxDepth ?? 5;
2204
- if (depth < maxDepth) {
2205
- const nestedSchema = parseNestedInclude(value, depth + 1);
2206
- if (nestedSchema) {
2207
- includes[relationName] = nestedSchema;
2208
- }
2209
- }
2210
- }
2211
- }
2212
- }
2213
- }
2214
-
2215
- // src/compiler/schema/queryBuilderSchemaBuilder.ts
2216
- var import_typescript11 = require("typescript");
2217
- function wrapInPaginatedResult(schema) {
2218
- return {
2219
- type: "object",
2220
- properties: {
2221
- items: {
2222
- type: "array",
2223
- items: schema
2224
- },
2225
- page: { type: "integer" },
2226
- pageSize: { type: "integer" },
2227
- totalItems: { type: "integer" }
2228
- },
2229
- required: ["items", "page", "pageSize", "totalItems"]
2230
- };
2231
- }
2232
-
2233
- // src/compiler/schema/openapi.ts
2234
- var METAL_ORM_WRAPPER_NAMES2 = ["BelongsToReference", "HasOneReference", "HasManyCollection", "ManyToManyCollection"];
2235
- function generateOpenAPI(controllers, checker, options = {}) {
2236
- const components = /* @__PURE__ */ new Map();
2237
- const ctx = {
2238
- checker,
2239
- components,
2240
- typeStack: /* @__PURE__ */ new Set(),
2241
- typeNameStack: [],
2242
- mode: "response"
2243
- };
2244
- const paths = {};
2245
- const { onProgress, onQueryBuilderProgress } = options;
2246
- let totalOperations = 0;
2247
- for (const controller of controllers) {
2248
- totalOperations += controller.operations.length;
2249
- }
2250
- let currentOperation = 0;
2251
- for (let i = 0; i < controllers.length; i++) {
2252
- const controller = controllers[i];
2253
- if (onProgress) {
2254
- onProgress(`Processing controller ${controller.className}`, i + 1, controllers.length);
2255
- }
2256
- for (const operation of controller.operations) {
2257
- const fullPath = convertToOpenApiPath(controller.basePath, operation.path);
2258
- if (!paths[fullPath]) {
2259
- paths[fullPath] = {};
2260
- }
2261
- const method = operation.httpMethod.toLowerCase();
2262
- const analysisResult = analyzeQueryBuilderWithServiceCalls(
2263
- operation.methodDeclaration,
2264
- checker,
2265
- null,
2266
- // TODO: Pass program when available
2267
- {},
2268
- {
2269
- methodName: operation.operationId,
2270
- httpMethod: operation.httpMethod,
2271
- path: operation.path,
2272
- operationId: operation.operationId
2273
- }
2274
- );
2275
- if (onQueryBuilderProgress) {
2276
- currentOperation++;
2277
- onQueryBuilderProgress({
2278
- controller: controller.className,
2279
- operation: operation.operationId,
2280
- method: operation.httpMethod,
2281
- path: operation.path,
2282
- queryBuilderDetected: analysisResult.detected,
2283
- entityName: analysisResult.schema?.entityName,
2284
- selectedFields: analysisResult.schema?.selectedFields,
2285
- isPaged: analysisResult.schema?.isPaged,
2286
- current: currentOperation,
2287
- total: totalOperations
2288
- });
2289
- }
2290
- paths[fullPath][method] = buildOperation(operation, ctx, controller.consumes);
2291
- }
2292
- }
2293
- const schemas = Object.fromEntries(components);
2294
- if (onProgress) {
2295
- onProgress("Generating and cleaning schemas", controllers.length, controllers.length);
2296
- }
2297
- cleanupMetalOrmWrappers(schemas, paths);
2298
- return {
2299
- openapi: "3.1.0",
2300
- info: {
2301
- title: options.title ?? "API",
2302
- version: options.version ?? "1.0.0"
2303
- },
2304
- components: {
2305
- schemas
2306
- },
2307
- paths
2308
- };
2309
- }
2310
- function cleanupMetalOrmWrappers(schemas, paths) {
2311
- const schemasToDelete = /* @__PURE__ */ new Set();
2312
- for (const wrapperName of METAL_ORM_WRAPPER_NAMES2) {
2313
- if (schemas[wrapperName]) {
2314
- schemasToDelete.add(wrapperName);
2315
- }
2316
- if (schemas[`${wrapperName}Api`]) {
2317
- schemasToDelete.add(`${wrapperName}Api`);
2318
- }
2319
- }
2320
- for (const schema of Object.values(schemas)) {
2321
- cleanupSchemaRefs(schema, schemasToDelete);
2322
- }
2323
- for (const pathItem of Object.values(paths)) {
2324
- cleanupPathItemRefs(pathItem, schemasToDelete);
2325
- }
2326
- for (const schemaName of schemasToDelete) {
2327
- delete schemas[schemaName];
2328
- }
2329
- }
2330
- function cleanupSchemaRefs(schema, schemasToDelete) {
2331
- if (typeof schema !== "object" || schema === null) {
2332
- return;
2333
- }
2334
- if (schema.properties) {
2335
- for (const propName of Object.keys(schema.properties)) {
2336
- const propSchema = schema.properties[propName];
2337
- if (propSchema.$ref && typeof propSchema.$ref === "string") {
2338
- const refName = propSchema.$ref.replace("#/components/schemas/", "");
2339
- if (schemasToDelete.has(refName)) {
2340
- delete schema.properties[propName];
2341
- if (schema.required && Array.isArray(schema.required)) {
2342
- schema.required = schema.required.filter((r) => r !== propName);
2343
- }
2344
- }
2345
- } else {
2346
- cleanupSchemaRefs(propSchema, schemasToDelete);
2347
- }
2348
- }
2349
- }
2350
- if (schema.items) {
2351
- cleanupSchemaRefs(schema.items, schemasToDelete);
2352
- }
2353
- if (schema.allOf) {
2354
- for (const item of schema.allOf) {
2355
- cleanupSchemaRefs(item, schemasToDelete);
2356
- }
2357
- }
2358
- }
2359
- function cleanupPathItemRefs(pathItem, schemasToDelete) {
2360
- if (typeof pathItem !== "object" || pathItem === null) {
2361
- return;
2362
- }
2363
- for (const method of Object.keys(pathItem)) {
2364
- const operation = pathItem[method];
2365
- if (typeof operation !== "object" || operation === null) continue;
2366
- if (operation.requestBody) {
2367
- cleanupRequestBodyRefs(operation.requestBody, schemasToDelete);
2368
- }
2369
- if (operation.responses) {
2370
- const responses = Object.values(operation.responses);
2371
- for (const response of responses) {
2372
- if (response.content) {
2373
- const contentTypes = Object.values(response.content);
2374
- for (const contentType of contentTypes) {
2375
- if (contentType.schema) {
2376
- cleanupSchemaRefs(contentType.schema, schemasToDelete);
2377
- }
2378
- }
2379
- }
2380
- }
2381
- }
2382
- }
2383
- }
2384
- function cleanupRequestBodyRefs(requestBody, schemasToDelete) {
2385
- if (typeof requestBody !== "object" || requestBody === null) return;
2386
- if (requestBody.content) {
2387
- const contentTypes = Object.values(requestBody.content);
2388
- for (const contentType of contentTypes) {
2389
- if (contentType.schema) {
2390
- cleanupSchemaRefs(contentType.schema, schemasToDelete);
2391
- }
2392
- }
2393
- }
2394
- if (requestBody.properties) {
2395
- for (const propName of Object.keys(requestBody.properties)) {
2396
- const propSchema = requestBody.properties[propName];
2397
- if (propSchema.$ref && typeof propSchema.$ref === "string") {
2398
- const refName = propSchema.$ref.replace("#/components/schemas/", "");
2399
- if (schemasToDelete.has(refName)) {
2400
- delete requestBody.properties[propName];
2401
- if (requestBody.required && Array.isArray(requestBody.required)) {
2402
- requestBody.required = requestBody.required.filter((r) => r !== propName);
2403
- }
2404
- }
2405
- } else {
2406
- cleanupSchemaRefs(propSchema, schemasToDelete);
2407
- }
2408
- }
2409
- }
2410
- }
2411
- function convertToOpenApiPath(basePath, path4) {
2412
- const base = basePath.endsWith("/") ? basePath.slice(0, -1) : basePath;
2413
- const converted = path4.replace(/:([^/]+)/g, "{$1}");
2414
- let fullPath = base + converted || "/";
2415
- if (fullPath.endsWith("/") && fullPath !== "/") {
2416
- fullPath = fullPath.slice(0, -1);
2417
- }
2418
- return fullPath;
2419
- }
2420
- function tryInferQueryBuilderSchema(operation, checker) {
2421
- return analyzeQueryBuilderForSchema(operation.methodDeclaration, checker) ?? null;
2422
- }
2423
- function getEntityTypeFromReturnType(operation, checker) {
2424
- const returnType = operation.returnType;
2425
- const unwrapPromise2 = (type) => {
2426
- const symbol2 = type.getSymbol();
2427
- if (symbol2?.getName() === "Promise") {
2428
- const typeArgs = type.typeArguments;
2429
- if (typeArgs && typeArgs.length > 0) {
2430
- return typeArgs[0];
2431
- }
2432
- }
2433
- return type;
2434
- };
2435
- const innerType = unwrapPromise2(returnType);
2436
- const symbol = innerType.getSymbol();
2437
- if (symbol?.getName() === "PaginatedResult") {
2438
- const typeArgs = innerType.typeArguments;
2439
- if (typeArgs && typeArgs.length > 0) {
2440
- return typeArgs[0];
2441
- }
2442
- }
2443
- return null;
2444
- }
2445
- function filterSchemaByQueryBuilder(querySchema, operation, ctx) {
2446
- const entityType = getEntityTypeFromReturnType(operation, ctx.checker);
2447
- if (!entityType) {
2448
- return {};
2449
- }
2450
- const entitySchema = typeToJsonSchema(entityType, ctx);
2451
- let baseSchema = entitySchema;
2452
- if (entitySchema.$ref && entitySchema.$ref.startsWith("#/components/schemas/")) {
2453
- const schemaName = entitySchema.$ref.replace("#/components/schemas/", "");
2454
- const componentSchema = ctx.components.get(schemaName);
2455
- if (componentSchema) {
2456
- baseSchema = componentSchema;
2457
- }
2458
- }
2459
- if (!baseSchema.properties || Object.keys(baseSchema.properties).length === 0) {
2460
- return {};
2461
- }
2462
- const filteredSchema = buildFilteredSchema(querySchema, baseSchema);
2463
- if (querySchema.isPaged) {
2464
- return wrapInPaginatedResult(filteredSchema);
2465
- }
2466
- return filteredSchema;
2467
- }
2468
- function buildFilteredSchema(querySchema, entitySchema) {
2469
- const properties = {};
2470
- const required = [];
2471
- for (const field of querySchema.selectedFields) {
2472
- if (entitySchema.properties?.[field]) {
2473
- properties[field] = entitySchema.properties[field];
2474
- if (entitySchema.required && entitySchema.required.includes(field)) {
2475
- required.push(field);
2476
- }
2477
- }
2478
- }
2479
- for (const [relationName, includeSpec] of Object.entries(querySchema.includes)) {
2480
- if (entitySchema.properties?.[relationName]) {
2481
- properties[relationName] = {
2482
- type: "object",
2483
- properties: {
2484
- id: { type: "integer" }
2485
- },
2486
- required: ["id"]
2487
- };
2488
- if (entitySchema.required && entitySchema.required.includes(relationName)) {
2489
- required.push(relationName);
2490
- }
2491
- }
2492
- }
2493
- const schema = {
2494
- type: "object",
2495
- properties
2496
- };
2497
- if (required.length > 0) {
2498
- schema.required = required;
2499
- }
2500
- return schema;
2501
- }
2502
- function buildOperation(operation, ctx, controllerConsumes) {
2503
- const op = {
2504
- operationId: operation.operationId,
2505
- responses: {}
2506
- };
2507
- const parameters = [];
2508
- buildPathParameters(operation, ctx, parameters);
2509
- buildQueryParameters(operation, ctx, parameters);
2510
- buildHeaderParameters(operation, ctx, parameters);
2511
- buildCookieParameters(operation, ctx, parameters);
2512
- if (parameters.length > 0) {
2513
- op.parameters = parameters;
2514
- }
2515
- const responseCtx = { ...ctx, mode: "response" };
2516
- let responseSchema;
2517
- const querySchema = tryInferQueryBuilderSchema(operation, ctx.checker);
2518
- if (querySchema) {
2519
- const entityType = getEntityTypeFromReturnType(operation, ctx.checker);
2520
- if (entityType) {
2521
- responseSchema = filterSchemaByQueryBuilder(querySchema, operation, responseCtx);
2522
- } else {
2523
- responseSchema = typeToJsonSchema(operation.returnType, responseCtx, operation.returnTypeNode);
2524
- }
2525
- } else {
2526
- responseSchema = typeToJsonSchema(operation.returnType, responseCtx, operation.returnTypeNode);
2527
- }
2528
- const status = operation.httpMethod === "POST" ? 201 : 200;
2529
- op.responses[status] = {
2530
- description: status === 201 ? "Created" : "OK",
2531
- content: {
2532
- "application/json": {
2533
- schema: responseSchema
2534
- }
2535
- }
2536
- };
2537
- if (["POST", "PUT", "PATCH"].includes(operation.httpMethod) && operation.bodyParamIndex !== null) {
2538
- const bodyParam = operation.parameters[operation.bodyParamIndex];
2539
- if (bodyParam) {
2540
- const requestCtx = { ...ctx, mode: "request" };
2541
- let bodySchema = typeToJsonSchema(bodyParam.type, requestCtx);
2542
- bodySchema = mergeBodySchemaAnnotations(bodyParam, requestCtx, bodySchema);
2543
- const contentType = operation.bodyContentType ?? controllerConsumes?.[0] ?? "application/json";
2544
- const requestBody = {
2545
- required: !bodyParam.isOptional,
2546
- content: {}
2547
- };
2548
- if (contentType === "multipart/form-data") {
2549
- requestBody.content["multipart/form-data"] = {
2550
- schema: bodySchema
2551
- };
2552
- } else {
2553
- requestBody.content[contentType] = {
2554
- schema: bodySchema
2555
- };
2556
- }
2557
- op.requestBody = requestBody;
2558
- }
2559
- }
2560
- return op;
2561
- }
2562
- function mergeBodySchemaAnnotations(bodyParam, ctx, schema) {
2563
- if (!schema.properties) return schema;
2564
- const typeSymbol = bodyParam.type.getSymbol();
2565
- if (!typeSymbol) return schema;
2566
- const declarations = typeSymbol.getDeclarations();
2567
- if (!declarations || declarations.length === 0) return schema;
2568
- const classDecl = declarations[0];
2569
- if (!import_typescript12.default.isClassDeclaration(classDecl)) return schema;
2570
- const result = { ...schema };
2571
- const props = { ...result.properties };
2572
- for (const member of classDecl.members) {
2573
- if (!import_typescript12.default.isPropertyDeclaration(member) || !member.name) continue;
2574
- const propName = import_typescript12.default.isIdentifier(member.name) ? member.name.text : null;
2575
- if (!propName) continue;
2576
- if (!props[propName]) continue;
2577
- const frags = extractPropertySchemaFragments(ctx.checker, member);
2578
- if (frags.length > 0) {
2579
- props[propName] = mergeFragments(props[propName], ...frags);
2580
- }
2581
- }
2582
- result.properties = props;
2583
- return result;
2584
- }
2585
-
2586
- // src/compiler/manifest/emit.ts
2587
- var import_typescript13 = __toESM(require("typescript"), 1);
2588
- function generateManifest(controllers, checker, version, validationMode = "ajv-runtime") {
2589
- const components = /* @__PURE__ */ new Map();
2590
- const ctx = {
2591
- checker,
2592
- components,
2593
- typeStack: /* @__PURE__ */ new Set(),
2594
- typeNameStack: [],
2595
- mode: "request"
2596
- };
2597
- const controllerEntries = controllers.map((ctrl) => ({
2598
- controllerId: ctrl.className,
2599
- basePath: ctrl.basePath,
2600
- operations: ctrl.operations.map((op) => buildOperationEntry(op, ctx))
2601
- }));
2602
- const validationConfig = validationMode === "precompiled" ? { mode: "precompiled", precompiledModule: null } : validationMode === "none" ? { mode: "none", precompiledModule: null } : { mode: "ajv-runtime", precompiledModule: null };
2603
- return {
2604
- manifestVersion: 1,
2605
- generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
2606
- generator: {
2607
- name: "adorn-api",
2608
- version,
2609
- typescript: import_typescript13.default.version
2610
- },
2611
- schemas: {
2612
- kind: "openapi-3.1",
2613
- file: "./openapi.json",
2614
- componentsSchemasPointer: "/components/schemas"
2615
- },
2616
- validation: validationConfig,
2617
- controllers: controllerEntries
2618
- };
2619
- }
2620
- function resolveSchemaRef2(schema, components) {
2621
- const ref = schema.$ref;
2622
- if (typeof ref !== "string" || !ref.startsWith("#/components/schemas/")) {
2623
- return schema;
2624
- }
2625
- const name = ref.replace("#/components/schemas/", "");
2626
- const next = components.get(name);
2627
- if (!next) return schema;
2628
- return resolveSchemaRef2(next, components);
2629
- }
2630
- function resolveAndCollectObjectProps2(schema, components) {
2631
- const resolved = resolveSchemaRef2(schema, components);
2632
- const properties = {};
2633
- const required = [];
2634
- const processSchema = (s) => {
2635
- const current = resolveSchemaRef2(s, components);
2636
- if (current.properties) {
2637
- for (const [key, val] of Object.entries(current.properties)) {
2638
- if (!properties[key]) {
2639
- properties[key] = val;
2640
- }
2641
- }
2642
- }
2643
- if (current.required) {
2644
- for (const req of current.required) {
2645
- if (!required.includes(req)) {
2646
- required.push(req);
2647
- }
2648
- }
2649
- }
2650
- if (current.allOf) {
2651
- for (const branch of current.allOf) {
2652
- processSchema(branch);
2653
- }
2654
- }
2655
- };
2656
- processSchema(resolved);
2657
- return { properties, required };
2658
- }
2659
- function isDeepObjectSchema2(schema, components) {
2660
- const resolved = resolveSchemaRef2(schema, components);
2661
- if (resolved.type === "array") {
2662
- return false;
2663
- }
2664
- if (resolved.type === "object" || resolved.properties || resolved.additionalProperties) {
2665
- return true;
2666
- }
2667
- if (resolved.allOf) {
2668
- for (const branch of resolved.allOf) {
2669
- if (isDeepObjectSchema2(branch, components)) {
2670
- return true;
2671
- }
2672
- }
2673
- }
2674
- return false;
2675
- }
2676
- function isObjectLikeSchema2(schema, components) {
2677
- const resolved = resolveSchemaRef2(schema, components);
2678
- if (resolved.type === "object" || resolved.properties || resolved.additionalProperties) {
2679
- return true;
2680
- }
2681
- if (resolved.allOf) {
2682
- for (const branch of resolved.allOf) {
2683
- if (isObjectLikeSchema2(branch, components)) {
2684
- return true;
2685
- }
2686
- }
2687
- }
2688
- if (resolved.type === "array" && resolved.items) {
2689
- const itemsSchema = resolveSchemaRef2(resolved.items, components);
2690
- return isObjectLikeSchema2(itemsSchema, components);
2691
- }
2692
- return false;
2693
- }
2694
- function buildOperationEntry(op, ctx) {
2695
- const args = {
2696
- body: null,
2697
- path: [],
2698
- query: [],
2699
- headers: [],
2700
- cookies: []
2701
- };
2702
- buildPathArgs(op, ctx, args);
2703
- buildQueryArgs(op, ctx, args);
2704
- buildHeaderArgs(op, ctx, args);
2705
- buildCookieArgs(op, ctx, args);
2706
- if (op.bodyParamIndex !== null) {
2707
- const bodyParam = op.parameters[op.bodyParamIndex];
2708
- if (bodyParam) {
2709
- const bodySchema = typeToJsonSchema(bodyParam.type, ctx);
2710
- const schemaRef2 = bodySchema.$ref ?? "#/components/schemas/InlineBody";
2711
- args.body = {
2712
- index: bodyParam.index,
2713
- required: !bodyParam.isOptional,
2714
- contentType: op.bodyContentType ?? "application/json",
2715
- schemaRef: schemaRef2
2716
- };
2717
- }
2718
- }
2719
- const responseSchema = typeToJsonSchema(op.returnType, ctx, op.returnTypeNode);
2720
- const status = op.httpMethod === "POST" ? 201 : 200;
2721
- let schemaRef = responseSchema.$ref;
2722
- let isArray = false;
2723
- if (!schemaRef && responseSchema.type === "array" && responseSchema.items?.$ref) {
2724
- schemaRef = responseSchema.items.$ref;
2725
- isArray = true;
2726
- } else if (!schemaRef) {
2727
- schemaRef = "#/components/schemas/InlineResponse";
2728
- }
2729
- return {
2730
- operationId: op.operationId,
2731
- http: {
2732
- method: op.httpMethod,
2733
- path: op.path
2734
- },
2735
- handler: {
2736
- methodName: op.methodName
2737
- },
2738
- args,
2739
- responses: [
2740
- {
2741
- status,
2742
- contentType: "application/json",
2743
- schemaRef,
2744
- isArray
2745
- }
2746
- ]
2747
- };
2748
- }
2749
- function buildPathArgs(op, ctx, args) {
2750
- for (const paramIndex of op.pathParamIndices) {
2751
- const param = op.parameters[paramIndex];
2752
- if (param) {
2753
- const paramSchema = typeToJsonSchema(param.type, ctx);
2754
- args.path.push({
2755
- name: param.name,
2756
- index: param.index,
2757
- required: !param.isOptional,
2758
- schemaRef: paramSchema.$ref ?? "#/components/schemas/InlinePathParam",
2759
- schemaType: paramSchema.type
2760
- });
2761
- }
2762
- }
2763
- }
2764
- function buildQueryArgs(op, ctx, args) {
2765
- if (op.queryObjectParamIndex !== null) {
2766
- const queryParam = op.parameters[op.queryObjectParamIndex];
2767
- if (!queryParam) return;
2768
- const querySchema = typeToJsonSchema(queryParam.type, ctx);
2769
- const { properties: queryObjProps, required: queryRequired } = resolveAndCollectObjectProps2(querySchema, ctx.components);
2770
- for (const [propName, propSchema] of Object.entries(queryObjProps)) {
2771
- const isRequired = queryRequired.includes(propName) ?? false;
2772
- const isDeepObject = isDeepObjectSchema2(propSchema, ctx.components);
2773
- const isObjectLike = isObjectLikeSchema2(propSchema, ctx.components);
2774
- let schemaRef = propSchema.$ref;
2775
- if (!schemaRef) {
2776
- schemaRef = "#/components/schemas/InlineQueryParam";
2777
- }
2778
- args.query.push({
2779
- name: propName,
2780
- index: queryParam.index,
2781
- required: isRequired,
2782
- schemaRef,
2783
- schemaType: propSchema.type,
2784
- serialization: isDeepObject ? { style: "deepObject", explode: true } : void 0,
2785
- content: !isDeepObject && isObjectLike ? "application/json" : void 0
2786
- });
2787
- }
2788
- }
2789
- for (const paramIndex of op.queryParamIndices) {
2790
- const param = op.parameters[paramIndex];
2791
- if (param) {
2792
- const paramSchema = typeToJsonSchema(param.type, ctx);
2793
- const isDeepObject = isDeepObjectSchema2(paramSchema, ctx.components);
2794
- const isObjectLike = isObjectLikeSchema2(paramSchema, ctx.components);
2795
- const schemaRef = paramSchema.$ref ?? "#/components/schemas/InlineQueryParam";
2796
- args.query.push({
2797
- name: param.name,
2798
- index: param.index,
2799
- required: !param.isOptional,
2800
- schemaRef,
2801
- schemaType: paramSchema.type,
2802
- serialization: isDeepObject ? { style: "deepObject", explode: true } : void 0,
2803
- content: !isDeepObject && isObjectLike ? "application/json" : void 0
2804
- });
2805
- }
2806
- }
2807
- }
2808
- function buildHeaderArgs(op, ctx, args) {
2809
- if (op.headerObjectParamIndex === null) return;
2810
- const headerParam = op.parameters[op.headerObjectParamIndex];
2811
- if (!headerParam) return;
2812
- const headerSchema = typeToJsonSchema(headerParam.type, ctx);
2813
- if (!headerSchema.properties) return;
2814
- for (const [propName, propSchema] of Object.entries(headerSchema.properties)) {
2815
- const isRequired = headerSchema.required?.includes(propName) ?? false;
2816
- let schemaRef = propSchema.$ref;
2817
- if (!schemaRef) {
2818
- schemaRef = "#/components/schemas/InlineHeaderParam";
2819
- }
2820
- args.headers.push({
2821
- name: propName,
2822
- index: headerParam.index,
2823
- required: isRequired,
2824
- schemaRef,
2825
- schemaType: propSchema.type
2826
- });
2827
- }
2828
- }
2829
- function buildCookieArgs(op, ctx, args) {
2830
- if (op.cookieObjectParamIndex === null) return;
2831
- const cookieParam = op.parameters[op.cookieObjectParamIndex];
2832
- if (!cookieParam) return;
2833
- const cookieSchema = typeToJsonSchema(cookieParam.type, ctx);
2834
- if (!cookieSchema.properties) return;
2835
- for (const [propName, propSchema] of Object.entries(cookieSchema.properties)) {
2836
- const isRequired = cookieSchema.required?.includes(propName) ?? false;
2837
- let schemaRef = propSchema.$ref;
2838
- if (!schemaRef) {
2839
- schemaRef = "#/components/schemas/InlineCookieParam";
2840
- }
2841
- args.cookies.push({
2842
- name: propName,
2843
- index: cookieParam.index,
2844
- required: isRequired,
2845
- schemaRef,
2846
- schemaType: propSchema.type,
2847
- serialization: { style: "form", explode: true }
2848
- });
2849
- }
2850
- }
2851
-
2852
- // src/compiler/validation/emitPrecompiledValidators.ts
2853
- var import_node_fs2 = __toESM(require("fs"), 1);
2854
- var import_node_path2 = __toESM(require("path"), 1);
2855
- var import_node_crypto = __toESM(require("crypto"), 1);
2856
- var import_ajv = __toESM(require("ajv"), 1);
2857
- var import_ajv_formats = __toESM(require("ajv-formats"), 1);
2858
- var OAS_SCHEMA_ONLY = /* @__PURE__ */ new Set(["discriminator", "xml", "externalDocs", "example"]);
2859
- function sanitizeSchemaForAjv(schema) {
2860
- if (schema === null || typeof schema !== "object") return schema;
2861
- if (Array.isArray(schema)) return schema.map(sanitizeSchemaForAjv);
2862
- const out = {};
2863
- for (const [k, v] of Object.entries(schema)) {
2864
- if (OAS_SCHEMA_ONLY.has(k)) continue;
2865
- if (k.startsWith("x-")) continue;
2866
- out[k] = sanitizeSchemaForAjv(v);
2867
- }
2868
- return out;
2869
- }
2870
- function rewriteComponentRefs(schema) {
2871
- if (schema === null || typeof schema !== "object") return schema;
2872
- if (Array.isArray(schema)) return schema.map(rewriteComponentRefs);
2873
- if (typeof schema.$ref === "string") {
2874
- const ref = schema.$ref;
2875
- const m = ref.match(/^#\/components\/schemas\/(.+)$/);
2876
- if (m) {
2877
- return { ...schema, $ref: m[1] };
2878
- }
2879
- }
2880
- const out = {};
2881
- for (const [k, v] of Object.entries(schema)) {
2882
- out[k] = rewriteComponentRefs(v);
2883
- }
2884
- return out;
2885
- }
2886
- function safeId(s) {
2887
- return s.replace(/[^A-Za-z0-9_]/g, "_").replace(/^[^A-Za-z_]/, "_$&");
2888
- }
2889
- function schemaNameFromRef(schemaRef) {
2890
- return schemaRef.replace(/^#\/components\/schemas\//, "");
2891
- }
2892
- async function emitPrecompiledValidators(opts) {
2893
- const outDir = opts.outDir;
2894
- const cjsPath = import_node_path2.default.join(outDir, "validators.cjs");
2895
- const esmPath = import_node_path2.default.join(outDir, "validators.mjs");
2896
- const metaPath = import_node_path2.default.join(outDir, "validators.meta.json");
2897
- import_node_fs2.default.mkdirSync(outDir, { recursive: true });
2898
- const schemas = [];
2899
- for (const [name, sch] of Object.entries(opts.openapi.components?.schemas ?? {})) {
2900
- const clean = rewriteComponentRefs(sanitizeSchemaForAjv(sch));
2901
- schemas.push({ ...clean, $id: name });
2902
- }
2903
- const opIndex = {};
2904
- for (const ctrl of opts.manifest.controllers ?? []) {
2905
- for (const op of ctrl.operations ?? []) {
2906
- const entry = { response: {} };
2907
- opIndex[op.operationId] = entry;
2908
- if (op.args.body?.schemaRef) {
2909
- const typeName = schemaNameFromRef(op.args.body.schemaRef);
2910
- const id = safeId(`op_${op.operationId}_body`);
2911
- schemas.push({ $id: id, $ref: typeName });
2912
- entry.body = id;
2913
- }
2914
- for (const r of op.responses ?? []) {
2915
- if (!r.schemaRef) continue;
2916
- const typeName = schemaNameFromRef(r.schemaRef);
2917
- const key = `${r.status}|${r.contentType}`;
2918
- const id = safeId(`op_${op.operationId}_res_${r.status}_${r.contentType}`);
2919
- schemas.push({ $id: id, $ref: typeName });
2920
- entry.response[key] = id;
2921
- }
2922
- }
2923
- }
2924
- const strictOpt = opts.strict === "off" ? false : opts.strict === "log" ? "log" : true;
2925
- const ajv = new import_ajv.default.default({
2926
- schemas,
2927
- allErrors: true,
2928
- strict: strictOpt,
2929
- code: { source: true }
2930
- });
2931
- import_ajv_formats.default.default(ajv, { mode: opts.formatsMode ?? "full" });
2932
- let cjs;
2933
- const standaloneModule = require("ajv/dist/standalone");
2934
- if (typeof standaloneModule === "function") {
2935
- cjs = standaloneModule(ajv);
2936
- } else if (standaloneModule && typeof standaloneModule.default === "function") {
2937
- cjs = standaloneModule.default(ajv);
2938
- } else {
2939
- throw new Error("Unable to find standalone code generator in ajv/dist/standalone");
2940
- }
2941
- cjs += "\n\n// --- adorn-api operation lookup (generated) ---\n";
2942
- cjs += "exports.validators = {\n";
2943
- for (const [operationId, v] of Object.entries(opIndex)) {
2944
- cjs += ` ${JSON.stringify(operationId)}: {
2945
- `;
2946
- cjs += ` body: ${v.body ? `exports[${JSON.stringify(v.body)}]` : "undefined"},
2947
- `;
2948
- cjs += " response: {\n";
2949
- for (const [key, id] of Object.entries(v.response)) {
2950
- cjs += ` ${JSON.stringify(key)}: exports[${JSON.stringify(id)}],
2951
- `;
2952
- }
2953
- cjs += " }\n";
2954
- cjs += " },\n";
2955
- }
2956
- cjs += "};\n";
2957
- import_node_fs2.default.writeFileSync(cjsPath, cjs, "utf8");
2958
- const esm = `// .adorn/validators.mjs (generated)
2959
- import { createRequire } from "node:module";
2960
- const require = createRequire(import.meta.url);
2961
- const cjs = require("./validators.cjs");
2962
-
2963
- export const validators = cjs.validators;
2964
-
2965
- export function validateBody(operationId, data) {
2966
- const v = validators?.[operationId]?.body;
2967
- if (!v) return { ok: true, errors: null };
2968
- const ok = v(data);
2969
- return { ok, errors: ok ? null : v.errors };
2970
- }
2971
-
2972
- export function validateResponse(operationId, status, contentType, data) {
2973
- const key = String(status) + "|" + String(contentType);
2974
- const v = validators?.[operationId]?.response?.[key];
2975
- if (!v) return { ok: true, errors: null };
2976
- const ok = v(data);
2977
- return { ok, errors: ok ? null : v.errors };
2978
- }
2979
- `;
2980
- import_node_fs2.default.writeFileSync(esmPath, esm, "utf8");
2981
- const hash = import_node_crypto.default.createHash("sha256").update(cjs).digest("hex");
2982
- import_node_fs2.default.writeFileSync(metaPath, JSON.stringify({ hash }, null, 2), "utf8");
2983
- return { validatorsCjsPath: cjsPath, validatorsEsmPath: esmPath, hash };
2984
- }
2985
-
2986
- // src/compiler/cache/isStale.ts
2987
- var import_node_fs3 = __toESM(require("fs"), 1);
2988
- var import_node_path3 = __toESM(require("path"), 1);
2989
- var import_meta = {};
2990
- function readJson(p) {
2991
- try {
2992
- return JSON.parse(import_node_fs3.default.readFileSync(p, "utf8"));
2993
- } catch {
2994
- return null;
2995
- }
2996
- }
2997
- function statMtimeMs(p) {
2998
- try {
2999
- return import_node_fs3.default.statSync(p).mtimeMs;
3000
- } catch {
3001
- return null;
3002
- }
3003
- }
3004
- function ensureAbs(p) {
3005
- return import_node_path3.default.isAbsolute(p) ? p : import_node_path3.default.resolve(p);
3006
- }
3007
- function collectTsconfigChain(tsconfigPathAbs) {
3008
- const out = [];
3009
- const seen = /* @__PURE__ */ new Set();
3010
- function visit(pAbs) {
3011
- if (seen.has(pAbs)) return;
3012
- seen.add(pAbs);
3013
- out.push(pAbs);
3014
- const raw = readJson(pAbs);
3015
- const ext = raw?.extends;
3016
- if (!ext) return;
3017
- let resolved = null;
3018
- if (ext.startsWith(".") || ext.startsWith("/") || /^[A-Za-z]:\\/.test(ext)) {
3019
- resolved = ensureAbs(import_node_path3.default.resolve(import_node_path3.default.dirname(pAbs), ext));
3020
- if (!resolved.endsWith(".json")) resolved += ".json";
3021
- } else {
3022
- try {
3023
- const req = module.createRequire?.(import_meta.url) ?? require;
3024
- resolved = req.resolve(ext);
3025
- } catch {
3026
- try {
3027
- const req = module.createRequire?.(import_meta.url) ?? require;
3028
- resolved = req.resolve(ext.endsWith(".json") ? ext : `${ext}.json`);
3029
- } catch {
3030
- resolved = null;
3031
- }
3032
- }
3033
- }
3034
- if (resolved) visit(resolved);
3035
- }
3036
- visit(tsconfigPathAbs);
3037
- return out;
3038
- }
3039
- function findLockfile(startDir) {
3040
- const names = ["pnpm-lock.yaml", "package-lock.json", "yarn.lock"];
3041
- let dir = startDir;
3042
- for (let i = 0; i < 20; i++) {
3043
- for (const n of names) {
3044
- const p = import_node_path3.default.join(dir, n);
3045
- const mt = statMtimeMs(p);
3046
- if (mt !== null) return { path: p, mtimeMs: mt };
3047
- }
3048
- const parent = import_node_path3.default.dirname(dir);
3049
- if (parent === dir) break;
3050
- dir = parent;
3051
- }
3052
- return null;
3053
- }
3054
- async function isStale(params) {
3055
- const outDirAbs = ensureAbs(params.outDir);
3056
- const tsconfigAbs = ensureAbs(params.project);
3057
- const manifestPath = import_node_path3.default.join(outDirAbs, "manifest.json");
3058
- const cachePath = import_node_path3.default.join(outDirAbs, "cache.json");
3059
- if (!import_node_fs3.default.existsSync(manifestPath)) return { stale: true, reason: "missing-manifest" };
3060
- const cache = readJson(cachePath);
3061
- if (!cache) return { stale: true, reason: "missing-cache" };
3062
- if (cache.generator.version !== params.adornVersion) {
3063
- return { stale: true, reason: "generator-version-changed", detail: `${cache.generator.version} -> ${params.adornVersion}` };
3064
- }
3065
- if (ensureAbs(cache.project.tsconfigPath) !== tsconfigAbs) {
3066
- return { stale: true, reason: "tsconfig-changed", detail: "different project path" };
3067
- }
3068
- const chain = collectTsconfigChain(tsconfigAbs);
3069
- for (const cfg of chain) {
3070
- const mt = statMtimeMs(cfg);
3071
- if (mt === null) return { stale: true, reason: "config-missing", detail: cfg };
3072
- const cachedMt = cache.project.configFiles[cfg];
3073
- if (cachedMt === null || Math.abs(cachedMt - mt) > 1e-4) {
3074
- return { stale: true, reason: "config-updated", detail: cfg };
3075
- }
3076
- }
3077
- if (cache.project.lockfile?.path) {
3078
- const mt = statMtimeMs(cache.project.lockfile.path);
3079
- if (mt === null) return { stale: true, reason: "lockfile-missing", detail: cache.project.lockfile.path };
3080
- if (Math.abs(cache.project.lockfile.mtimeMs - mt) > 1e-4) {
3081
- return { stale: true, reason: "lockfile-updated", detail: cache.project.lockfile.path };
3082
- }
3083
- }
3084
- for (const [file, cachedMt] of Object.entries(cache.inputs)) {
3085
- const mt = statMtimeMs(file);
3086
- if (mt === null) return { stale: true, reason: "input-missing", detail: file };
3087
- if (Math.abs(cachedMt - mt) > 1e-4) return { stale: true, reason: "input-updated", detail: file };
3088
- }
3089
- return { stale: false, reason: "up-to-date" };
3090
- }
3091
-
3092
- // src/compiler/cache/writeCache.ts
3093
- var import_node_fs4 = __toESM(require("fs"), 1);
3094
- var import_node_path4 = __toESM(require("path"), 1);
3095
- var import_typescript14 = __toESM(require("typescript"), 1);
3096
- function statMtimeMs2(p) {
3097
- return import_node_fs4.default.statSync(p).mtimeMs;
3098
- }
3099
- function ensureDir(p) {
3100
- import_node_fs4.default.mkdirSync(p, { recursive: true });
3101
- }
3102
- function isProjectSourceFile(f) {
3103
- if (f.includes(`${import_node_path4.default.sep}node_modules${import_node_path4.default.sep}`)) return false;
3104
- if (f.includes(`${import_node_path4.default.sep}typescript${import_node_path4.default.sep}lib${import_node_path4.default.sep}`)) return false;
3105
- return /\.(ts|tsx|mts|cts)$/.test(f);
3106
- }
3107
- function writeCache(params) {
3108
- const outDirAbs = import_node_path4.default.isAbsolute(params.outDir) ? params.outDir : import_node_path4.default.resolve(params.outDir);
3109
- ensureDir(outDirAbs);
3110
- const configFiles = collectTsconfigChain(params.tsconfigAbs);
3111
- const configMtimes = {};
3112
- for (const cfg of configFiles) configMtimes[cfg] = statMtimeMs2(cfg);
3113
- const lock = findLockfile(import_node_path4.default.dirname(params.tsconfigAbs));
3114
- const inputs = {};
3115
- for (const sf of params.program.getSourceFiles()) {
3116
- const f = sf.fileName;
3117
- if (!isProjectSourceFile(f)) continue;
3118
- try {
3119
- inputs[f] = statMtimeMs2(f);
3120
- } catch {
3121
- }
3122
- }
3123
- const cache = {
3124
- cacheVersion: 1,
3125
- generator: {
3126
- name: "adorn-api",
3127
- version: params.adornVersion,
3128
- typescript: import_typescript14.default.version
3129
- },
3130
- project: {
3131
- tsconfigPath: params.tsconfigAbs,
3132
- configFiles: configMtimes,
3133
- lockfile: lock ?? null
3134
- },
3135
- inputs
3136
- };
3137
- import_node_fs4.default.writeFileSync(import_node_path4.default.join(outDirAbs, "cache.json"), JSON.stringify(cache, null, 2), "utf8");
3138
- }
3139
-
3140
- // src/cli/progress.ts
3141
- var import_node_process = __toESM(require("process"), 1);
3142
- var ProgressTracker = class {
3143
- phases = /* @__PURE__ */ new Map();
3144
- startTime;
3145
- verbose = false;
3146
- quiet = false;
3147
- indentLevel = 0;
3148
- constructor(options = {}) {
3149
- this.verbose = options.verbose ?? false;
3150
- this.quiet = options.quiet ?? false;
3151
- this.startTime = performance.now();
3152
- }
3153
- /**
3154
- * Start a new phase.
3155
- */
3156
- startPhase(name, message) {
3157
- this.phases.set(name, {
3158
- name,
3159
- startTime: performance.now(),
3160
- status: "running",
3161
- message
3162
- });
3163
- if (!this.quiet) {
3164
- this.log(`\u25CF ${message || name}`);
3165
- }
3166
- }
3167
- /**
3168
- * Complete a phase successfully.
3169
- */
3170
- completePhase(name, message) {
3171
- const phase = this.phases.get(name);
3172
- if (phase) {
3173
- phase.endTime = performance.now();
3174
- phase.status = "completed";
3175
- phase.message = message;
3176
- }
3177
- if (!this.quiet) {
3178
- const elapsed = phase ? this.formatElapsed(phase.startTime, phase.endTime) : "";
3179
- const status = this.verbose ? `\u2713 ${message || name} ${elapsed}` : `\u2713 ${message || name} ${elapsed}`;
3180
- this.log(status);
3181
- }
3182
- }
3183
- /**
3184
- * Mark a phase as failed.
3185
- */
3186
- failPhase(name, message) {
3187
- const phase = this.phases.get(name);
3188
- if (phase) {
3189
- phase.endTime = performance.now();
3190
- phase.status = "failed";
3191
- phase.message = message;
3192
- }
3193
- if (!this.quiet) {
3194
- this.log(`\u2717 ${message || name}`);
3195
- }
3196
- }
3197
- /**
3198
- * Log a verbose message.
3199
- */
3200
- verboseLog(message) {
3201
- if (this.verbose && !this.quiet) {
3202
- const elapsed = this.formatElapsed(this.startTime);
3203
- this.log(`[${elapsed}] ${message}`);
3204
- }
3205
- }
3206
- /**
3207
- * Log a regular message.
3208
- */
3209
- log(message) {
3210
- const indent = " ".repeat(this.indentLevel);
3211
- import_node_process.default.stdout.write(indent + message + "\n");
3212
- }
3213
- /**
3214
- * Log a sub-message (indented).
3215
- */
3216
- logSub(message) {
3217
- this.indentLevel++;
3218
- this.log(message);
3219
- this.indentLevel--;
3220
- }
3221
- /**
3222
- * Get the total elapsed time in milliseconds.
3223
- */
3224
- getTotalElapsed() {
3225
- return performance.now() - this.startTime;
3226
- }
3227
- /**
3228
- * Format elapsed time as a human-readable string.
3229
- */
3230
- formatElapsed(startTime, endTime) {
3231
- const elapsed = (endTime ?? performance.now()) - (startTime ?? this.startTime);
3232
- if (elapsed < 1) {
3233
- return `${(elapsed * 1e3).toFixed(0)}ms`;
3234
- } else if (elapsed < 1e3) {
3235
- return `${elapsed.toFixed(0)}ms`;
3236
- } else {
3237
- return `${(elapsed / 1e3).toFixed(2)}s`;
3238
- }
3239
- }
3240
- /**
3241
- * Get all completed phases with their timings.
3242
- */
3243
- getPhases() {
3244
- return Array.from(this.phases.values());
3245
- }
3246
- /**
3247
- * Print a build summary.
3248
- */
3249
- printSummary(stats) {
3250
- if (this.quiet) return;
3251
- this.log("");
3252
- this.log("Build Summary:");
3253
- this.log(` Controllers: ${stats.controllers}`);
3254
- this.log(` Operations: ${stats.operations}`);
3255
- this.log(` Schemas: ${stats.schemas}`);
3256
- this.log(` Source files: ${stats.sourceFiles}`);
3257
- this.log(` Output dir: ${stats.artifactsWritten[0]?.split("/").slice(0, -1).join("/") || ".adorn"}`);
3258
- this.log("");
3259
- this.log("Timings:");
3260
- for (const phase of this.phases.values()) {
3261
- if (phase.status === "completed" && phase.endTime) {
3262
- const elapsed = phase.endTime - phase.startTime;
3263
- const timeStr = elapsed < 1 ? `${(elapsed * 1e3).toFixed(0)}ms` : elapsed < 1e3 ? `${elapsed.toFixed(0)}ms` : `${(elapsed / 1e3).toFixed(2)}s`;
3264
- this.log(` ${phase.name.padEnd(20)} ${timeStr}`);
3265
- }
3266
- }
3267
- this.log(` ${"\u2500".repeat(21)}`);
3268
- this.log(` Total time: ${this.formatElapsed()}`);
3269
- this.log("");
3270
- }
3271
- /**
3272
- * Print artifact list.
3273
- */
3274
- printArtifacts(artifacts) {
3275
- if (this.quiet) return;
3276
- this.log("Written artifacts:");
3277
- for (const artifact of artifacts) {
3278
- const sizeStr = artifact.size ? ` (${artifact.size >= 1024 ? `${(artifact.size / 1024).toFixed(1)} KB` : `${artifact.size} B`})` : "";
3279
- this.log(` \u251C\u2500\u2500 ${artifact.name}${sizeStr}`);
3280
- }
3281
- }
3282
- };
3283
- var Spinner = class {
3284
- frames = ["\u280B", "\u2819", "\u2839", "\u2838", "\u28B0", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
3285
- interval;
3286
- message;
3287
- current = 0;
3288
- total = 0;
3289
- customStatus;
3290
- frameIndex = 0;
3291
- lastLineLength = 0;
3292
- constructor(message = "") {
3293
- this.message = message;
3294
- }
3295
- /**
3296
- * Start the spinner.
3297
- */
3298
- start() {
3299
- this.interval = setInterval(() => {
3300
- const frame = this.frames[this.frameIndex];
3301
- let output;
3302
- if (this.customStatus) {
3303
- output = `\r${frame} ${this.customStatus}`;
3304
- } else if (this.total > 0) {
3305
- output = `\r${frame} ${this.message} (${this.current}/${this.total})`;
3306
- } else {
3307
- output = `\r${frame} ${this.message}`;
3308
- }
3309
- this.lastLineLength = output.length - 1;
3310
- import_node_process.default.stdout.write(output);
3311
- if (import_node_process.default.stdout.writable) {
3312
- import_node_process.default.stdout.write("");
3313
- }
3314
- this.frameIndex = (this.frameIndex + 1) % this.frames.length;
3315
- }, 80);
3316
- }
3317
- /**
3318
- * Set progress counters.
3319
- */
3320
- setProgress(current, total) {
3321
- this.current = current;
3322
- this.total = total;
3323
- }
3324
- /**
3325
- * Set a custom status message (overrides counters).
3326
- */
3327
- setStatus(status) {
3328
- this.customStatus = status;
3329
- const frame = this.frames[this.frameIndex];
3330
- const output = `\r${frame} ${status}`;
3331
- const clearLength = Math.max(this.lastLineLength, output.length - 1);
3332
- import_node_process.default.stdout.write("\r" + " ".repeat(clearLength) + "\r");
3333
- this.lastLineLength = output.length - 1;
3334
- import_node_process.default.stdout.write(output);
3335
- if (import_node_process.default.stdout.writable) {
3336
- import_node_process.default.stdout.write("");
3337
- }
3338
- }
3339
- /**
3340
- * Clear the custom status message.
3341
- */
3342
- clearStatus() {
3343
- this.customStatus = void 0;
3344
- }
3345
- /**
3346
- * Stop the spinner with a completion message.
3347
- */
3348
- stop(completedMessage) {
3349
- if (this.interval) {
3350
- clearInterval(this.interval);
3351
- this.interval = void 0;
3352
- }
3353
- import_node_process.default.stdout.write("\r" + " ".repeat(this.lastLineLength) + "\r");
3354
- if (completedMessage) {
3355
- import_node_process.default.stdout.write(completedMessage + "\n");
3356
- }
3357
- if (import_node_process.default.stdout.writable) {
3358
- import_node_process.default.stdout.write("");
3359
- }
3360
- }
3361
- /**
3362
- * Stop the spinner with a failure message.
3363
- */
3364
- fail(failedMessage) {
3365
- this.stop();
3366
- if (failedMessage) {
3367
- import_node_process.default.stdout.write(`\u2717 ${failedMessage}
3368
- `);
3369
- }
3370
- }
3371
- };
3372
-
3373
- // src/compiler/schema/partitioner.ts
3374
- var DEFAULT_CONFIG = {
3375
- strategy: "auto",
3376
- threshold: 50,
3377
- maxGroupSize: 50,
3378
- complexityThreshold: 10,
3379
- verbose: false
3380
- };
3381
- function calculateSchemaComplexity(schema) {
3382
- let propertyCount = 0;
3383
- let nestedDepth = 0;
3384
- let refCount = 0;
3385
- let hasUnion = false;
3386
- let hasIntersection = false;
3387
- let hasEnum = false;
3388
- const jsonSize = JSON.stringify(schema).length;
3389
- const analyze = (s, depth) => {
3390
- if (!s || typeof s !== "object") return;
3391
- nestedDepth = Math.max(nestedDepth, depth);
3392
- if (s.type === "object" && s.properties) {
3393
- propertyCount += Object.keys(s.properties).length;
3394
- for (const prop of Object.values(s.properties)) {
3395
- analyze(prop, depth + 1);
3396
- }
3397
- }
3398
- if (s.$ref) refCount++;
3399
- if (s.anyOf || s.oneOf) hasUnion = true;
3400
- if (s.allOf) hasIntersection = true;
3401
- if (s.enum) hasEnum = true;
3402
- if (s.items) {
3403
- analyze(s.items, depth + 1);
3404
- }
3405
- };
3406
- analyze(schema, 0);
3407
- const complexity = propertyCount * 1 + nestedDepth * 2 + refCount * 0.5 + (hasUnion ? 5 : 0) + (hasIntersection ? 5 : 0) + (hasEnum ? 1 : 0);
3408
- return {
3409
- propertyCount,
3410
- nestedDepth,
3411
- refCount,
3412
- hasUnion,
3413
- hasIntersection,
3414
- hasEnum,
3415
- jsonSize
3416
- };
3417
- }
3418
- function countExternalRefs(schema, allSchemas) {
3419
- let count = 0;
3420
- const analyze = (s) => {
3421
- if (!s || typeof s !== "object") return;
3422
- if (s.$ref && typeof s.$ref === "string") {
3423
- const refName = s.$ref.replace("#/components/schemas/", "");
3424
- if (refName && allSchemas.has(refName)) {
3425
- count++;
3426
- }
3427
- }
3428
- if (s.properties) {
3429
- for (const prop of Object.values(s.properties)) {
3430
- analyze(prop);
3431
- }
3432
- }
3433
- if (s.items) analyze(s.items);
3434
- if (s.anyOf) s.anyOf.forEach(analyze);
3435
- if (s.oneOf) s.oneOf.forEach(analyze);
3436
- if (s.allOf) s.allOf.forEach(analyze);
3437
- };
3438
- analyze(schema);
3439
- return count;
3440
- }
3441
- function analyzeDependencyDensity(schemas) {
3442
- let totalDeps = 0;
3443
- let maxDeps = 0;
3444
- for (const schema of schemas.values()) {
3445
- const deps = countExternalRefs(schema, schemas);
3446
- totalDeps += deps;
3447
- maxDeps = Math.max(maxDeps, deps);
3448
- }
3449
- return {
3450
- avgDeps: schemas.size > 0 ? totalDeps / schemas.size : 0,
3451
- maxDeps
3452
- };
3453
- }
3454
- function partitionByController(schemas, graph, config) {
3455
- const groups = /* @__PURE__ */ new Map();
3456
- const sharedSchemas = /* @__PURE__ */ new Map();
3457
- groups.set("_shared", sharedSchemas);
3458
- const schemaUsage = /* @__PURE__ */ new Map();
3459
- for (const [nodeId, node] of graph.nodes.entries()) {
3460
- if (node.kind === "Operation") {
3461
- const opNode = node;
3462
- const returnType = opNode.operation?.returnType;
3463
- if (returnType && schemas.has(returnType)) {
3464
- if (!schemaUsage.has(returnType)) {
3465
- schemaUsage.set(returnType, /* @__PURE__ */ new Set());
3466
- }
3467
- schemaUsage.get(returnType).add(node.metadata.name);
3468
- }
3469
- }
3470
- }
3471
- for (const [schemaName, schema] of schemas.entries()) {
3472
- const usage = schemaUsage.get(schemaName);
3473
- if (!usage || usage.size === 0) {
3474
- sharedSchemas.set(schemaName, schema);
3475
- } else if (usage.size === 1) {
3476
- const controllerName = Array.from(usage)[0].split(":")[0] || "default";
3477
- const groupName = controllerName.toLowerCase();
3478
- if (!groups.has(groupName)) {
3479
- groups.set(groupName, /* @__PURE__ */ new Map());
3480
- }
3481
- groups.get(groupName).set(schemaName, schema);
3482
- } else {
3483
- sharedSchemas.set(schemaName, schema);
3484
- }
3485
- }
3486
- return Array.from(groups.entries()).map(([name, schemaMap]) => {
3487
- let totalComplexity = 0;
3488
- const dependencies = [];
3489
- for (const [schemaName, schema] of schemaMap.entries()) {
3490
- totalComplexity += calculateSchemaComplexity(schema).propertyCount;
3491
- const deps = countExternalRefs(schema, schemas);
3492
- for (let i = 0; i < deps; i++) {
3493
- dependencies.push(schemaName);
3494
- }
3495
- }
3496
- return {
3497
- name,
3498
- schemas: schemaMap,
3499
- complexity: totalComplexity,
3500
- dependencies
3501
- };
3502
- });
3503
- }
3504
- function partitionByDependency(schemas, schemaGraph, config) {
3505
- const groups = /* @__PURE__ */ new Map();
3506
- const sccs = schemaGraph.findStronglyConnectedComponents();
3507
- const processed = /* @__PURE__ */ new Set();
3508
- for (const scc of sccs) {
3509
- if (scc.length === 1 && processed.has(scc[0])) continue;
3510
- const groupSchemas = /* @__PURE__ */ new Map();
3511
- const groupName = `dependent-${groups.size + 1}`;
3512
- for (const nodeId of scc) {
3513
- const node = schemaGraph.getGraph().nodes.get(nodeId);
3514
- if (node && node.kind === "TypeDefinition") {
3515
- const schemaName = node.metadata.name;
3516
- if (schemas.has(schemaName)) {
3517
- groupSchemas.set(schemaName, schemas.get(schemaName));
3518
- processed.add(nodeId);
3519
- }
3520
- }
3521
- }
3522
- if (groupSchemas.size > 0) {
3523
- groups.set(groupName, groupSchemas);
3524
- }
3525
- }
3526
- for (const [nodeId, node] of schemaGraph.getGraph().nodes.entries()) {
3527
- if (processed.has(nodeId)) continue;
3528
- if (node.kind !== "TypeDefinition") continue;
3529
- const schemaName = node.metadata.name;
3530
- if (!schemas.has(schemaName)) continue;
3531
- const groupSchemas = /* @__PURE__ */ new Map();
3532
- groupSchemas.set(schemaName, schemas.get(schemaName));
3533
- groups.set(`standalone-${groups.size + 1}`, groupSchemas);
3534
- processed.add(nodeId);
3535
- }
3536
- return Array.from(groups.entries()).map(([name, schemaMap]) => {
3537
- let totalComplexity = 0;
3538
- const dependencies = [];
3539
- for (const [schemaName, schema] of schemaMap.entries()) {
3540
- totalComplexity += calculateSchemaComplexity(schema).propertyCount;
3541
- const deps = countExternalRefs(schema, schemas);
3542
- for (let i = 0; i < deps; i++) {
3543
- dependencies.push(schemaName);
3544
- }
3545
- }
3546
- return {
3547
- name,
3548
- schemas: schemaMap,
3549
- complexity: totalComplexity,
3550
- dependencies
3551
- };
3552
- });
3553
- }
3554
- function partitionBySize(schemas, config) {
3555
- const sortedSchemas = Array.from(schemas.entries()).sort((a, b) => {
3556
- const complexityA = calculateSchemaComplexity(a[1]).propertyCount;
3557
- const complexityB = calculateSchemaComplexity(b[1]).propertyCount;
3558
- return complexityB - complexityA;
3559
- });
3560
- const groups = /* @__PURE__ */ new Map();
3561
- let currentGroup = /* @__PURE__ */ new Map();
3562
- let currentCount = 0;
3563
- let groupIndex = 1;
3564
- for (const [schemaName, schema] of sortedSchemas) {
3565
- if (currentCount >= config.maxGroupSize) {
3566
- groups.set(`group-${groupIndex}`, currentGroup);
3567
- currentGroup = /* @__PURE__ */ new Map();
3568
- currentCount = 0;
3569
- groupIndex++;
3570
- }
3571
- currentGroup.set(schemaName, schema);
3572
- currentCount++;
3573
- }
3574
- if (currentCount > 0) {
3575
- groups.set(`group-${groupIndex}`, currentGroup);
3576
- }
3577
- return Array.from(groups.entries()).map(([name, schemaMap]) => {
3578
- let totalComplexity = 0;
3579
- const dependencies = [];
3580
- for (const [schemaName, schema] of schemaMap.entries()) {
3581
- totalComplexity += calculateSchemaComplexity(schema).propertyCount;
3582
- const deps = countExternalRefs(schema, schemas);
3583
- for (let i = 0; i < deps; i++) {
3584
- dependencies.push(schemaName);
3585
- }
3586
- }
3587
- return {
3588
- name,
3589
- schemas: schemaMap,
3590
- complexity: totalComplexity,
3591
- dependencies
3592
- };
3593
- });
3594
- }
3595
- function determineBestStrategy(schemas, graph, schemaGraph, config) {
3596
- const schemaCount = schemas.size;
3597
- const { avgDeps } = analyzeDependencyDensity(schemas);
3598
- let controllerGroups = 0;
3599
- for (const node of graph.nodes.values()) {
3600
- if (node.kind === "Controller") {
3601
- controllerGroups++;
3602
- }
3603
- }
3604
- if (schemaCount < config.threshold) {
3605
- return "none";
3606
- }
3607
- if (avgDeps > 3) {
3608
- return "dependency";
3609
- }
3610
- if (controllerGroups > 1 && avgDeps < 2) {
3611
- return "controller";
3612
- }
3613
- return "size";
3614
- }
3615
- function partitionSchemas(schemas, graph, schemaGraph, config = {}) {
3616
- const finalConfig = { ...DEFAULT_CONFIG, ...config };
3617
- const schemaCount = schemas.size;
3618
- let totalComplexity = 0;
3619
- let totalSchemas = 0;
3620
- const { avgDeps } = analyzeDependencyDensity(schemas);
3621
- let controllerGroups = 0;
3622
- for (const node of graph.nodes.values()) {
3623
- if (node.kind === "Controller") {
3624
- controllerGroups++;
3625
- }
3626
- }
3627
- for (const schema of schemas.values()) {
3628
- totalComplexity += calculateSchemaComplexity(schema).propertyCount;
3629
- totalSchemas++;
3630
- }
3631
- const avgComplexity = totalSchemas > 0 ? totalComplexity / totalSchemas : 0;
3632
- let strategy = finalConfig.strategy;
3633
- let recommendation = "";
3634
- if (strategy === "auto") {
3635
- strategy = determineBestStrategy(schemas, graph, schemaGraph, finalConfig);
3636
- if (schemaCount < finalConfig.threshold) {
3637
- recommendation = `Schema count (${schemaCount}) below threshold (${finalConfig.threshold}), single file optimal`;
3638
- } else if (strategy === "dependency") {
3639
- recommendation = `High dependency density (${avgDeps.toFixed(2)} avg refs/schema), using dependency-based partitioning`;
3640
- } else if (strategy === "controller") {
3641
- recommendation = `Found ${controllerGroups} controller groups with low coupling, using controller-based partitioning`;
3642
- } else {
3643
- recommendation = `Using size-based partitioning with max ${finalConfig.maxGroupSize} schemas per group`;
3644
- }
3645
- }
3646
- let groups = [];
3647
- if (strategy === "none") {
3648
- groups = [{
3649
- name: "all",
3650
- schemas: new Map(schemas),
3651
- complexity: totalComplexity,
3652
- dependencies: []
3653
- }];
3654
- recommendation = recommendation || "Single file mode (--split not specified)";
3655
- } else if (strategy === "controller") {
3656
- groups = partitionByController(schemas, graph, finalConfig);
3657
- } else if (strategy === "dependency") {
3658
- groups = partitionByDependency(schemas, schemaGraph, finalConfig);
3659
- } else {
3660
- groups = partitionBySize(schemas, finalConfig);
3661
- }
3662
- const shouldSplit = strategy !== "none" && schemaCount >= finalConfig.threshold;
3663
- return {
3664
- shouldSplit,
3665
- strategy,
3666
- groups,
3667
- recommendation,
3668
- metrics: {
3669
- totalSchemas: schemaCount,
3670
- averageComplexity: avgComplexity,
3671
- avgDependencyDensity: avgDeps,
3672
- controllerGroups
3673
- }
3674
- };
3675
- }
3676
-
3677
- // src/compiler/schema/splitOpenapi.ts
3678
- var import_node_fs5 = require("fs");
3679
- var import_node_path5 = require("path");
3680
- function sanitizeFilename(name) {
3681
- return name.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "");
3682
- }
3683
- function getSchemaFilename(group) {
3684
- const name = sanitizeFilename(group.name);
3685
- return `schemas/${name}.json`;
3686
- }
3687
- function collectAllSchemas(groups) {
3688
- const result = /* @__PURE__ */ new Map();
3689
- for (const group of groups) {
3690
- for (const [schemaName, schema] of group.schemas.entries()) {
3691
- result.set(schemaName, { schema, group: group.name });
3692
- }
3693
- }
3694
- return result;
3695
- }
3696
- function convertToExternalRef(schema, schemaMap) {
3697
- if (!schema || typeof schema !== "object") return schema;
3698
- const result = { ...schema };
3699
- if (schema.$ref && typeof schema.$ref === "string") {
3700
- const refName = schema.$ref.replace("#/components/schemas/", "");
3701
- if (refName && schemaMap.has(refName)) {
3702
- const target = schemaMap.get(refName);
3703
- const filename = sanitizeFilename(target.group);
3704
- result.$ref = `schemas/${filename}.json#/components/schemas/${refName}`;
3705
- }
3706
- }
3707
- const nestedProps = ["properties", "items", "additionalProperties"];
3708
- for (const prop of nestedProps) {
3709
- if (prop in result) {
3710
- const value = result[prop];
3711
- if (Array.isArray(value)) {
3712
- result[prop] = value.map(
3713
- (item) => typeof item === "object" ? convertToExternalRef(item, schemaMap) : item
3714
- );
3715
- } else if (typeof value === "object" && value !== null) {
3716
- result[prop] = convertToExternalRef(value, schemaMap);
3717
- }
3718
- }
3719
- }
3720
- const arrayProps = ["anyOf", "oneOf", "allOf"];
3721
- for (const prop of arrayProps) {
3722
- if (prop in result && Array.isArray(result[prop])) {
3723
- result[prop] = result[prop].map(
3724
- (item) => typeof item === "object" ? convertToExternalRef(item, schemaMap) : item
3725
- );
3726
- }
3727
- }
3728
- return result;
3729
- }
3730
- function generateSchemaFileContent(group, schemaMap) {
3731
- const content = {};
3732
- for (const [schemaName, schema] of group.schemas.entries()) {
3733
- content[schemaName] = convertToExternalRef(schema, schemaMap);
3734
- }
3735
- return content;
3736
- }
3737
- function generateSchemaIndex(groups, schemaMap) {
3738
- const index = {
3739
- schemas: {}
3740
- };
3741
- for (const [schemaName, { group }] of schemaMap.entries()) {
3742
- const filename = sanitizeFilename(group);
3743
- index.schemas[schemaName] = {
3744
- $ref: `schemas/${filename}.json#/components/schemas/${schemaName}`
3745
- };
3746
- }
3747
- return index;
3748
- }
3749
- function generateModularOpenAPI(openapi, partitioning, config) {
3750
- const {
3751
- outputDir,
3752
- schemasDir = "schemas",
3753
- createIndexFile = true,
3754
- prettyPrint = true,
3755
- onProgress
3756
- } = config;
3757
- const indent = prettyPrint ? 2 : 0;
3758
- let totalSize = 0;
3759
- const schemaFiles = [];
3760
- (0, import_node_fs5.mkdirSync)(outputDir, { recursive: true });
3761
- if (!partitioning.shouldSplit || partitioning.groups.length === 1) {
3762
- if (onProgress) {
3763
- onProgress("Writing single OpenAPI file", 1, 1);
3764
- }
3765
- const mainPath2 = (0, import_node_path5.resolve)(outputDir, "openapi.json");
3766
- (0, import_node_fs5.writeFileSync)(mainPath2, JSON.stringify(openapi, null, indent));
3767
- totalSize = Buffer.byteLength(JSON.stringify(openapi));
3768
- return {
3769
- mainSpec: mainPath2,
3770
- schemaFiles: [],
3771
- totalSize,
3772
- splitEnabled: false
3773
- };
3774
- }
3775
- const schemasPath = (0, import_node_path5.resolve)(outputDir, schemasDir);
3776
- (0, import_node_fs5.mkdirSync)(schemasPath, { recursive: true });
3777
- if (onProgress) {
3778
- onProgress("Creating schemas directory", 0, partitioning.groups.length + 2);
3779
- }
3780
- const schemaMap = collectAllSchemas(partitioning.groups);
3781
- const schemaToFile = /* @__PURE__ */ new Map();
3782
- for (let i = 0; i < partitioning.groups.length; i++) {
3783
- const group = partitioning.groups[i];
3784
- if (onProgress) {
3785
- onProgress(`Writing schema group ${group.name} (${group.schemas.size} schemas)`, i + 1, partitioning.groups.length + 2);
3786
- }
3787
- const filename = getSchemaFilename(group);
3788
- const filePath = (0, import_node_path5.resolve)(outputDir, filename);
3789
- const content = generateSchemaFileContent(group, schemaMap);
3790
- (0, import_node_fs5.writeFileSync)(filePath, JSON.stringify(content, null, indent));
3791
- for (const schemaName of group.schemas.keys()) {
3792
- schemaToFile.set(schemaName, filename);
3793
- }
3794
- schemaFiles.push(filePath);
3795
- totalSize += Buffer.byteLength(JSON.stringify(content));
3796
- }
3797
- let indexFile;
3798
- if (createIndexFile) {
3799
- if (onProgress) {
3800
- onProgress("Generating index file", partitioning.groups.length + 1, partitioning.groups.length + 2);
3801
- }
3802
- const indexPath = (0, import_node_path5.resolve)(outputDir, "schemas/index.json");
3803
- const indexContent = generateSchemaIndex(partitioning.groups, schemaMap);
3804
- (0, import_node_fs5.writeFileSync)(indexPath, JSON.stringify(indexContent, null, indent));
3805
- totalSize += Buffer.byteLength(JSON.stringify(indexContent));
3806
- indexFile = indexPath;
3807
- }
3808
- if (onProgress) {
3809
- onProgress("Generating main OpenAPI spec", partitioning.groups.length + 2, partitioning.groups.length + 2);
3810
- }
3811
- const mainSpec = generateMainSpec(openapi, schemaMap, schemaToFile);
3812
- const mainPath = (0, import_node_path5.resolve)(outputDir, "openapi.json");
3813
- (0, import_node_fs5.writeFileSync)(mainPath, JSON.stringify(mainSpec, null, indent));
3814
- totalSize += Buffer.byteLength(JSON.stringify(mainSpec));
3815
- return {
3816
- mainSpec: mainPath,
3817
- schemaFiles,
3818
- indexFile,
3819
- totalSize,
3820
- splitEnabled: true
3821
- };
3822
- }
3823
- function generateMainSpec(original, schemaMap, schemaToFile) {
3824
- const schemas = {};
3825
- for (const [schemaName, { group }] of schemaMap.entries()) {
3826
- const filename = sanitizeFilename(group);
3827
- schemas[schemaName] = {
3828
- $ref: `schemas/${filename}.json#/components/schemas/${schemaName}`
3829
- };
3830
- }
3831
- return {
3832
- ...original,
3833
- components: {
3834
- ...original.components,
3835
- schemas
3836
- },
3837
- "x-original-schemas": Object.keys(schemaMap).length,
3838
- "x-split-enabled": true,
3839
- "x-schema-files": Array.from(new Set(schemaToFile.values()))
3840
- };
3841
- }
3842
-
3843
- // src/compiler/graph/types.ts
3844
- function createGraph(tsVersion) {
3845
- return {
3846
- nodes: /* @__PURE__ */ new Map(),
3847
- roots: /* @__PURE__ */ new Set(),
3848
- version: "1.0.0",
3849
- metadata: {
3850
- createdAt: (/* @__PURE__ */ new Date()).toISOString(),
3851
- generatedBy: "adorn-api-gems",
3852
- tsVersion
3853
- }
3854
- };
3855
- }
3856
- function addNode(graph, node) {
3857
- graph.nodes.set(node.id, node);
3858
- }
3859
- function addEdge(graph, sourceId, targetId, relation, properties) {
3860
- const sourceNode = graph.nodes.get(sourceId);
3861
- if (!sourceNode) {
3862
- throw new Error(`Source node ${sourceId} not found`);
3863
- }
3864
- const targetExists = graph.nodes.has(targetId);
3865
- if (!targetExists) {
3866
- throw new Error(`Target node ${targetId} not found`);
3867
- }
3868
- sourceNode.edges.push({
3869
- targetId,
3870
- relation,
3871
- properties
3872
- });
3873
- }
3874
- function getEdgesByRelation(graph, relation) {
3875
- const edges = [];
3876
- for (const [id, node] of graph.nodes.entries()) {
3877
- for (const edge of node.edges) {
3878
- if (edge.relation === relation) {
3879
- edges.push({ sourceId: id, edge });
3880
- }
3881
- }
3882
- }
3883
- return edges;
3884
- }
3885
-
3886
- // src/compiler/graph/builder.ts
3887
- var import_typescript15 = __toESM(require("typescript"), 1);
3888
-
3889
- // src/compiler/graph/schemaGraph.ts
3890
- var SchemaGraph = class {
3891
- graph;
3892
- adjacency = /* @__PURE__ */ new Map();
3893
- reverseAdjacency = /* @__PURE__ */ new Map();
3894
- constructor(graph) {
3895
- this.graph = graph;
3896
- this.buildAdjacencyLists();
3897
- }
3898
- /**
3899
- * Build adjacency lists for faster traversal
3900
- */
3901
- buildAdjacencyLists() {
3902
- for (const [id, node] of this.graph.nodes.entries()) {
3903
- this.adjacency.set(id, /* @__PURE__ */ new Set());
3904
- this.reverseAdjacency.set(id, /* @__PURE__ */ new Set());
3905
- }
3906
- for (const [sourceId, node] of this.graph.nodes.entries()) {
3907
- for (const edge of node.edges) {
3908
- this.adjacency.get(sourceId)?.add(edge.targetId);
3909
- this.reverseAdjacency.get(edge.targetId)?.add(sourceId);
3910
- }
3911
- }
3912
- }
3913
- /**
3914
- * Find all nodes that use a given type
3915
- */
3916
- findTypeUsages(typeId) {
3917
- const usages = [];
3918
- const usesEdges = getEdgesByRelation(this.graph, "uses");
3919
- for (const { sourceId, edge } of usesEdges) {
3920
- if (edge.targetId === typeId) {
3921
- usages.push(sourceId);
3922
- }
3923
- }
3924
- return usages;
3925
- }
3926
- /**
3927
- * Detect cycles in the dependency graph
3928
- */
3929
- detectCycles() {
3930
- const visited = /* @__PURE__ */ new Set();
3931
- const recursionStack = /* @__PURE__ */ new Set();
3932
- const cycles = [];
3933
- for (const nodeId of this.graph.nodes.keys()) {
3934
- if (!visited.has(nodeId)) {
3935
- this.detectCyclesDFS(nodeId, visited, recursionStack, [], cycles);
3936
- }
3937
- }
3938
- return {
3939
- hasCycles: cycles.length > 0,
3940
- cycles,
3941
- cycleCount: cycles.length
3942
- };
3943
- }
3944
- /**
3945
- * Depth-first search for cycle detection
3946
- */
3947
- detectCyclesDFS(nodeId, visited, recursionStack, path4, cycles) {
3948
- visited.add(nodeId);
3949
- recursionStack.add(nodeId);
3950
- path4.push(nodeId);
3951
- const neighbors = this.adjacency.get(nodeId) || /* @__PURE__ */ new Set();
3952
- for (const neighbor of neighbors) {
3953
- if (!visited.has(neighbor)) {
3954
- this.detectCyclesDFS(neighbor, visited, recursionStack, path4, cycles);
3955
- } else if (recursionStack.has(neighbor)) {
3956
- const cycleStart = path4.indexOf(neighbor);
3957
- cycles.push([...path4.slice(cycleStart), neighbor]);
3958
- }
3959
- }
3960
- recursionStack.delete(nodeId);
3961
- path4.pop();
3962
- }
3963
- /**
3964
- * Find strongly connected components using Tarjan's algorithm
3965
- */
3966
- findStronglyConnectedComponents() {
3967
- let index = 0;
3968
- const stack = [];
3969
- const indices = /* @__PURE__ */ new Map();
3970
- const lowlinks = /* @__PURE__ */ new Map();
3971
- const onStack = /* @__PURE__ */ new Set();
3972
- const sccs = [];
3973
- const strongConnect = (v) => {
3974
- indices.set(v, index);
3975
- lowlinks.set(v, index);
3976
- index++;
3977
- stack.push(v);
3978
- onStack.add(v);
3979
- const neighbors = this.adjacency.get(v) || /* @__PURE__ */ new Set();
3980
- for (const w of neighbors) {
3981
- if (!indices.has(w)) {
3982
- strongConnect(w);
3983
- lowlinks.set(v, Math.min(lowlinks.get(v), lowlinks.get(w)));
3984
- } else if (onStack.has(w)) {
3985
- lowlinks.set(v, Math.min(lowlinks.get(v), indices.get(w)));
3986
- }
3987
- }
3988
- if (lowlinks.get(v) === indices.get(v)) {
3989
- const scc = [];
3990
- let w;
3991
- do {
3992
- w = stack.pop();
3993
- onStack.delete(w);
3994
- scc.push(w);
3995
- } while (w !== v);
3996
- sccs.push(scc);
3997
- }
3998
- };
3999
- for (const nodeId of this.graph.nodes.keys()) {
4000
- if (!indices.has(nodeId)) {
4001
- strongConnect(nodeId);
4002
- }
4003
- }
4004
- return sccs;
4005
- }
4006
- /**
4007
- * Topological sort of the graph
4008
- */
4009
- topologicalSort() {
4010
- const inDegree = /* @__PURE__ */ new Map();
4011
- for (const nodeId of this.graph.nodes.keys()) {
4012
- inDegree.set(nodeId, 0);
4013
- }
4014
- for (const [sourceId, node] of this.graph.nodes.entries()) {
4015
- for (const edge of node.edges) {
4016
- inDegree.set(
4017
- edge.targetId,
4018
- (inDegree.get(edge.targetId) || 0) + 1
4019
- );
4020
- }
4021
- }
4022
- const queue = [];
4023
- for (const [nodeId, degree] of inDegree.entries()) {
4024
- if (degree === 0) {
4025
- queue.push(nodeId);
4026
- }
4027
- }
4028
- const sorted = [];
4029
- while (queue.length > 0) {
4030
- const current = queue.shift();
4031
- sorted.push(current);
4032
- const neighbors = this.adjacency.get(current) || /* @__PURE__ */ new Set();
4033
- for (const neighbor of neighbors) {
4034
- inDegree.set(neighbor, inDegree.get(neighbor) - 1);
4035
- if (inDegree.get(neighbor) === 0) {
4036
- queue.push(neighbor);
4037
- }
4038
- }
4039
- }
4040
- return sorted;
4041
- }
4042
- /**
4043
- * Find nodes reachable from a given start node
4044
- */
4045
- findReachable(startNodeId) {
4046
- const reachable = /* @__PURE__ */ new Set();
4047
- const visited = /* @__PURE__ */ new Set();
4048
- const queue = [startNodeId];
4049
- while (queue.length > 0) {
4050
- const current = queue.shift();
4051
- if (visited.has(current)) continue;
4052
- visited.add(current);
4053
- reachable.add(current);
4054
- const neighbors = this.adjacency.get(current) || /* @__PURE__ */ new Set();
4055
- for (const neighbor of neighbors) {
4056
- if (!visited.has(neighbor)) {
4057
- queue.push(neighbor);
4058
- }
4059
- }
4060
- }
4061
- return reachable;
4062
- }
4063
- /**
4064
- * Find shortest path between two nodes (BFS)
4065
- */
4066
- findShortestPath(fromId, toId) {
4067
- const visited = /* @__PURE__ */ new Set();
4068
- const previous = /* @__PURE__ */ new Map();
4069
- const queue = [fromId];
4070
- visited.add(fromId);
4071
- while (queue.length > 0) {
4072
- const current = queue.shift();
4073
- if (current === toId) {
4074
- return this.reconstructPath(previous, toId);
4075
- }
4076
- const neighbors = this.adjacency.get(current) || /* @__PURE__ */ new Set();
4077
- for (const neighbor of neighbors) {
4078
- if (!visited.has(neighbor)) {
4079
- visited.add(neighbor);
4080
- previous.set(neighbor, current);
4081
- queue.push(neighbor);
4082
- }
4083
- }
4084
- }
4085
- return null;
4086
- }
4087
- /**
4088
- * Reconstruct path from previous map
4089
- */
4090
- reconstructPath(previous, toId) {
4091
- const path4 = [toId];
4092
- let current = toId;
4093
- while (current !== void 0) {
4094
- current = previous.get(current);
4095
- if (current !== void 0) {
4096
- path4.unshift(current);
4097
- }
4098
- }
4099
- return path4;
4100
- }
4101
- /**
4102
- * Get nodes grouped by their depth from roots
4103
- */
4104
- getDepthGroups() {
4105
- const depths = /* @__PURE__ */ new Map();
4106
- const groups = /* @__PURE__ */ new Map();
4107
- for (const rootId of this.graph.roots) {
4108
- const queue = [rootId];
4109
- depths.set(rootId, 0);
4110
- while (queue.length > 0) {
4111
- const current = queue.shift();
4112
- const currentDepth = depths.get(current);
4113
- for (const neighbor of this.adjacency.get(current) || /* @__PURE__ */ new Set()) {
4114
- const newDepth = currentDepth + 1;
4115
- if (!depths.has(neighbor) || depths.get(neighbor) > newDepth) {
4116
- depths.set(neighbor, newDepth);
4117
- queue.push(neighbor);
4118
- }
4119
- }
4120
- }
4121
- }
4122
- for (const [nodeId, depth] of depths.entries()) {
4123
- if (!groups.has(depth)) {
4124
- groups.set(depth, []);
4125
- }
4126
- groups.get(depth).push(nodeId);
4127
- }
4128
- return groups;
4129
- }
4130
- /**
4131
- * Get the underlying graph
4132
- */
4133
- getGraph() {
4134
- return this.graph;
4135
- }
4136
- };
4137
-
4138
- // src/cli.ts
4139
- var import_typescript16 = __toESM(require("typescript"), 1);
4140
- var import_node_process2 = __toESM(require("process"), 1);
4141
- var import_meta2 = {};
4142
- var ADORN_VERSION = (() => {
4143
- const tryReadPackageJson = (filePath) => {
4144
- try {
4145
- const pkg = JSON.parse((0, import_node_fs6.readFileSync)(filePath, "utf-8"));
4146
- return pkg.version ?? null;
4147
- } catch {
4148
- return null;
4149
- }
4150
- };
4151
- const potentialPaths = [];
4152
- try {
4153
- const importMetaUrl = import_meta2?.url;
4154
- if (importMetaUrl && typeof importMetaUrl === "string" && importMetaUrl.length > 0) {
4155
- const cliDir = (0, import_node_path6.dirname)((0, import_node_url.fileURLToPath)(importMetaUrl));
4156
- potentialPaths.push(
4157
- (0, import_node_path6.resolve)(cliDir, "..", "package.json"),
4158
- (0, import_node_path6.resolve)(cliDir, "package.json")
4159
- );
4160
- }
4161
- } catch {
4162
- }
4163
- const cwd = import_node_process2.default.cwd();
4164
- potentialPaths.push(
4165
- (0, import_node_path6.resolve)(cwd, "package.json"),
4166
- (0, import_node_path6.resolve)(cwd, "node_modules", "adorn-api", "package.json"),
4167
- (0, import_node_path6.resolve)(cwd, "..", "package.json"),
4168
- (0, import_node_path6.resolve)(cwd, "..", "..", "package.json")
4169
- );
4170
- for (const pkgPath of potentialPaths) {
4171
- const version = tryReadPackageJson(pkgPath);
4172
- if (version) {
4173
- return version;
4174
- }
4175
- }
4176
- return "0.0.0";
4177
- })();
4178
- function log(msg, options) {
4179
- if (options?.indent) {
4180
- import_node_process2.default.stdout.write(" " + msg + "\n");
4181
- } else {
4182
- import_node_process2.default.stdout.write(msg + "\n");
4183
- }
4184
- }
4185
- function formatBytes(bytes) {
4186
- if (bytes < 1024) return `${bytes} B`;
4187
- if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
4188
- return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
4189
- }
4190
- function getFileSize(path4) {
4191
- try {
4192
- return (0, import_node_fs6.statSync)(path4).size;
4193
- } catch {
4194
- return void 0;
4195
- }
4196
- }
4197
- function sanitizeForJson(obj) {
4198
- if (obj === null || obj === void 0) return obj;
4199
- if (typeof obj !== "object") return obj;
4200
- if (Array.isArray(obj)) {
4201
- return obj.map((item) => sanitizeForJson(item));
4202
- }
4203
- const result = {};
4204
- for (const [key, value] of Object.entries(obj)) {
4205
- if (key.startsWith("__@") || key.startsWith("[")) continue;
4206
- if (typeof value === "function") continue;
4207
- if (value !== null && typeof value === "object") {
4208
- const typeName = value.constructor?.name;
4209
- if (typeName && !["Object", "Array", "String", "Number", "Boolean", "Date", "RegExp"].includes(typeName)) {
4210
- continue;
4211
- }
4212
- }
4213
- result[key] = sanitizeForJson(value);
4214
- }
4215
- return result;
4216
- }
4217
- function buildControllerGraph(controllers) {
4218
- const graph = createGraph(import_typescript16.default.version);
4219
- const nodeMap = /* @__PURE__ */ new Map();
4220
- for (const ctrl of controllers) {
4221
- const nodeId = `Controller:${ctrl.className}`;
4222
- const node = {
4223
- id: nodeId,
4224
- kind: "Controller",
4225
- metadata: {
4226
- name: ctrl.className,
4227
- sourceLocation: { filePath: "", line: 0, column: 0 },
4228
- tags: /* @__PURE__ */ new Set(),
4229
- annotations: /* @__PURE__ */ new Map()
4230
- },
4231
- edges: [],
4232
- controller: {
4233
- basePath: ctrl.basePath
4234
- }
4235
- };
4236
- addNode(graph, node);
4237
- nodeMap.set(nodeId, node);
4238
- }
4239
- let opIndex = 0;
4240
- for (const ctrl of controllers) {
4241
- for (const op of ctrl.operations) {
4242
- const nodeId = `Operation:${op.operationId}`;
4243
- const node = {
4244
- id: nodeId,
4245
- kind: "Operation",
4246
- metadata: {
4247
- name: op.operationId,
4248
- sourceLocation: { filePath: "", line: 0, column: 0 },
4249
- tags: /* @__PURE__ */ new Set(),
4250
- annotations: /* @__PURE__ */ new Map()
4251
- },
4252
- edges: [],
4253
- operation: {
4254
- httpMethod: op.httpMethod,
4255
- path: op.path,
4256
- operationId: op.operationId,
4257
- returnType: op.returnType || ""
4258
- }
4259
- };
4260
- addNode(graph, node);
4261
- nodeMap.set(nodeId, node);
4262
- const ctrlNode = nodeMap.get(`Controller:${ctrl.className}`);
4263
- if (ctrlNode) {
4264
- addEdge(graph, ctrlNode.id, node.id, "contains");
4265
- }
4266
- opIndex++;
4267
- }
4268
- }
4269
- for (const ctrl of controllers) {
4270
- for (const op of ctrl.operations) {
4271
- if (op.returnType && !nodeMap.has(op.returnType)) {
4272
- const node = {
4273
- id: op.returnType,
4274
- kind: "TypeDefinition",
4275
- metadata: {
4276
- name: op.returnType,
4277
- sourceLocation: { filePath: "", line: 0, column: 0 },
4278
- tags: /* @__PURE__ */ new Set(),
4279
- annotations: /* @__PURE__ */ new Map()
4280
- },
4281
- edges: [],
4282
- typeDef: {
4283
- isGeneric: false,
4284
- properties: /* @__PURE__ */ new Map()
4285
- }
4286
- };
4287
- addNode(graph, node);
4288
- nodeMap.set(op.returnType, node);
4289
- const opNodeId = `Operation:${op.operationId}`;
4290
- const opNode = nodeMap.get(opNodeId);
4291
- if (opNode) {
4292
- addEdge(graph, opNode.id, node.id, "uses");
4293
- }
4294
- }
4295
- }
4296
- }
4297
- return graph;
4298
- }
4299
- async function buildCommand(args) {
4300
- const progress = new ProgressTracker({ verbose: args.includes("--verbose"), quiet: args.includes("--quiet") });
4301
- const projectIndex = args.indexOf("-p");
4302
- const projectPath = projectIndex !== -1 ? args[projectIndex + 1] : "./tsconfig.json";
4303
- const outputDir = args.includes("--output") ? args[args.indexOf("--output") + 1] : ".adorn";
4304
- const ifStale = args.includes("--if-stale");
4305
- const validationModeIndex = args.indexOf("--validation-mode");
4306
- const validationMode = validationModeIndex !== -1 ? args[validationModeIndex + 1] : "ajv-runtime";
4307
- const verbose = args.includes("--verbose");
4308
- const quiet = args.includes("--quiet");
4309
- const split = args.includes("--split");
4310
- const showQueryBuilder = args.includes("--show-query-builder");
4311
- const splitStrategyIndex = args.indexOf("--split-strategy");
4312
- const splitStrategy = splitStrategyIndex !== -1 ? args[splitStrategyIndex + 1] : void 0;
4313
- const splitThresholdIndex = args.indexOf("--split-threshold");
4314
- const splitThreshold = splitThresholdIndex !== -1 ? parseInt(args[splitThresholdIndex + 1], 10) : 50;
4315
- if (validationMode !== "none" && validationMode !== "ajv-runtime" && validationMode !== "precompiled") {
4316
- console.error(`Invalid validation mode: ${validationMode}. Valid values: none, ajv-runtime, precompiled`);
4317
- import_node_process2.default.exit(1);
4318
- }
4319
- const outputPath = (0, import_node_path6.resolve)(outputDir);
4320
- if (!quiet) {
4321
- log(`adorn-api v${ADORN_VERSION} - Building API artifacts`);
4322
- log("");
4323
- }
4324
- if (ifStale) {
4325
- progress.startPhase("staleness-check", "Checking for stale artifacts");
4326
- const stale = await isStale({
4327
- outDir: outputDir,
4328
- project: projectPath,
4329
- adornVersion: ADORN_VERSION,
4330
- typescriptVersion: import_typescript16.default.version
4331
- });
4332
- if (!stale.stale) {
4333
- progress.completePhase("staleness-check");
4334
- if (!quiet) {
4335
- log("adorn-api: artifacts up-to-date");
4336
- }
4337
- return;
4338
- }
4339
- progress.completePhase("staleness-check", `Artifacts stale (${stale.reason})`);
4340
- if (verbose) {
4341
- progress.verboseLog(`Stale reason: ${stale.detail || stale.reason}`);
4342
- }
4343
- } else {
4344
- progress.startPhase("configuration", "Initializing build");
4345
- progress.completePhase("configuration", "Build forced (--if-stale not used)");
4346
- }
4347
- progress.startPhase("program", "Loading TypeScript configuration");
4348
- if (verbose) {
4349
- progress.verboseLog(`Loading ${projectPath}`);
4350
- }
4351
- const { program, checker, sourceFiles } = createProgramFromConfig(projectPath);
4352
- const projectSourceFiles = sourceFiles.filter((sf) => !sf.fileName.includes("node_modules"));
4353
- progress.completePhase("program");
4354
- if (verbose) {
4355
- progress.verboseLog(`Found ${projectSourceFiles.length} source files`);
4356
- }
4357
- progress.startPhase("scan", "Scanning for controllers");
4358
- const controllers = scanControllers(sourceFiles, checker);
4359
- if (controllers.length === 0) {
4360
- console.warn("No controllers found!");
4361
- import_node_process2.default.exit(1);
4362
- }
4363
- const totalOperations = controllers.reduce((sum, ctrl) => sum + ctrl.operations.length, 0);
4364
- progress.completePhase("scan", `Found ${controllers.length} controller(s) with ${totalOperations} operation(s)`);
4365
- if (verbose) {
4366
- for (const ctrl of controllers) {
4367
- progress.verboseLog(`Controller: ${ctrl.className} (${ctrl.basePath}) - ${ctrl.operations.length} operations`);
4368
- }
4369
- }
4370
- progress.startPhase("openapi", "Generating OpenAPI schema");
4371
- const queryBuilderStats = {
4372
- totalOperations,
4373
- detected: 0,
4374
- fallback: 0,
4375
- operations: []
4376
- };
4377
- const openapiSpinner = new Spinner("Processing schemas");
4378
- if (!quiet) openapiSpinner.start();
4379
- const openapi = generateOpenAPI(controllers, checker, {
4380
- title: "API",
4381
- version: "1.0.0",
4382
- onProgress: (message, current, total) => {
4383
- if (!quiet) {
4384
- openapiSpinner.setStatus(`${message} (${current}/${total})`);
4385
- }
4386
- },
4387
- onQueryBuilderProgress: (info) => {
4388
- if (info.queryBuilderDetected) {
4389
- queryBuilderStats.detected++;
4390
- } else {
4391
- queryBuilderStats.fallback++;
4392
- }
4393
- queryBuilderStats.operations.push({
4394
- operationId: info.operation,
4395
- method: info.method,
4396
- path: info.path,
4397
- detected: info.queryBuilderDetected,
4398
- entityName: info.entityName,
4399
- selectedFields: info.selectedFields,
4400
- isPaged: info.isPaged
4401
- });
4402
- if (showQueryBuilder || verbose) {
4403
- if (info.queryBuilderDetected) {
4404
- const fieldsStr = info.selectedFields && info.selectedFields.length > 0 ? ` [select: ${info.selectedFields.join(",")}]` : "";
4405
- const pagedStr = info.isPaged ? " (paged)" : "";
4406
- progress.verboseLog(` \u2713 Query builder: ${info.method} ${info.path} \u2192 ${info.entityName}${fieldsStr}${pagedStr}`);
4407
- } else {
4408
- progress.verboseLog(` \u25CB No query builder: ${info.method} ${info.path} \u2192 using full entity schema`);
4409
- }
4410
- }
4411
- }
4412
- });
4413
- if (!quiet) {
4414
- openapiSpinner.setStatus(`Processed ${controllers.length} controllers, ${totalOperations} operations`);
4415
- }
4416
- if (!quiet) openapiSpinner.stop();
4417
- const schemaCount = Object.keys(openapi.components?.schemas || {}).length;
4418
- let splitEnabled = false;
4419
- if (split && schemaCount >= splitThreshold) {
4420
- progress.verboseLog(`Schema count (${schemaCount}) >= threshold (${splitThreshold}), analyzing for auto-split...`);
4421
- const graph = buildControllerGraph(controllers);
4422
- const schemaGraph = new SchemaGraph(graph);
4423
- const schemasMap = new Map(Object.entries(openapi.components?.schemas || {}));
4424
- const strategy = splitStrategy || "auto";
4425
- const partitioning = partitionSchemas(schemasMap, graph, schemaGraph, {
4426
- strategy,
4427
- threshold: splitThreshold,
4428
- verbose
4429
- });
4430
- splitEnabled = partitioning.shouldSplit;
4431
- if (splitEnabled) {
4432
- progress.verboseLog(`Partitioning result: ${partitioning.strategy} strategy`);
4433
- progress.verboseLog(`Recommendation: ${partitioning.recommendation}`);
4434
- if (!quiet) {
4435
- log(` Auto-split enabled: ${partitioning.strategy} strategy`);
4436
- }
4437
- const splitSpinner = new Spinner("Writing split schema files");
4438
- if (!quiet) splitSpinner.start();
4439
- generateModularOpenAPI(openapi, partitioning, {
4440
- outputDir: outputPath,
4441
- schemasDir: "schemas",
4442
- createIndexFile: true,
4443
- prettyPrint: true,
4444
- onProgress: (step, index, total) => {
4445
- if (!quiet) {
4446
- progress.logSub(`${step} (${index}/${total})`);
4447
- }
4448
- }
4449
- });
4450
- if (!quiet) splitSpinner.stop();
4451
- if (!quiet) {
4452
- log(` Schema groups: ${partitioning.groups.length}`);
4453
- }
4454
- } else {
4455
- if (!quiet) {
4456
- log(` Auto-split not needed: ${partitioning.recommendation}`);
4457
- }
4458
- }
4459
- } else if (!split) {
4460
- if (!quiet) {
4461
- log(` Splitting disabled (--split not specified)`);
4462
- }
4463
- } else {
4464
- if (!quiet) {
4465
- log(` Schema count (${schemaCount}) below threshold (${splitThreshold}), single file mode`);
4466
- }
4467
- }
4468
- progress.completePhase("openapi", `Generated ${schemaCount} schema(s)${splitEnabled ? " (split into groups)" : ""}`);
4469
- if (showQueryBuilder && totalOperations > 0) {
4470
- log("");
4471
- log("Query Builder Analysis:");
4472
- log(` Operations analyzed: ${totalOperations}`);
4473
- log(` Patterns detected: ${queryBuilderStats.detected} (${Math.round(queryBuilderStats.detected / totalOperations * 100)}%)`);
4474
- log(` Full schemas used: ${queryBuilderStats.fallback} (${Math.round(queryBuilderStats.fallback / totalOperations * 100)}%)`);
4475
- if (queryBuilderStats.detected > 0) {
4476
- const totalFields = queryBuilderStats.operations.filter((op) => op.detected && op.selectedFields).reduce((sum, op) => sum + (op.selectedFields?.length || 0), 0);
4477
- log(` Fields selected: ${totalFields} total (avg ${Math.round(totalFields / queryBuilderStats.detected)} per query)`);
4478
- }
4479
- }
4480
- progress.startPhase("manifest", "Generating manifest");
4481
- const manifest = generateManifest(controllers, checker, ADORN_VERSION, validationMode);
4482
- progress.completePhase("manifest");
4483
- progress.startPhase("write", "Writing artifacts");
4484
- (0, import_node_fs6.mkdirSync)(outputPath, { recursive: true });
4485
- const openapiPath = (0, import_node_path6.resolve)(outputPath, "openapi.json");
4486
- const manifestPath = (0, import_node_path6.resolve)(outputPath, "manifest.json");
4487
- if (!splitEnabled) {
4488
- (0, import_node_fs6.writeFileSync)(openapiPath, JSON.stringify(sanitizeForJson(openapi), null, 2));
4489
- }
4490
- (0, import_node_fs6.writeFileSync)(manifestPath, JSON.stringify(manifest, null, 2));
4491
- const artifacts = [
4492
- { name: "openapi.json", size: getFileSize(openapiPath) },
4493
- { name: "manifest.json", size: getFileSize(manifestPath) }
4494
- ];
4495
- if (splitEnabled) {
4496
- const schemasDir = (0, import_node_path6.resolve)(outputPath, "schemas");
4497
- if ((0, import_node_fs6.existsSync)(schemasDir)) {
4498
- const fs4 = await import("fs");
4499
- const files = fs4.readdirSync(schemasDir);
4500
- for (const file of files) {
4501
- const filePath = (0, import_node_path6.resolve)(schemasDir, file);
4502
- artifacts.push({ name: `schemas/${file}`, size: getFileSize(filePath) });
4503
- }
4504
- }
4505
- }
4506
- if (verbose) {
4507
- for (const artifact of artifacts) {
4508
- progress.verboseLog(`Written: ${artifact.name} (${formatBytes(artifact.size || 0)})`);
4509
- }
4510
- }
4511
- if (validationMode === "precompiled") {
4512
- progress.startPhase("validators", "Generating precompiled validators");
4513
- const manifestObj = JSON.parse((0, import_node_fs6.readFileSync)(manifestPath, "utf-8"));
4514
- const spinner = new Spinner("Generating validators...");
4515
- if (!quiet) spinner.start();
4516
- await emitPrecompiledValidators({
4517
- outDir: outputPath,
4518
- openapi,
4519
- manifest: manifestObj,
4520
- strict: "off",
4521
- formatsMode: "full"
4522
- });
4523
- if (!quiet) spinner.stop();
4524
- manifestObj.validation = {
4525
- mode: "precompiled",
4526
- precompiledModule: "./validators.mjs"
4527
- };
4528
- (0, import_node_fs6.writeFileSync)(manifestPath, JSON.stringify(manifestObj, null, 2));
4529
- const validatorsCjsPath = (0, import_node_path6.resolve)(outputPath, "validators.cjs");
4530
- const validatorsMjsPath = (0, import_node_path6.resolve)(outputPath, "validators.mjs");
4531
- const validatorsMetaPath = (0, import_node_path6.resolve)(outputPath, "validators.meta.json");
4532
- artifacts.push(
4533
- { name: "validators.cjs", size: getFileSize(validatorsCjsPath) },
4534
- { name: "validators.mjs", size: getFileSize(validatorsMjsPath) },
4535
- { name: "validators.meta.json", size: getFileSize(validatorsMetaPath) }
4536
- );
4537
- progress.completePhase("validators");
4538
- if (verbose) {
4539
- progress.verboseLog("Precompiled validators generated successfully");
4540
- }
4541
- }
4542
- progress.startPhase("cache", "Writing cache");
4543
- writeCache({
4544
- outDir: outputDir,
4545
- tsconfigAbs: (0, import_node_path6.resolve)(projectPath),
4546
- program,
4547
- adornVersion: ADORN_VERSION
4548
- });
4549
- const cachePath = (0, import_node_path6.resolve)(outputPath, "cache.json");
4550
- artifacts.push({ name: "cache.json", size: getFileSize(cachePath) });
4551
- progress.completePhase("cache");
4552
- if (verbose) {
4553
- progress.verboseLog(`Written: cache.json (${formatBytes(getFileSize(cachePath) || 0)})`);
4554
- }
4555
- const stats = {
4556
- controllers: controllers.length,
4557
- operations: totalOperations,
4558
- schemas: schemaCount,
4559
- sourceFiles: projectSourceFiles.length,
4560
- artifactsWritten: artifacts.map((a) => a.name),
4561
- splitEnabled,
4562
- queryBuilder: {
4563
- detected: queryBuilderStats.detected,
4564
- fallback: queryBuilderStats.fallback,
4565
- total: totalOperations
4566
- }
4567
- };
4568
- progress.printSummary(stats);
4569
- progress.printArtifacts(artifacts);
4570
- }
4571
- function cleanCommand(args) {
4572
- const quiet = args.includes("--quiet");
4573
- const outputDir = args.includes("--output") ? args[args.indexOf("--output") + 1] : ".adorn";
4574
- const outputPath = (0, import_node_path6.resolve)(outputDir);
4575
- if ((0, import_node_fs6.existsSync)(outputPath)) {
4576
- (0, import_node_fs6.rmSync)(outputPath, { recursive: true, force: true });
4577
- }
4578
- if (!quiet) {
4579
- log(`adorn-api: cleaned ${outputDir}`);
4580
- }
4581
- }
4582
- var command = import_node_process2.default.argv[2];
4583
- if (command === "build") {
4584
- buildCommand(import_node_process2.default.argv.slice(3)).catch((err) => {
4585
- console.error(err);
4586
- import_node_process2.default.exit(1);
4587
- });
4588
- } else if (command === "clean") {
4589
- cleanCommand(import_node_process2.default.argv.slice(3));
4590
- } else {
4591
- console.log(`
4592
- adorn-api CLI v${ADORN_VERSION}
4593
-
4594
- Commands:
4595
- build Generate OpenAPI and manifest from TypeScript source
4596
- clean Remove generated artifacts
4597
-
4598
- Options:
4599
- -p <path> Path to tsconfig.json (default: ./tsconfig.json)
4600
- --output <dir> Output directory (default: .adorn)
4601
- --if-stale Only rebuild if artifacts are stale
4602
- --validation-mode <mode> Validation mode: none, ajv-runtime, precompiled (default: ajv-runtime)
4603
- --split Enable automatic schema splitting (default: disabled)
4604
- --split-strategy <mode> Override splitting strategy: controller, dependency, size, auto (default: auto)
4605
- --split-threshold <num> Schema count threshold for auto-split (default: 50)
4606
- --verbose Show detailed progress information
4607
- --quiet Suppress non-essential output
4608
- --show-query-builder Show query builder inspection details and statistics
4609
-
4610
- Examples:
4611
- adorn-api build -p ./tsconfig.json --output .adorn
4612
- adorn-api build --if-stale
4613
- adorn-api build --validation-mode precompiled
4614
- adorn-api build --verbose
4615
- adorn-api build --show-query-builder # Show query builder analysis details
4616
- adorn-api build --split # Enable split mode
4617
- adorn-api build --split-strategy controller # Force controller-based splitting
4618
- adorn-api build --split-threshold 100 # Increase threshold to 100
4619
- adorn-api clean
4620
- `);
4621
- }
4622
- //# sourceMappingURL=cli.cjs.map