@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
@@ -1,658 +0,0 @@
1
- import fp from 'fastify-plugin';
2
-
3
- // src/docs/openapi.ts
4
-
5
- // src/registry/ResourceRegistry.ts
6
- var ResourceRegistry = class {
7
- _resources;
8
- _frozen;
9
- constructor() {
10
- this._resources = /* @__PURE__ */ new Map();
11
- this._frozen = false;
12
- }
13
- /**
14
- * Register a resource
15
- */
16
- register(resource, options = {}) {
17
- if (this._frozen) {
18
- throw new Error(
19
- `Registry frozen. Cannot register '${resource.name}' after startup.`
20
- );
21
- }
22
- if (this._resources.has(resource.name)) {
23
- throw new Error(`Resource '${resource.name}' already registered.`);
24
- }
25
- const entry = {
26
- name: resource.name,
27
- displayName: resource.displayName,
28
- tag: resource.tag,
29
- prefix: resource.prefix,
30
- module: options.module ?? void 0,
31
- adapter: resource.adapter ? {
32
- type: resource.adapter.type,
33
- name: resource.adapter.name
34
- } : null,
35
- permissions: resource.permissions,
36
- presets: resource._appliedPresets ?? [],
37
- routes: [],
38
- // Populated later by getIntrospection()
39
- additionalRoutes: resource.additionalRoutes.map((r) => ({
40
- method: r.method,
41
- path: r.path,
42
- handler: typeof r.handler === "string" ? r.handler : r.handler.name || "anonymous",
43
- summary: r.summary,
44
- description: r.description,
45
- permissions: r.permissions,
46
- wrapHandler: r.wrapHandler,
47
- schema: r.schema
48
- // Include schema for OpenAPI docs
49
- })),
50
- events: Object.keys(resource.events ?? {}),
51
- registeredAt: (/* @__PURE__ */ new Date()).toISOString(),
52
- disableDefaultRoutes: resource.disableDefaultRoutes,
53
- openApiSchemas: options.openApiSchemas,
54
- plugin: resource.toPlugin()
55
- // Store plugin factory
56
- };
57
- this._resources.set(resource.name, entry);
58
- return this;
59
- }
60
- /**
61
- * Get resource by name
62
- */
63
- get(name) {
64
- return this._resources.get(name);
65
- }
66
- /**
67
- * Get all resources
68
- */
69
- getAll() {
70
- return Array.from(this._resources.values());
71
- }
72
- /**
73
- * Get resources by module
74
- */
75
- getByModule(moduleName) {
76
- return this.getAll().filter((r) => r.module === moduleName);
77
- }
78
- /**
79
- * Get resources by preset
80
- */
81
- getByPreset(presetName) {
82
- return this.getAll().filter((r) => r.presets.includes(presetName));
83
- }
84
- /**
85
- * Check if resource exists
86
- */
87
- has(name) {
88
- return this._resources.has(name);
89
- }
90
- /**
91
- * Get registry statistics
92
- */
93
- getStats() {
94
- const resources = this.getAll();
95
- const presetCounts = {};
96
- for (const r of resources) {
97
- for (const preset of r.presets) {
98
- presetCounts[preset] = (presetCounts[preset] ?? 0) + 1;
99
- }
100
- }
101
- return {
102
- totalResources: resources.length,
103
- byModule: this._groupBy(resources, "module"),
104
- presetUsage: presetCounts,
105
- totalRoutes: resources.reduce((sum, r) => {
106
- const defaultRouteCount = r.disableDefaultRoutes ? 0 : 5;
107
- return sum + (r.additionalRoutes?.length ?? 0) + defaultRouteCount;
108
- }, 0),
109
- totalEvents: resources.reduce((sum, r) => sum + (r.events?.length ?? 0), 0)
110
- };
111
- }
112
- /**
113
- * Get full introspection data
114
- */
115
- getIntrospection() {
116
- return {
117
- resources: this.getAll().map((r) => {
118
- const defaultRoutes = r.disableDefaultRoutes ? [] : [
119
- { method: "GET", path: r.prefix, operation: "list" },
120
- { method: "GET", path: `${r.prefix}/:id`, operation: "get" },
121
- { method: "POST", path: r.prefix, operation: "create" },
122
- { method: "PATCH", path: `${r.prefix}/:id`, operation: "update" },
123
- { method: "DELETE", path: `${r.prefix}/:id`, operation: "delete" }
124
- ];
125
- return {
126
- name: r.name,
127
- displayName: r.displayName,
128
- prefix: r.prefix,
129
- module: r.module,
130
- presets: r.presets,
131
- permissions: r.permissions,
132
- routes: [
133
- ...defaultRoutes,
134
- ...r.additionalRoutes?.map((ar) => ({
135
- method: ar.method,
136
- path: `${r.prefix}${ar.path}`,
137
- operation: typeof ar.handler === "string" ? ar.handler : "custom",
138
- handler: typeof ar.handler === "string" ? ar.handler : void 0,
139
- summary: ar.summary
140
- })) ?? []
141
- ],
142
- events: r.events
143
- };
144
- }),
145
- stats: this.getStats(),
146
- generatedAt: (/* @__PURE__ */ new Date()).toISOString()
147
- };
148
- }
149
- /**
150
- * Freeze registry (prevent further registrations)
151
- */
152
- freeze() {
153
- this._frozen = true;
154
- }
155
- /**
156
- * Check if frozen
157
- */
158
- isFrozen() {
159
- return this._frozen;
160
- }
161
- /**
162
- * Unfreeze registry (for testing)
163
- */
164
- _unfreeze() {
165
- this._frozen = false;
166
- }
167
- /**
168
- * Clear all resources (for testing)
169
- */
170
- _clear() {
171
- this._resources.clear();
172
- this._frozen = false;
173
- }
174
- /**
175
- * Group by key
176
- */
177
- _groupBy(arr, key) {
178
- const result = {};
179
- for (const item of arr) {
180
- const k = String(item[key] ?? "uncategorized");
181
- result[k] = (result[k] ?? 0) + 1;
182
- }
183
- return result;
184
- }
185
- };
186
- var registryKey = /* @__PURE__ */ Symbol.for("arc.resourceRegistry");
187
- var globalScope = globalThis;
188
- var resourceRegistry = globalScope[registryKey] ?? new ResourceRegistry();
189
- if (!globalScope[registryKey]) {
190
- globalScope[registryKey] = resourceRegistry;
191
- }
192
-
193
- // src/docs/openapi.ts
194
- var openApiPlugin = async (fastify, opts = {}) => {
195
- const {
196
- title = "Arc API",
197
- version = "1.0.0",
198
- description,
199
- serverUrl,
200
- prefix = "/_docs",
201
- apiPrefix = "",
202
- authRoles = []
203
- } = opts;
204
- const buildSpec = () => {
205
- const resources = resourceRegistry.getAll();
206
- const paths = {};
207
- const tags = [];
208
- for (const resource of resources) {
209
- tags.push({
210
- name: resource.tag || resource.name,
211
- description: `${resource.displayName || resource.name} operations`
212
- });
213
- const resourcePaths = generateResourcePaths(resource, apiPrefix);
214
- Object.assign(paths, resourcePaths);
215
- }
216
- return {
217
- openapi: "3.0.3",
218
- info: {
219
- title,
220
- version,
221
- ...description && { description }
222
- },
223
- ...serverUrl && {
224
- servers: [{ url: serverUrl }]
225
- },
226
- paths,
227
- components: {
228
- schemas: generateSchemas(resources),
229
- securitySchemes: {
230
- bearerAuth: {
231
- type: "http",
232
- scheme: "bearer",
233
- bearerFormat: "JWT"
234
- },
235
- orgHeader: {
236
- type: "apiKey",
237
- in: "header",
238
- name: "x-organization-id"
239
- }
240
- }
241
- },
242
- tags
243
- // Note: Security is defined per-operation, not globally
244
- // This allows public routes to have no security requirement
245
- };
246
- };
247
- fastify.get(`${prefix}/openapi.json`, async (request, reply) => {
248
- if (authRoles.length > 0) {
249
- const user = request.user;
250
- const hasRole = authRoles.some((role) => user?.roles?.includes(role));
251
- if (!hasRole && !user?.roles?.includes("superadmin")) {
252
- reply.code(403).send({ error: "Access denied" });
253
- return;
254
- }
255
- }
256
- const spec = buildSpec();
257
- return spec;
258
- });
259
- fastify.log?.info?.(`OpenAPI spec available at ${prefix}/openapi.json`);
260
- };
261
- function toOpenApiPath(path) {
262
- return path.replace(/:([^/]+)/g, "{$1}");
263
- }
264
- function convertSchemaToParameters(schema) {
265
- const params = [];
266
- const properties = schema.properties || {};
267
- const required = schema.required || [];
268
- for (const [name, prop] of Object.entries(properties)) {
269
- const description = prop.description;
270
- const { description: _, ...schemaProps } = prop;
271
- const param = {
272
- name,
273
- in: "query",
274
- required: required.includes(name),
275
- schema: schemaProps
276
- };
277
- if (description) {
278
- param.description = description;
279
- }
280
- params.push(param);
281
- }
282
- return params;
283
- }
284
- var DEFAULT_LIST_PARAMS = [
285
- { name: "page", in: "query", schema: { type: "integer" }, description: "Page number" },
286
- { name: "limit", in: "query", schema: { type: "integer" }, description: "Items per page" },
287
- { name: "sort", in: "query", schema: { type: "string" }, description: "Sort field (prefix with - for descending)" }
288
- ];
289
- function generateResourcePaths(resource, apiPrefix = "") {
290
- const paths = {};
291
- const basePath = `${apiPrefix}${resource.prefix}`;
292
- if (resource.disableDefaultRoutes && (!resource.additionalRoutes || resource.additionalRoutes.length === 0)) {
293
- return paths;
294
- }
295
- if (!resource.disableDefaultRoutes) {
296
- const listParams = resource.openApiSchemas?.listQuery ? convertSchemaToParameters(resource.openApiSchemas.listQuery) : DEFAULT_LIST_PARAMS;
297
- paths[basePath] = {
298
- get: createOperation(resource, "list", "List all", {
299
- parameters: listParams,
300
- responses: {
301
- "200": {
302
- description: "List of items",
303
- content: {
304
- "application/json": {
305
- schema: {
306
- type: "object",
307
- properties: {
308
- success: { type: "boolean" },
309
- data: { type: "array", items: { $ref: `#/components/schemas/${resource.name}` } },
310
- pagination: { $ref: "#/components/schemas/Pagination" }
311
- }
312
- }
313
- }
314
- }
315
- }
316
- }
317
- }),
318
- post: createOperation(resource, "create", "Create new", {
319
- requestBody: {
320
- required: true,
321
- content: {
322
- "application/json": {
323
- schema: { $ref: `#/components/schemas/${resource.name}Input` }
324
- }
325
- }
326
- },
327
- responses: {
328
- "201": {
329
- description: "Created successfully",
330
- content: {
331
- "application/json": {
332
- schema: {
333
- type: "object",
334
- properties: {
335
- success: { type: "boolean" },
336
- data: { $ref: `#/components/schemas/${resource.name}` },
337
- message: { type: "string" }
338
- }
339
- }
340
- }
341
- }
342
- }
343
- }
344
- })
345
- };
346
- paths[toOpenApiPath(`${basePath}/:id`)] = {
347
- get: createOperation(resource, "get", "Get by ID", {
348
- parameters: [
349
- { name: "id", in: "path", required: true, schema: { type: "string" } }
350
- ],
351
- responses: {
352
- "200": {
353
- description: "Item found",
354
- content: {
355
- "application/json": {
356
- schema: {
357
- type: "object",
358
- properties: {
359
- success: { type: "boolean" },
360
- data: { $ref: `#/components/schemas/${resource.name}` }
361
- }
362
- }
363
- }
364
- }
365
- },
366
- "404": { description: "Not found" }
367
- }
368
- }),
369
- patch: createOperation(resource, "update", "Update", {
370
- parameters: [
371
- { name: "id", in: "path", required: true, schema: { type: "string" } }
372
- ],
373
- requestBody: {
374
- required: true,
375
- content: {
376
- "application/json": {
377
- schema: { $ref: `#/components/schemas/${resource.name}Input` }
378
- }
379
- }
380
- },
381
- responses: {
382
- "200": {
383
- description: "Updated successfully",
384
- content: {
385
- "application/json": {
386
- schema: {
387
- type: "object",
388
- properties: {
389
- success: { type: "boolean" },
390
- data: { $ref: `#/components/schemas/${resource.name}` },
391
- message: { type: "string" }
392
- }
393
- }
394
- }
395
- }
396
- }
397
- }
398
- }),
399
- delete: createOperation(resource, "delete", "Delete", {
400
- parameters: [
401
- { name: "id", in: "path", required: true, schema: { type: "string" } }
402
- ],
403
- responses: {
404
- "200": {
405
- description: "Deleted successfully",
406
- content: {
407
- "application/json": {
408
- schema: {
409
- type: "object",
410
- properties: {
411
- success: { type: "boolean" },
412
- message: { type: "string" }
413
- }
414
- }
415
- }
416
- }
417
- }
418
- }
419
- })
420
- };
421
- }
422
- for (const route of resource.additionalRoutes || []) {
423
- const fullPath = toOpenApiPath(`${basePath}${route.path}`);
424
- const method = route.method.toLowerCase();
425
- if (!paths[fullPath]) {
426
- paths[fullPath] = {};
427
- }
428
- const handlerName = typeof route.handler === "string" ? route.handler : "handler";
429
- const isPublicRoute = route.permissions?._isPublic === true;
430
- const requiresAuthForRoute = !!route.permissions && !isPublicRoute;
431
- const extras = {
432
- parameters: extractPathParams(route.path),
433
- responses: {
434
- "200": { description: route.description || "Success" }
435
- }
436
- };
437
- const routeSchema = route.schema;
438
- if (routeSchema?.body && ["post", "put", "patch"].includes(method)) {
439
- extras.requestBody = {
440
- required: true,
441
- content: {
442
- "application/json": {
443
- schema: routeSchema.body
444
- }
445
- }
446
- };
447
- }
448
- if (routeSchema?.querystring) {
449
- const queryParams = convertSchemaToParameters(routeSchema.querystring);
450
- extras.parameters = [...extras.parameters || [], ...queryParams];
451
- }
452
- if (routeSchema?.response) {
453
- const responseSchemas = routeSchema.response;
454
- for (const [statusCode, schema] of Object.entries(responseSchemas)) {
455
- extras.responses[statusCode] = {
456
- description: schema.description || `Response ${statusCode}`,
457
- content: {
458
- "application/json": {
459
- schema
460
- }
461
- }
462
- };
463
- }
464
- }
465
- paths[fullPath][method] = createOperation(
466
- resource,
467
- handlerName,
468
- route.summary ?? handlerName,
469
- extras,
470
- requiresAuthForRoute
471
- );
472
- }
473
- return paths;
474
- }
475
- function createOperation(resource, operation, summary, extras, requiresAuthOverride) {
476
- const permissions = resource.permissions || {};
477
- const operationPermission = permissions[operation];
478
- const isPublic = operationPermission?._isPublic === true;
479
- const requiresAuth = requiresAuthOverride !== void 0 ? requiresAuthOverride : typeof operationPermission === "function" && !isPublic;
480
- return {
481
- tags: [resource.tag || "Resource"],
482
- summary: `${summary} ${(resource.displayName || resource.name).toLowerCase()}`,
483
- operationId: `${resource.name}_${operation}`,
484
- // Only add security requirement if route requires auth
485
- ...requiresAuth && {
486
- security: [{ bearerAuth: [] }]
487
- },
488
- responses: {
489
- ...requiresAuth && {
490
- "401": { description: "Unauthorized" },
491
- "403": { description: "Forbidden" }
492
- },
493
- "500": { description: "Internal server error" }
494
- },
495
- ...extras
496
- };
497
- }
498
- function extractPathParams(path) {
499
- const params = [];
500
- const matches = path.matchAll(/:([^/]+)/g);
501
- for (const match of matches) {
502
- const paramName = match[1];
503
- if (paramName) {
504
- params.push({
505
- name: paramName,
506
- in: "path",
507
- required: true,
508
- schema: { type: "string" }
509
- });
510
- }
511
- }
512
- return params;
513
- }
514
- function generateSchemas(resources) {
515
- const schemas = {
516
- // Common schemas
517
- Pagination: {
518
- type: "object",
519
- properties: {
520
- page: { type: "integer" },
521
- limit: { type: "integer" },
522
- total: { type: "integer" },
523
- totalPages: { type: "integer" },
524
- hasNextPage: { type: "boolean" },
525
- hasPrevPage: { type: "boolean" }
526
- }
527
- },
528
- Error: {
529
- type: "object",
530
- properties: {
531
- success: { type: "boolean", example: false },
532
- error: { type: "string" },
533
- code: { type: "string" },
534
- requestId: { type: "string" },
535
- timestamp: { type: "string" }
536
- }
537
- }
538
- };
539
- for (const resource of resources) {
540
- const storedSchemas = resource.openApiSchemas;
541
- if (storedSchemas?.response) {
542
- schemas[resource.name] = {
543
- type: "object",
544
- description: resource.displayName,
545
- ...storedSchemas.response
546
- };
547
- } else if (storedSchemas?.createBody) {
548
- schemas[resource.name] = {
549
- type: "object",
550
- description: resource.displayName,
551
- properties: {
552
- _id: { type: "string", description: "Unique identifier" },
553
- ...storedSchemas.createBody.properties ?? {},
554
- createdAt: { type: "string", format: "date-time", description: "Creation timestamp" },
555
- updatedAt: { type: "string", format: "date-time", description: "Last update timestamp" }
556
- }
557
- };
558
- } else {
559
- schemas[resource.name] = {
560
- type: "object",
561
- description: resource.displayName,
562
- properties: {
563
- _id: { type: "string", description: "Unique identifier" },
564
- createdAt: { type: "string", format: "date-time", description: "Creation timestamp" },
565
- updatedAt: { type: "string", format: "date-time", description: "Last update timestamp" }
566
- }
567
- };
568
- }
569
- if (storedSchemas?.createBody) {
570
- schemas[`${resource.name}Input`] = {
571
- type: "object",
572
- description: `${resource.displayName} create input`,
573
- ...storedSchemas.createBody
574
- };
575
- if (storedSchemas.updateBody) {
576
- schemas[`${resource.name}Update`] = {
577
- type: "object",
578
- description: `${resource.displayName} update input`,
579
- ...storedSchemas.updateBody
580
- };
581
- }
582
- } else {
583
- schemas[`${resource.name}Input`] = {
584
- type: "object",
585
- description: `${resource.displayName} input`
586
- };
587
- }
588
- }
589
- return schemas;
590
- }
591
- var openapi_default = fp(openApiPlugin, {
592
- name: "arc-openapi",
593
- fastify: "5.x"
594
- });
595
- var scalarPlugin = async (fastify, opts = {}) => {
596
- const {
597
- routePrefix = "/docs",
598
- specUrl = "/_docs/openapi.json",
599
- title = "API Documentation",
600
- theme = "default",
601
- showSidebar = true,
602
- darkMode = false,
603
- authRoles = [],
604
- customCss = "",
605
- favicon
606
- } = opts;
607
- const scalarConfig = JSON.stringify({
608
- spec: { url: specUrl },
609
- theme,
610
- showSidebar,
611
- darkMode,
612
- ...favicon && { favicon }
613
- });
614
- const html = `<!DOCTYPE html>
615
- <html>
616
- <head>
617
- <meta charset="utf-8">
618
- <meta name="viewport" content="width=device-width, initial-scale=1">
619
- <title>${title}</title>
620
- ${favicon ? `<link rel="icon" href="${favicon}">` : ""}
621
- <style>
622
- body { margin: 0; padding: 0; }
623
- ${customCss}
624
- </style>
625
- </head>
626
- <body>
627
- <script id="api-reference" data-url="${specUrl}"></script>
628
- <script>
629
- var configuration = ${scalarConfig};
630
- document.getElementById('api-reference').dataset.configuration = JSON.stringify(configuration);
631
- </script>
632
- <script src="https://cdn.jsdelivr.net/npm/@scalar/api-reference"></script>
633
- </body>
634
- </html>`;
635
- fastify.get(routePrefix, async (request, reply) => {
636
- if (authRoles.length > 0) {
637
- const user = request.user;
638
- const hasRole = authRoles.some((role) => user?.roles?.includes(role));
639
- if (!hasRole && !user?.roles?.includes("superadmin")) {
640
- reply.code(403).send({ error: "Access denied" });
641
- return;
642
- }
643
- }
644
- reply.type("text/html").send(html);
645
- });
646
- if (!routePrefix.endsWith("/")) {
647
- fastify.get(`${routePrefix}/`, async (_, reply) => {
648
- reply.redirect(routePrefix);
649
- });
650
- }
651
- fastify.log?.info?.(`Scalar API docs available at ${routePrefix}`);
652
- };
653
- var scalar_default = fp(scalarPlugin, {
654
- name: "arc-scalar",
655
- fastify: "5.x"
656
- });
657
-
658
- export { openapi_default as openApiPlugin, openApiPlugin as openApiPluginFn, scalar_default as scalarPlugin, scalarPlugin as scalarPluginFn };