@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,109 @@
1
+ import { t as __exportAll } from "./chunk-C7Uep-_p.mjs";
2
+ import { f as isArcError } from "./errors-B9bZok84.mjs";
3
+ import fp from "fastify-plugin";
4
+
5
+ //#region src/plugins/errorHandler.ts
6
+ var errorHandler_exports = /* @__PURE__ */ __exportAll({
7
+ default: () => errorHandlerPlugin,
8
+ errorHandlerPlugin: () => errorHandlerPlugin
9
+ });
10
+ async function errorHandlerPluginFn(fastify, options = {}) {
11
+ const isProduction = process.env.NODE_ENV === "production";
12
+ const { includeStack = !isProduction, onError, errorMap = {} } = options;
13
+ fastify.setErrorHandler(async (error, request, reply) => {
14
+ if (onError) try {
15
+ await onError(error, request);
16
+ } catch (callbackError) {
17
+ request.log.error({ err: callbackError }, "Error in onError callback");
18
+ }
19
+ const requestId = request.id;
20
+ const response = {
21
+ success: false,
22
+ error: error.message || "Internal Server Error",
23
+ code: "INTERNAL_ERROR",
24
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
25
+ ...requestId && { requestId }
26
+ };
27
+ let statusCode = 500;
28
+ if (isArcError(error)) {
29
+ statusCode = error.statusCode;
30
+ response.code = error.code;
31
+ if (error.details) response.details = error.details;
32
+ if (error.requestId) response.requestId = error.requestId;
33
+ if (error.cause) request.log.error({ cause: error.cause }, "Error cause chain");
34
+ } else if ("validation" in error && Array.isArray(error.validation)) {
35
+ statusCode = 400;
36
+ response.code = "VALIDATION_ERROR";
37
+ response.error = "Validation failed";
38
+ response.details = { errors: error.validation?.map((v) => ({
39
+ field: v.instancePath?.replace(/^\//, "") || v.params?.missingProperty || "unknown",
40
+ message: v.message || "Invalid value",
41
+ keyword: v.keyword
42
+ })) };
43
+ } else if ("statusCode" in error && typeof error.statusCode === "number") {
44
+ statusCode = error.statusCode;
45
+ response.code = statusCodeToCode(statusCode);
46
+ } else if (error.name && errorMap[error.name]) {
47
+ const mapping = errorMap[error.name];
48
+ statusCode = mapping.statusCode;
49
+ response.code = mapping.code;
50
+ if (mapping.message) response.error = mapping.message;
51
+ } else if (error.name === "ValidationError" && "errors" in error) {
52
+ statusCode = 400;
53
+ response.code = "VALIDATION_ERROR";
54
+ const mongooseErrors = error.errors;
55
+ if (includeStack) response.details = { errors: Object.entries(mongooseErrors).map(([field, err]) => ({
56
+ field: err.path || field,
57
+ message: err.message
58
+ })) };
59
+ else response.details = { errorCount: Object.keys(mongooseErrors).length };
60
+ } else if (error.name === "CastError") {
61
+ statusCode = 400;
62
+ response.code = "INVALID_ID";
63
+ response.error = "Invalid identifier format";
64
+ } else if (error.name === "MongoServerError" && error.code === 11e3) {
65
+ statusCode = 409;
66
+ response.code = "DUPLICATE_KEY";
67
+ response.error = "Resource already exists";
68
+ const keyValue = error.keyValue;
69
+ if (keyValue && includeStack) response.details = { duplicateFields: Object.keys(keyValue) };
70
+ }
71
+ if (includeStack && error.stack) response.stack = error.stack;
72
+ if (statusCode >= 500) request.log.error({
73
+ err: error,
74
+ statusCode
75
+ }, "Server error");
76
+ else if (statusCode >= 400) request.log.warn({
77
+ err: error,
78
+ statusCode
79
+ }, "Client error");
80
+ return reply.status(statusCode).send(response);
81
+ });
82
+ }
83
+ /**
84
+ * Map HTTP status code to error code
85
+ */
86
+ function statusCodeToCode(statusCode) {
87
+ return {
88
+ 400: "BAD_REQUEST",
89
+ 401: "UNAUTHORIZED",
90
+ 403: "FORBIDDEN",
91
+ 404: "NOT_FOUND",
92
+ 405: "METHOD_NOT_ALLOWED",
93
+ 409: "CONFLICT",
94
+ 422: "UNPROCESSABLE_ENTITY",
95
+ 429: "RATE_LIMITED",
96
+ 500: "INTERNAL_ERROR",
97
+ 502: "BAD_GATEWAY",
98
+ 503: "SERVICE_UNAVAILABLE",
99
+ 504: "GATEWAY_TIMEOUT"
100
+ }[statusCode] ?? "ERROR";
101
+ }
102
+ const errorHandlerPlugin = fp(errorHandlerPluginFn, {
103
+ name: "arc-error-handler",
104
+ fastify: "5.x"
105
+ });
106
+
107
+ //#endregion
108
+ export { errorHandler_exports as n, errorHandlerPlugin as t };
109
+ //# sourceMappingURL=errorHandler-C1okiriz.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errorHandler-C1okiriz.mjs","names":[],"sources":["../src/plugins/errorHandler.ts"],"sourcesContent":["/**\n * Error Handler Plugin\n *\n * Global error handling for Arc applications.\n * Catches all errors and returns a consistent JSON response.\n *\n * @example\n * import { errorHandlerPlugin } from '@classytic/arc/plugins';\n *\n * await fastify.register(errorHandlerPlugin, {\n * includeStack: process.env.NODE_ENV !== 'production',\n * onError: (error, request) => {\n * // Log to external service (Sentry, etc.)\n * Sentry.captureException(error);\n * }\n * });\n */\n\nimport type { FastifyInstance, FastifyRequest, FastifyReply, FastifyError } from 'fastify';\nimport fp from 'fastify-plugin';\nimport { ArcError, isArcError, ValidationError } from '../utils/errors.js';\n\nexport interface ErrorHandlerOptions {\n /**\n * Include stack trace in error responses (default: false in production)\n */\n includeStack?: boolean;\n\n /**\n * Custom error callback for logging to external services\n */\n onError?: (error: Error, request: FastifyRequest) => void | Promise<void>;\n\n /**\n * Map specific error types to custom responses\n */\n errorMap?: Record<string, {\n statusCode: number;\n code: string;\n message?: string;\n }>;\n}\n\ninterface ErrorResponse {\n success: false;\n error: string;\n code: string;\n timestamp: string;\n requestId?: string;\n details?: Record<string, unknown>;\n stack?: string;\n}\n\nasync function errorHandlerPluginFn(\n fastify: FastifyInstance,\n options: ErrorHandlerOptions = {}\n): Promise<void> {\n const isProduction = process.env.NODE_ENV === 'production';\n const {\n includeStack = !isProduction,\n onError,\n errorMap = {},\n } = options;\n\n fastify.setErrorHandler(async (error: FastifyError | Error, request: FastifyRequest, reply: FastifyReply) => {\n // Call custom error handler if provided\n if (onError) {\n try {\n await onError(error, request);\n } catch (callbackError) {\n request.log.error({ err: callbackError }, 'Error in onError callback');\n }\n }\n\n // Get request ID if available\n const requestId = (request as { id?: string }).id;\n\n // Build base response\n const response: ErrorResponse = {\n success: false,\n error: error.message || 'Internal Server Error',\n code: 'INTERNAL_ERROR',\n timestamp: new Date().toISOString(),\n ...(requestId && { requestId }),\n };\n\n let statusCode = 500;\n\n // Handle ArcError (our error classes)\n if (isArcError(error)) {\n statusCode = error.statusCode;\n response.code = error.code;\n if (error.details) {\n response.details = error.details;\n }\n if (error.requestId) {\n response.requestId = error.requestId;\n }\n // Log cause chain for debugging (cause is now properly serialized via toJSON)\n if (error.cause) {\n request.log.error({ cause: error.cause }, 'Error cause chain');\n }\n }\n // Handle Fastify validation errors\n else if ('validation' in error && Array.isArray((error as FastifyError).validation)) {\n statusCode = 400;\n response.code = 'VALIDATION_ERROR';\n response.error = 'Validation failed';\n response.details = {\n errors: (error as FastifyError).validation?.map((v) => ({\n field: v.instancePath?.replace(/^\\//, '') || v.params?.missingProperty || 'unknown',\n message: v.message || 'Invalid value',\n keyword: v.keyword,\n })),\n };\n }\n // Handle Fastify errors with statusCode\n else if ('statusCode' in error && typeof (error as FastifyError).statusCode === 'number') {\n statusCode = (error as FastifyError).statusCode!;\n response.code = statusCodeToCode(statusCode);\n }\n // Handle mapped error types\n else if (error.name && errorMap[error.name]) {\n const mapping = errorMap[error.name]!;\n statusCode = mapping.statusCode;\n response.code = mapping.code;\n if (mapping.message) {\n response.error = mapping.message;\n }\n }\n // Handle Mongoose validation errors\n else if (error.name === 'ValidationError' && 'errors' in error) {\n statusCode = 400;\n response.code = 'VALIDATION_ERROR';\n const mongooseErrors = (error as { errors: Record<string, { message: string; path: string }> }).errors;\n\n // Security: Don't expose schema field names when details are hidden\n if (includeStack) {\n response.details = {\n errors: Object.entries(mongooseErrors).map(([field, err]) => ({\n field: err.path || field,\n message: err.message,\n })),\n };\n } else {\n response.details = { errorCount: Object.keys(mongooseErrors).length };\n }\n }\n // Handle Mongoose CastError (invalid ObjectId, etc.)\n else if (error.name === 'CastError') {\n statusCode = 400;\n response.code = 'INVALID_ID';\n response.error = 'Invalid identifier format';\n }\n // Handle duplicate key errors (MongoDB)\n else if (error.name === 'MongoServerError' && (error as { code?: number }).code === 11000) {\n statusCode = 409;\n response.code = 'DUPLICATE_KEY';\n response.error = 'Resource already exists';\n const keyValue = (error as { keyValue?: Record<string, unknown> }).keyValue;\n\n // Security: Don't expose schema field names when details are hidden\n if (keyValue && includeStack) {\n response.details = { duplicateFields: Object.keys(keyValue) };\n }\n }\n\n // Include stack trace if enabled\n if (includeStack && error.stack) {\n response.stack = error.stack;\n }\n\n // Log server errors\n if (statusCode >= 500) {\n request.log.error({ err: error, statusCode }, 'Server error');\n } else if (statusCode >= 400) {\n request.log.warn({ err: error, statusCode }, 'Client error');\n }\n\n return reply.status(statusCode).send(response);\n });\n}\n\n/**\n * Map HTTP status code to error code\n */\nfunction statusCodeToCode(statusCode: number): string {\n const codes: Record<number, string> = {\n 400: 'BAD_REQUEST',\n 401: 'UNAUTHORIZED',\n 403: 'FORBIDDEN',\n 404: 'NOT_FOUND',\n 405: 'METHOD_NOT_ALLOWED',\n 409: 'CONFLICT',\n 422: 'UNPROCESSABLE_ENTITY',\n 429: 'RATE_LIMITED',\n 500: 'INTERNAL_ERROR',\n 502: 'BAD_GATEWAY',\n 503: 'SERVICE_UNAVAILABLE',\n 504: 'GATEWAY_TIMEOUT',\n };\n return codes[statusCode] ?? 'ERROR';\n}\n\nexport const errorHandlerPlugin = fp(errorHandlerPluginFn, {\n name: 'arc-error-handler',\n fastify: '5.x',\n});\n\nexport default errorHandlerPlugin;\n"],"mappings":"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"}
@@ -0,0 +1,212 @@
1
+ //#region src/utils/errors.ts
2
+ /**
3
+ * Base Arc Error
4
+ *
5
+ * All Arc errors extend this class and produce a consistent error envelope:
6
+ * {
7
+ * success: false,
8
+ * error: "Human-readable message",
9
+ * code: "MACHINE_CODE",
10
+ * requestId: "uuid", // For tracing
11
+ * timestamp: "ISO date", // When error occurred
12
+ * details: { ... } // Additional context
13
+ * }
14
+ */
15
+ var ArcError = class ArcError extends Error {
16
+ name;
17
+ code;
18
+ statusCode;
19
+ details;
20
+ cause;
21
+ timestamp;
22
+ requestId;
23
+ constructor(message, options = {}) {
24
+ super(message, options.cause ? { cause: options.cause } : void 0);
25
+ this.name = "ArcError";
26
+ this.code = options.code ?? "ARC_ERROR";
27
+ this.statusCode = options.statusCode ?? 500;
28
+ this.details = options.details;
29
+ this.cause = options.cause;
30
+ this.timestamp = (/* @__PURE__ */ new Date()).toISOString();
31
+ this.requestId = options.requestId;
32
+ if (Error.captureStackTrace) Error.captureStackTrace(this, this.constructor);
33
+ }
34
+ /**
35
+ * Set request ID (typically from request context)
36
+ */
37
+ withRequestId(requestId) {
38
+ this.requestId = requestId;
39
+ return this;
40
+ }
41
+ /**
42
+ * Convert to JSON response.
43
+ * Includes cause chain when present for debugging visibility.
44
+ */
45
+ toJSON() {
46
+ return {
47
+ success: false,
48
+ error: this.message,
49
+ code: this.code,
50
+ timestamp: this.timestamp,
51
+ ...this.requestId && { requestId: this.requestId },
52
+ ...this.details && { details: this.details },
53
+ ...this.cause && { cause: this.cause instanceof ArcError ? this.cause.toJSON() : {
54
+ message: this.cause.message,
55
+ name: this.cause.name
56
+ } }
57
+ };
58
+ }
59
+ };
60
+ /**
61
+ * Not Found Error - 404
62
+ */
63
+ var NotFoundError = class extends ArcError {
64
+ constructor(resource, identifier) {
65
+ const message = identifier ? `${resource} with identifier '${identifier}' not found` : `${resource} not found`;
66
+ super(message, {
67
+ code: "NOT_FOUND",
68
+ statusCode: 404,
69
+ details: {
70
+ resource,
71
+ identifier
72
+ }
73
+ });
74
+ this.name = "NotFoundError";
75
+ }
76
+ };
77
+ /**
78
+ * Validation Error - 400
79
+ */
80
+ var ValidationError = class extends ArcError {
81
+ errors;
82
+ constructor(message, errors = []) {
83
+ super(message, {
84
+ code: "VALIDATION_ERROR",
85
+ statusCode: 400,
86
+ details: { errors }
87
+ });
88
+ this.name = "ValidationError";
89
+ this.errors = errors;
90
+ }
91
+ };
92
+ /**
93
+ * Unauthorized Error - 401
94
+ */
95
+ var UnauthorizedError = class extends ArcError {
96
+ constructor(message = "Authentication required") {
97
+ super(message, {
98
+ code: "UNAUTHORIZED",
99
+ statusCode: 401
100
+ });
101
+ this.name = "UnauthorizedError";
102
+ }
103
+ };
104
+ /**
105
+ * Forbidden Error - 403
106
+ */
107
+ var ForbiddenError = class extends ArcError {
108
+ constructor(message = "Access denied") {
109
+ super(message, {
110
+ code: "FORBIDDEN",
111
+ statusCode: 403
112
+ });
113
+ this.name = "ForbiddenError";
114
+ }
115
+ };
116
+ /**
117
+ * Conflict Error - 409
118
+ */
119
+ var ConflictError = class extends ArcError {
120
+ constructor(message, field) {
121
+ super(message, {
122
+ code: "CONFLICT",
123
+ statusCode: 409,
124
+ details: field ? { field } : void 0
125
+ });
126
+ this.name = "ConflictError";
127
+ }
128
+ };
129
+ /**
130
+ * Organization Required Error - 403
131
+ */
132
+ var OrgRequiredError = class extends ArcError {
133
+ organizations;
134
+ constructor(message, organizations) {
135
+ super(message, {
136
+ code: "ORG_SELECTION_REQUIRED",
137
+ statusCode: 403,
138
+ details: organizations ? { organizations } : void 0
139
+ });
140
+ this.name = "OrgRequiredError";
141
+ this.organizations = organizations;
142
+ }
143
+ };
144
+ /**
145
+ * Organization Access Denied Error - 403
146
+ */
147
+ var OrgAccessDeniedError = class extends ArcError {
148
+ constructor(orgId) {
149
+ super("Organization access denied", {
150
+ code: "ORG_ACCESS_DENIED",
151
+ statusCode: 403,
152
+ details: orgId ? { organizationId: orgId } : void 0
153
+ });
154
+ this.name = "OrgAccessDeniedError";
155
+ }
156
+ };
157
+ /**
158
+ * Rate Limit Error - 429
159
+ */
160
+ var RateLimitError = class extends ArcError {
161
+ retryAfter;
162
+ constructor(message = "Too many requests", retryAfter) {
163
+ super(message, {
164
+ code: "RATE_LIMITED",
165
+ statusCode: 429,
166
+ details: retryAfter ? { retryAfter } : void 0
167
+ });
168
+ this.name = "RateLimitError";
169
+ this.retryAfter = retryAfter;
170
+ }
171
+ };
172
+ /**
173
+ * Service Unavailable Error - 503
174
+ */
175
+ var ServiceUnavailableError = class extends ArcError {
176
+ constructor(message = "Service temporarily unavailable") {
177
+ super(message, {
178
+ code: "SERVICE_UNAVAILABLE",
179
+ statusCode: 503
180
+ });
181
+ this.name = "ServiceUnavailableError";
182
+ }
183
+ };
184
+ /**
185
+ * Create error from status code
186
+ */
187
+ function createError(statusCode, message, details) {
188
+ return new ArcError(message, {
189
+ code: {
190
+ 400: "BAD_REQUEST",
191
+ 401: "UNAUTHORIZED",
192
+ 403: "FORBIDDEN",
193
+ 404: "NOT_FOUND",
194
+ 409: "CONFLICT",
195
+ 429: "RATE_LIMITED",
196
+ 500: "INTERNAL_ERROR",
197
+ 503: "SERVICE_UNAVAILABLE"
198
+ }[statusCode] ?? "ERROR",
199
+ statusCode,
200
+ details
201
+ });
202
+ }
203
+ /**
204
+ * Check if error is an Arc error
205
+ */
206
+ function isArcError(error) {
207
+ return error instanceof ArcError;
208
+ }
209
+
210
+ //#endregion
211
+ export { OrgAccessDeniedError as a, ServiceUnavailableError as c, createError as d, isArcError as f, NotFoundError as i, UnauthorizedError as l, ConflictError as n, OrgRequiredError as o, ForbiddenError as r, RateLimitError as s, ArcError as t, ValidationError as u };
212
+ //# sourceMappingURL=errors-B9bZok84.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors-B9bZok84.mjs","names":[],"sources":["../src/utils/errors.ts"],"sourcesContent":["/**\n * Error Classes\n *\n * Standard error types for the Arc framework.\n */\n\nexport interface ErrorDetails {\n code?: string;\n statusCode?: number;\n details?: Record<string, unknown>;\n cause?: Error;\n requestId?: string;\n}\n\n/**\n * Base Arc Error\n *\n * All Arc errors extend this class and produce a consistent error envelope:\n * {\n * success: false,\n * error: \"Human-readable message\",\n * code: \"MACHINE_CODE\",\n * requestId: \"uuid\", // For tracing\n * timestamp: \"ISO date\", // When error occurred\n * details: { ... } // Additional context\n * }\n */\nexport class ArcError extends Error {\n override name: string;\n readonly code: string;\n readonly statusCode: number;\n readonly details?: Record<string, unknown>;\n override readonly cause?: Error;\n readonly timestamp: string;\n requestId?: string;\n\n constructor(message: string, options: ErrorDetails = {}) {\n // Pass cause to native Error for proper chain support (Node 16.9+)\n super(message, options.cause ? { cause: options.cause } : undefined);\n this.name = 'ArcError';\n this.code = options.code ?? 'ARC_ERROR';\n this.statusCode = options.statusCode ?? 500;\n this.details = options.details;\n // cause is now set by super() — keep explicit assignment for TypeScript override\n this.cause = options.cause;\n this.timestamp = new Date().toISOString();\n this.requestId = options.requestId;\n\n // Maintain proper stack trace\n if (Error.captureStackTrace) {\n Error.captureStackTrace(this, this.constructor);\n }\n }\n\n /**\n * Set request ID (typically from request context)\n */\n withRequestId(requestId: string): this {\n this.requestId = requestId;\n return this;\n }\n\n /**\n * Convert to JSON response.\n * Includes cause chain when present for debugging visibility.\n */\n toJSON(): Record<string, unknown> {\n return {\n success: false,\n error: this.message,\n code: this.code,\n timestamp: this.timestamp,\n ...(this.requestId && { requestId: this.requestId }),\n ...(this.details && { details: this.details }),\n ...(this.cause && {\n cause: this.cause instanceof ArcError\n ? this.cause.toJSON()\n : { message: (this.cause as Error).message, name: (this.cause as Error).name },\n }),\n };\n }\n}\n\n/**\n * Not Found Error - 404\n */\nexport class NotFoundError extends ArcError {\n constructor(resource: string, identifier?: string) {\n const message = identifier\n ? `${resource} with identifier '${identifier}' not found`\n : `${resource} not found`;\n\n super(message, {\n code: 'NOT_FOUND',\n statusCode: 404,\n details: { resource, identifier },\n });\n this.name = 'NotFoundError';\n }\n}\n\n/**\n * Validation Error - 400\n */\nexport class ValidationError extends ArcError {\n readonly errors: Array<{ field: string; message: string }>;\n\n constructor(\n message: string,\n errors: Array<{ field: string; message: string }> = []\n ) {\n super(message, {\n code: 'VALIDATION_ERROR',\n statusCode: 400,\n details: { errors },\n });\n this.name = 'ValidationError';\n this.errors = errors;\n }\n}\n\n/**\n * Unauthorized Error - 401\n */\nexport class UnauthorizedError extends ArcError {\n constructor(message = 'Authentication required') {\n super(message, {\n code: 'UNAUTHORIZED',\n statusCode: 401,\n });\n this.name = 'UnauthorizedError';\n }\n}\n\n/**\n * Forbidden Error - 403\n */\nexport class ForbiddenError extends ArcError {\n constructor(message = 'Access denied') {\n super(message, {\n code: 'FORBIDDEN',\n statusCode: 403,\n });\n this.name = 'ForbiddenError';\n }\n}\n\n/**\n * Conflict Error - 409\n */\nexport class ConflictError extends ArcError {\n constructor(message: string, field?: string) {\n super(message, {\n code: 'CONFLICT',\n statusCode: 409,\n details: field ? { field } : undefined,\n });\n this.name = 'ConflictError';\n }\n}\n\n/**\n * Organization Required Error - 403\n */\nexport class OrgRequiredError extends ArcError {\n readonly organizations?: Array<{ id: string; roles?: string[] }>;\n\n constructor(\n message: string,\n organizations?: Array<{ id: string; roles?: string[] }>\n ) {\n super(message, {\n code: 'ORG_SELECTION_REQUIRED',\n statusCode: 403,\n details: organizations ? { organizations } : undefined,\n });\n this.name = 'OrgRequiredError';\n this.organizations = organizations;\n }\n}\n\n/**\n * Organization Access Denied Error - 403\n */\nexport class OrgAccessDeniedError extends ArcError {\n constructor(orgId?: string) {\n super('Organization access denied', {\n code: 'ORG_ACCESS_DENIED',\n statusCode: 403,\n details: orgId ? { organizationId: orgId } : undefined,\n });\n this.name = 'OrgAccessDeniedError';\n }\n}\n\n/**\n * Rate Limit Error - 429\n */\nexport class RateLimitError extends ArcError {\n readonly retryAfter?: number;\n\n constructor(message = 'Too many requests', retryAfter?: number) {\n super(message, {\n code: 'RATE_LIMITED',\n statusCode: 429,\n details: retryAfter ? { retryAfter } : undefined,\n });\n this.name = 'RateLimitError';\n this.retryAfter = retryAfter;\n }\n}\n\n/**\n * Service Unavailable Error - 503\n */\nexport class ServiceUnavailableError extends ArcError {\n constructor(message = 'Service temporarily unavailable') {\n super(message, {\n code: 'SERVICE_UNAVAILABLE',\n statusCode: 503,\n });\n this.name = 'ServiceUnavailableError';\n }\n}\n\n/**\n * Create error from status code\n */\nexport function createError(\n statusCode: number,\n message: string,\n details?: Record<string, unknown>\n): ArcError {\n const codes: Record<number, string> = {\n 400: 'BAD_REQUEST',\n 401: 'UNAUTHORIZED',\n 403: 'FORBIDDEN',\n 404: 'NOT_FOUND',\n 409: 'CONFLICT',\n 429: 'RATE_LIMITED',\n 500: 'INTERNAL_ERROR',\n 503: 'SERVICE_UNAVAILABLE',\n };\n\n return new ArcError(message, {\n code: codes[statusCode] ?? 'ERROR',\n statusCode,\n details,\n });\n}\n\n/**\n * Check if error is an Arc error\n */\nexport function isArcError(error: unknown): error is ArcError {\n return error instanceof ArcError;\n}\n"],"mappings":"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"}
@@ -0,0 +1,125 @@
1
+ //#region src/utils/errors.d.ts
2
+ /**
3
+ * Error Classes
4
+ *
5
+ * Standard error types for the Arc framework.
6
+ */
7
+ interface ErrorDetails {
8
+ code?: string;
9
+ statusCode?: number;
10
+ details?: Record<string, unknown>;
11
+ cause?: Error;
12
+ requestId?: string;
13
+ }
14
+ /**
15
+ * Base Arc Error
16
+ *
17
+ * All Arc errors extend this class and produce a consistent error envelope:
18
+ * {
19
+ * success: false,
20
+ * error: "Human-readable message",
21
+ * code: "MACHINE_CODE",
22
+ * requestId: "uuid", // For tracing
23
+ * timestamp: "ISO date", // When error occurred
24
+ * details: { ... } // Additional context
25
+ * }
26
+ */
27
+ declare class ArcError extends Error {
28
+ name: string;
29
+ readonly code: string;
30
+ readonly statusCode: number;
31
+ readonly details?: Record<string, unknown>;
32
+ readonly cause?: Error;
33
+ readonly timestamp: string;
34
+ requestId?: string;
35
+ constructor(message: string, options?: ErrorDetails);
36
+ /**
37
+ * Set request ID (typically from request context)
38
+ */
39
+ withRequestId(requestId: string): this;
40
+ /**
41
+ * Convert to JSON response.
42
+ * Includes cause chain when present for debugging visibility.
43
+ */
44
+ toJSON(): Record<string, unknown>;
45
+ }
46
+ /**
47
+ * Not Found Error - 404
48
+ */
49
+ declare class NotFoundError extends ArcError {
50
+ constructor(resource: string, identifier?: string);
51
+ }
52
+ /**
53
+ * Validation Error - 400
54
+ */
55
+ declare class ValidationError extends ArcError {
56
+ readonly errors: Array<{
57
+ field: string;
58
+ message: string;
59
+ }>;
60
+ constructor(message: string, errors?: Array<{
61
+ field: string;
62
+ message: string;
63
+ }>);
64
+ }
65
+ /**
66
+ * Unauthorized Error - 401
67
+ */
68
+ declare class UnauthorizedError extends ArcError {
69
+ constructor(message?: string);
70
+ }
71
+ /**
72
+ * Forbidden Error - 403
73
+ */
74
+ declare class ForbiddenError extends ArcError {
75
+ constructor(message?: string);
76
+ }
77
+ /**
78
+ * Conflict Error - 409
79
+ */
80
+ declare class ConflictError extends ArcError {
81
+ constructor(message: string, field?: string);
82
+ }
83
+ /**
84
+ * Organization Required Error - 403
85
+ */
86
+ declare class OrgRequiredError extends ArcError {
87
+ readonly organizations?: Array<{
88
+ id: string;
89
+ roles?: string[];
90
+ }>;
91
+ constructor(message: string, organizations?: Array<{
92
+ id: string;
93
+ roles?: string[];
94
+ }>);
95
+ }
96
+ /**
97
+ * Organization Access Denied Error - 403
98
+ */
99
+ declare class OrgAccessDeniedError extends ArcError {
100
+ constructor(orgId?: string);
101
+ }
102
+ /**
103
+ * Rate Limit Error - 429
104
+ */
105
+ declare class RateLimitError extends ArcError {
106
+ readonly retryAfter?: number;
107
+ constructor(message?: string, retryAfter?: number);
108
+ }
109
+ /**
110
+ * Service Unavailable Error - 503
111
+ */
112
+ declare class ServiceUnavailableError extends ArcError {
113
+ constructor(message?: string);
114
+ }
115
+ /**
116
+ * Create error from status code
117
+ */
118
+ declare function createError(statusCode: number, message: string, details?: Record<string, unknown>): ArcError;
119
+ /**
120
+ * Check if error is an Arc error
121
+ */
122
+ declare function isArcError(error: unknown): error is ArcError;
123
+ //#endregion
124
+ export { NotFoundError as a, RateLimitError as c, ValidationError as d, createError as f, ForbiddenError as i, ServiceUnavailableError as l, ConflictError as n, OrgAccessDeniedError as o, isArcError as p, ErrorDetails as r, OrgRequiredError as s, ArcError as t, UnauthorizedError as u };
125
+ //# sourceMappingURL=errors-ChKiFz62.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors-ChKiFz62.d.mts","names":[],"sources":["../src/utils/errors.ts"],"mappings":";;AAMA;;;;UAAiB,YAAA;EACf,IAAA;EACA,UAAA;EACA,OAAA,GAAU,MAAA;EACV,KAAA,GAAQ,KAAA;EACR,SAAA;AAAA;;;AAgBF;;;;;;;;;;;cAAa,QAAA,SAAiB,KAAA;EACnB,IAAA;EAAA,SACA,IAAA;EAAA,SACA,UAAA;EAAA,SACA,OAAA,GAAU,MAAA;EAAA,SACD,KAAA,GAAQ,KAAA;EAAA,SACjB,SAAA;EACT,SAAA;cAEY,OAAA,UAAiB,OAAA,GAAS,YAAA;;;;EAqBtC,aAAA,CAAc,SAAA;EAAd;;;;EASA,MAAA,CAAA,GAAU,MAAA;AAAA;AAoBZ;;;AAAA,cAAa,aAAA,SAAsB,QAAA;cACrB,QAAA,UAAkB,UAAA;AAAA;;;;cAiBnB,eAAA,SAAwB,QAAA;EAAA,SAC1B,MAAA,EAAQ,KAAA;IAAQ,KAAA;IAAe,OAAA;EAAA;cAGtC,OAAA,UACA,MAAA,GAAQ,KAAA;IAAQ,KAAA;IAAe,OAAA;EAAA;AAAA;;;;cAetB,iBAAA,SAA0B,QAAA;cACzB,OAAA;AAAA;;;;cAYD,cAAA,SAAuB,QAAA;cACtB,OAAA;AAAA;AAdd;;;AAAA,cA0Ba,aAAA,SAAsB,QAAA;cACrB,OAAA,UAAiB,KAAA;AAAA;;;;cAalB,gBAAA,SAAyB,QAAA;EAAA,SAC3B,aAAA,GAAgB,KAAA;IAAQ,EAAA;IAAY,KAAA;EAAA;cAG3C,OAAA,UACA,aAAA,GAAgB,KAAA;IAAQ,EAAA;IAAY,KAAA;EAAA;AAAA;;;;cAe3B,oBAAA,SAA6B,QAAA;cAC5B,KAAA;AAAA;;;;cAaD,cAAA,SAAuB,QAAA;EAAA,SACzB,UAAA;cAEG,OAAA,WAA+B,UAAA;AAAA;;;;cAchC,uBAAA,SAAgC,QAAA;cAC/B,OAAA;AAAA;;;;iBAYE,WAAA,CACd,UAAA,UACA,OAAA,UACA,OAAA,GAAU,MAAA,oBACT,QAAA;;;;iBAsBa,UAAA,CAAW,KAAA,YAAiB,KAAA,IAAS,QAAA"}
@@ -0,0 +1,125 @@
1
+ import { i as EventTransport, n as EventHandler, r as EventLogger, t as DomainEvent } from "./EventTransport-BD2U0BTc.mjs";
2
+ import { FastifyPluginAsync } from "fastify";
3
+
4
+ //#region src/events/retry.d.ts
5
+ interface RetryOptions {
6
+ /**
7
+ * Max retry attempts (not counting the initial attempt).
8
+ * @default 3
9
+ */
10
+ maxRetries?: number;
11
+ /**
12
+ * Initial backoff delay in ms. Doubles on each retry (exponential backoff).
13
+ * @default 1000
14
+ */
15
+ backoffMs?: number;
16
+ /**
17
+ * Maximum backoff delay in ms (caps exponential growth).
18
+ * @default 30000
19
+ */
20
+ maxBackoffMs?: number;
21
+ /**
22
+ * Jitter factor (0-1). Adds randomness to prevent thundering herd.
23
+ * 0 = no jitter, 1 = full jitter (delay ∈ [0, calculated]).
24
+ * @default 0.1
25
+ */
26
+ jitter?: number;
27
+ /**
28
+ * Callback when all retries are exhausted. The event is "dead".
29
+ * Use this to publish to a `$deadLetter` channel, log, alert, etc.
30
+ */
31
+ onDead?: (event: DomainEvent, errors: Error[]) => void | Promise<void>;
32
+ /**
33
+ * Optional name for logging/debugging.
34
+ */
35
+ name?: string;
36
+ /**
37
+ * Logger for retry warnings and error messages (default: console).
38
+ * Pass `fastify.log` to integrate with your application logger.
39
+ */
40
+ logger?: EventLogger;
41
+ }
42
+ /**
43
+ * Wrap an event handler with retry logic and dead letter support.
44
+ *
45
+ * On failure, retries with exponential backoff (with jitter).
46
+ * After all retries exhausted, calls `onDead` callback if provided.
47
+ */
48
+ declare function withRetry(handler: EventHandler, options?: RetryOptions): EventHandler;
49
+ /**
50
+ * Create a dead letter publisher that sends failed events to a `$deadLetter` channel.
51
+ *
52
+ * @example
53
+ * ```typescript
54
+ * import { withRetry, createDeadLetterPublisher } from '@classytic/arc/events';
55
+ *
56
+ * const toDlq = createDeadLetterPublisher(fastify.events);
57
+ *
58
+ * await fastify.events.subscribe('order.created', withRetry(handler, {
59
+ * maxRetries: 3,
60
+ * onDead: toDlq,
61
+ * }));
62
+ *
63
+ * // Monitor dead letters
64
+ * await fastify.events.subscribe('$deadLetter', async (event) => {
65
+ * console.error('Dead letter:', event.payload);
66
+ * await alertOps(event.payload);
67
+ * });
68
+ * ```
69
+ */
70
+ declare function createDeadLetterPublisher(events: {
71
+ publish: <T>(type: string, payload: T, meta?: Record<string, unknown>) => Promise<void>;
72
+ }): (event: DomainEvent, errors: Error[]) => Promise<void>;
73
+ //#endregion
74
+ //#region src/events/eventPlugin.d.ts
75
+ interface EventPluginOptions {
76
+ /** Event transport (default: MemoryEventTransport) */
77
+ transport?: EventTransport;
78
+ /** Enable event logging (default: false) */
79
+ logEvents?: boolean;
80
+ /**
81
+ * Fail-open mode for runtime resilience (default: true).
82
+ * - true: publish/subscribe/close errors are logged and suppressed
83
+ * - false: errors are thrown to caller
84
+ */
85
+ failOpen?: boolean;
86
+ /**
87
+ * Write-Ahead Log (WAL) configuration for at-least-once delivery guarantees.
88
+ * If provided, events will be saved to the WAL *before* passing to the transport.
89
+ * After a successful publish, they are acknowledged.
90
+ */
91
+ wal?: {
92
+ save: (event: DomainEvent) => Promise<void>;
93
+ acknowledge?: (eventId: string) => Promise<void>;
94
+ };
95
+ /**
96
+ * Auto-wrap all subscribed handlers with retry logic.
97
+ * When enabled, failed handler invocations are retried with exponential backoff.
98
+ */
99
+ retry?: Pick<RetryOptions, "maxRetries" | "backoffMs" | "maxBackoffMs" | "jitter">;
100
+ /**
101
+ * Dead letter queue for events that exhaust all retries.
102
+ * Requires `retry` to be enabled. If `retry` is set but no custom `store`,
103
+ * failed events are published to the `$deadLetter` event type by default.
104
+ */
105
+ deadLetterQueue?: {
106
+ /** Custom store function. If omitted, publishes to '$deadLetter' event type. */store?: (event: DomainEvent, errors: Error[]) => void | Promise<void>;
107
+ };
108
+ /** Callback after successful publish (for metrics/tracking) */
109
+ onPublish?: (event: DomainEvent) => void;
110
+ /** Callback on publish failure (for metrics/alerting) */
111
+ onPublishError?: (event: DomainEvent, error: Error) => void;
112
+ }
113
+ declare module "fastify" {
114
+ interface FastifyInstance {
115
+ events: {
116
+ /** Publish an event */publish: <T>(type: string, payload: T, meta?: Partial<DomainEvent["meta"]>) => Promise<void>; /** Subscribe to events */
117
+ subscribe: (pattern: string, handler: EventHandler) => Promise<() => void>; /** Get transport name */
118
+ transportName: string;
119
+ };
120
+ }
121
+ }
122
+ declare const eventPlugin: FastifyPluginAsync<EventPluginOptions>;
123
+ //#endregion
124
+ export { withRetry as a, createDeadLetterPublisher as i, eventPlugin as n, RetryOptions as r, EventPluginOptions as t };
125
+ //# sourceMappingURL=eventPlugin-CTrLH3mt.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"eventPlugin-CTrLH3mt.d.mts","names":[],"sources":["../src/events/retry.ts","../src/events/eventPlugin.ts"],"mappings":";;;;UAiCiB,YAAA;EAoDN;;;;EA/CT,UAAA;EA8CA;;;;EAxCA,SAAA;EA0Ca;AA6Ef;;;EAjHE,YAAA;EAkHwD;;;;;EA3GxD,MAAA;EA4GiD;;;;EAtGjD,MAAA,IAAU,KAAA,EAAO,WAAA,EAAa,MAAA,EAAQ,KAAA,cAAmB,OAAA;EAqGpB;;;EAhGrC,IAAA;EAgGA;;;;EA1FA,MAAA,GAAS,WAAA;AAAA;;;;;;AC1CX;iBDmDgB,SAAA,CACd,OAAA,EAAS,YAAA,EACT,OAAA,GAAS,YAAA,GACR,YAAA;;;;;;;;;;;;;;;;;;;;;;iBA6Ea,yBAAA,CACd,MAAA;EAAU,OAAA,MAAa,IAAA,UAAc,OAAA,EAAS,CAAA,EAAG,IAAA,GAAO,MAAA,sBAA4B,OAAA;AAAA,KAClF,KAAA,EAAO,WAAA,EAAa,MAAA,EAAQ,KAAA,OAAY,OAAA;;;UCrI3B,kBAAA;ED+BL;EC7BV,SAAA,GAAY,cAAA;ED6BkB;EC3B9B,SAAA;EDgCA;;;;;EC1BA,QAAA;EDyCuB;;;;;ECnCvB,GAAA;IACE,IAAA,GAAO,KAAA,EAAO,WAAA,KAAgB,OAAA;IAC9B,WAAA,IAAe,OAAA,aAAoB,OAAA;EAAA;EDmC5B;;;;EC7BT,KAAA,GAAQ,IAAA,CACN,YAAA;ED0GY;;;;;EClGd,eAAA;IDoGS,gFClGP,KAAA,IAAS,KAAA,EAAO,WAAA,EAAa,MAAA,EAAQ,KAAA,cAAmB,OAAA;EAAA;EDkGT;EC/FjD,SAAA,IAAa,KAAA,EAAO,WAAA;ED8FV;EC5FV,cAAA,IAAkB,KAAA,EAAO,WAAA,EAAa,KAAA,EAAO,KAAA;AAAA;AAAA;EAAA,UAInC,eAAA;IACR,MAAA;MDuF+C,uBCrF7C,OAAA,MACE,IAAA,UACA,OAAA,EAAS,CAAA,EACT,IAAA,GAAO,OAAA,CAAQ,WAAA,cACZ,OAAA,QDiFT;MC/EI,SAAA,GACE,OAAA,UACA,OAAA,EAAS,YAAA,KACN,OAAA,cD6EP;MC3EE,aAAA;IAAA;EAAA;AAAA;AAAA,cAKA,WAAA,EAAa,kBAAA,CAAmB,kBAAA"}