adorn-api 1.0.22 → 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 (380) 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 -122
  210. package/dist/cli/progress.d.ts.map +0 -1
  211. package/dist/cli.cjs +0 -4390
  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 -4371
  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 -8
  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 -76
  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/splitOpenapi.d.ts +0 -46
  286. package/dist/compiler/schema/splitOpenapi.d.ts.map +0 -1
  287. package/dist/compiler/schema/typeToJsonSchema.d.ts +0 -26
  288. package/dist/compiler/schema/typeToJsonSchema.d.ts.map +0 -1
  289. package/dist/compiler/schema/types.d.ts +0 -70
  290. package/dist/compiler/schema/types.d.ts.map +0 -1
  291. package/dist/compiler/schema/unionHandler.d.ts +0 -70
  292. package/dist/compiler/schema/unionHandler.d.ts.map +0 -1
  293. package/dist/compiler/transform/dedup.d.ts +0 -35
  294. package/dist/compiler/transform/dedup.d.ts.map +0 -1
  295. package/dist/compiler/transform/flatten.d.ts +0 -50
  296. package/dist/compiler/transform/flatten.d.ts.map +0 -1
  297. package/dist/compiler/transform/index.d.ts +0 -7
  298. package/dist/compiler/transform/index.d.ts.map +0 -1
  299. package/dist/compiler/transform/inline.d.ts +0 -46
  300. package/dist/compiler/transform/inline.d.ts.map +0 -1
  301. package/dist/compiler/validation/emitPrecompiledValidators.d.ts +0 -62
  302. package/dist/compiler/validation/emitPrecompiledValidators.d.ts.map +0 -1
  303. package/dist/compiler/validation/index.d.ts +0 -5
  304. package/dist/compiler/validation/index.d.ts.map +0 -1
  305. package/dist/decorators/Auth.d.ts +0 -22
  306. package/dist/decorators/Auth.d.ts.map +0 -1
  307. package/dist/decorators/Controller.d.ts +0 -17
  308. package/dist/decorators/Controller.d.ts.map +0 -1
  309. package/dist/decorators/Public.d.ts +0 -15
  310. package/dist/decorators/Public.d.ts.map +0 -1
  311. package/dist/decorators/Use.d.ts +0 -23
  312. package/dist/decorators/Use.d.ts.map +0 -1
  313. package/dist/decorators/methods.d.ts +0 -26
  314. package/dist/decorators/methods.d.ts.map +0 -1
  315. package/dist/express.cjs +0 -1186
  316. package/dist/express.cjs.map +0 -1
  317. package/dist/express.d.ts +0 -8
  318. package/dist/express.d.ts.map +0 -1
  319. package/dist/express.js +0 -1150
  320. package/dist/express.js.map +0 -1
  321. package/dist/http.d.ts +0 -33
  322. package/dist/http.d.ts.map +0 -1
  323. package/dist/index.cjs +0 -724
  324. package/dist/index.cjs.map +0 -1
  325. package/dist/metal/applyListQuery.d.ts +0 -100
  326. package/dist/metal/applyListQuery.d.ts.map +0 -1
  327. package/dist/metal/index.cjs +0 -278
  328. package/dist/metal/index.cjs.map +0 -1
  329. package/dist/metal/index.d.ts +0 -15
  330. package/dist/metal/index.d.ts.map +0 -1
  331. package/dist/metal/index.js +0 -243
  332. package/dist/metal/index.js.map +0 -1
  333. package/dist/metal/listQuery.d.ts +0 -26
  334. package/dist/metal/listQuery.d.ts.map +0 -1
  335. package/dist/metal/queryOptions.d.ts +0 -16
  336. package/dist/metal/queryOptions.d.ts.map +0 -1
  337. package/dist/metal/readMetalBag.d.ts +0 -69
  338. package/dist/metal/readMetalBag.d.ts.map +0 -1
  339. package/dist/metal/registerMetalEntities.d.ts +0 -26
  340. package/dist/metal/registerMetalEntities.d.ts.map +0 -1
  341. package/dist/metal/schemaFromEntity.d.ts +0 -41
  342. package/dist/metal/schemaFromEntity.d.ts.map +0 -1
  343. package/dist/metal/searchWhere.d.ts +0 -97
  344. package/dist/metal/searchWhere.d.ts.map +0 -1
  345. package/dist/metal/symbolMetadata.d.ts +0 -8
  346. package/dist/metal/symbolMetadata.d.ts.map +0 -1
  347. package/dist/runtime/auth/runtime.d.ts +0 -183
  348. package/dist/runtime/auth/runtime.d.ts.map +0 -1
  349. package/dist/runtime/metadata/bucket.d.ts +0 -2
  350. package/dist/runtime/metadata/bucket.d.ts.map +0 -1
  351. package/dist/runtime/metadata/key.d.ts +0 -2
  352. package/dist/runtime/metadata/key.d.ts.map +0 -1
  353. package/dist/runtime/metadata/read.d.ts +0 -2
  354. package/dist/runtime/metadata/read.d.ts.map +0 -1
  355. package/dist/runtime/metadata/types.d.ts +0 -95
  356. package/dist/runtime/metadata/types.d.ts.map +0 -1
  357. package/dist/runtime/polyfill.d.ts +0 -2
  358. package/dist/runtime/polyfill.d.ts.map +0 -1
  359. package/dist/runtime/upload.d.ts +0 -44
  360. package/dist/runtime/upload.d.ts.map +0 -1
  361. package/dist/runtime/validation/ajv.d.ts +0 -120
  362. package/dist/runtime/validation/ajv.d.ts.map +0 -1
  363. package/dist/runtime/validation/index.d.ts +0 -11
  364. package/dist/runtime/validation/index.d.ts.map +0 -1
  365. package/dist/schema/decorators.d.ts +0 -37
  366. package/dist/schema/decorators.d.ts.map +0 -1
  367. package/dist/schema/index.cjs +0 -214
  368. package/dist/schema/index.cjs.map +0 -1
  369. package/dist/schema/index.d.ts +0 -2
  370. package/dist/schema/index.d.ts.map +0 -1
  371. package/dist/schema/index.js +0 -163
  372. package/dist/schema/index.js.map +0 -1
  373. package/dist/scripts/adorn-example.cjs +0 -404
  374. package/dist/scripts/adorn-example.cjs.map +0 -1
  375. package/dist/utils/operationId.d.ts +0 -2
  376. package/dist/utils/operationId.d.ts.map +0 -1
  377. package/dist/utils/path.d.ts +0 -2
  378. package/dist/utils/path.d.ts.map +0 -1
  379. package/dist/utils/port.d.ts +0 -9
  380. package/dist/utils/port.d.ts.map +0 -1
@@ -0,0 +1,439 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import {
3
+ parsePagination,
4
+ parseIdOrThrow,
5
+ parseFilter,
6
+ createFilterMappings,
7
+ withSession,
8
+ createPagedQueryDtoClass,
9
+ createPagedResponseDtoClass,
10
+ createMetalCrudDtos,
11
+ createMetalCrudDtoClasses,
12
+ createMetalDtoOverrides
13
+ } from "./metal-orm/index";
14
+ import { HttpError } from "../core/errors";
15
+ import { Column, Entity, PrimaryKey, col } from "metal-orm";
16
+ import { getDtoMeta } from "../core/metadata";
17
+
18
+ describe("metal-orm helpers", () => {
19
+ describe("parsePagination", () => {
20
+ it("uses defaults when no query provided", () => {
21
+ const result = parsePagination({});
22
+ expect(result.page).toBe(1);
23
+ expect(result.pageSize).toBe(25);
24
+ });
25
+
26
+ it("applies custom defaults", () => {
27
+ const result = parsePagination({}, { defaultPageSize: 50, maxPageSize: 200 });
28
+ expect(result.page).toBe(1);
29
+ expect(result.pageSize).toBe(50);
30
+ });
31
+
32
+ it("parses page and pageSize from query", () => {
33
+ const result = parsePagination({ page: "2", pageSize: "10" });
34
+ expect(result.page).toBe(2);
35
+ expect(result.pageSize).toBe(10);
36
+ });
37
+
38
+ it("clamps pageSize to max", () => {
39
+ const result = parsePagination({ pageSize: "150" }, { maxPageSize: 100 });
40
+ expect(result.pageSize).toBe(100);
41
+ });
42
+
43
+ it("clamps page to min of 1", () => {
44
+ const result = parsePagination({ page: "0" });
45
+ expect(result.page).toBe(1);
46
+ });
47
+
48
+ it("handles invalid values as defaults", () => {
49
+ const result = parsePagination({ page: "invalid", pageSize: "not-a-number" });
50
+ expect(result.page).toBe(1);
51
+ expect(result.pageSize).toBe(25);
52
+ });
53
+ });
54
+
55
+ describe("parseIdOrThrow", () => {
56
+ it("parses valid ID", () => {
57
+ const result = parseIdOrThrow("123", "User");
58
+ expect(result).toBe(123);
59
+ });
60
+
61
+ it("parses number ID", () => {
62
+ const result = parseIdOrThrow(456, "Post");
63
+ expect(result).toBe(456);
64
+ });
65
+
66
+ it("throws error for invalid ID", () => {
67
+ expect(() => parseIdOrThrow("invalid", "User")).toThrow(HttpError);
68
+ expect(() => parseIdOrThrow("invalid", "User")).toThrow("Invalid User id.");
69
+ });
70
+
71
+ it("throws error for negative ID", () => {
72
+ expect(() => parseIdOrThrow("-1", "User")).toThrow(HttpError);
73
+ });
74
+
75
+ it("throws error for zero ID", () => {
76
+ expect(() => parseIdOrThrow("0", "User")).toThrow(HttpError);
77
+ });
78
+ });
79
+
80
+ describe("parseFilter", () => {
81
+ it("returns undefined when query is undefined", () => {
82
+ const mappings = { nameContains: { field: "name" as const, operator: "contains" as const } };
83
+ const result = parseFilter<{ name: string }, "name">(undefined, mappings);
84
+ expect(result).toBeUndefined();
85
+ });
86
+
87
+ it("returns undefined when no matching query keys", () => {
88
+ const mappings = { nameContains: { field: "name" as const, operator: "contains" as const } };
89
+ const result = parseFilter<{ name: string }, "name">({ unknown: "value" }, mappings);
90
+ expect(result).toBeUndefined();
91
+ });
92
+
93
+ it("builds filter with contains operator", () => {
94
+ const mappings = { nameContains: { field: "name" as const, operator: "contains" as const } };
95
+ const result = parseFilter<{ name: string }, "name">({ nameContains: "John" }, mappings);
96
+ expect(result).toEqual({ name: { contains: "John" } });
97
+ });
98
+
99
+ it("builds filter with equals operator", () => {
100
+ const mappings = { userId: { field: "userId" as const, operator: "equals" as const } };
101
+ const result = parseFilter<{ userId: number }, "userId">({ userId: 123 }, mappings);
102
+ expect(result).toEqual({ userId: { equals: 123 } });
103
+ });
104
+
105
+ it("builds filter with multiple fields", () => {
106
+ const mappings = {
107
+ nameContains: { field: "name" as const, operator: "contains" as const },
108
+ userId: { field: "userId" as const, operator: "equals" as const }
109
+ };
110
+ const result = parseFilter<{ name: string; userId: number }, "name" | "userId">(
111
+ { nameContains: "John", userId: 123 },
112
+ mappings
113
+ );
114
+ expect(result).toEqual({ name: { contains: "John" }, userId: { equals: 123 } });
115
+ });
116
+
117
+ it("ignores empty string values", () => {
118
+ const mappings = { nameContains: { field: "name" as const, operator: "contains" as const } };
119
+ const result = parseFilter<{ name: string }, "name">({ nameContains: "" }, mappings);
120
+ expect(result).toBeUndefined();
121
+ });
122
+
123
+ it("ignores null values", () => {
124
+ const mappings = { nameContains: { field: "name" as const, operator: "contains" as const } };
125
+ const result = parseFilter<{ name: string }, "name">({ nameContains: null }, mappings);
126
+ expect(result).toBeUndefined();
127
+ });
128
+ });
129
+
130
+ describe("createFilterMappings", () => {
131
+ it("creates filter mappings from field definitions", () => {
132
+ const entity = { name: "", email: "", userId: 0 } as const;
133
+ const mappings = createFilterMappings(entity, [
134
+ { queryKey: "nameContains", field: "name", operator: "contains" },
135
+ { queryKey: "emailContains", field: "email", operator: "contains" },
136
+ { queryKey: "userId", field: "userId", operator: "equals" }
137
+ ]);
138
+
139
+ expect(mappings.nameContains).toEqual({ field: "name", operator: "contains" });
140
+ expect(mappings.emailContains).toEqual({ field: "email", operator: "contains" });
141
+ expect(mappings.userId).toEqual({ field: "userId", operator: "equals" });
142
+ });
143
+
144
+ it("uses equals as default operator", () => {
145
+ const entity = { name: "" } as const;
146
+ const mappings = createFilterMappings(entity, [
147
+ { queryKey: "name", field: "name" }
148
+ ]);
149
+
150
+ expect(mappings.name).toEqual({ field: "name", operator: "equals" });
151
+ });
152
+ });
153
+
154
+ describe("withSession", () => {
155
+ it("disposes session after handler completes", async () => {
156
+ let disposed = false;
157
+
158
+ const mockSession = {
159
+ dispose: async () => {
160
+ disposed = true;
161
+ }
162
+ } as any;
163
+
164
+ const createSession = () => mockSession;
165
+
166
+ const result = await withSession(createSession, async (_session) => {
167
+ return { id: 1 };
168
+ });
169
+
170
+ expect(result).toEqual({ id: 1 });
171
+ expect(disposed).toBe(true);
172
+ });
173
+
174
+ it("disposes session even when handler throws", async () => {
175
+ let disposed = false;
176
+
177
+ const mockSession = {
178
+ dispose: async () => {
179
+ disposed = true;
180
+ }
181
+ } as any;
182
+
183
+ const createSession = () => mockSession;
184
+
185
+ await expect(
186
+ withSession(createSession, async () => {
187
+ throw new Error("Test error");
188
+ })
189
+ ).rejects.toThrow("Test error");
190
+
191
+ expect(disposed).toBe(true);
192
+ });
193
+ });
194
+
195
+ describe("createPagedQueryDtoClass", () => {
196
+ it("creates DTO with pagination fields", () => {
197
+ const PagedQueryDto = createPagedQueryDtoClass();
198
+ expect(PagedQueryDto.name).toBe("PagedQueryDto");
199
+ });
200
+
201
+ it("applies custom defaults", () => {
202
+ const PagedQueryDto = createPagedQueryDtoClass({
203
+ defaultPageSize: 50,
204
+ maxPageSize: 200
205
+ });
206
+ expect(PagedQueryDto.name).toBe("PagedQueryDto");
207
+ });
208
+
209
+ it("uses custom name when provided", () => {
210
+ const PagedQueryDto = createPagedQueryDtoClass({
211
+ name: "UserPagedQueryDto"
212
+ });
213
+ const meta = getDtoMeta(PagedQueryDto);
214
+ expect(meta?.name).toBe("UserPagedQueryDto");
215
+ });
216
+ });
217
+
218
+ describe("createPagedResponseDtoClass", () => {
219
+ it("creates DTO with pagination fields", () => {
220
+ class ItemDto {}
221
+
222
+ const PagedResponseDto = createPagedResponseDtoClass({
223
+ itemDto: ItemDto as any,
224
+ description: "Test response"
225
+ });
226
+
227
+ expect(PagedResponseDto.name).toBe("PagedResponseDto");
228
+ });
229
+
230
+ it("uses custom name when provided", () => {
231
+ class ItemDto {}
232
+
233
+ const PagedResponseDto = createPagedResponseDtoClass({
234
+ itemDto: ItemDto as any,
235
+ name: "UserPagedResponseDto"
236
+ });
237
+
238
+ const meta = getDtoMeta(PagedResponseDto);
239
+ expect(meta?.name).toBe("UserPagedResponseDto");
240
+ });
241
+ });
242
+
243
+ describe("createMetalCrudDtos", () => {
244
+ @Entity({ tableName: "crud_dto_entities" })
245
+ class CrudDtoEntity {
246
+ @PrimaryKey(col.autoIncrement(col.int()))
247
+ id!: number;
248
+
249
+ @Column(col.notNull(col.text()))
250
+ name!: string;
251
+
252
+ @Column(col.text())
253
+ nickname?: string | null;
254
+ }
255
+
256
+ it("creates CRUD DTO decorators with defaults", () => {
257
+ const crud = createMetalCrudDtos(CrudDtoEntity, {
258
+ mutationExclude: ["id"]
259
+ });
260
+
261
+ @crud.response
262
+ class CrudDto {}
263
+
264
+ @crud.create
265
+ class CreateCrudDto {}
266
+
267
+ @crud.update
268
+ class UpdateCrudDto {}
269
+
270
+ @crud.params
271
+ class CrudParamsDto {}
272
+
273
+ const responseMeta = getDtoMeta(CrudDto);
274
+ const createMeta = getDtoMeta(CreateCrudDto);
275
+ const updateMeta = getDtoMeta(UpdateCrudDto);
276
+ const paramsMeta = getDtoMeta(CrudParamsDto);
277
+
278
+ expect(responseMeta?.fields.id).toBeDefined();
279
+ expect(createMeta?.fields.id).toBeUndefined();
280
+ expect(updateMeta?.fields.name?.optional).toBe(true);
281
+ expect(Object.keys(paramsMeta?.fields ?? {})).toEqual(["id"]);
282
+ });
283
+ });
284
+
285
+ describe("createMetalCrudDtoClasses", () => {
286
+ @Entity({ tableName: "crud_dto_class_entities" })
287
+ class CrudDtoClassEntity {
288
+ @PrimaryKey(col.autoIncrement(col.int()))
289
+ id!: number;
290
+
291
+ @Column(col.notNull(col.text()))
292
+ name!: string;
293
+
294
+ @Column(col.text())
295
+ nickname?: string | null;
296
+ }
297
+
298
+ it("builds ready-to-export DTO classes", () => {
299
+ const classes = createMetalCrudDtoClasses(CrudDtoClassEntity, {
300
+ mutationExclude: ["id"]
301
+ });
302
+
303
+ const responseMeta = getDtoMeta(classes.response);
304
+ const createMeta = getDtoMeta(classes.create);
305
+ const paramsMeta = getDtoMeta(classes.params);
306
+
307
+ expect(classes.response.name).toBe("CrudDtoClassEntityDto");
308
+ expect(responseMeta?.fields.id).toBeDefined();
309
+ expect(createMeta?.fields.id).toBeUndefined();
310
+ expect(paramsMeta?.fields).toEqual({ id: expect.any(Object) });
311
+ });
312
+
313
+ it("applies custom name overrides", () => {
314
+ const classes = createMetalCrudDtoClasses(CrudDtoClassEntity, {
315
+ baseName: "Person",
316
+ names: {
317
+ response: "PersonDto",
318
+ params: "PersonIdDto"
319
+ }
320
+ });
321
+
322
+ expect(classes.response.name).toBe("PersonDto");
323
+ expect(classes.params.name).toBe("PersonIdDto");
324
+ expect(classes.create.name).toBe("CreatePersonDto");
325
+ });
326
+ });
327
+
328
+ describe("createMetalDtoOverrides", () => {
329
+ @Entity({ tableName: "posts" })
330
+ class PostEntity {
331
+ @PrimaryKey(col.autoIncrement(col.int()))
332
+ id!: number;
333
+
334
+ @Column(col.notNull(col.text()))
335
+ title!: string;
336
+
337
+ @Column(col.text())
338
+ body?: string | null;
339
+
340
+ @Column(col.references(col.notNull(col.int()), { table: "users", column: "id" }))
341
+ userId!: number;
342
+
343
+ @Column(col.notNull(col.text()))
344
+ createdAt!: string;
345
+ }
346
+
347
+ @Entity({ tableName: "users" })
348
+ class UserEntity {
349
+ @PrimaryKey(col.autoIncrement(col.int()))
350
+ id!: number;
351
+
352
+ @Column(col.notNull(col.text()))
353
+ name!: string;
354
+
355
+ @Column(col.text())
356
+ email?: string | null;
357
+
358
+ @Column(col.notNull(col.text()))
359
+ createdAt!: string;
360
+ }
361
+
362
+ it("generates ID convention for primary key", () => {
363
+ const overrides = createMetalDtoOverrides(PostEntity);
364
+ expect(overrides.id.kind).toBe("integer");
365
+ expect((overrides.id as any).minimum).toBe(1);
366
+ expect((overrides.id as any).description).toBe("PostEntity id.");
367
+ });
368
+
369
+ it("generates FK convention from references metadata", () => {
370
+ const overrides = createMetalDtoOverrides(PostEntity);
371
+ expect(overrides.userId.kind).toBe("integer");
372
+ expect((overrides.userId as any).minimum).toBe(1);
373
+ expect((overrides.userId as any).description).toBe("User id.");
374
+ });
375
+
376
+ it("generates minLength for required text columns", () => {
377
+ const overrides = createMetalDtoOverrides(PostEntity);
378
+ expect(overrides.title.kind).toBe("string");
379
+ expect((overrides.title as any).minLength).toBe(1);
380
+ });
381
+
382
+ it("generates nullable for optional text columns", () => {
383
+ const overrides = createMetalDtoOverrides(PostEntity);
384
+ expect(overrides.body.kind).toBe("string");
385
+ expect((overrides.body as any).nullable).toBe(true);
386
+ });
387
+
388
+ it("allows custom entity name for descriptions", () => {
389
+ const overrides = createMetalDtoOverrides(PostEntity, {
390
+ entityName: "Post"
391
+ });
392
+ expect((overrides.id as any).description).toBe("Post id.");
393
+ });
394
+
395
+ it("allows selective overrides", () => {
396
+ const overrides = createMetalDtoOverrides(PostEntity, {
397
+ overrides: {
398
+ title: { kind: "string", minLength: 3, maxLength: 100 }
399
+ }
400
+ });
401
+ expect((overrides.title as any).minLength).toBe(3);
402
+ expect((overrides.title as any).maxLength).toBe(100);
403
+ });
404
+
405
+ it("excludes specified fields", () => {
406
+ const overrides = createMetalDtoOverrides(PostEntity, {
407
+ exclude: ["id", "createdAt"]
408
+ });
409
+ expect(overrides.id).toBeUndefined();
410
+ expect(overrides.createdAt).toBeUndefined();
411
+ expect(overrides.title).toBeDefined();
412
+ });
413
+
414
+ it("handles datetime type for timestamps", () => {
415
+ @Entity({ tableName: "events" })
416
+ class EventEntity {
417
+ @PrimaryKey(col.autoIncrement(col.int()))
418
+ id!: number;
419
+
420
+ @Column(col.notNull(col.text()))
421
+ name!: string;
422
+
423
+ @Column(col.notNull(col.datetime()))
424
+ eventDate!: string;
425
+ }
426
+
427
+ const overrides = createMetalDtoOverrides(EventEntity);
428
+ expect(overrides.eventDate.kind).toBe("string");
429
+ expect((overrides.eventDate as any).format).toBe("date-time");
430
+ });
431
+
432
+ it("extracts entity name from snake_case table name", () => {
433
+ const overrides = createMetalDtoOverrides(UserEntity, {
434
+ entityName: "User"
435
+ });
436
+ expect((overrides.id as any).description).toBe("User id.");
437
+ });
438
+ });
439
+ });
@@ -0,0 +1,39 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import {
3
+ normalizeSingle,
4
+ parseBoolean,
5
+ parseId,
6
+ parseInteger,
7
+ parseNumber
8
+ } from "../coerce";
9
+
10
+ describe("coerce helpers", () => {
11
+ it("normalizes single values", () => {
12
+ expect(normalizeSingle([" 1 "])).toBe("1");
13
+ expect(normalizeSingle("")).toBeUndefined();
14
+ expect(normalizeSingle("", { empty: "allow" })).toBe("");
15
+ });
16
+
17
+ it("parses numbers with range controls", () => {
18
+ expect(parseNumber(" 1.5 ")).toBe(1.5);
19
+ expect(parseNumber("nope")).toBeUndefined();
20
+ expect(parseNumber("10", { max: 5 })).toBeUndefined();
21
+ expect(parseNumber("10", { max: 5, clamp: true })).toBe(5);
22
+ });
23
+
24
+ it("parses integers and ids", () => {
25
+ expect(parseInteger("2")).toBe(2);
26
+ expect(parseInteger("2.2")).toBeUndefined();
27
+ expect(parseInteger("-2", { min: 0, clamp: true })).toBe(0);
28
+ expect(parseId("1")).toBe(1);
29
+ expect(parseId("0")).toBeUndefined();
30
+ expect(parseId("0", { min: 0 })).toBe(0);
31
+ });
32
+
33
+ it("parses booleans", () => {
34
+ expect(parseBoolean("true")).toBe(true);
35
+ expect(parseBoolean("FALSE")).toBe(false);
36
+ expect(parseBoolean("0")).toBe(false);
37
+ expect(parseBoolean("yes")).toBeUndefined();
38
+ });
39
+ });
@@ -0,0 +1,68 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import { Dto, Field, MergeDto, OmitDto, PartialDto, PickDto } from "../decorators";
3
+ import { getDtoMeta } from "../metadata";
4
+ import { t } from "../schema";
5
+
6
+ @Dto()
7
+ class BaseDto {
8
+ @Field(t.integer())
9
+ id!: number;
10
+
11
+ @Field(t.string({ minLength: 1 }))
12
+ name!: string;
13
+
14
+ @Field(t.optional(t.boolean()))
15
+ active?: boolean;
16
+ }
17
+
18
+ @PickDto(BaseDto, ["id", "name"])
19
+ class PickedDto {}
20
+
21
+ @OmitDto(BaseDto, ["id"], {
22
+ overrides: {
23
+ active: { optional: false }
24
+ }
25
+ })
26
+ class OmittedDto {}
27
+
28
+ @PartialDto(OmittedDto)
29
+ class PartialOmittedDto {}
30
+
31
+ @Dto()
32
+ class AuditDto {
33
+ @Field(t.string({ minLength: 3 }))
34
+ name!: string;
35
+
36
+ @Field(t.string())
37
+ updatedBy!: string;
38
+ }
39
+
40
+ @MergeDto([PickedDto, AuditDto])
41
+ class MergedDto {}
42
+
43
+ describe("dto composition decorators", () => {
44
+ it("picks and omits fields", () => {
45
+ const pickMeta = getDtoMeta(PickedDto);
46
+ expect(pickMeta).toBeTruthy();
47
+ expect(Object.keys(pickMeta!.fields)).toEqual(["id", "name"]);
48
+
49
+ const omitMeta = getDtoMeta(OmittedDto);
50
+ expect(omitMeta).toBeTruthy();
51
+ expect(Object.keys(omitMeta!.fields)).toEqual(["name", "active"]);
52
+ expect(omitMeta!.fields.active.optional).toBe(false);
53
+ });
54
+
55
+ it("marks partial DTOs as optional", () => {
56
+ const meta = getDtoMeta(PartialOmittedDto);
57
+ expect(meta).toBeTruthy();
58
+ expect(meta!.fields.name.optional).toBe(true);
59
+ expect(meta!.fields.active.optional).toBe(true);
60
+ });
61
+
62
+ it("merges DTO fields with later overrides", () => {
63
+ const meta = getDtoMeta(MergedDto);
64
+ expect(meta).toBeTruthy();
65
+ expect(Object.keys(meta!.fields)).toEqual(["id", "name", "updatedBy"]);
66
+ expect((meta!.fields.name.schema as { minLength?: number }).minLength).toBe(3);
67
+ });
68
+ });
@@ -0,0 +1,82 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import { t, type SchemaNode } from "../schema";
3
+ import {
4
+ buildSchemaFromNode,
5
+ buildSchemaFromDto,
6
+ createSchemaContext
7
+ } from "../schema-builder";
8
+ import { registerDto } from "../metadata";
9
+
10
+ describe("schema builder", () => {
11
+ it("maps primitive string options", () => {
12
+ const schema = buildSchemaFromNode(
13
+ t.string({ minLength: 2, maxLength: 4, pattern: "^[a-z]+$" }),
14
+ createSchemaContext()
15
+ );
16
+
17
+ expect(schema).toEqual({
18
+ type: "string",
19
+ minLength: 2,
20
+ maxLength: 4,
21
+ pattern: "^[a-z]+$"
22
+ });
23
+ });
24
+
25
+ it("adds nullability to schemas", () => {
26
+ const schema = buildSchemaFromNode(
27
+ t.nullable(t.integer({ minimum: 1 })),
28
+ createSchemaContext()
29
+ );
30
+
31
+ expect(schema).toEqual({
32
+ type: ["integer", "null"],
33
+ minimum: 1
34
+ });
35
+ });
36
+
37
+ it("builds dto schemas into components", () => {
38
+ class UserDto {
39
+ name!: string;
40
+ nickname?: string;
41
+ }
42
+
43
+ registerDto(UserDto, {
44
+ name: "UserDto",
45
+ fields: {
46
+ name: { schema: t.string({ minLength: 1 }) },
47
+ nickname: { schema: t.optional(t.string()) }
48
+ },
49
+ additionalProperties: false
50
+ });
51
+
52
+ const context = createSchemaContext();
53
+ const ref = buildSchemaFromDto(UserDto, context);
54
+
55
+ expect(ref).toEqual({ $ref: "#/components/schemas/UserDto" });
56
+ expect(context.components.UserDto).toEqual({
57
+ type: "object",
58
+ properties: {
59
+ name: { type: "string", minLength: 1 },
60
+ nickname: { type: "string" }
61
+ },
62
+ required: ["name"],
63
+ additionalProperties: false
64
+ });
65
+ });
66
+
67
+ it("supports arrays and enums", () => {
68
+ const schemaNode: SchemaNode = t.array(
69
+ t.enum(["draft", "published", "archived"])
70
+ );
71
+
72
+ const schema = buildSchemaFromNode(schemaNode, createSchemaContext());
73
+
74
+ expect(schema).toEqual({
75
+ type: "array",
76
+ items: {
77
+ enum: ["draft", "published", "archived"],
78
+ type: "string"
79
+ }
80
+ });
81
+ });
82
+ });