@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,1069 +0,0 @@
1
- import fp from 'fastify-plugin';
2
- import { randomUUID } from 'crypto';
3
- import { createRequire } from 'module';
4
-
5
- // src/plugins/requestId.ts
6
- var requestIdPlugin = async (fastify, opts = {}) => {
7
- const {
8
- header = "x-request-id",
9
- generator = randomUUID,
10
- setResponseHeader = true
11
- } = opts;
12
- if (!fastify.hasRequestDecorator("requestId")) {
13
- fastify.decorateRequest("requestId", "");
14
- }
15
- fastify.addHook("onRequest", async (request) => {
16
- const incomingId = request.headers[header];
17
- const requestId = typeof incomingId === "string" && incomingId.trim() ? incomingId.trim() : generator();
18
- request.id = requestId;
19
- request.requestId = requestId;
20
- });
21
- if (setResponseHeader) {
22
- fastify.addHook("onSend", async (request, reply) => {
23
- reply.header(header, request.requestId);
24
- });
25
- }
26
- fastify.log?.debug?.("Request ID plugin registered");
27
- };
28
- var requestId_default = fp(requestIdPlugin, {
29
- name: "arc-request-id",
30
- fastify: "5.x"
31
- });
32
- var httpMetrics = {
33
- requestsTotal: {},
34
- requestDurations: []};
35
- var healthPlugin = async (fastify, opts = {}) => {
36
- const {
37
- prefix = "/_health",
38
- checks = [],
39
- metrics = false,
40
- metricsCollector,
41
- version,
42
- collectHttpMetrics = metrics
43
- } = opts;
44
- fastify.get(`${prefix}/live`, {
45
- schema: {
46
- tags: ["Health"],
47
- summary: "Liveness probe",
48
- description: "Returns 200 if the process is alive",
49
- response: {
50
- 200: {
51
- type: "object",
52
- properties: {
53
- status: { type: "string", enum: ["ok"] },
54
- timestamp: { type: "string" },
55
- version: { type: "string" }
56
- }
57
- }
58
- }
59
- }
60
- }, async () => {
61
- return {
62
- status: "ok",
63
- timestamp: (/* @__PURE__ */ new Date()).toISOString(),
64
- ...version ? { version } : {}
65
- };
66
- });
67
- fastify.get(`${prefix}/ready`, {
68
- schema: {
69
- tags: ["Health"],
70
- summary: "Readiness probe",
71
- description: "Returns 200 if all dependencies are healthy",
72
- response: {
73
- 200: {
74
- type: "object",
75
- properties: {
76
- status: { type: "string", enum: ["ready", "not_ready"] },
77
- timestamp: { type: "string" },
78
- checks: {
79
- type: "array",
80
- items: {
81
- type: "object",
82
- properties: {
83
- name: { type: "string" },
84
- healthy: { type: "boolean" },
85
- duration: { type: "number" },
86
- error: { type: "string" }
87
- }
88
- }
89
- }
90
- }
91
- },
92
- 503: {
93
- type: "object",
94
- properties: {
95
- status: { type: "string", enum: ["not_ready"] },
96
- timestamp: { type: "string" },
97
- checks: { type: "array" }
98
- }
99
- }
100
- }
101
- }
102
- }, async (_, reply) => {
103
- const results = await runChecks(checks);
104
- const criticalFailed = results.some(
105
- (r) => !r.healthy && (checks.find((c) => c.name === r.name)?.critical ?? true)
106
- );
107
- const response = {
108
- status: criticalFailed ? "not_ready" : "ready",
109
- timestamp: (/* @__PURE__ */ new Date()).toISOString(),
110
- checks: results
111
- };
112
- if (criticalFailed) {
113
- reply.code(503);
114
- }
115
- return response;
116
- });
117
- if (metrics) {
118
- fastify.get(`${prefix}/metrics`, async (_, reply) => {
119
- reply.type("text/plain; charset=utf-8");
120
- if (metricsCollector) {
121
- return await metricsCollector();
122
- }
123
- const uptime = process.uptime();
124
- const memory = process.memoryUsage();
125
- const cpu = process.cpuUsage();
126
- const lines = [
127
- "# HELP process_uptime_seconds Process uptime in seconds",
128
- "# TYPE process_uptime_seconds gauge",
129
- `process_uptime_seconds ${uptime.toFixed(2)}`,
130
- "",
131
- "# HELP process_memory_heap_bytes Heap memory usage in bytes",
132
- "# TYPE process_memory_heap_bytes gauge",
133
- `process_memory_heap_bytes{type="used"} ${memory.heapUsed}`,
134
- `process_memory_heap_bytes{type="total"} ${memory.heapTotal}`,
135
- "",
136
- "# HELP process_memory_rss_bytes RSS memory in bytes",
137
- "# TYPE process_memory_rss_bytes gauge",
138
- `process_memory_rss_bytes ${memory.rss}`,
139
- "",
140
- "# HELP process_memory_external_bytes External memory in bytes",
141
- "# TYPE process_memory_external_bytes gauge",
142
- `process_memory_external_bytes ${memory.external}`,
143
- "",
144
- "# HELP process_cpu_user_microseconds User CPU time in microseconds",
145
- "# TYPE process_cpu_user_microseconds counter",
146
- `process_cpu_user_microseconds ${cpu.user}`,
147
- "",
148
- "# HELP process_cpu_system_microseconds System CPU time in microseconds",
149
- "# TYPE process_cpu_system_microseconds counter",
150
- `process_cpu_system_microseconds ${cpu.system}`,
151
- ""
152
- ];
153
- if (collectHttpMetrics && Object.keys(httpMetrics.requestsTotal).length > 0) {
154
- lines.push(
155
- "# HELP http_requests_total Total HTTP requests by status code",
156
- "# TYPE http_requests_total counter"
157
- );
158
- for (const [status, count] of Object.entries(httpMetrics.requestsTotal)) {
159
- lines.push(`http_requests_total{status="${status}"} ${count}`);
160
- }
161
- lines.push("");
162
- if (httpMetrics.requestDurations.length > 0) {
163
- const sorted = [...httpMetrics.requestDurations].sort((a, b) => a - b);
164
- const p50 = sorted[Math.floor(sorted.length * 0.5)] || 0;
165
- const p95 = sorted[Math.floor(sorted.length * 0.95)] || 0;
166
- const p99 = sorted[Math.floor(sorted.length * 0.99)] || 0;
167
- const sum = sorted.reduce((a, b) => a + b, 0);
168
- lines.push(
169
- "# HELP http_request_duration_milliseconds HTTP request duration",
170
- "# TYPE http_request_duration_milliseconds summary",
171
- `http_request_duration_milliseconds{quantile="0.5"} ${p50.toFixed(2)}`,
172
- `http_request_duration_milliseconds{quantile="0.95"} ${p95.toFixed(2)}`,
173
- `http_request_duration_milliseconds{quantile="0.99"} ${p99.toFixed(2)}`,
174
- `http_request_duration_milliseconds_sum ${sum.toFixed(2)}`,
175
- `http_request_duration_milliseconds_count ${sorted.length}`,
176
- ""
177
- );
178
- }
179
- }
180
- return lines.join("\n");
181
- });
182
- }
183
- if (collectHttpMetrics) {
184
- fastify.addHook("onRequest", async (request) => {
185
- request._startTime = Date.now();
186
- });
187
- fastify.addHook("onResponse", async (request, reply) => {
188
- const duration = Date.now() - (request._startTime || Date.now());
189
- const statusBucket = `${Math.floor(reply.statusCode / 100)}xx`;
190
- httpMetrics.requestsTotal[statusBucket] = (httpMetrics.requestsTotal[statusBucket] || 0) + 1;
191
- httpMetrics.requestDurations.push(duration);
192
- if (httpMetrics.requestDurations.length > 1e4) {
193
- httpMetrics.requestDurations.shift();
194
- }
195
- });
196
- }
197
- fastify.log?.info?.(`Health plugin registered at ${prefix}`);
198
- };
199
- async function runChecks(checks) {
200
- const results = [];
201
- for (const check of checks) {
202
- const start = Date.now();
203
- const timeout = check.timeout ?? 5e3;
204
- try {
205
- const checkPromise = Promise.resolve(check.check());
206
- const timeoutPromise = new Promise((_, reject) => {
207
- setTimeout(() => reject(new Error("Health check timeout")), timeout);
208
- });
209
- const healthy = await Promise.race([checkPromise, timeoutPromise]);
210
- results.push({
211
- name: check.name,
212
- healthy: Boolean(healthy),
213
- duration: Date.now() - start
214
- });
215
- } catch (err) {
216
- results.push({
217
- name: check.name,
218
- healthy: false,
219
- duration: Date.now() - start,
220
- error: err.message
221
- });
222
- }
223
- }
224
- return results;
225
- }
226
- var health_default = fp(healthPlugin, {
227
- name: "arc-health",
228
- fastify: "5.x"
229
- });
230
- var require2 = createRequire(import.meta.url);
231
- var trace;
232
- var context;
233
- var SpanStatusCode;
234
- var NodeTracerProvider;
235
- var BatchSpanProcessor;
236
- var OTLPTraceExporter;
237
- var HttpInstrumentation;
238
- var MongoDBInstrumentation;
239
- var getNodeAutoInstrumentations;
240
- var isAvailable = false;
241
- try {
242
- const api = require2("@opentelemetry/api");
243
- trace = api.trace;
244
- context = api.context;
245
- SpanStatusCode = api.SpanStatusCode;
246
- const sdkNode = require2("@opentelemetry/sdk-node");
247
- NodeTracerProvider = sdkNode.NodeTracerProvider;
248
- BatchSpanProcessor = sdkNode.BatchSpanProcessor;
249
- const exporterTraceOtlp = require2("@opentelemetry/exporter-trace-otlp-http");
250
- OTLPTraceExporter = exporterTraceOtlp.OTLPTraceExporter;
251
- const instrHttp = require2("@opentelemetry/instrumentation-http");
252
- HttpInstrumentation = instrHttp.HttpInstrumentation;
253
- const instrMongo = require2("@opentelemetry/instrumentation-mongodb");
254
- MongoDBInstrumentation = instrMongo.MongoDBInstrumentation;
255
- const autoInstr = require2("@opentelemetry/auto-instrumentations-node");
256
- getNodeAutoInstrumentations = autoInstr.getNodeAutoInstrumentations;
257
- isAvailable = true;
258
- } catch (e) {
259
- }
260
- function createTracerProvider(options) {
261
- if (!isAvailable) {
262
- return null;
263
- }
264
- const { serviceName = "@classytic/arc", exporterUrl = "http://localhost:4318/v1/traces" } = options;
265
- const exporter = new OTLPTraceExporter({
266
- url: exporterUrl
267
- });
268
- const provider = new NodeTracerProvider({
269
- resource: {
270
- attributes: {
271
- "service.name": serviceName,
272
- "service.version": "1.0.0"
273
- }
274
- }
275
- });
276
- provider.addSpanProcessor(new BatchSpanProcessor(exporter));
277
- provider.register();
278
- return provider;
279
- }
280
- async function tracingPlugin(fastify, options = {}) {
281
- const {
282
- serviceName = "@classytic/arc",
283
- autoInstrumentation = true,
284
- traceRepository = true,
285
- traceController = true,
286
- sampleRate = 1
287
- } = options;
288
- if (!isAvailable) {
289
- fastify.log.warn("OpenTelemetry not installed. Tracing disabled.");
290
- fastify.log.warn("Install: npm install @opentelemetry/api @opentelemetry/sdk-node");
291
- return;
292
- }
293
- const provider = createTracerProvider(options);
294
- if (!provider) {
295
- return;
296
- }
297
- if (autoInstrumentation && getNodeAutoInstrumentations) {
298
- getNodeAutoInstrumentations({
299
- "@opentelemetry/instrumentation-http": {
300
- enabled: true
301
- },
302
- "@opentelemetry/instrumentation-mongodb": {
303
- enabled: true
304
- }
305
- });
306
- }
307
- const tracer = trace.getTracer(serviceName);
308
- fastify.decorateRequest("tracer", void 0);
309
- fastify.addHook("onRequest", async (request, reply) => {
310
- if (Math.random() > sampleRate) {
311
- return;
312
- }
313
- const span = tracer.startSpan(`HTTP ${request.method} ${request.url}`, {
314
- kind: 1,
315
- // SpanKind.SERVER
316
- attributes: {
317
- "http.method": request.method,
318
- "http.url": request.url,
319
- "http.target": request.routeOptions?.url ?? request.url,
320
- "http.host": request.hostname,
321
- "http.scheme": request.protocol,
322
- "http.user_agent": request.headers["user-agent"]
323
- }
324
- });
325
- request.tracer = {
326
- tracer,
327
- currentSpan: span
328
- };
329
- context.with(trace.setSpan(context.active(), span), () => {
330
- });
331
- });
332
- fastify.addHook("onResponse", async (request, reply) => {
333
- if (!request.tracer?.currentSpan) {
334
- return;
335
- }
336
- const span = request.tracer.currentSpan;
337
- span.setAttributes({
338
- "http.status_code": reply.statusCode,
339
- "http.response_content_length": reply.getHeader("content-length")
340
- });
341
- if (reply.statusCode >= 500) {
342
- span.setStatus({
343
- code: SpanStatusCode.ERROR,
344
- message: `HTTP ${reply.statusCode}`
345
- });
346
- } else {
347
- span.setStatus({ code: SpanStatusCode.OK });
348
- }
349
- span.end();
350
- });
351
- fastify.addHook("onError", async (request, reply, error) => {
352
- if (!request.tracer?.currentSpan) {
353
- return;
354
- }
355
- const span = request.tracer.currentSpan;
356
- span.recordException(error);
357
- span.setStatus({
358
- code: SpanStatusCode.ERROR,
359
- message: error.message
360
- });
361
- });
362
- fastify.log.info({ serviceName }, "OpenTelemetry tracing enabled");
363
- }
364
- function createSpan(request, name, fn, attributes) {
365
- if (!isAvailable || !request.tracer) {
366
- return fn(null);
367
- }
368
- const { tracer, currentSpan } = request.tracer;
369
- const span = tracer.startSpan(
370
- name,
371
- {
372
- parent: currentSpan,
373
- attributes: attributes || {}
374
- },
375
- trace.setSpan(context.active(), currentSpan)
376
- );
377
- return context.with(trace.setSpan(context.active(), span), async () => {
378
- try {
379
- const result = await fn(span);
380
- span.setStatus({ code: SpanStatusCode.OK });
381
- return result;
382
- } catch (error) {
383
- span.recordException(error);
384
- span.setStatus({
385
- code: SpanStatusCode.ERROR,
386
- message: error.message
387
- });
388
- throw error;
389
- } finally {
390
- span.end();
391
- }
392
- });
393
- }
394
- function traced(spanName) {
395
- return function(target, propertyKey, descriptor) {
396
- const originalMethod = descriptor.value;
397
- descriptor.value = async function(...args) {
398
- const request = args.find((arg) => arg && arg.tracer);
399
- if (!request?.tracer) {
400
- return originalMethod.apply(this, args);
401
- }
402
- const name = spanName || `${target.constructor.name}.${propertyKey}`;
403
- return createSpan(request, name, async (span) => {
404
- if (span) {
405
- span.setAttribute("db.operation", propertyKey);
406
- span.setAttribute("db.system", "mongodb");
407
- }
408
- return originalMethod.apply(this, args);
409
- });
410
- };
411
- return descriptor;
412
- };
413
- }
414
- function isTracingAvailable() {
415
- return isAvailable;
416
- }
417
- var tracing_default = fp(tracingPlugin, {
418
- name: "arc-tracing",
419
- fastify: "5.x"
420
- });
421
- var gracefulShutdownPlugin = async (fastify, opts = {}) => {
422
- const {
423
- timeout = 3e4,
424
- onShutdown,
425
- signals = ["SIGTERM", "SIGINT"],
426
- logEvents = true
427
- } = opts;
428
- let isShuttingDown = false;
429
- const shutdown = async (signal) => {
430
- if (isShuttingDown) {
431
- if (logEvents) {
432
- fastify.log?.warn?.({ signal }, "Shutdown already in progress, ignoring signal");
433
- }
434
- return;
435
- }
436
- isShuttingDown = true;
437
- if (logEvents) {
438
- fastify.log?.info?.({ signal, timeout }, "Shutdown signal received, starting graceful shutdown");
439
- }
440
- const forceExitTimer = setTimeout(() => {
441
- if (logEvents) {
442
- fastify.log?.error?.("Graceful shutdown timeout exceeded, forcing exit");
443
- }
444
- process.exit(1);
445
- }, timeout);
446
- forceExitTimer.unref();
447
- try {
448
- if (logEvents) {
449
- fastify.log?.info?.("Closing server to new connections");
450
- }
451
- await fastify.close();
452
- if (onShutdown) {
453
- if (logEvents) {
454
- fastify.log?.info?.("Running custom shutdown handler");
455
- }
456
- await onShutdown();
457
- }
458
- if (logEvents) {
459
- fastify.log?.info?.("Graceful shutdown complete");
460
- }
461
- clearTimeout(forceExitTimer);
462
- process.exit(0);
463
- } catch (err) {
464
- if (logEvents) {
465
- fastify.log?.error?.({ error: err.message }, "Error during shutdown");
466
- }
467
- clearTimeout(forceExitTimer);
468
- process.exit(1);
469
- }
470
- };
471
- for (const signal of signals) {
472
- process.on(signal, () => {
473
- void shutdown(signal);
474
- });
475
- }
476
- fastify.decorate("shutdown", async () => {
477
- await shutdown("MANUAL");
478
- });
479
- if (logEvents) {
480
- fastify.log?.debug?.({ signals }, "Graceful shutdown plugin registered");
481
- }
482
- };
483
- var gracefulShutdown_default = fp(gracefulShutdownPlugin, {
484
- name: "arc-graceful-shutdown",
485
- fastify: "5.x"
486
- });
487
-
488
- // src/utils/errors.ts
489
- var ArcError = class extends Error {
490
- name;
491
- code;
492
- statusCode;
493
- details;
494
- cause;
495
- timestamp;
496
- requestId;
497
- constructor(message, options = {}) {
498
- super(message);
499
- this.name = "ArcError";
500
- this.code = options.code ?? "ARC_ERROR";
501
- this.statusCode = options.statusCode ?? 500;
502
- this.details = options.details;
503
- this.cause = options.cause;
504
- this.timestamp = (/* @__PURE__ */ new Date()).toISOString();
505
- this.requestId = options.requestId;
506
- if (Error.captureStackTrace) {
507
- Error.captureStackTrace(this, this.constructor);
508
- }
509
- }
510
- /**
511
- * Set request ID (typically from request context)
512
- */
513
- withRequestId(requestId) {
514
- this.requestId = requestId;
515
- return this;
516
- }
517
- /**
518
- * Convert to JSON response
519
- */
520
- toJSON() {
521
- return {
522
- success: false,
523
- error: this.message,
524
- code: this.code,
525
- timestamp: this.timestamp,
526
- ...this.requestId && { requestId: this.requestId },
527
- ...this.details && { details: this.details }
528
- };
529
- }
530
- };
531
- function isArcError(error) {
532
- return error instanceof ArcError;
533
- }
534
-
535
- // src/plugins/errorHandler.ts
536
- async function errorHandlerPluginFn(fastify, options = {}) {
537
- const {
538
- includeStack = process.env.NODE_ENV !== "production",
539
- onError,
540
- errorMap = {}
541
- } = options;
542
- fastify.setErrorHandler(async (error, request, reply) => {
543
- if (onError) {
544
- try {
545
- await onError(error, request);
546
- } catch (callbackError) {
547
- request.log.error({ err: callbackError }, "Error in onError callback");
548
- }
549
- }
550
- const requestId = request.id;
551
- const response = {
552
- success: false,
553
- error: error.message || "Internal Server Error",
554
- code: "INTERNAL_ERROR",
555
- timestamp: (/* @__PURE__ */ new Date()).toISOString(),
556
- ...requestId && { requestId }
557
- };
558
- let statusCode = 500;
559
- if (isArcError(error)) {
560
- statusCode = error.statusCode;
561
- response.code = error.code;
562
- if (error.details) {
563
- response.details = error.details;
564
- }
565
- if (error.requestId) {
566
- response.requestId = error.requestId;
567
- }
568
- } else if ("validation" in error && Array.isArray(error.validation)) {
569
- statusCode = 400;
570
- response.code = "VALIDATION_ERROR";
571
- response.error = "Validation failed";
572
- response.details = {
573
- errors: error.validation?.map((v) => ({
574
- field: v.instancePath?.replace(/^\//, "") || v.params?.missingProperty || "unknown",
575
- message: v.message || "Invalid value",
576
- keyword: v.keyword
577
- }))
578
- };
579
- } else if ("statusCode" in error && typeof error.statusCode === "number") {
580
- statusCode = error.statusCode;
581
- response.code = statusCodeToCode(statusCode);
582
- } else if (error.name && errorMap[error.name]) {
583
- const mapping = errorMap[error.name];
584
- statusCode = mapping.statusCode;
585
- response.code = mapping.code;
586
- if (mapping.message) {
587
- response.error = mapping.message;
588
- }
589
- } else if (error.name === "ValidationError" && "errors" in error) {
590
- statusCode = 400;
591
- response.code = "VALIDATION_ERROR";
592
- const mongooseErrors = error.errors;
593
- if (process.env.NODE_ENV === "production") {
594
- response.details = { errorCount: Object.keys(mongooseErrors).length };
595
- } else {
596
- response.details = {
597
- errors: Object.entries(mongooseErrors).map(([field, err]) => ({
598
- field: err.path || field,
599
- message: err.message
600
- }))
601
- };
602
- }
603
- } else if (error.name === "CastError") {
604
- statusCode = 400;
605
- response.code = "INVALID_ID";
606
- response.error = "Invalid identifier format";
607
- } else if (error.name === "MongoServerError" && error.code === 11e3) {
608
- statusCode = 409;
609
- response.code = "DUPLICATE_KEY";
610
- response.error = "Resource already exists";
611
- const keyValue = error.keyValue;
612
- if (keyValue && process.env.NODE_ENV !== "production") {
613
- response.details = { duplicateFields: Object.keys(keyValue) };
614
- }
615
- }
616
- if (includeStack && error.stack) {
617
- response.stack = error.stack;
618
- }
619
- if (statusCode >= 500) {
620
- request.log.error({ err: error, statusCode }, "Server error");
621
- } else if (statusCode >= 400) {
622
- request.log.warn({ err: error, statusCode }, "Client error");
623
- }
624
- return reply.status(statusCode).send(response);
625
- });
626
- }
627
- function statusCodeToCode(statusCode) {
628
- const codes = {
629
- 400: "BAD_REQUEST",
630
- 401: "UNAUTHORIZED",
631
- 403: "FORBIDDEN",
632
- 404: "NOT_FOUND",
633
- 405: "METHOD_NOT_ALLOWED",
634
- 409: "CONFLICT",
635
- 422: "UNPROCESSABLE_ENTITY",
636
- 429: "RATE_LIMITED",
637
- 500: "INTERNAL_ERROR",
638
- 502: "BAD_GATEWAY",
639
- 503: "SERVICE_UNAVAILABLE",
640
- 504: "GATEWAY_TIMEOUT"
641
- };
642
- return codes[statusCode] ?? "ERROR";
643
- }
644
- var errorHandlerPlugin = fp(errorHandlerPluginFn, {
645
- name: "arc-error-handler",
646
- fastify: "5.x"
647
- });
648
- var errorHandler_default = errorHandlerPlugin;
649
-
650
- // src/hooks/HookSystem.ts
651
- var HookSystem = class {
652
- hooks;
653
- logger;
654
- constructor(options) {
655
- this.hooks = /* @__PURE__ */ new Map();
656
- this.logger = options?.logger ?? { error: (...args) => console.error(...args) };
657
- }
658
- /**
659
- * Generate hook key
660
- */
661
- getKey(resource, operation, phase) {
662
- return `${resource}:${operation}:${phase}`;
663
- }
664
- /**
665
- * Register a hook
666
- * Supports both object parameter and positional arguments
667
- */
668
- register(resourceOrOptions, operation, phase, handler, priority = 10) {
669
- let resource;
670
- let finalOperation;
671
- let finalPhase;
672
- let finalHandler;
673
- let finalPriority;
674
- if (typeof resourceOrOptions === "object") {
675
- resource = resourceOrOptions.resource;
676
- finalOperation = resourceOrOptions.operation;
677
- finalPhase = resourceOrOptions.phase;
678
- finalHandler = resourceOrOptions.handler;
679
- finalPriority = resourceOrOptions.priority ?? 10;
680
- } else {
681
- resource = resourceOrOptions;
682
- finalOperation = operation;
683
- finalPhase = phase;
684
- finalHandler = handler;
685
- finalPriority = priority;
686
- }
687
- const key = this.getKey(resource, finalOperation, finalPhase);
688
- if (!this.hooks.has(key)) {
689
- this.hooks.set(key, []);
690
- }
691
- const registration = {
692
- resource,
693
- operation: finalOperation,
694
- phase: finalPhase,
695
- handler: finalHandler,
696
- priority: finalPriority
697
- };
698
- const hooks = this.hooks.get(key);
699
- hooks.push(registration);
700
- hooks.sort((a, b) => a.priority - b.priority);
701
- return () => {
702
- const idx = hooks.indexOf(registration);
703
- if (idx !== -1) {
704
- hooks.splice(idx, 1);
705
- }
706
- };
707
- }
708
- /**
709
- * Register before hook
710
- */
711
- before(resource, operation, handler, priority = 10) {
712
- return this.register(resource, operation, "before", handler, priority);
713
- }
714
- /**
715
- * Register after hook
716
- */
717
- after(resource, operation, handler, priority = 10) {
718
- return this.register(resource, operation, "after", handler, priority);
719
- }
720
- /**
721
- * Execute hooks for a given context
722
- */
723
- async execute(ctx) {
724
- const key = this.getKey(ctx.resource, ctx.operation, ctx.phase);
725
- const hooks = this.hooks.get(key) ?? [];
726
- const wildcardKey = this.getKey("*", ctx.operation, ctx.phase);
727
- const wildcardHooks = this.hooks.get(wildcardKey) ?? [];
728
- const allHooks = [...wildcardHooks, ...hooks];
729
- allHooks.sort((a, b) => a.priority - b.priority);
730
- let result = ctx.data;
731
- for (const hook of allHooks) {
732
- const handlerContext = {
733
- resource: ctx.resource,
734
- operation: ctx.operation,
735
- phase: ctx.phase,
736
- data: result,
737
- result: ctx.result,
738
- user: ctx.user,
739
- context: ctx.context,
740
- meta: ctx.meta
741
- };
742
- const hookResult = await hook.handler(handlerContext);
743
- if (hookResult !== void 0 && hookResult !== null) {
744
- result = hookResult;
745
- }
746
- }
747
- return result;
748
- }
749
- /**
750
- * Execute before hooks
751
- */
752
- async executeBefore(resource, operation, data, options) {
753
- const result = await this.execute({
754
- resource,
755
- operation,
756
- phase: "before",
757
- data,
758
- user: options?.user,
759
- context: options?.context,
760
- meta: options?.meta
761
- });
762
- return result ?? data;
763
- }
764
- /**
765
- * Execute after hooks
766
- * Errors in after hooks are logged but don't fail the request
767
- */
768
- async executeAfter(resource, operation, result, options) {
769
- try {
770
- await this.execute({
771
- resource,
772
- operation,
773
- phase: "after",
774
- result,
775
- user: options?.user,
776
- context: options?.context,
777
- meta: options?.meta
778
- });
779
- } catch (error) {
780
- this.logger.error(
781
- `[HookSystem] Error in after hook for ${resource}:${operation}:`,
782
- error
783
- );
784
- }
785
- }
786
- /**
787
- * Get all registered hooks
788
- */
789
- getAll() {
790
- const all = [];
791
- for (const hooks of this.hooks.values()) {
792
- all.push(...hooks);
793
- }
794
- return all;
795
- }
796
- /**
797
- * Get hooks for a specific resource
798
- */
799
- getForResource(resource) {
800
- const all = [];
801
- for (const [key, hooks] of this.hooks.entries()) {
802
- if (key.startsWith(`${resource}:`)) {
803
- all.push(...hooks);
804
- }
805
- }
806
- return all;
807
- }
808
- /**
809
- * Clear all hooks
810
- */
811
- clear() {
812
- this.hooks.clear();
813
- }
814
- /**
815
- * Clear hooks for a specific resource
816
- */
817
- clearResource(resource) {
818
- for (const key of this.hooks.keys()) {
819
- if (key.startsWith(`${resource}:`)) {
820
- this.hooks.delete(key);
821
- }
822
- }
823
- }
824
- };
825
- var hookSystem = new HookSystem();
826
-
827
- // src/registry/ResourceRegistry.ts
828
- var ResourceRegistry = class {
829
- _resources;
830
- _frozen;
831
- constructor() {
832
- this._resources = /* @__PURE__ */ new Map();
833
- this._frozen = false;
834
- }
835
- /**
836
- * Register a resource
837
- */
838
- register(resource, options = {}) {
839
- if (this._frozen) {
840
- throw new Error(
841
- `Registry frozen. Cannot register '${resource.name}' after startup.`
842
- );
843
- }
844
- if (this._resources.has(resource.name)) {
845
- throw new Error(`Resource '${resource.name}' already registered.`);
846
- }
847
- const entry = {
848
- name: resource.name,
849
- displayName: resource.displayName,
850
- tag: resource.tag,
851
- prefix: resource.prefix,
852
- module: options.module ?? void 0,
853
- adapter: resource.adapter ? {
854
- type: resource.adapter.type,
855
- name: resource.adapter.name
856
- } : null,
857
- permissions: resource.permissions,
858
- presets: resource._appliedPresets ?? [],
859
- routes: [],
860
- // Populated later by getIntrospection()
861
- additionalRoutes: resource.additionalRoutes.map((r) => ({
862
- method: r.method,
863
- path: r.path,
864
- handler: typeof r.handler === "string" ? r.handler : r.handler.name || "anonymous",
865
- summary: r.summary,
866
- description: r.description,
867
- permissions: r.permissions,
868
- wrapHandler: r.wrapHandler,
869
- schema: r.schema
870
- // Include schema for OpenAPI docs
871
- })),
872
- events: Object.keys(resource.events ?? {}),
873
- registeredAt: (/* @__PURE__ */ new Date()).toISOString(),
874
- disableDefaultRoutes: resource.disableDefaultRoutes,
875
- openApiSchemas: options.openApiSchemas,
876
- plugin: resource.toPlugin()
877
- // Store plugin factory
878
- };
879
- this._resources.set(resource.name, entry);
880
- return this;
881
- }
882
- /**
883
- * Get resource by name
884
- */
885
- get(name) {
886
- return this._resources.get(name);
887
- }
888
- /**
889
- * Get all resources
890
- */
891
- getAll() {
892
- return Array.from(this._resources.values());
893
- }
894
- /**
895
- * Get resources by module
896
- */
897
- getByModule(moduleName) {
898
- return this.getAll().filter((r) => r.module === moduleName);
899
- }
900
- /**
901
- * Get resources by preset
902
- */
903
- getByPreset(presetName) {
904
- return this.getAll().filter((r) => r.presets.includes(presetName));
905
- }
906
- /**
907
- * Check if resource exists
908
- */
909
- has(name) {
910
- return this._resources.has(name);
911
- }
912
- /**
913
- * Get registry statistics
914
- */
915
- getStats() {
916
- const resources = this.getAll();
917
- const presetCounts = {};
918
- for (const r of resources) {
919
- for (const preset of r.presets) {
920
- presetCounts[preset] = (presetCounts[preset] ?? 0) + 1;
921
- }
922
- }
923
- return {
924
- totalResources: resources.length,
925
- byModule: this._groupBy(resources, "module"),
926
- presetUsage: presetCounts,
927
- totalRoutes: resources.reduce((sum, r) => {
928
- const defaultRouteCount = r.disableDefaultRoutes ? 0 : 5;
929
- return sum + (r.additionalRoutes?.length ?? 0) + defaultRouteCount;
930
- }, 0),
931
- totalEvents: resources.reduce((sum, r) => sum + (r.events?.length ?? 0), 0)
932
- };
933
- }
934
- /**
935
- * Get full introspection data
936
- */
937
- getIntrospection() {
938
- return {
939
- resources: this.getAll().map((r) => {
940
- const defaultRoutes = r.disableDefaultRoutes ? [] : [
941
- { method: "GET", path: r.prefix, operation: "list" },
942
- { method: "GET", path: `${r.prefix}/:id`, operation: "get" },
943
- { method: "POST", path: r.prefix, operation: "create" },
944
- { method: "PATCH", path: `${r.prefix}/:id`, operation: "update" },
945
- { method: "DELETE", path: `${r.prefix}/:id`, operation: "delete" }
946
- ];
947
- return {
948
- name: r.name,
949
- displayName: r.displayName,
950
- prefix: r.prefix,
951
- module: r.module,
952
- presets: r.presets,
953
- permissions: r.permissions,
954
- routes: [
955
- ...defaultRoutes,
956
- ...r.additionalRoutes?.map((ar) => ({
957
- method: ar.method,
958
- path: `${r.prefix}${ar.path}`,
959
- operation: typeof ar.handler === "string" ? ar.handler : "custom",
960
- handler: typeof ar.handler === "string" ? ar.handler : void 0,
961
- summary: ar.summary
962
- })) ?? []
963
- ],
964
- events: r.events
965
- };
966
- }),
967
- stats: this.getStats(),
968
- generatedAt: (/* @__PURE__ */ new Date()).toISOString()
969
- };
970
- }
971
- /**
972
- * Freeze registry (prevent further registrations)
973
- */
974
- freeze() {
975
- this._frozen = true;
976
- }
977
- /**
978
- * Check if frozen
979
- */
980
- isFrozen() {
981
- return this._frozen;
982
- }
983
- /**
984
- * Unfreeze registry (for testing)
985
- */
986
- _unfreeze() {
987
- this._frozen = false;
988
- }
989
- /**
990
- * Clear all resources (for testing)
991
- */
992
- _clear() {
993
- this._resources.clear();
994
- this._frozen = false;
995
- }
996
- /**
997
- * Group by key
998
- */
999
- _groupBy(arr, key) {
1000
- const result = {};
1001
- for (const item of arr) {
1002
- const k = String(item[key] ?? "uncategorized");
1003
- result[k] = (result[k] ?? 0) + 1;
1004
- }
1005
- return result;
1006
- }
1007
- };
1008
- var registryKey = /* @__PURE__ */ Symbol.for("arc.resourceRegistry");
1009
- var globalScope = globalThis;
1010
- var resourceRegistry = globalScope[registryKey] ?? new ResourceRegistry();
1011
- if (!globalScope[registryKey]) {
1012
- globalScope[registryKey] = resourceRegistry;
1013
- }
1014
-
1015
- // src/core/arcCorePlugin.ts
1016
- function hasEvents(instance) {
1017
- return "events" in instance && instance.events != null && typeof instance.events.publish === "function";
1018
- }
1019
- var arcCorePlugin = async (fastify, opts = {}) => {
1020
- const {
1021
- emitEvents = true,
1022
- hookSystem: hookSystem2,
1023
- registry,
1024
- useGlobalSingletons = false
1025
- } = opts;
1026
- const actualHookSystem = useGlobalSingletons ? hookSystem : hookSystem2 ?? new HookSystem();
1027
- const actualRegistry = useGlobalSingletons ? resourceRegistry : registry ?? new ResourceRegistry();
1028
- fastify.decorate("arc", {
1029
- hooks: actualHookSystem,
1030
- registry: actualRegistry,
1031
- emitEvents
1032
- });
1033
- if (emitEvents) {
1034
- const eventOperations = ["create", "update", "delete"];
1035
- for (const operation of eventOperations) {
1036
- actualHookSystem.after("*", operation, async (ctx) => {
1037
- if (!hasEvents(fastify)) return;
1038
- const eventType = `${ctx.resource}.${operation}d`;
1039
- const payload = {
1040
- resource: ctx.resource,
1041
- operation: ctx.operation,
1042
- data: ctx.result,
1043
- userId: ctx.user?.id ?? ctx.user?._id,
1044
- organizationId: ctx.context?.organizationId,
1045
- timestamp: (/* @__PURE__ */ new Date()).toISOString()
1046
- };
1047
- try {
1048
- await fastify.events.publish(eventType, payload);
1049
- } catch (error) {
1050
- fastify.log?.warn?.(
1051
- { eventType, error },
1052
- "Failed to emit event"
1053
- );
1054
- }
1055
- });
1056
- }
1057
- }
1058
- fastify.addHook("onClose", async () => {
1059
- actualHookSystem.clear();
1060
- actualRegistry._clear();
1061
- });
1062
- fastify.log?.info?.("✅ Arc core plugin enabled (instance-scoped hooks & registry)");
1063
- };
1064
- var arcCorePlugin_default = fp(arcCorePlugin, {
1065
- name: "arc-core",
1066
- fastify: "5.x"
1067
- });
1068
-
1069
- export { arcCorePlugin_default as arcCorePlugin, arcCorePlugin as arcCorePluginFn, createSpan, errorHandler_default as errorHandlerPlugin, errorHandlerPlugin as errorHandlerPluginFn, gracefulShutdown_default as gracefulShutdownPlugin, gracefulShutdownPlugin as gracefulShutdownPluginFn, health_default as healthPlugin, healthPlugin as healthPluginFn, isTracingAvailable, requestId_default as requestIdPlugin, requestIdPlugin as requestIdPluginFn, traced, tracing_default as tracingPlugin };