@classytic/arc 1.1.0 → 2.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (322) hide show
  1. package/README.md +247 -794
  2. package/bin/arc.js +91 -52
  3. package/dist/EventTransport-BD2U0BTc.d.mts +100 -0
  4. package/dist/EventTransport-BD2U0BTc.d.mts.map +1 -0
  5. package/dist/HookSystem-BsGV-j2l.mjs +405 -0
  6. package/dist/HookSystem-BsGV-j2l.mjs.map +1 -0
  7. package/dist/ResourceRegistry-DsN4KJjV.mjs +250 -0
  8. package/dist/ResourceRegistry-DsN4KJjV.mjs.map +1 -0
  9. package/dist/adapters/index.d.mts +5 -0
  10. package/dist/adapters/index.mjs +3 -0
  11. package/dist/audit/index.d.mts +82 -0
  12. package/dist/audit/index.d.mts.map +1 -0
  13. package/dist/audit/index.mjs +276 -0
  14. package/dist/audit/index.mjs.map +1 -0
  15. package/dist/audit/mongodb.d.mts +5 -0
  16. package/dist/audit/mongodb.mjs +3 -0
  17. package/dist/audited-C3T5DTUx.mjs +141 -0
  18. package/dist/audited-C3T5DTUx.mjs.map +1 -0
  19. package/dist/auth/index.d.mts +189 -0
  20. package/dist/auth/index.d.mts.map +1 -0
  21. package/dist/auth/index.mjs +1102 -0
  22. package/dist/auth/index.mjs.map +1 -0
  23. package/dist/auth/redis-session.d.mts +44 -0
  24. package/dist/auth/redis-session.d.mts.map +1 -0
  25. package/dist/auth/redis-session.mjs +76 -0
  26. package/dist/auth/redis-session.mjs.map +1 -0
  27. package/dist/betterAuthOpenApi-BrHKeSAx.mjs +250 -0
  28. package/dist/betterAuthOpenApi-BrHKeSAx.mjs.map +1 -0
  29. package/dist/cache/index.d.mts +146 -0
  30. package/dist/cache/index.d.mts.map +1 -0
  31. package/dist/cache/index.mjs +92 -0
  32. package/dist/cache/index.mjs.map +1 -0
  33. package/dist/caching-Bl28lYsR.mjs +94 -0
  34. package/dist/caching-Bl28lYsR.mjs.map +1 -0
  35. package/dist/chunk-C7Uep-_p.mjs +20 -0
  36. package/dist/circuitBreaker-DeY4FCjs.mjs +1097 -0
  37. package/dist/circuitBreaker-DeY4FCjs.mjs.map +1 -0
  38. package/dist/cli/commands/describe.d.mts +19 -0
  39. package/dist/cli/commands/describe.d.mts.map +1 -0
  40. package/dist/cli/commands/describe.mjs +239 -0
  41. package/dist/cli/commands/describe.mjs.map +1 -0
  42. package/dist/cli/commands/docs.d.mts +14 -0
  43. package/dist/cli/commands/docs.d.mts.map +1 -0
  44. package/dist/cli/commands/docs.mjs +53 -0
  45. package/dist/cli/commands/docs.mjs.map +1 -0
  46. package/dist/cli/commands/{generate.d.ts → generate.d.mts} +3 -1
  47. package/dist/cli/commands/generate.d.mts.map +1 -0
  48. package/dist/cli/commands/generate.mjs +358 -0
  49. package/dist/cli/commands/generate.mjs.map +1 -0
  50. package/dist/cli/commands/{init.d.ts → init.d.mts} +12 -8
  51. package/dist/cli/commands/init.d.mts.map +1 -0
  52. package/dist/cli/commands/{init.js → init.mjs} +807 -616
  53. package/dist/cli/commands/init.mjs.map +1 -0
  54. package/dist/cli/commands/introspect.d.mts +11 -0
  55. package/dist/cli/commands/introspect.d.mts.map +1 -0
  56. package/dist/cli/commands/introspect.mjs +76 -0
  57. package/dist/cli/commands/introspect.mjs.map +1 -0
  58. package/dist/cli/index.d.mts +17 -0
  59. package/dist/cli/index.d.mts.map +1 -0
  60. package/dist/cli/index.mjs +157 -0
  61. package/dist/cli/index.mjs.map +1 -0
  62. package/dist/constants-DdXFXQtN.mjs +85 -0
  63. package/dist/constants-DdXFXQtN.mjs.map +1 -0
  64. package/dist/core/index.d.mts +5 -0
  65. package/dist/core/index.mjs +4 -0
  66. package/dist/createApp-CUgNqegw.mjs +560 -0
  67. package/dist/createApp-CUgNqegw.mjs.map +1 -0
  68. package/dist/defineResource-k0_BDn8v.mjs +2197 -0
  69. package/dist/defineResource-k0_BDn8v.mjs.map +1 -0
  70. package/dist/discovery/index.d.mts +47 -0
  71. package/dist/discovery/index.d.mts.map +1 -0
  72. package/dist/discovery/index.mjs +110 -0
  73. package/dist/discovery/index.mjs.map +1 -0
  74. package/dist/docs/index.d.mts +163 -0
  75. package/dist/docs/index.d.mts.map +1 -0
  76. package/dist/docs/index.mjs +73 -0
  77. package/dist/docs/index.mjs.map +1 -0
  78. package/dist/elevation-BRy3yFWT.mjs +113 -0
  79. package/dist/elevation-BRy3yFWT.mjs.map +1 -0
  80. package/dist/elevation-B_2dRLVP.d.mts +88 -0
  81. package/dist/elevation-B_2dRLVP.d.mts.map +1 -0
  82. package/dist/errorHandler-BbcgBmIH.d.mts +73 -0
  83. package/dist/errorHandler-BbcgBmIH.d.mts.map +1 -0
  84. package/dist/errorHandler-C1okiriz.mjs +109 -0
  85. package/dist/errorHandler-C1okiriz.mjs.map +1 -0
  86. package/dist/errors-B9bZok84.mjs +212 -0
  87. package/dist/errors-B9bZok84.mjs.map +1 -0
  88. package/dist/errors-ChKiFz62.d.mts +125 -0
  89. package/dist/errors-ChKiFz62.d.mts.map +1 -0
  90. package/dist/eventPlugin-CTrLH3mt.d.mts +125 -0
  91. package/dist/eventPlugin-CTrLH3mt.d.mts.map +1 -0
  92. package/dist/eventPlugin-DGR_B2on.mjs +230 -0
  93. package/dist/eventPlugin-DGR_B2on.mjs.map +1 -0
  94. package/dist/events/index.d.mts +54 -0
  95. package/dist/events/index.d.mts.map +1 -0
  96. package/dist/events/index.mjs +52 -0
  97. package/dist/events/index.mjs.map +1 -0
  98. package/dist/events/transports/redis-stream-entry.d.mts +2 -0
  99. package/dist/events/transports/redis-stream-entry.mjs +178 -0
  100. package/dist/events/transports/redis-stream-entry.mjs.map +1 -0
  101. package/dist/events/transports/redis.d.mts +77 -0
  102. package/dist/events/transports/redis.d.mts.map +1 -0
  103. package/dist/events/transports/redis.mjs +125 -0
  104. package/dist/events/transports/redis.mjs.map +1 -0
  105. package/dist/externalPaths-DlINfKbP.d.mts +51 -0
  106. package/dist/externalPaths-DlINfKbP.d.mts.map +1 -0
  107. package/dist/factory/index.d.mts +64 -0
  108. package/dist/factory/index.d.mts.map +1 -0
  109. package/dist/factory/index.mjs +3 -0
  110. package/dist/fastifyAdapter-BkrGrlFi.d.mts +217 -0
  111. package/dist/fastifyAdapter-BkrGrlFi.d.mts.map +1 -0
  112. package/dist/fields-DyaDVX4J.d.mts +110 -0
  113. package/dist/fields-DyaDVX4J.d.mts.map +1 -0
  114. package/dist/fields-iagOozy0.mjs +115 -0
  115. package/dist/fields-iagOozy0.mjs.map +1 -0
  116. package/dist/hooks/index.d.mts +4 -0
  117. package/dist/hooks/index.mjs +3 -0
  118. package/dist/idempotency/index.d.mts +97 -0
  119. package/dist/idempotency/index.d.mts.map +1 -0
  120. package/dist/idempotency/index.mjs +320 -0
  121. package/dist/idempotency/index.mjs.map +1 -0
  122. package/dist/idempotency/mongodb.d.mts +2 -0
  123. package/dist/idempotency/mongodb.mjs +115 -0
  124. package/dist/idempotency/mongodb.mjs.map +1 -0
  125. package/dist/idempotency/redis.d.mts +2 -0
  126. package/dist/idempotency/redis.mjs +104 -0
  127. package/dist/idempotency/redis.mjs.map +1 -0
  128. package/dist/index.d.mts +261 -0
  129. package/dist/index.d.mts.map +1 -0
  130. package/dist/index.mjs +105 -0
  131. package/dist/index.mjs.map +1 -0
  132. package/dist/integrations/event-gateway.d.mts +47 -0
  133. package/dist/integrations/event-gateway.d.mts.map +1 -0
  134. package/dist/integrations/event-gateway.mjs +44 -0
  135. package/dist/integrations/event-gateway.mjs.map +1 -0
  136. package/dist/integrations/index.d.mts +5 -0
  137. package/dist/integrations/index.mjs +1 -0
  138. package/dist/integrations/jobs.d.mts +104 -0
  139. package/dist/integrations/jobs.d.mts.map +1 -0
  140. package/dist/integrations/jobs.mjs +124 -0
  141. package/dist/integrations/jobs.mjs.map +1 -0
  142. package/dist/integrations/streamline.d.mts +61 -0
  143. package/dist/integrations/streamline.d.mts.map +1 -0
  144. package/dist/integrations/streamline.mjs +126 -0
  145. package/dist/integrations/streamline.mjs.map +1 -0
  146. package/dist/integrations/websocket.d.mts +83 -0
  147. package/dist/integrations/websocket.d.mts.map +1 -0
  148. package/dist/integrations/websocket.mjs +289 -0
  149. package/dist/integrations/websocket.mjs.map +1 -0
  150. package/dist/interface-B01JvPVc.d.mts +78 -0
  151. package/dist/interface-B01JvPVc.d.mts.map +1 -0
  152. package/dist/interface-CZe8IkMf.d.mts +55 -0
  153. package/dist/interface-CZe8IkMf.d.mts.map +1 -0
  154. package/dist/interface-Ch8HU9uM.d.mts +1098 -0
  155. package/dist/interface-Ch8HU9uM.d.mts.map +1 -0
  156. package/dist/introspectionPlugin-rFdO8ZUa.mjs +54 -0
  157. package/dist/introspectionPlugin-rFdO8ZUa.mjs.map +1 -0
  158. package/dist/keys-BqNejWup.mjs +43 -0
  159. package/dist/keys-BqNejWup.mjs.map +1 -0
  160. package/dist/logger-Df2O2WsW.mjs +79 -0
  161. package/dist/logger-Df2O2WsW.mjs.map +1 -0
  162. package/dist/memory-cQgelFOj.mjs +144 -0
  163. package/dist/memory-cQgelFOj.mjs.map +1 -0
  164. package/dist/migrations/index.d.mts +157 -0
  165. package/dist/migrations/index.d.mts.map +1 -0
  166. package/dist/migrations/index.mjs +261 -0
  167. package/dist/migrations/index.mjs.map +1 -0
  168. package/dist/mongodb-BfJVlUJH.mjs +94 -0
  169. package/dist/mongodb-BfJVlUJH.mjs.map +1 -0
  170. package/dist/mongodb-CGzRbfAK.d.mts +119 -0
  171. package/dist/mongodb-CGzRbfAK.d.mts.map +1 -0
  172. package/dist/mongodb-JN-9JA7K.d.mts +72 -0
  173. package/dist/mongodb-JN-9JA7K.d.mts.map +1 -0
  174. package/dist/openapi-G3Cw7XuM.mjs +524 -0
  175. package/dist/openapi-G3Cw7XuM.mjs.map +1 -0
  176. package/dist/org/index.d.mts +69 -0
  177. package/dist/org/index.d.mts.map +1 -0
  178. package/dist/org/index.mjs +514 -0
  179. package/dist/org/index.mjs.map +1 -0
  180. package/dist/org/types.d.mts +83 -0
  181. package/dist/org/types.d.mts.map +1 -0
  182. package/dist/org/types.mjs +1 -0
  183. package/dist/permissions/index.d.mts +279 -0
  184. package/dist/permissions/index.d.mts.map +1 -0
  185. package/dist/permissions/index.mjs +579 -0
  186. package/dist/permissions/index.mjs.map +1 -0
  187. package/dist/plugins/index.d.mts +173 -0
  188. package/dist/plugins/index.d.mts.map +1 -0
  189. package/dist/plugins/index.mjs +523 -0
  190. package/dist/plugins/index.mjs.map +1 -0
  191. package/dist/plugins/response-cache.d.mts +88 -0
  192. package/dist/plugins/response-cache.d.mts.map +1 -0
  193. package/dist/plugins/response-cache.mjs +284 -0
  194. package/dist/plugins/response-cache.mjs.map +1 -0
  195. package/dist/plugins/tracing-entry.d.mts +2 -0
  196. package/dist/plugins/tracing-entry.mjs +186 -0
  197. package/dist/plugins/tracing-entry.mjs.map +1 -0
  198. package/dist/pluralize-CEweyOEm.mjs +87 -0
  199. package/dist/pluralize-CEweyOEm.mjs.map +1 -0
  200. package/dist/policies/{index.d.ts → index.d.mts} +204 -169
  201. package/dist/policies/index.d.mts.map +1 -0
  202. package/dist/policies/index.mjs +322 -0
  203. package/dist/policies/index.mjs.map +1 -0
  204. package/dist/presets/{index.d.ts → index.d.mts} +63 -131
  205. package/dist/presets/index.d.mts.map +1 -0
  206. package/dist/presets/index.mjs +144 -0
  207. package/dist/presets/index.mjs.map +1 -0
  208. package/dist/presets/multiTenant.d.mts +25 -0
  209. package/dist/presets/multiTenant.d.mts.map +1 -0
  210. package/dist/presets/multiTenant.mjs +114 -0
  211. package/dist/presets/multiTenant.mjs.map +1 -0
  212. package/dist/presets-BITljm96.mjs +120 -0
  213. package/dist/presets-BITljm96.mjs.map +1 -0
  214. package/dist/presets-DzSMwlKj.d.mts +58 -0
  215. package/dist/presets-DzSMwlKj.d.mts.map +1 -0
  216. package/dist/prisma-DJbMt3yf.mjs +628 -0
  217. package/dist/prisma-DJbMt3yf.mjs.map +1 -0
  218. package/dist/prisma-Dg9GoVdj.d.mts +275 -0
  219. package/dist/prisma-Dg9GoVdj.d.mts.map +1 -0
  220. package/dist/queryCachePlugin-7THaI5mt.d.mts +72 -0
  221. package/dist/queryCachePlugin-7THaI5mt.d.mts.map +1 -0
  222. package/dist/queryCachePlugin-DMBnp2Q0.mjs +139 -0
  223. package/dist/queryCachePlugin-DMBnp2Q0.mjs.map +1 -0
  224. package/dist/redis-D-JAeLtm.d.mts +50 -0
  225. package/dist/redis-D-JAeLtm.d.mts.map +1 -0
  226. package/dist/redis-stream-Bdh_vUU8.d.mts +104 -0
  227. package/dist/redis-stream-Bdh_vUU8.d.mts.map +1 -0
  228. package/dist/registry/index.d.mts +12 -0
  229. package/dist/registry/index.d.mts.map +1 -0
  230. package/dist/registry/index.mjs +4 -0
  231. package/dist/requestContext-QQD6ROJc.mjs +56 -0
  232. package/dist/requestContext-QQD6ROJc.mjs.map +1 -0
  233. package/dist/schemaConverter-BwrmWroW.mjs +99 -0
  234. package/dist/schemaConverter-BwrmWroW.mjs.map +1 -0
  235. package/dist/schemas/index.d.mts +64 -0
  236. package/dist/schemas/index.d.mts.map +1 -0
  237. package/dist/schemas/index.mjs +83 -0
  238. package/dist/schemas/index.mjs.map +1 -0
  239. package/dist/scope/index.d.mts +22 -0
  240. package/dist/scope/index.d.mts.map +1 -0
  241. package/dist/scope/index.mjs +66 -0
  242. package/dist/scope/index.mjs.map +1 -0
  243. package/dist/sessionManager-jPKLbHE0.d.mts +187 -0
  244. package/dist/sessionManager-jPKLbHE0.d.mts.map +1 -0
  245. package/dist/sse-B3c3_yZp.mjs +124 -0
  246. package/dist/sse-B3c3_yZp.mjs.map +1 -0
  247. package/dist/testing/index.d.mts +908 -0
  248. package/dist/testing/index.d.mts.map +1 -0
  249. package/dist/testing/index.mjs +1977 -0
  250. package/dist/testing/index.mjs.map +1 -0
  251. package/dist/tracing-Cc7vVQPp.d.mts +71 -0
  252. package/dist/tracing-Cc7vVQPp.d.mts.map +1 -0
  253. package/dist/typeGuards-DhMNLuvU.mjs +10 -0
  254. package/dist/typeGuards-DhMNLuvU.mjs.map +1 -0
  255. package/dist/types/index.d.mts +947 -0
  256. package/dist/types/index.d.mts.map +1 -0
  257. package/dist/types/index.mjs +15 -0
  258. package/dist/types/index.mjs.map +1 -0
  259. package/dist/types-Beqn1Un7.mjs +39 -0
  260. package/dist/types-Beqn1Un7.mjs.map +1 -0
  261. package/dist/types-CIgB7UUl.d.mts +446 -0
  262. package/dist/types-CIgB7UUl.d.mts.map +1 -0
  263. package/dist/types-aYB4V7uN.d.mts +87 -0
  264. package/dist/types-aYB4V7uN.d.mts.map +1 -0
  265. package/dist/utils/index.d.mts +748 -0
  266. package/dist/utils/index.d.mts.map +1 -0
  267. package/dist/utils/index.mjs +6 -0
  268. package/package.json +194 -68
  269. package/dist/BaseController-DVAiHxEQ.d.ts +0 -233
  270. package/dist/adapters/index.d.ts +0 -237
  271. package/dist/adapters/index.js +0 -668
  272. package/dist/arcCorePlugin-CsShQdyP.d.ts +0 -273
  273. package/dist/audit/index.d.ts +0 -195
  274. package/dist/audit/index.js +0 -319
  275. package/dist/auth/index.d.ts +0 -47
  276. package/dist/auth/index.js +0 -174
  277. package/dist/cli/commands/docs.d.ts +0 -11
  278. package/dist/cli/commands/docs.js +0 -474
  279. package/dist/cli/commands/generate.js +0 -334
  280. package/dist/cli/commands/introspect.d.ts +0 -8
  281. package/dist/cli/commands/introspect.js +0 -338
  282. package/dist/cli/index.d.ts +0 -4
  283. package/dist/cli/index.js +0 -3269
  284. package/dist/core/index.d.ts +0 -220
  285. package/dist/core/index.js +0 -2786
  286. package/dist/createApp-Ce9wl8W9.d.ts +0 -77
  287. package/dist/docs/index.d.ts +0 -166
  288. package/dist/docs/index.js +0 -658
  289. package/dist/errors-8WIxGS_6.d.ts +0 -122
  290. package/dist/events/index.d.ts +0 -117
  291. package/dist/events/index.js +0 -89
  292. package/dist/factory/index.d.ts +0 -38
  293. package/dist/factory/index.js +0 -1652
  294. package/dist/hooks/index.d.ts +0 -4
  295. package/dist/hooks/index.js +0 -199
  296. package/dist/idempotency/index.d.ts +0 -323
  297. package/dist/idempotency/index.js +0 -500
  298. package/dist/index-B4t03KQ0.d.ts +0 -1366
  299. package/dist/index.d.ts +0 -135
  300. package/dist/index.js +0 -4756
  301. package/dist/migrations/index.d.ts +0 -185
  302. package/dist/migrations/index.js +0 -274
  303. package/dist/org/index.d.ts +0 -129
  304. package/dist/org/index.js +0 -220
  305. package/dist/permissions/index.d.ts +0 -144
  306. package/dist/permissions/index.js +0 -103
  307. package/dist/plugins/index.d.ts +0 -46
  308. package/dist/plugins/index.js +0 -1069
  309. package/dist/policies/index.js +0 -196
  310. package/dist/presets/index.js +0 -384
  311. package/dist/presets/multiTenant.d.ts +0 -39
  312. package/dist/presets/multiTenant.js +0 -112
  313. package/dist/registry/index.d.ts +0 -16
  314. package/dist/registry/index.js +0 -253
  315. package/dist/testing/index.d.ts +0 -618
  316. package/dist/testing/index.js +0 -48020
  317. package/dist/types/index.d.ts +0 -4
  318. package/dist/types/index.js +0 -8
  319. package/dist/types-B99TBmFV.d.ts +0 -76
  320. package/dist/types-BvckRbs2.d.ts +0 -143
  321. package/dist/utils/index.d.ts +0 -679
  322. package/dist/utils/index.js +0 -931
@@ -0,0 +1 @@
1
+ {"version":3,"file":"circuitBreaker-DeY4FCjs.mjs","names":["MAX_LIMIT"],"sources":["../src/utils/queryParser.ts","../src/utils/responseSchemas.ts","../src/utils/stateMachine.ts","../src/utils/circuitBreaker.ts"],"sourcesContent":["/**\r\n * Arc Query Parser - Default URL-to-Query Parser\r\n *\r\n * Framework-agnostic query parser that converts URL parameters to query options.\r\n * This is Arc's built-in parser; users can swap in MongoKit's QueryParser,\r\n * pgkit's parser, or any custom parser implementing QueryParserInterface.\r\n *\r\n * @example\r\n * // Use Arc default parser (auto-applied if no queryParser option)\r\n * defineResource({ name: 'product', adapter: ... });\r\n *\r\n * // Use MongoKit's QueryParser (recommended for MongoDB - has $lookup, aggregations, etc.)\r\n * import { QueryParser } from '@classytic/mongokit';\r\n * defineResource({\r\n * name: 'product',\r\n * adapter: ...,\r\n * queryParser: new QueryParser(),\r\n * });\r\n *\r\n * // Use custom parser for SQL databases\r\n * defineResource({\r\n * name: 'user',\r\n * adapter: ...,\r\n * queryParser: new PgQueryParser(),\r\n * });\r\n */\r\n\r\nimport type { ParsedQuery, PopulateOption, QueryParserInterface } from '../types/index.js';\r\nimport { MAX_REGEX_LENGTH, MAX_SEARCH_LENGTH, MAX_FILTER_DEPTH, DEFAULT_MAX_LIMIT as MAX_LIMIT, DEFAULT_LIMIT, RESERVED_QUERY_PARAMS } from '../constants.js';\r\n\r\n// ============================================================================\r\n// Dangerous Patterns (ReDoS protection)\r\n// ============================================================================\r\n\r\n/**\r\n * Regex patterns that can cause catastrophic backtracking (ReDoS attacks)\r\n * Detects:\r\n * - Quantifiers: {n,m}\r\n * - Possessive quantifiers: *+, ++, ?+\r\n * - Nested quantifiers: (a+)+, (a*)*\r\n * - Backreferences: \\1, \\2, etc.\r\n */\r\nconst DANGEROUS_REGEX_PATTERNS = /(\\{[0-9,]+\\}|\\*\\+|\\+\\+|\\?\\+|(\\(.+\\))\\+|\\(\\?\\:|\\\\[0-9]|(\\[.+\\]).+(\\[.+\\]))/;\r\n\r\n// ============================================================================\r\n// Arc Query Parser\r\n// ============================================================================\r\n\r\nexport interface ArcQueryParserOptions {\r\n /** Maximum allowed limit value (default: 1000) */\r\n maxLimit?: number;\r\n /** Default limit for pagination (default: 20) */\r\n defaultLimit?: number;\r\n /** Maximum regex pattern length (default: 500) */\r\n maxRegexLength?: number;\r\n /** Maximum search query length (default: 200) */\r\n maxSearchLength?: number;\r\n /** Maximum filter nesting depth (default: 10) */\r\n maxFilterDepth?: number;\r\n}\r\n\r\n/**\r\n * Arc's default query parser\r\n *\r\n * Converts URL query parameters to a structured query format:\r\n * - Pagination: ?page=1&limit=20\r\n * - Sorting: ?sort=-createdAt,name (- prefix = descending)\r\n * - Filtering: ?status=active&price[gte]=100&price[lte]=500\r\n * - Search: ?search=keyword\r\n * - Populate: ?populate=author,category\r\n * - Field selection: ?select=name,price,status\r\n * - Keyset pagination: ?after=cursor_value\r\n *\r\n * For advanced MongoDB features ($lookup, aggregations), use MongoKit's QueryParser.\r\n */\r\nexport class ArcQueryParser implements QueryParserInterface {\r\n private readonly maxLimit: number;\r\n private readonly defaultLimit: number;\r\n private readonly maxRegexLength: number;\r\n private readonly maxSearchLength: number;\r\n private readonly maxFilterDepth: number;\r\n\r\n /** Supported filter operators */\r\n private readonly operators: Record<string, string> = {\r\n eq: '$eq',\r\n ne: '$ne',\r\n gt: '$gt',\r\n gte: '$gte',\r\n lt: '$lt',\r\n lte: '$lte',\r\n in: '$in',\r\n nin: '$nin',\r\n like: '$regex',\r\n contains: '$regex',\r\n regex: '$regex',\r\n exists: '$exists',\r\n };\r\n\r\n constructor(options: ArcQueryParserOptions = {}) {\r\n this.maxLimit = options.maxLimit ?? MAX_LIMIT;\r\n this.defaultLimit = options.defaultLimit ?? DEFAULT_LIMIT;\r\n this.maxRegexLength = options.maxRegexLength ?? MAX_REGEX_LENGTH;\r\n this.maxSearchLength = options.maxSearchLength ?? MAX_SEARCH_LENGTH;\r\n this.maxFilterDepth = options.maxFilterDepth ?? MAX_FILTER_DEPTH;\r\n }\r\n\r\n /**\r\n * Parse URL query parameters into structured query options\r\n */\r\n parse(query: Record<string, unknown> | null | undefined): ParsedQuery {\r\n const q = query ?? {};\r\n\r\n // Extract pagination params\r\n const page = this.parseNumber(q.page, 1);\r\n const limit = Math.min(this.parseNumber(q.limit, this.defaultLimit), this.maxLimit);\r\n const after = this.parseString(q.after ?? q.cursor);\r\n\r\n // Extract sort\r\n const sort = this.parseSort(q.sort);\r\n\r\n // Extract populate — handles both simple string and bracket notation object\r\n const { populate, populateOptions } = this.parsePopulate(q.populate);\r\n\r\n // Extract search\r\n const search = this.parseSearch(q.search);\r\n\r\n // Extract select\r\n const select = this.parseSelect(q.select);\r\n\r\n // Extract filters (everything else)\r\n const filters = this.parseFilters(q);\r\n\r\n return {\r\n filters,\r\n limit,\r\n sort,\r\n populate,\r\n populateOptions,\r\n search,\r\n page: after ? undefined : page,\r\n after,\r\n select,\r\n };\r\n }\r\n\r\n // ============================================================================\r\n // Parse Helpers\r\n // ============================================================================\r\n\r\n private parseNumber(value: unknown, defaultValue: number): number {\r\n if (value === undefined || value === null) return defaultValue;\r\n const num = parseInt(String(value), 10);\r\n return Number.isNaN(num) ? defaultValue : Math.max(1, num);\r\n }\r\n\r\n private parseString(value: unknown): string | undefined {\r\n if (value === undefined || value === null) return undefined;\r\n const str = String(value).trim();\r\n return str.length > 0 ? str : undefined;\r\n }\r\n\r\n /**\r\n * Parse populate parameter — handles both simple string and bracket notation.\r\n *\r\n * Simple: ?populate=author,category → { populate: 'author,category' }\r\n * Bracket: ?populate[author][select]=name,email → { populateOptions: [{ path: 'author', select: 'name email' }] }\r\n */\r\n private parsePopulate(value: unknown): { populate?: string; populateOptions?: PopulateOption[] } {\r\n if (value === undefined || value === null) return {};\r\n\r\n // Simple string: ?populate=author,category\r\n if (typeof value === 'string') {\r\n const trimmed = value.trim();\r\n return trimmed.length > 0 ? { populate: trimmed } : {};\r\n }\r\n\r\n // Bracket notation object: ?populate[author][select]=name,email\r\n // qs parses this as { author: { select: 'name,email' } }\r\n if (typeof value === 'object' && !Array.isArray(value)) {\r\n const obj = value as Record<string, unknown>;\r\n const keys = Object.keys(obj);\r\n if (keys.length === 0) return {};\r\n\r\n const options: PopulateOption[] = [];\r\n for (const path of keys) {\r\n // Validate path name (prevent injection)\r\n if (!/^[a-zA-Z_][a-zA-Z0-9_.]*$/.test(path)) continue;\r\n\r\n const config = obj[path];\r\n if (typeof config === 'object' && config !== null && !Array.isArray(config)) {\r\n const cfg = config as Record<string, unknown>;\r\n const option: PopulateOption = { path };\r\n\r\n // Parse select: convert comma-separated to space-separated (Mongoose format)\r\n if (typeof cfg.select === 'string') {\r\n option.select = cfg.select.split(',').map(s => s.trim()).filter(Boolean).join(' ');\r\n }\r\n\r\n // Parse match (filter conditions)\r\n if (typeof cfg.match === 'object' && cfg.match !== null) {\r\n option.match = cfg.match as Record<string, unknown>;\r\n }\r\n\r\n options.push(option);\r\n } else {\r\n // Simple value like populate[author]=true → treat as simple populate\r\n options.push({ path });\r\n }\r\n }\r\n\r\n return options.length > 0 ? { populateOptions: options } : {};\r\n }\r\n\r\n return {};\r\n }\r\n\r\n private parseSort(value: unknown): Record<string, 1 | -1> | undefined {\r\n if (!value) return undefined;\r\n\r\n const sortStr = String(value);\r\n const result: Record<string, 1 | -1> = {};\r\n\r\n for (const field of sortStr.split(',')) {\r\n const trimmed = field.trim();\r\n if (!trimmed) continue;\r\n\r\n // Validate field name (prevent injection)\r\n if (!/^-?[a-zA-Z_][a-zA-Z0-9_.]*$/.test(trimmed)) continue;\r\n\r\n if (trimmed.startsWith('-')) {\r\n result[trimmed.slice(1)] = -1;\r\n } else {\r\n result[trimmed] = 1;\r\n }\r\n }\r\n\r\n return Object.keys(result).length > 0 ? result : undefined;\r\n }\r\n\r\n private parseSearch(value: unknown): string | undefined {\r\n if (!value) return undefined;\r\n\r\n const search = String(value).trim();\r\n if (search.length === 0) return undefined;\r\n if (search.length > this.maxSearchLength) {\r\n return search.slice(0, this.maxSearchLength);\r\n }\r\n\r\n return search;\r\n }\r\n\r\n private parseSelect(value: unknown): Record<string, 0 | 1> | undefined {\r\n if (!value) return undefined;\r\n\r\n const selectStr = String(value);\r\n const result: Record<string, 0 | 1> = {};\r\n\r\n for (const field of selectStr.split(',')) {\r\n const trimmed = field.trim();\r\n if (!trimmed) continue;\r\n\r\n // Validate field name (prevent injection)\r\n if (!/^-?[a-zA-Z_][a-zA-Z0-9_.]*$/.test(trimmed)) continue;\r\n\r\n if (trimmed.startsWith('-')) {\r\n result[trimmed.slice(1)] = 0;\r\n } else {\r\n result[trimmed] = 1;\r\n }\r\n }\r\n\r\n return Object.keys(result).length > 0 ? result : undefined;\r\n }\r\n\r\n /**\r\n * Check if a value exceeds the maximum nesting depth.\r\n * Prevents filter bombs where deeply nested objects consume excessive memory/CPU.\r\n */\r\n private exceedsDepth(obj: unknown, currentDepth: number = 0): boolean {\r\n if (currentDepth > this.maxFilterDepth) return true;\r\n if (obj === null || obj === undefined) return false;\r\n if (Array.isArray(obj)) {\r\n return obj.some(v => this.exceedsDepth(v, currentDepth));\r\n }\r\n if (typeof obj !== 'object') return false;\r\n return Object.values(obj as Record<string, unknown>).some(\r\n v => this.exceedsDepth(v, currentDepth + 1)\r\n );\r\n }\r\n\r\n private parseFilters(query: Record<string, unknown>): Record<string, unknown> {\r\n const filters: Record<string, unknown> = {};\r\n\r\n for (const [key, value] of Object.entries(query)) {\r\n if (RESERVED_QUERY_PARAMS.has(key)) continue;\r\n if (value === undefined || value === null) continue;\r\n\r\n // Validate field name (prevent injection)\r\n if (!/^[a-zA-Z_][a-zA-Z0-9_.]*$/.test(key)) continue;\r\n\r\n // Enforce max filter depth (prevents filter bombs)\r\n if (this.exceedsDepth(value)) continue;\r\n\r\n // Handle nested object format from qs parser: { price: { gte: '40', lte: '100' } }\r\n // This happens when URL is ?price[gte]=40&price[lte]=100 and qs parses it\r\n if (typeof value === 'object' && value !== null && !Array.isArray(value)) {\r\n const operatorObj = value as Record<string, unknown>;\r\n const operatorKeys = Object.keys(operatorObj);\r\n\r\n // Check if all keys are known operators\r\n const allOperators = operatorKeys.every(op => this.operators[op]);\r\n\r\n if (allOperators && operatorKeys.length > 0) {\r\n // Convert operator object: { gte: '40', lte: '100' } → { $gte: 40, $lte: 100 }\r\n const mongoFilters: Record<string, unknown> = {};\r\n for (const [op, opValue] of Object.entries(operatorObj)) {\r\n const mongoOp = this.operators[op];\r\n if (mongoOp) {\r\n mongoFilters[mongoOp] = this.parseFilterValue(opValue, op);\r\n }\r\n }\r\n filters[key] = mongoFilters;\r\n continue;\r\n }\r\n }\r\n\r\n // Handle key-based bracket notation: price[gte]=100 (when not parsed by qs)\r\n const match = key.match(/^([a-zA-Z_][a-zA-Z0-9_.]*)(?:\\[([a-z]+)\\])?$/);\r\n if (!match) continue;\r\n\r\n const [, fieldName, operator] = match;\r\n if (!fieldName) continue;\r\n\r\n if (operator && this.operators[operator]) {\r\n // Operator filter: status[ne]=deleted → { status: { $ne: 'deleted' } }\r\n const mongoOp = this.operators[operator];\r\n const parsedValue = this.parseFilterValue(value, operator);\r\n\r\n if (!filters[fieldName]) {\r\n filters[fieldName] = {};\r\n }\r\n (filters[fieldName] as Record<string, unknown>)[mongoOp] = parsedValue;\r\n } else if (!operator) {\r\n // Direct equality: status=active → { status: 'active' }\r\n filters[fieldName] = this.parseFilterValue(value);\r\n }\r\n }\r\n\r\n return filters;\r\n }\r\n\r\n private parseFilterValue(value: unknown, operator?: string): unknown {\r\n // Handle arrays (for $in, $nin operators)\r\n if (operator === 'in' || operator === 'nin') {\r\n if (Array.isArray(value)) {\r\n return value.map(v => this.coerceValue(v));\r\n }\r\n if (typeof value === 'string' && value.includes(',')) {\r\n return value.split(',').map(v => this.coerceValue(v.trim()));\r\n }\r\n return [this.coerceValue(value)];\r\n }\r\n\r\n // Handle regex operators\r\n if (operator === 'like' || operator === 'contains' || operator === 'regex') {\r\n return this.sanitizeRegex(String(value));\r\n }\r\n\r\n // Handle exists operator\r\n if (operator === 'exists') {\r\n const str = String(value).toLowerCase();\r\n return str === 'true' || str === '1';\r\n }\r\n\r\n return this.coerceValue(value);\r\n }\r\n\r\n private coerceValue(value: unknown): unknown {\r\n if (value === 'true') return true;\r\n if (value === 'false') return false;\r\n if (value === 'null') return null;\r\n\r\n // Try to parse as number\r\n if (typeof value === 'string') {\r\n const num = Number(value);\r\n if (!Number.isNaN(num) && value.trim() !== '') {\r\n return num;\r\n }\r\n }\r\n\r\n return value;\r\n }\r\n\r\n // ============================================================================\r\n // OpenAPI Schema Generation\r\n // ============================================================================\r\n\r\n /**\r\n * Generate OpenAPI-compatible JSON Schema for query parameters.\r\n * Arc's defineResource() auto-detects this method and uses it\r\n * to document list endpoint query parameters in OpenAPI/Swagger.\r\n */\r\n getQuerySchema(): {\r\n type: 'object';\r\n properties: Record<string, unknown>;\r\n required?: string[];\r\n } {\r\n const operatorEntries = Object.entries(this.operators);\r\n const operatorLines = operatorEntries.map(([op, mongoOp]) => {\r\n const desc: Record<string, string> = {\r\n eq: 'Equal (default when no operator specified)',\r\n ne: 'Not equal',\r\n gt: 'Greater than',\r\n gte: 'Greater than or equal',\r\n lt: 'Less than',\r\n lte: 'Less than or equal',\r\n in: 'In list (comma-separated values)',\r\n nin: 'Not in list',\r\n like: 'Pattern match (case-insensitive)',\r\n contains: 'Contains substring (case-insensitive)',\r\n regex: 'Regex pattern',\r\n exists: 'Field exists (true/false)',\r\n };\r\n return ` ${op} → ${mongoOp}: ${desc[op] || op}`;\r\n });\r\n\r\n return {\r\n type: 'object',\r\n properties: {\r\n page: {\r\n type: 'integer',\r\n description: 'Page number for offset pagination',\r\n default: 1,\r\n minimum: 1,\r\n },\r\n limit: {\r\n type: 'integer',\r\n description: 'Number of items per page',\r\n default: this.defaultLimit,\r\n minimum: 1,\r\n maximum: this.maxLimit,\r\n },\r\n sort: {\r\n type: 'string',\r\n description: 'Sort fields (comma-separated). Prefix with - for descending. Example: -createdAt,name',\r\n },\r\n search: {\r\n type: 'string',\r\n description: 'Full-text search query',\r\n maxLength: this.maxSearchLength,\r\n },\r\n select: {\r\n type: 'string',\r\n description: 'Fields to include/exclude (comma-separated). Prefix with - to exclude. Example: name,email,-password',\r\n },\r\n populate: {\r\n type: 'string',\r\n description: 'Fields to populate/join (comma-separated). Example: author,category',\r\n },\r\n after: {\r\n type: 'string',\r\n description: 'Cursor value for keyset pagination',\r\n },\r\n _filterOperators: {\r\n type: 'string',\r\n description: ['Available filter operators (use as field[operator]=value):', ...operatorLines].join('\\n'),\r\n 'x-internal': true,\r\n },\r\n },\r\n };\r\n }\r\n\r\n // ============================================================================\r\n // Regex Sanitization\r\n // ============================================================================\r\n\r\n private sanitizeRegex(pattern: string): string {\r\n // Limit length\r\n let sanitized = pattern.slice(0, this.maxRegexLength);\r\n\r\n // Check for dangerous patterns\r\n if (DANGEROUS_REGEX_PATTERNS.test(sanitized)) {\r\n // Escape the entire pattern to treat as literal string\r\n sanitized = sanitized.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\r\n }\r\n\r\n return sanitized;\r\n }\r\n}\r\n\r\n/**\r\n * Create a new ArcQueryParser instance\r\n */\r\nexport function createQueryParser(options?: ArcQueryParserOptions): ArcQueryParser {\r\n return new ArcQueryParser(options);\r\n}\r\n\r\nexport default ArcQueryParser;\r\n","/**\n * Response Schemas\n *\n * Standard JSON Schema definitions for API responses.\n */\n\nimport type { AnyRecord } from '../types/index.js';\n\n// ============================================================================\n// Schema Types\n// ============================================================================\n\nexport interface JsonSchema {\n type: string;\n properties?: Record<string, JsonSchema | AnyRecord>;\n required?: string[];\n items?: JsonSchema | AnyRecord;\n additionalProperties?: boolean | JsonSchema;\n description?: string;\n example?: unknown;\n [key: string]: unknown;\n}\n\n// ============================================================================\n// Response Wrapper Schemas\n// ============================================================================\n\n/**\n * Base success response schema\n */\nexport const successResponseSchema: JsonSchema = {\n type: 'object',\n properties: {\n success: { type: 'boolean', example: true },\n },\n required: ['success'],\n};\n\n/**\n * Error response schema\n */\nexport const errorResponseSchema: JsonSchema = {\n type: 'object',\n properties: {\n success: { type: 'boolean', example: false },\n error: { type: 'string', description: 'Error message' },\n code: { type: 'string', description: 'Error code' },\n message: { type: 'string', description: 'Detailed message' },\n },\n required: ['success', 'error'],\n};\n\n/**\n * Pagination schema - matches MongoKit/Arc runtime format\n *\n * Runtime format (flat fields):\n * { page, limit, total, pages, hasNext, hasPrev }\n */\nexport const paginationSchema: JsonSchema = {\n type: 'object',\n properties: {\n page: { type: 'integer', example: 1 },\n limit: { type: 'integer', example: 20 },\n total: { type: 'integer', example: 100 },\n pages: { type: 'integer', example: 5 },\n hasNext: { type: 'boolean', example: true },\n hasPrev: { type: 'boolean', example: false },\n },\n required: ['page', 'limit', 'total', 'pages', 'hasNext', 'hasPrev'],\n};\n\n// ============================================================================\n// Schema Builders\n// ============================================================================\n\n/**\n * Wrap a data schema in a success response\n */\nexport function wrapResponse(dataSchema: JsonSchema): JsonSchema {\n return {\n type: 'object',\n properties: {\n success: { type: 'boolean', example: true },\n data: dataSchema,\n },\n required: ['success', 'data'],\n // Allow extra fields (fieldPermissions, meta spreads, etc.)\n additionalProperties: true,\n };\n}\n\n/**\n * Create a list response schema with pagination - matches MongoKit/Arc runtime format\n *\n * Runtime format:\n * { success, docs: [...], page, limit, total, pages, hasNext, hasPrev }\n *\n * Note: Uses 'docs' array (not 'data') with flat pagination fields\n */\nexport function listResponse(itemSchema: JsonSchema): JsonSchema {\n return {\n type: 'object',\n properties: {\n success: { type: 'boolean', example: true },\n docs: {\n type: 'array',\n items: itemSchema,\n },\n // Flat pagination fields (not nested)\n page: { type: 'integer', example: 1 },\n limit: { type: 'integer', example: 20 },\n total: { type: 'integer', example: 100 },\n pages: { type: 'integer', example: 5 },\n hasNext: { type: 'boolean', example: false },\n hasPrev: { type: 'boolean', example: false },\n },\n required: ['success', 'docs'],\n // Allow extra fields (fieldPermissions, meta spreads, etc.)\n additionalProperties: true,\n };\n}\n\n/**\n * Alias for listResponse - matches local responseSchemas.js naming\n */\nexport const paginateWrapper = listResponse;\n\n/**\n * Create a single item response schema\n *\n * Runtime format: { success, data: {...} }\n */\nexport function itemResponse(itemSchema: JsonSchema): JsonSchema {\n return wrapResponse(itemSchema);\n}\n\n/**\n * Alias for itemResponse - matches local responseSchemas.js naming\n */\nexport const itemWrapper = itemResponse;\n\n/**\n * Create a create/update response schema\n */\nexport function mutationResponse(itemSchema: JsonSchema): JsonSchema {\n return {\n type: 'object',\n properties: {\n success: { type: 'boolean', example: true },\n data: itemSchema,\n message: { type: 'string', example: 'Created successfully' },\n },\n required: ['success', 'data'],\n // Allow extra fields (fieldPermissions, meta spreads, etc.)\n additionalProperties: true,\n };\n}\n\n/**\n * Create a delete response schema\n *\n * Runtime format: { success, message }\n */\nexport function deleteResponse(): JsonSchema {\n return {\n type: 'object',\n properties: {\n success: { type: 'boolean', example: true },\n message: { type: 'string', example: 'Deleted successfully' },\n },\n required: ['success'],\n // Allow extra fields (fieldPermissions, meta spreads, etc.)\n additionalProperties: true,\n };\n}\n\n/**\n * Alias for deleteResponse - matches local responseSchemas.js naming\n */\nexport const messageWrapper = deleteResponse;\n\n// ============================================================================\n// HTTP Status Response Schemas\n// ============================================================================\n\nexport const responses = {\n 200: (schema: JsonSchema) => ({\n description: 'Successful response',\n content: {\n 'application/json': { schema },\n },\n }),\n\n 201: (schema: JsonSchema) => ({\n description: 'Created successfully',\n content: {\n 'application/json': { schema: mutationResponse(schema) },\n },\n }),\n\n 400: {\n description: 'Bad Request',\n content: {\n 'application/json': {\n schema: {\n ...errorResponseSchema,\n properties: {\n ...errorResponseSchema.properties,\n code: { type: 'string', example: 'VALIDATION_ERROR' },\n details: {\n type: 'object',\n properties: {\n errors: {\n type: 'array',\n items: {\n type: 'object',\n properties: {\n field: { type: 'string' },\n message: { type: 'string' },\n },\n },\n },\n },\n },\n },\n },\n },\n },\n },\n\n 401: {\n description: 'Unauthorized',\n content: {\n 'application/json': {\n schema: {\n ...errorResponseSchema,\n properties: {\n ...errorResponseSchema.properties,\n code: { type: 'string', example: 'UNAUTHORIZED' },\n },\n },\n },\n },\n },\n\n 403: {\n description: 'Forbidden',\n content: {\n 'application/json': {\n schema: {\n ...errorResponseSchema,\n properties: {\n ...errorResponseSchema.properties,\n code: { type: 'string', example: 'FORBIDDEN' },\n },\n },\n },\n },\n },\n\n 404: {\n description: 'Not Found',\n content: {\n 'application/json': {\n schema: {\n ...errorResponseSchema,\n properties: {\n ...errorResponseSchema.properties,\n code: { type: 'string', example: 'NOT_FOUND' },\n },\n },\n },\n },\n },\n\n 409: {\n description: 'Conflict',\n content: {\n 'application/json': {\n schema: {\n ...errorResponseSchema,\n properties: {\n ...errorResponseSchema.properties,\n code: { type: 'string', example: 'CONFLICT' },\n },\n },\n },\n },\n },\n\n 500: {\n description: 'Internal Server Error',\n content: {\n 'application/json': {\n schema: {\n ...errorResponseSchema,\n properties: {\n ...errorResponseSchema.properties,\n code: { type: 'string', example: 'INTERNAL_ERROR' },\n },\n },\n },\n },\n },\n};\n\n// ============================================================================\n// Query Parameter Schemas\n// ============================================================================\n\nexport const queryParams = {\n pagination: {\n page: {\n type: 'integer',\n minimum: 1,\n default: 1,\n description: 'Page number',\n },\n limit: {\n type: 'integer',\n minimum: 1,\n maximum: 100,\n default: 20,\n description: 'Items per page',\n },\n },\n\n sorting: {\n sort: {\n type: 'string',\n description: 'Sort field (prefix with - for descending)',\n example: '-createdAt',\n },\n },\n\n filtering: {\n select: {\n description: 'Fields to include (space-separated or object)',\n example: 'name email createdAt',\n },\n populate: {\n description: 'Relations to populate (comma-separated string or bracket-notation object)',\n example: 'author,category',\n },\n },\n};\n\n/**\n * Get standard list query parameters schema\n */\nexport function getListQueryParams(): AnyRecord {\n return {\n type: 'object',\n properties: {\n ...queryParams.pagination,\n ...queryParams.sorting,\n ...queryParams.filtering,\n },\n // Allow additional/complex query params (e.g., bracket-notation populate, filters)\n // Without this, qs-parsed nested objects like ?populate[author][select]=name would be rejected\n additionalProperties: true,\n };\n}\n\n// ============================================================================\n// Default CRUD Schemas\n// ============================================================================\n\n/**\n * Generic item schema that allows any properties.\n * Used as default when no user schema is provided.\n * Enables fast-json-stringify while still passing through all fields.\n */\nconst genericItemSchema: JsonSchema = {\n type: 'object',\n additionalProperties: true,\n};\n\n/**\n * Recursively strip `example` keys from a schema object.\n * The `example` keyword is OpenAPI metadata — not standard JSON Schema —\n * and triggers Ajv strict mode errors when used on routes without the\n * `keywords: ['example']` AJV config (e.g., raw Fastify without createApp).\n */\nfunction stripExamples<T>(schema: T): T {\n if (schema === null || typeof schema !== 'object') return schema;\n if (Array.isArray(schema)) return schema.map(stripExamples) as T;\n\n const result: Record<string, unknown> = {};\n for (const [key, value] of Object.entries(schema as Record<string, unknown>)) {\n if (key === 'example') continue;\n result[key] = stripExamples(value);\n }\n return result as T;\n}\n\n/**\n * Get default response schemas for all CRUD operations.\n *\n * When routes have response schemas, Fastify compiles them with\n * fast-json-stringify for 2-3x faster serialization and prevents\n * accidental field disclosure.\n *\n * These defaults use `additionalProperties: true` so all fields pass through.\n * Override with specific schemas for full serialization performance + safety.\n *\n * Note: `example` properties are stripped from defaults so they work with\n * any Fastify instance (not just createApp which adds `keywords: ['example']`).\n */\nexport function getDefaultCrudSchemas(): Record<string, Record<string, unknown>> {\n return stripExamples({\n list: {\n querystring: getListQueryParams(),\n response: { 200: listResponse(genericItemSchema) },\n },\n get: {\n response: { 200: itemResponse(genericItemSchema) },\n },\n create: {\n response: { 201: mutationResponse(genericItemSchema) },\n },\n update: {\n response: { 200: itemResponse(genericItemSchema) },\n },\n delete: {\n response: { 200: deleteResponse() },\n },\n });\n}\n","/**\n * State Machine Utility\n *\n * Pure utility for validating state transitions in workflow systems.\n * Zero dependencies, framework-agnostic.\n *\n * @example\n * const orderState = createStateMachine('Order', {\n * approve: ['pending', 'draft'],\n * cancel: ['pending', 'approved'],\n * fulfill: ['approved'],\n * });\n *\n * // Check if transition is allowed\n * if (orderState.can('approve', currentStatus)) {\n * // Perform approval\n * }\n *\n * // Assert transition (throws if invalid)\n * orderState.assert('approve', currentStatus, ValidationError);\n */\n\nexport interface StateMachine {\n /**\n * Synchronously check if action can be performed from current status.\n * Only checks the transition map — does NOT evaluate guards.\n * Use `canAsync()` when guards need to be evaluated.\n */\n can(action: string, status: string | null | undefined): boolean;\n\n /**\n * Asynchronously check if action can be performed, including guard evaluation.\n * Falls back to simple transition check when no guard is defined.\n */\n canAsync(action: string, status: string | null | undefined, context?: any): Promise<boolean>;\n\n /**\n * Assert action can be performed, throw error if invalid\n * @param action - Action to perform\n * @param status - Current status\n * @param errorFactory - Optional error constructor\n * @param message - Optional custom error message\n */\n assert(\n action: string,\n status: string | null | undefined,\n errorFactory?: (msg: string) => Error,\n message?: string\n ): void;\n\n /**\n * Get transition history\n */\n getHistory?(): TransitionHistoryEntry[];\n\n /**\n * Record a transition\n */\n recordTransition?(from: string, to: string, action: string, metadata?: any): void;\n\n /**\n * Clear history\n */\n clearHistory?(): void;\n\n /**\n * Get available actions for current status\n */\n getAvailableActions?(status: string): string[];\n}\n\nexport interface TransitionHistoryEntry {\n from: string;\n to: string;\n action: string;\n timestamp: Date;\n metadata?: any;\n}\n\nexport interface TransitionGuard {\n (context: { from: string; to: string; action: string; data?: any }): boolean | Promise<boolean>;\n}\n\nexport interface TransitionAction {\n (context: { from: string; to: string; action: string; data?: any }): void | Promise<void>;\n}\n\nexport interface TransitionHook {\n before?: TransitionAction;\n after?: TransitionAction;\n}\n\nexport type TransitionConfig = Record<\n string,\n | string[]\n | {\n from: string[];\n to?: string;\n guard?: TransitionGuard;\n before?: TransitionAction;\n after?: TransitionAction;\n }\n>;\n\n/**\n * Create a state machine for validating transitions\n *\n * @param name - Name of the state machine (used in error messages)\n * @param transitions - Map of actions to allowed source statuses\n * @param options - Additional options (history, guards, actions)\n * @returns State machine with can() and assert() methods\n *\n * @example\n * // Basic usage\n * const transferState = createStateMachine('Transfer', {\n * approve: ['draft'],\n * dispatch: ['approved'],\n * receive: ['dispatched', 'in_transit'],\n * cancel: ['draft', 'approved'],\n * });\n *\n * @example\n * // With guards and actions\n * const orderState = createStateMachine('Order', {\n * approve: {\n * from: ['pending'],\n * to: 'approved',\n * guard: ({ data }) => data.paymentConfirmed,\n * before: ({ from, to }) => console.log(`Approving order from ${from} to ${to}`),\n * after: ({ data }) => sendApprovalEmail(data.customerId),\n * },\n * }, { trackHistory: true });\n */\nexport function createStateMachine(\n name: string,\n transitions: TransitionConfig = {},\n options: { trackHistory?: boolean } = {}\n): StateMachine {\n const normalized = new Map<\n string,\n {\n from: Set<string>;\n to?: string;\n guard?: TransitionGuard;\n before?: TransitionAction;\n after?: TransitionAction;\n }\n >();\n const history: TransitionHistoryEntry[] | undefined = options.trackHistory ? [] : undefined;\n\n // Normalize transition config (support both array and object formats)\n Object.entries(transitions).forEach(([action, allowed]) => {\n if (Array.isArray(allowed)) {\n // Simple array format: action: ['state1', 'state2']\n normalized.set(action, { from: new Set(allowed) });\n } else if (typeof allowed === 'object' && 'from' in allowed) {\n // Object format with guards/actions\n normalized.set(action, {\n from: new Set(Array.isArray(allowed.from) ? allowed.from : [allowed.from]),\n to: allowed.to,\n guard: allowed.guard,\n before: allowed.before,\n after: allowed.after,\n });\n }\n });\n\n const can = (action: string, status: string | null | undefined): boolean => {\n const transition = normalized.get(action);\n if (!transition || !status) return false;\n return transition.from.has(status);\n };\n\n const canAsync = async (\n action: string,\n status: string | null | undefined,\n context?: any\n ): Promise<boolean> => {\n const transition = normalized.get(action);\n if (!transition || !status) return false;\n\n // Check if transition is allowed from current state\n if (!transition.from.has(status)) return false;\n\n // Check guard condition if present\n if (transition.guard) {\n try {\n const guardResult = await transition.guard({\n from: status,\n to: transition.to || '',\n action,\n data: context,\n });\n return guardResult;\n } catch {\n return false;\n }\n }\n\n return true;\n };\n\n const assert = (\n action: string,\n status: string | null | undefined,\n errorFactory?: (msg: string) => Error,\n message?: string\n ): void => {\n if (can(action, status)) return;\n\n const errorMessage =\n message || `${name} cannot '${action}' when status is '${status || 'unknown'}'`;\n\n if (typeof errorFactory === 'function') {\n throw errorFactory(errorMessage);\n }\n throw new Error(errorMessage);\n };\n\n const recordTransition = (from: string, to: string, action: string, metadata?: any): void => {\n if (history) {\n history.push({\n from,\n to,\n action,\n timestamp: new Date(),\n metadata,\n });\n }\n };\n\n const getHistory = (): TransitionHistoryEntry[] => {\n return history ? [...history] : [];\n };\n\n const clearHistory = (): void => {\n if (history) {\n history.length = 0;\n }\n };\n\n const getAvailableActions = (status: string): string[] => {\n const actions: string[] = [];\n for (const [action, transition] of normalized.entries()) {\n if (transition.from.has(status)) {\n actions.push(action);\n }\n }\n return actions;\n };\n\n return {\n can,\n canAsync,\n assert,\n recordTransition,\n getHistory,\n clearHistory,\n getAvailableActions,\n };\n}\n\n/**\n * Execute a state transition with guards and actions\n *\n * @example\n * const result = await executeTransition(\n * orderState,\n * 'approve',\n * 'pending',\n * 'approved',\n * { paymentConfirmed: true }\n * );\n *\n * if (result.success) {\n * console.log('Transition successful');\n * } else {\n * console.error(result.error);\n * }\n */\nexport async function executeTransition(\n stateMachine: StateMachine,\n action: string,\n from: string,\n to: string,\n context?: any\n): Promise<{ success: boolean; error?: string }> {\n try {\n // Check if transition is allowed (use canAsync for guard evaluation)\n const canTransition = await stateMachine.canAsync(action, from, context);\n if (!canTransition) {\n return {\n success: false,\n error: `Cannot transition from ${from} to ${to} via ${action}`,\n };\n }\n\n // Record the transition if history is enabled\n if (stateMachine.recordTransition) {\n stateMachine.recordTransition(from, to, action, context);\n }\n\n return { success: true };\n } catch (error: any) {\n return {\n success: false,\n error: error.message || 'Transition failed',\n };\n }\n}\n","/**\n * Circuit Breaker Pattern\n *\n * Wraps external service calls with failure protection.\n * Prevents cascading failures by \"opening\" the circuit when\n * a service is failing, allowing it time to recover.\n *\n * States:\n * - CLOSED: Normal operation, requests pass through\n * - OPEN: Too many failures, all requests fail fast\n * - HALF_OPEN: Testing if service recovered, limited requests\n *\n * @example\n * import { CircuitBreaker } from '@classytic/arc/utils';\n *\n * const paymentBreaker = new CircuitBreaker(async (amount) => {\n * return await stripe.charges.create({ amount });\n * }, {\n * failureThreshold: 5,\n * resetTimeout: 30000,\n * timeout: 5000,\n * });\n *\n * try {\n * const result = await paymentBreaker.call(100);\n * } catch (error) {\n * // Handle failure or circuit open\n * }\n */\n\nexport const CircuitState = {\n CLOSED: 'CLOSED',\n OPEN: 'OPEN',\n HALF_OPEN: 'HALF_OPEN',\n} as const;\n\nexport type CircuitState = (typeof CircuitState)[keyof typeof CircuitState];\n\nexport interface CircuitBreakerOptions {\n /**\n * Number of failures before opening circuit\n * @default 5\n */\n failureThreshold?: number;\n\n /**\n * Time in ms before attempting to close circuit\n * @default 60000 (60 seconds)\n */\n resetTimeout?: number;\n\n /**\n * Request timeout in ms\n * @default 10000 (10 seconds)\n */\n timeout?: number;\n\n /**\n * Number of successful requests in HALF_OPEN before closing\n * @default 1\n */\n successThreshold?: number;\n\n /**\n * Fallback function when circuit is open\n */\n fallback?: (...args: any[]) => Promise<any>;\n\n /**\n * Callback when state changes\n */\n onStateChange?: (from: CircuitState, to: CircuitState) => void;\n\n /**\n * Callback on error\n */\n onError?: (error: Error) => void;\n\n /**\n * Name for logging/monitoring\n */\n name?: string;\n}\n\nexport interface CircuitBreakerStats {\n name?: string;\n state: CircuitState;\n failures: number;\n successes: number;\n totalCalls: number;\n openedAt: number | null;\n lastCallAt: number | null;\n}\n\nexport class CircuitBreakerError extends Error {\n state: CircuitState;\n\n constructor(\n message: string,\n state: CircuitState\n ) {\n super(message);\n this.name = 'CircuitBreakerError';\n this.state = state;\n }\n}\n\nexport class CircuitBreaker<T extends (...args: any[]) => Promise<any>> {\n private state: CircuitState = CircuitState.CLOSED;\n private failures: number = 0;\n private successes: number = 0;\n private totalCalls: number = 0;\n private nextAttempt: number = 0;\n private lastCallAt: number | null = null;\n private openedAt: number | null = null;\n\n private readonly failureThreshold: number;\n private readonly resetTimeout: number;\n private readonly timeout: number;\n private readonly successThreshold: number;\n private readonly fallback?: (...args: any[]) => Promise<any>;\n private readonly onStateChange?: (from: CircuitState, to: CircuitState) => void;\n private readonly onError?: (error: Error) => void;\n private readonly name: string;\n\n private readonly fn: T;\n\n constructor(\n fn: T,\n options: CircuitBreakerOptions = {}\n ) {\n this.fn = fn;\n this.failureThreshold = options.failureThreshold ?? 5;\n this.resetTimeout = options.resetTimeout ?? 60000;\n this.timeout = options.timeout ?? 10000;\n this.successThreshold = options.successThreshold ?? 1;\n this.fallback = options.fallback;\n this.onStateChange = options.onStateChange;\n this.onError = options.onError;\n this.name = options.name ?? 'CircuitBreaker';\n }\n\n /**\n * Call the wrapped function with circuit breaker protection\n */\n async call(...args: Parameters<T>): Promise<ReturnType<T>> {\n this.totalCalls++;\n this.lastCallAt = Date.now();\n\n // Check circuit state\n if (this.state === CircuitState.OPEN) {\n if (Date.now() < this.nextAttempt) {\n // Circuit still open, fail fast\n const error = new CircuitBreakerError(\n `Circuit breaker is OPEN for ${this.name}`,\n CircuitState.OPEN\n );\n\n // Use fallback if available\n if (this.fallback) {\n return this.fallback(...args);\n }\n\n throw error;\n }\n\n // Try transitioning to HALF_OPEN\n this.setState(CircuitState.HALF_OPEN);\n }\n\n try {\n // Execute with timeout\n const result = await this.executeWithTimeout(args);\n\n // Success\n this.onSuccess();\n return result;\n } catch (error) {\n // Failure\n this.onFailure(error as Error);\n throw error;\n }\n }\n\n /**\n * Execute function with timeout\n */\n private async executeWithTimeout(args: Parameters<T>): Promise<ReturnType<T>> {\n return new Promise<ReturnType<T>>((resolve, reject) => {\n const timeoutId = setTimeout(() => {\n reject(new Error(`Request timeout after ${this.timeout}ms`));\n }, this.timeout);\n\n this.fn(...args)\n .then((result) => {\n clearTimeout(timeoutId);\n resolve(result);\n })\n .catch((error) => {\n clearTimeout(timeoutId);\n reject(error);\n });\n });\n }\n\n /**\n * Handle successful call\n */\n private onSuccess(): void {\n this.failures = 0;\n this.successes++;\n\n if (this.state === CircuitState.HALF_OPEN) {\n // Check if we should close the circuit\n if (this.successes >= this.successThreshold) {\n this.setState(CircuitState.CLOSED);\n this.successes = 0;\n }\n }\n }\n\n /**\n * Handle failed call\n */\n private onFailure(error: Error): void {\n this.failures++;\n this.successes = 0;\n\n if (this.onError) {\n this.onError(error);\n }\n\n if (this.state === CircuitState.HALF_OPEN || this.failures >= this.failureThreshold) {\n this.setState(CircuitState.OPEN);\n this.nextAttempt = Date.now() + this.resetTimeout;\n this.openedAt = Date.now();\n }\n }\n\n /**\n * Change circuit state\n */\n private setState(newState: CircuitState): void {\n const oldState = this.state;\n if (oldState !== newState) {\n this.state = newState;\n\n if (this.onStateChange) {\n this.onStateChange(oldState, newState);\n }\n }\n }\n\n /**\n * Manually open the circuit\n */\n open(): void {\n this.setState(CircuitState.OPEN);\n this.nextAttempt = Date.now() + this.resetTimeout;\n this.openedAt = Date.now();\n }\n\n /**\n * Manually close the circuit\n */\n close(): void {\n this.failures = 0;\n this.successes = 0;\n this.setState(CircuitState.CLOSED);\n this.openedAt = null;\n }\n\n /**\n * Get current statistics\n */\n getStats(): CircuitBreakerStats {\n return {\n name: this.name,\n state: this.state,\n failures: this.failures,\n successes: this.successes,\n totalCalls: this.totalCalls,\n openedAt: this.openedAt,\n lastCallAt: this.lastCallAt,\n };\n }\n\n /**\n * Get current state\n */\n getState(): CircuitState {\n return this.state;\n }\n\n /**\n * Check if circuit is open\n */\n isOpen(): boolean {\n return this.state === CircuitState.OPEN;\n }\n\n /**\n * Check if circuit is closed\n */\n isClosed(): boolean {\n return this.state === CircuitState.CLOSED;\n }\n\n /**\n * Reset statistics\n */\n reset(): void {\n this.failures = 0;\n this.successes = 0;\n this.totalCalls = 0;\n this.lastCallAt = null;\n this.openedAt = null;\n this.setState(CircuitState.CLOSED);\n }\n}\n\n/**\n * Create a circuit breaker with sensible defaults\n *\n * @example\n * const emailBreaker = createCircuitBreaker(\n * async (to, subject, body) => sendEmail(to, subject, body),\n * { name: 'email-service' }\n * );\n */\nexport function createCircuitBreaker<T extends (...args: any[]) => Promise<any>>(\n fn: T,\n options?: CircuitBreakerOptions\n): CircuitBreaker<T> {\n return new CircuitBreaker(fn, options);\n}\n\n/**\n * Circuit breaker registry for managing multiple breakers\n */\nexport class CircuitBreakerRegistry {\n private breakers: Map<string, CircuitBreaker<any>> = new Map();\n\n /**\n * Register a circuit breaker\n */\n register<T extends (...args: any[]) => Promise<any>>(\n name: string,\n fn: T,\n options?: Omit<CircuitBreakerOptions, 'name'>\n ): CircuitBreaker<T> {\n const breaker = new CircuitBreaker(fn, { ...options, name });\n this.breakers.set(name, breaker);\n return breaker;\n }\n\n /**\n * Get a circuit breaker by name\n */\n get(name: string): CircuitBreaker<any> | undefined {\n return this.breakers.get(name);\n }\n\n /**\n * Get all breakers\n */\n getAll(): Map<string, CircuitBreaker<any>> {\n return this.breakers;\n }\n\n /**\n * Get statistics for all breakers\n */\n getAllStats(): Record<string, CircuitBreakerStats> {\n const stats: Record<string, CircuitBreakerStats> = {};\n for (const [name, breaker] of this.breakers.entries()) {\n stats[name] = breaker.getStats();\n }\n return stats;\n }\n\n /**\n * Reset all breakers\n */\n resetAll(): void {\n for (const breaker of this.breakers.values()) {\n breaker.reset();\n }\n }\n\n /**\n * Open all breakers\n */\n openAll(): void {\n for (const breaker of this.breakers.values()) {\n breaker.open();\n }\n }\n\n /**\n * Close all breakers\n */\n closeAll(): void {\n for (const breaker of this.breakers.values()) {\n breaker.close();\n }\n }\n}\n\n/**\n * Create a new CircuitBreakerRegistry instance.\n * Use this instead of a global singleton — attach to fastify.arc or pass explicitly.\n */\nexport function createCircuitBreakerRegistry(): CircuitBreakerRegistry {\n return new CircuitBreakerRegistry();\n}\n"],"mappings":"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"}
@@ -0,0 +1,19 @@
1
+ //#region src/cli/commands/describe.d.ts
2
+ /**
3
+ * Arc CLI - Describe Command
4
+ *
5
+ * Machine-readable resource metadata for AI agents.
6
+ * Outputs JSON with fields, permissions, pipeline, routes, events —
7
+ * everything an LLM needs to understand and generate code for the API.
8
+ *
9
+ * @example
10
+ * ```bash
11
+ * arc describe ./src/resources.js --json
12
+ * arc describe ./src/resources.js --resource product
13
+ * arc describe ./src/resources.js --pretty
14
+ * ```
15
+ */
16
+ declare function describe(args: string[]): Promise<void>;
17
+ //#endregion
18
+ export { describe as default, describe };
19
+ //# sourceMappingURL=describe.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"describe.d.mts","names":[],"sources":["../../../src/cli/commands/describe.ts"],"mappings":";;AAmVA;;;;;;;;;;;;;iBAAsB,QAAA,CAAS,IAAA,aAAiB,OAAA"}
@@ -0,0 +1,239 @@
1
+ import { t as CRUD_OPERATIONS } from "../../constants-DdXFXQtN.mjs";
2
+ import { resolve } from "node:path";
3
+ import { pathToFileURL } from "node:url";
4
+
5
+ //#region src/cli/commands/describe.ts
6
+ /**
7
+ * Arc CLI - Describe Command
8
+ *
9
+ * Machine-readable resource metadata for AI agents.
10
+ * Outputs JSON with fields, permissions, pipeline, routes, events —
11
+ * everything an LLM needs to understand and generate code for the API.
12
+ *
13
+ * @example
14
+ * ```bash
15
+ * arc describe ./src/resources.js --json
16
+ * arc describe ./src/resources.js --resource product
17
+ * arc describe ./src/resources.js --pretty
18
+ * ```
19
+ */
20
+ function describePermission(check) {
21
+ if (!check || typeof check !== "function") return { type: "custom" };
22
+ const fn = check;
23
+ if (fn._isPublic === true) return { type: "public" };
24
+ if (Array.isArray(fn._roles)) return {
25
+ type: "requireRoles",
26
+ roles: fn._roles
27
+ };
28
+ const src = check.toString();
29
+ if (src.includes("ctx.user") && !src.includes("roles") && src.length < 200) return { type: "requireAuth" };
30
+ return { type: "custom" };
31
+ }
32
+ function describePermissions(perms) {
33
+ if (!perms) return {};
34
+ const result = {};
35
+ for (const op of CRUD_OPERATIONS) {
36
+ const check = perms[op];
37
+ if (check) result[op] = describePermission(check);
38
+ }
39
+ return result;
40
+ }
41
+ function describeFields(fieldPerms) {
42
+ if (!fieldPerms || Object.keys(fieldPerms).length === 0) return void 0;
43
+ const result = {};
44
+ for (const [field, perm] of Object.entries(fieldPerms)) {
45
+ const desc = { type: perm._type };
46
+ if (perm.roles?.length) desc.roles = perm.roles;
47
+ if (perm.redactValue !== void 0) desc.redactValue = perm.redactValue;
48
+ result[field] = desc;
49
+ }
50
+ return result;
51
+ }
52
+ function describePipeline(pipe) {
53
+ if (!pipe) return void 0;
54
+ const steps = [];
55
+ if (Array.isArray(pipe)) steps.push(...pipe);
56
+ else {
57
+ const seen = /* @__PURE__ */ new Set();
58
+ for (const opSteps of Object.values(pipe)) if (Array.isArray(opSteps)) for (const step of opSteps) {
59
+ const key = `${step._type}:${step.name}`;
60
+ if (!seen.has(key)) {
61
+ seen.add(key);
62
+ steps.push(step);
63
+ }
64
+ }
65
+ }
66
+ if (steps.length === 0) return void 0;
67
+ const guards = [];
68
+ const transforms = [];
69
+ const interceptors = [];
70
+ for (const step of steps) {
71
+ const desc = { name: step.name };
72
+ if (step.operations?.length) desc.operations = [...step.operations];
73
+ switch (step._type) {
74
+ case "guard":
75
+ guards.push(desc);
76
+ break;
77
+ case "transform":
78
+ transforms.push(desc);
79
+ break;
80
+ case "interceptor":
81
+ interceptors.push(desc);
82
+ break;
83
+ }
84
+ }
85
+ return {
86
+ guards,
87
+ transforms,
88
+ interceptors
89
+ };
90
+ }
91
+ function describeRoutes(resource) {
92
+ const routes = [];
93
+ if (!resource.disableDefaultRoutes) {
94
+ const disabled = new Set(resource.disabledRoutes ?? []);
95
+ for (const { method, suffix, op } of [
96
+ {
97
+ method: "GET",
98
+ suffix: "",
99
+ op: "list"
100
+ },
101
+ {
102
+ method: "GET",
103
+ suffix: "/:id",
104
+ op: "get"
105
+ },
106
+ {
107
+ method: "POST",
108
+ suffix: "",
109
+ op: "create"
110
+ },
111
+ {
112
+ method: "PATCH",
113
+ suffix: "/:id",
114
+ op: "update"
115
+ },
116
+ {
117
+ method: "DELETE",
118
+ suffix: "/:id",
119
+ op: "delete"
120
+ }
121
+ ]) if (!disabled.has(op)) {
122
+ const route = {
123
+ method,
124
+ path: `${resource.prefix}${suffix}`,
125
+ operation: op
126
+ };
127
+ const perm = resource.permissions[op];
128
+ if (perm) route.permission = describePermission(perm);
129
+ routes.push(route);
130
+ }
131
+ }
132
+ for (const ar of resource.additionalRoutes) routes.push({
133
+ method: ar.method,
134
+ path: `${resource.prefix}${ar.path}`,
135
+ operation: typeof ar.handler === "string" ? ar.handler : "custom",
136
+ summary: ar.summary,
137
+ description: ar.description,
138
+ permission: describePermission(ar.permissions)
139
+ });
140
+ return routes;
141
+ }
142
+ function describeEvents(resourceName, events) {
143
+ if (!events) return [];
144
+ return Object.entries(events).map(([action, def]) => ({
145
+ name: `${resourceName}:${action}`,
146
+ description: def.description,
147
+ hasSchema: !!def.schema
148
+ }));
149
+ }
150
+ function describeMiddlewares(middlewares) {
151
+ if (!middlewares) return [];
152
+ const ops = [];
153
+ for (const [op, handlers] of Object.entries(middlewares)) if (handlers?.length) ops.push(`${op}(${handlers.length})`);
154
+ return ops;
155
+ }
156
+ function describeResource(resource, module) {
157
+ return {
158
+ name: resource.name,
159
+ displayName: resource.displayName,
160
+ prefix: resource.prefix,
161
+ tag: resource.tag,
162
+ module,
163
+ adapter: resource.adapter ? {
164
+ type: resource.adapter.type,
165
+ name: resource.adapter.name
166
+ } : null,
167
+ permissions: describePermissions(resource.permissions),
168
+ presets: resource._appliedPresets ?? [],
169
+ fields: describeFields(resource.fields),
170
+ pipeline: describePipeline(resource.pipe),
171
+ routes: describeRoutes(resource),
172
+ events: describeEvents(resource.name, resource.events),
173
+ schemaOptions: Object.keys(resource.schemaOptions ?? {}).length > 0 ? resource.schemaOptions : void 0,
174
+ rateLimit: resource.rateLimit,
175
+ middlewares: describeMiddlewares(resource.middlewares)
176
+ };
177
+ }
178
+ async function describe(args) {
179
+ try {
180
+ const flags = new Set(args.filter((a) => a.startsWith("--")));
181
+ const positional = args.filter((a) => !a.startsWith("--"));
182
+ const pretty = flags.has("--pretty") || !flags.has("--json");
183
+ const filterResource = positional[1];
184
+ const entryPath = positional[0];
185
+ if (!entryPath) {
186
+ console.log("Usage: arc describe <entry-file> [resource-name] [--json] [--pretty]\n");
187
+ console.log("Outputs machine-readable JSON metadata for AI agents.\n");
188
+ console.log("Options:");
189
+ console.log(" --json Output compact JSON (default if piped)");
190
+ console.log(" --pretty Output formatted JSON (default if terminal)");
191
+ console.log("\nExamples:");
192
+ console.log(" arc describe ./src/resources.js");
193
+ console.log(" arc describe ./src/resources.js product");
194
+ console.log(" arc describe ./src/resources.js --json | jq .");
195
+ return;
196
+ }
197
+ const entryModule = await import(pathToFileURL(resolve(process.cwd(), entryPath)).href);
198
+ const resources = [];
199
+ function tryCollect(value) {
200
+ if (value && typeof value === "object" && "name" in value && "_registryMeta" in value && "toPlugin" in value) resources.push(value);
201
+ }
202
+ for (const exported of Object.values(entryModule)) if (Array.isArray(exported)) exported.forEach(tryCollect);
203
+ else tryCollect(exported);
204
+ if (resources.length === 0) throw new Error("No resource definitions found in entry file.\nMake sure your file exports defineResource() results:\n export const productResource = defineResource({ ... });");
205
+ const filtered = filterResource ? resources.filter((r) => r.name === filterResource) : resources;
206
+ if (filterResource && filtered.length === 0) throw new Error(`Resource '${filterResource}' not found.\nAvailable: ${resources.map((r) => r.name).join(", ")}`);
207
+ const described = filtered.map((r) => describeResource(r, r._registryMeta?.module));
208
+ const presetCounts = {};
209
+ let totalPipelineSteps = 0;
210
+ let totalFields = 0;
211
+ for (const res of described) {
212
+ for (const preset of res.presets) presetCounts[preset] = (presetCounts[preset] ?? 0) + 1;
213
+ if (res.pipeline) totalPipelineSteps += res.pipeline.guards.length + res.pipeline.transforms.length + res.pipeline.interceptors.length;
214
+ if (res.fields) totalFields += Object.keys(res.fields).length;
215
+ }
216
+ const output = {
217
+ $schema: "arc-describe/v1",
218
+ generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
219
+ resources: described,
220
+ stats: {
221
+ totalResources: described.length,
222
+ totalRoutes: described.reduce((sum, r) => sum + r.routes.length, 0),
223
+ totalEvents: described.reduce((sum, r) => sum + r.events.length, 0),
224
+ totalFields,
225
+ presetUsage: presetCounts,
226
+ pipelineSteps: totalPipelineSteps
227
+ }
228
+ };
229
+ const json = pretty ? JSON.stringify(output, null, 2) : JSON.stringify(output);
230
+ console.log(json);
231
+ } catch (error) {
232
+ if (error instanceof Error) throw error;
233
+ throw new Error(String(error));
234
+ }
235
+ }
236
+
237
+ //#endregion
238
+ export { describe as default, describe };
239
+ //# sourceMappingURL=describe.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"describe.mjs","names":[],"sources":["../../../src/cli/commands/describe.ts"],"sourcesContent":["/**\n * Arc CLI - Describe Command\n *\n * Machine-readable resource metadata for AI agents.\n * Outputs JSON with fields, permissions, pipeline, routes, events —\n * everything an LLM needs to understand and generate code for the API.\n *\n * @example\n * ```bash\n * arc describe ./src/resources.js --json\n * arc describe ./src/resources.js --resource product\n * arc describe ./src/resources.js --pretty\n * ```\n */\n\nimport { resolve } from 'node:path';\nimport { pathToFileURL } from 'node:url';\nimport type { ResourceDefinition } from '../../core/defineResource.js';\nimport type {\n AnyRecord,\n ResourcePermissions,\n RouteSchemaOptions,\n EventDefinition,\n RateLimitConfig,\n MiddlewareConfig,\n} from '../../types/index.js';\nimport type { PermissionCheck } from '../../permissions/types.js';\nimport type { FieldPermissionMap, FieldPermission } from '../../permissions/fields.js';\nimport type { PipelineConfig, PipelineStep } from '../../pipeline/types.js';\nimport { CRUD_OPERATIONS } from '../../constants.js';\n\n// ---------------------------------------------------------------------------\n// Output Schema\n// ---------------------------------------------------------------------------\n\ninterface DescribeOutput {\n $schema: 'arc-describe/v1';\n generatedAt: string;\n resources: DescribedResource[];\n stats: DescribeStats;\n}\n\ninterface DescribedResource {\n name: string;\n displayName: string;\n prefix: string;\n tag: string;\n module?: string;\n adapter: { type: string; name: string } | null;\n\n permissions: Record<string, PermissionDescription>;\n presets: string[];\n\n fields?: Record<string, FieldDescription>;\n pipeline?: PipelineDescription;\n\n routes: RouteDescription[];\n events: EventDescription[];\n\n schemaOptions?: RouteSchemaOptions;\n rateLimit?: RateLimitConfig | false;\n middlewares: string[];\n}\n\ninterface PermissionDescription {\n type: 'public' | 'requireAuth' | 'requireRoles' | 'custom';\n roles?: readonly string[];\n}\n\ninterface FieldDescription {\n type: string;\n roles?: readonly string[];\n redactValue?: unknown;\n}\n\ninterface PipelineDescription {\n guards: PipelineStepDescription[];\n transforms: PipelineStepDescription[];\n interceptors: PipelineStepDescription[];\n}\n\ninterface PipelineStepDescription {\n name: string;\n operations?: string[];\n}\n\ninterface RouteDescription {\n method: string;\n path: string;\n operation: string;\n summary?: string;\n description?: string;\n permission?: PermissionDescription;\n}\n\ninterface EventDescription {\n name: string;\n description?: string;\n hasSchema: boolean;\n}\n\ninterface DescribeStats {\n totalResources: number;\n totalRoutes: number;\n totalEvents: number;\n totalFields: number;\n presetUsage: Record<string, number>;\n pipelineSteps: number;\n}\n\n// ---------------------------------------------------------------------------\n// Permission Introspection\n// ---------------------------------------------------------------------------\n\nfunction describePermission(check: unknown): PermissionDescription {\n if (!check || typeof check !== 'function') {\n return { type: 'custom' };\n }\n\n const fn = check as PermissionCheck;\n\n // allowPublic() sets _isPublic = true\n if (fn._isPublic === true) {\n return { type: 'public' };\n }\n\n // requireRoles() sets _roles = [...]\n if (Array.isArray(fn._roles)) {\n return { type: 'requireRoles', roles: fn._roles as string[] };\n }\n\n // requireAuth() — function that checks ctx.user\n // Infer from function source as a best-effort heuristic\n const src = check.toString();\n if (src.includes('ctx.user') && !src.includes('roles') && src.length < 200) {\n return { type: 'requireAuth' };\n }\n\n return { type: 'custom' };\n}\n\nfunction describePermissions(perms?: ResourcePermissions): Record<string, PermissionDescription> {\n if (!perms) return {};\n\n const result: Record<string, PermissionDescription> = {};\n for (const op of CRUD_OPERATIONS) {\n const check = perms[op];\n if (check) {\n result[op] = describePermission(check);\n }\n }\n\n return result;\n}\n\n// ---------------------------------------------------------------------------\n// Field Permission Introspection\n// ---------------------------------------------------------------------------\n\nfunction describeFields(fieldPerms?: FieldPermissionMap): Record<string, FieldDescription> | undefined {\n if (!fieldPerms || Object.keys(fieldPerms).length === 0) return undefined;\n\n const result: Record<string, FieldDescription> = {};\n for (const [field, perm] of Object.entries(fieldPerms)) {\n const desc: FieldDescription = { type: perm._type };\n if (perm.roles?.length) desc.roles = perm.roles;\n if (perm.redactValue !== undefined) desc.redactValue = perm.redactValue;\n result[field] = desc;\n }\n return result;\n}\n\n// ---------------------------------------------------------------------------\n// Pipeline Introspection\n// ---------------------------------------------------------------------------\n\nfunction describePipeline(pipe?: PipelineConfig): PipelineDescription | undefined {\n if (!pipe) return undefined;\n\n const steps: PipelineStep[] = [];\n\n if (Array.isArray(pipe)) {\n steps.push(...pipe);\n } else {\n // Per-operation map — collect all unique steps\n const seen = new Set<string>();\n for (const opSteps of Object.values(pipe)) {\n if (Array.isArray(opSteps)) {\n for (const step of opSteps) {\n const key = `${step._type}:${step.name}`;\n if (!seen.has(key)) {\n seen.add(key);\n steps.push(step);\n }\n }\n }\n }\n }\n\n if (steps.length === 0) return undefined;\n\n const guards: PipelineStepDescription[] = [];\n const transforms: PipelineStepDescription[] = [];\n const interceptors: PipelineStepDescription[] = [];\n\n for (const step of steps) {\n const desc: PipelineStepDescription = { name: step.name };\n if (step.operations?.length) desc.operations = [...step.operations];\n\n switch (step._type) {\n case 'guard': guards.push(desc); break;\n case 'transform': transforms.push(desc); break;\n case 'interceptor': interceptors.push(desc); break;\n }\n }\n\n return { guards, transforms, interceptors };\n}\n\n// ---------------------------------------------------------------------------\n// Route Introspection\n// ---------------------------------------------------------------------------\n\nfunction describeRoutes(resource: ResourceDefinition<unknown>): RouteDescription[] {\n const routes: RouteDescription[] = [];\n\n // Default CRUD routes\n if (!resource.disableDefaultRoutes) {\n const disabled = new Set(resource.disabledRoutes ?? []);\n const crudOps = [\n { method: 'GET', suffix: '', op: 'list' },\n { method: 'GET', suffix: '/:id', op: 'get' },\n { method: 'POST', suffix: '', op: 'create' },\n { method: 'PATCH', suffix: '/:id', op: 'update' },\n { method: 'DELETE', suffix: '/:id', op: 'delete' },\n ] as const;\n\n for (const { method, suffix, op } of crudOps) {\n if (!disabled.has(op)) {\n const route: RouteDescription = {\n method,\n path: `${resource.prefix}${suffix}`,\n operation: op,\n };\n const perm = resource.permissions[op];\n if (perm) route.permission = describePermission(perm);\n routes.push(route);\n }\n }\n }\n\n // Additional routes\n for (const ar of resource.additionalRoutes) {\n routes.push({\n method: ar.method,\n path: `${resource.prefix}${ar.path}`,\n operation: typeof ar.handler === 'string' ? ar.handler : 'custom',\n summary: ar.summary,\n description: ar.description,\n permission: describePermission(ar.permissions),\n });\n }\n\n return routes;\n}\n\n// ---------------------------------------------------------------------------\n// Event Introspection\n// ---------------------------------------------------------------------------\n\nfunction describeEvents(\n resourceName: string,\n events?: Record<string, EventDefinition>,\n): EventDescription[] {\n if (!events) return [];\n\n return Object.entries(events).map(([action, def]) => ({\n name: `${resourceName}:${action}`,\n description: def.description,\n hasSchema: !!def.schema,\n }));\n}\n\n// ---------------------------------------------------------------------------\n// Middleware Introspection\n// ---------------------------------------------------------------------------\n\nfunction describeMiddlewares(middlewares?: MiddlewareConfig): string[] {\n if (!middlewares) return [];\n\n const ops: string[] = [];\n for (const [op, handlers] of Object.entries(middlewares)) {\n if (handlers?.length) {\n ops.push(`${op}(${handlers.length})`);\n }\n }\n return ops;\n}\n\n// ---------------------------------------------------------------------------\n// Main Describe Function\n// ---------------------------------------------------------------------------\n\nfunction describeResource(\n resource: ResourceDefinition<unknown>,\n module?: string,\n): DescribedResource {\n return {\n name: resource.name,\n displayName: resource.displayName,\n prefix: resource.prefix,\n tag: resource.tag,\n module,\n\n adapter: resource.adapter\n ? { type: resource.adapter.type, name: resource.adapter.name }\n : null,\n\n permissions: describePermissions(resource.permissions),\n presets: resource._appliedPresets ?? [],\n\n fields: describeFields(resource.fields),\n pipeline: describePipeline(resource.pipe),\n\n routes: describeRoutes(resource),\n events: describeEvents(resource.name, resource.events),\n\n schemaOptions: Object.keys(resource.schemaOptions ?? {}).length > 0\n ? resource.schemaOptions\n : undefined,\n rateLimit: resource.rateLimit,\n middlewares: describeMiddlewares(resource.middlewares),\n };\n}\n\n// ---------------------------------------------------------------------------\n// CLI Entry\n// ---------------------------------------------------------------------------\n\nexport async function describe(args: string[]): Promise<void> {\n try {\n // Parse flags\n const flags = new Set(args.filter(a => a.startsWith('--')));\n const positional = args.filter(a => !a.startsWith('--'));\n const pretty = flags.has('--pretty') || !flags.has('--json');\n const filterResource = positional[1]; // optional resource name filter\n\n const entryPath = positional[0];\n if (!entryPath) {\n console.log('Usage: arc describe <entry-file> [resource-name] [--json] [--pretty]\\n');\n console.log('Outputs machine-readable JSON metadata for AI agents.\\n');\n console.log('Options:');\n console.log(' --json Output compact JSON (default if piped)');\n console.log(' --pretty Output formatted JSON (default if terminal)');\n console.log('\\nExamples:');\n console.log(' arc describe ./src/resources.js');\n console.log(' arc describe ./src/resources.js product');\n console.log(' arc describe ./src/resources.js --json | jq .');\n return;\n }\n\n // Dynamically import user's entry file (pathToFileURL needed for Windows)\n const entryFileUrl = pathToFileURL(resolve(process.cwd(), entryPath)).href;\n const entryModule = await import(entryFileUrl);\n\n // Collect ResourceDefinition objects\n // Also handles arrays of resources (e.g. `export const resources = [r1, r2]`)\n const resources: ResourceDefinition<unknown>[] = [];\n\n function tryCollect(value: unknown): void {\n if (\n value &&\n typeof value === 'object' &&\n 'name' in value &&\n '_registryMeta' in value &&\n 'toPlugin' in value\n ) {\n resources.push(value as ResourceDefinition<unknown>);\n }\n }\n\n for (const exported of Object.values(entryModule)) {\n if (Array.isArray(exported)) {\n exported.forEach(tryCollect);\n } else {\n tryCollect(exported);\n }\n }\n\n if (resources.length === 0) {\n throw new Error(\n 'No resource definitions found in entry file.\\nMake sure your file exports defineResource() results:\\n export const productResource = defineResource({ ... });',\n );\n }\n\n // Filter to single resource if requested\n const filtered = filterResource\n ? resources.filter(r => r.name === filterResource)\n : resources;\n\n if (filterResource && filtered.length === 0) {\n throw new Error(\n `Resource '${filterResource}' not found.\\nAvailable: ${resources.map(r => r.name).join(', ')}`,\n );\n }\n\n // Build described resources\n const described = filtered.map(r =>\n describeResource(r, (r._registryMeta as AnyRecord | undefined)?.module as string | undefined),\n );\n\n // Compute stats\n const presetCounts: Record<string, number> = {};\n let totalPipelineSteps = 0;\n let totalFields = 0;\n\n for (const res of described) {\n for (const preset of res.presets) {\n presetCounts[preset] = (presetCounts[preset] ?? 0) + 1;\n }\n if (res.pipeline) {\n totalPipelineSteps += res.pipeline.guards.length\n + res.pipeline.transforms.length\n + res.pipeline.interceptors.length;\n }\n if (res.fields) {\n totalFields += Object.keys(res.fields).length;\n }\n }\n\n const output: DescribeOutput = {\n $schema: 'arc-describe/v1',\n generatedAt: new Date().toISOString(),\n resources: described,\n stats: {\n totalResources: described.length,\n totalRoutes: described.reduce((sum, r) => sum + r.routes.length, 0),\n totalEvents: described.reduce((sum, r) => sum + r.events.length, 0),\n totalFields,\n presetUsage: presetCounts,\n pipelineSteps: totalPipelineSteps,\n },\n };\n\n // Output\n const json = pretty\n ? JSON.stringify(output, null, 2)\n : JSON.stringify(output);\n\n console.log(json);\n } catch (error: unknown) {\n if (error instanceof Error) throw error;\n throw new Error(String(error));\n }\n}\n\nexport default describe;\n"],"mappings":"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"}
@@ -0,0 +1,14 @@
1
+ //#region src/cli/commands/docs.d.ts
2
+ /**
3
+ * Arc CLI - Docs Command
4
+ *
5
+ * Export OpenAPI specification from registered resources.
6
+ * Requires an entry file that exports defineResource() results.
7
+ */
8
+ declare function exportDocs(args: string[]): Promise<void>;
9
+ declare const _default: {
10
+ exportDocs: typeof exportDocs;
11
+ };
12
+ //#endregion
13
+ export { _default as default, exportDocs };
14
+ //# sourceMappingURL=docs.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"docs.d.mts","names":[],"sources":["../../../src/cli/commands/docs.ts"],"mappings":";;AAyBA;;;;;iBAAsB,UAAA,CAAW,IAAA,aAAiB,OAAA;AAAA,cA8DjD,QAAA"}
@@ -0,0 +1,53 @@
1
+ import { t as ResourceRegistry } from "../../ResourceRegistry-DsN4KJjV.mjs";
2
+ import { t as buildOpenApiSpec } from "../../openapi-G3Cw7XuM.mjs";
3
+ import { writeFileSync } from "node:fs";
4
+ import { resolve } from "node:path";
5
+ import { pathToFileURL } from "node:url";
6
+
7
+ //#region src/cli/commands/docs.ts
8
+ /**
9
+ * Arc CLI - Docs Command
10
+ *
11
+ * Export OpenAPI specification from registered resources.
12
+ * Requires an entry file that exports defineResource() results.
13
+ */
14
+ function parseDocsArgs(args) {
15
+ const outputPath = args.find((a) => a.endsWith(".json")) ?? "./openapi.json";
16
+ return {
17
+ entryPath: args.find((a) => !a.endsWith(".json")),
18
+ outputPath
19
+ };
20
+ }
21
+ async function exportDocs(args) {
22
+ const { entryPath, outputPath } = parseDocsArgs(args);
23
+ console.log("Exporting OpenAPI specification...\n");
24
+ if (!entryPath) throw new Error("Missing entry file.\n\nUsage: arc docs <entry-file> [output.json]\nExample: arc docs ./src/resources.js ./openapi.json");
25
+ const entryModule = await import(pathToFileURL(resolve(process.cwd(), entryPath)).href);
26
+ const registry = new ResourceRegistry();
27
+ let registered = 0;
28
+ function tryRegister(value) {
29
+ if (value && typeof value === "object" && "name" in value && "_registryMeta" in value && "toPlugin" in value) {
30
+ registry.register(value, value._registryMeta ?? {});
31
+ registered++;
32
+ }
33
+ }
34
+ for (const exported of Object.values(entryModule)) if (Array.isArray(exported)) exported.forEach(tryRegister);
35
+ else tryRegister(exported);
36
+ if (registered === 0) throw new Error("No resource definitions found in entry file.\nMake sure your file exports defineResource() results:\n export const productResource = defineResource({ ... });");
37
+ const resources = registry.getAll();
38
+ const spec = buildOpenApiSpec(resources, {
39
+ title: "Arc API",
40
+ version: "1.0.0",
41
+ description: "Auto-generated from Arc resources"
42
+ });
43
+ const fullPath = resolve(process.cwd(), outputPath);
44
+ writeFileSync(fullPath, JSON.stringify(spec, null, 2));
45
+ console.log(`OpenAPI spec exported to: ${fullPath}`);
46
+ console.log(`\nResources included: ${resources.length}`);
47
+ console.log(`Total endpoints: ${Object.keys(spec.paths).length}`);
48
+ }
49
+ var docs_default = { exportDocs };
50
+
51
+ //#endregion
52
+ export { docs_default as default, exportDocs };
53
+ //# sourceMappingURL=docs.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"docs.mjs","names":[],"sources":["../../../src/cli/commands/docs.ts"],"sourcesContent":["/**\n * Arc CLI - Docs Command\n *\n * Export OpenAPI specification from registered resources.\n * Requires an entry file that exports defineResource() results.\n */\n\nimport { writeFileSync } from 'node:fs';\nimport { resolve } from 'node:path';\nimport { pathToFileURL } from 'node:url';\nimport { ResourceRegistry } from '../../registry/index.js';\nimport type { RegistryEntry } from '../../types/index.js';\nimport { buildOpenApiSpec } from '../../docs/openapi.js';\n\ninterface ParsedDocsArgs {\n entryPath?: string;\n outputPath: string;\n}\n\nfunction parseDocsArgs(args: string[]): ParsedDocsArgs {\n const outputPath = args.find((a) => a.endsWith('.json')) ?? './openapi.json';\n const entryPath = args.find((a) => !a.endsWith('.json'));\n return { entryPath, outputPath };\n}\n\nexport async function exportDocs(args: string[]): Promise<void> {\n const { entryPath, outputPath } = parseDocsArgs(args);\n\n console.log('Exporting OpenAPI specification...\\n');\n\n if (!entryPath) {\n throw new Error(\n 'Missing entry file.\\n\\nUsage: arc docs <entry-file> [output.json]\\nExample: arc docs ./src/resources.js ./openapi.json',\n );\n }\n\n // Dynamically import user's entry file\n const entryFileUrl = pathToFileURL(resolve(process.cwd(), entryPath)).href;\n const entryModule = await import(entryFileUrl);\n\n // Collect ResourceDefinition objects from exports\n // Also handles arrays of resources (e.g. `export const resources = [r1, r2]`)\n const registry = new ResourceRegistry();\n let registered = 0;\n\n function tryRegister(value: unknown): void {\n if (\n value &&\n typeof value === 'object' &&\n 'name' in value &&\n '_registryMeta' in value &&\n 'toPlugin' in value\n ) {\n registry.register(value as any, (value as any)._registryMeta ?? {});\n registered++;\n }\n }\n\n for (const exported of Object.values(entryModule)) {\n if (Array.isArray(exported)) {\n exported.forEach(tryRegister);\n } else {\n tryRegister(exported);\n }\n }\n\n if (registered === 0) {\n throw new Error(\n 'No resource definitions found in entry file.\\nMake sure your file exports defineResource() results:\\n export const productResource = defineResource({ ... });',\n );\n }\n\n const resources: RegistryEntry[] = registry.getAll();\n\n const spec = buildOpenApiSpec(resources, {\n title: 'Arc API',\n version: '1.0.0',\n description: 'Auto-generated from Arc resources',\n });\n\n // Write to file (resolve handles both relative and absolute paths)\n const fullPath = resolve(process.cwd(), outputPath);\n writeFileSync(fullPath, JSON.stringify(spec, null, 2));\n\n console.log(`OpenAPI spec exported to: ${fullPath}`);\n console.log(`\\nResources included: ${resources.length}`);\n console.log(`Total endpoints: ${Object.keys(spec.paths).length}`);\n}\n\nexport default { exportDocs };\n"],"mappings":";;;;;;;;;;;;;AAmBA,SAAS,cAAc,MAAgC;CACrD,MAAM,aAAa,KAAK,MAAM,MAAM,EAAE,SAAS,QAAQ,CAAC,IAAI;AAE5D,QAAO;EAAE,WADS,KAAK,MAAM,MAAM,CAAC,EAAE,SAAS,QAAQ,CAAC;EACpC;EAAY;;AAGlC,eAAsB,WAAW,MAA+B;CAC9D,MAAM,EAAE,WAAW,eAAe,cAAc,KAAK;AAErD,SAAQ,IAAI,uCAAuC;AAEnD,KAAI,CAAC,UACH,OAAM,IAAI,MACR,yHACD;CAKH,MAAM,cAAc,MAAM,OADL,cAAc,QAAQ,QAAQ,KAAK,EAAE,UAAU,CAAC,CAAC;CAKtE,MAAM,WAAW,IAAI,kBAAkB;CACvC,IAAI,aAAa;CAEjB,SAAS,YAAY,OAAsB;AACzC,MACE,SACA,OAAO,UAAU,YACjB,UAAU,SACV,mBAAmB,SACnB,cAAc,OACd;AACA,YAAS,SAAS,OAAe,MAAc,iBAAiB,EAAE,CAAC;AACnE;;;AAIJ,MAAK,MAAM,YAAY,OAAO,OAAO,YAAY,CAC/C,KAAI,MAAM,QAAQ,SAAS,CACzB,UAAS,QAAQ,YAAY;KAE7B,aAAY,SAAS;AAIzB,KAAI,eAAe,EACjB,OAAM,IAAI,MACR,iKACD;CAGH,MAAM,YAA6B,SAAS,QAAQ;CAEpD,MAAM,OAAO,iBAAiB,WAAW;EACvC,OAAO;EACP,SAAS;EACT,aAAa;EACd,CAAC;CAGF,MAAM,WAAW,QAAQ,QAAQ,KAAK,EAAE,WAAW;AACnD,eAAc,UAAU,KAAK,UAAU,MAAM,MAAM,EAAE,CAAC;AAEtD,SAAQ,IAAI,6BAA6B,WAAW;AACpD,SAAQ,IAAI,yBAAyB,UAAU,SAAS;AACxD,SAAQ,IAAI,oBAAoB,OAAO,KAAK,KAAK,MAAM,CAAC,SAAS;;AAGnE,mBAAe,EAAE,YAAY"}
@@ -1,3 +1,4 @@
1
+ //#region src/cli/commands/generate.d.ts
1
2
  /**
2
3
  * Arc CLI - Generate Command
3
4
  *
@@ -12,5 +13,6 @@
12
13
  * Generate command handler
13
14
  */
14
15
  declare function generate(type: string | undefined, args: string[]): Promise<void>;
15
-
16
+ //#endregion
16
17
  export { generate as default, generate };
18
+ //# sourceMappingURL=generate.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"generate.d.mts","names":[],"sources":["../../../src/cli/commands/generate.ts"],"mappings":";;AAmRA;;;;;;;;;;;;iBAAsB,QAAA,CACpB,IAAA,sBACA,IAAA,aACC,OAAA"}