@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
package/dist/index.mjs ADDED
@@ -0,0 +1,105 @@
1
+ import { a as DEFAULT_SORT, c as HOOK_OPERATIONS, d as MAX_REGEX_LENGTH, f as MAX_SEARCH_LENGTH, h as SYSTEM_FIELDS, i as DEFAULT_MAX_LIMIT, l as HOOK_PHASES, m as RESERVED_QUERY_PARAMS, n as DEFAULT_ID_FIELD, o as DEFAULT_TENANT_FIELD, p as MUTATION_OPERATIONS, r as DEFAULT_LIMIT, s as DEFAULT_UPDATE_METHOD, t as CRUD_OPERATIONS, u as MAX_FILTER_DEPTH } from "./constants-DdXFXQtN.mjs";
2
+ import { a as createMongooseAdapter, i as MongooseAdapter, r as createPrismaAdapter, t as PrismaAdapter } from "./prisma-DJbMt3yf.mjs";
3
+ import { a as validateResourceConfig, g as BaseController, i as formatValidationErrors, l as pipe, m as getControllerScope, n as defineResource, r as assertValidConfig, t as ResourceDefinition } from "./defineResource-k0_BDn8v.mjs";
4
+ import { n as applyFieldWritePermissions, r as fields, t as applyFieldReadPermissions } from "./fields-iagOozy0.mjs";
5
+ import { i as NotFoundError, l as UnauthorizedError, r as ForbiddenError, t as ArcError, u as ValidationError } from "./errors-B9bZok84.mjs";
6
+ import { t as requestContext } from "./requestContext-QQD6ROJc.mjs";
7
+ import { a as presets_exports, c as readOnly, i as ownerWithAdminBypass, n as authenticated, o as publicRead, r as fullPublic, s as publicReadAdminWrite, t as adminOnly } from "./presets-BITljm96.mjs";
8
+ import { allOf, allowPublic, anyOf, createDynamicPermissionMatrix, createOrgPermissions, denyAll, requireAuth, requireOrgMembership, requireOrgRole, requireOwnership, requireRoles, requireTeamMembership, when } from "./permissions/index.mjs";
9
+ import { n as configureArcLogger, t as arcLog } from "./logger-Df2O2WsW.mjs";
10
+
11
+ //#region src/pipeline/guard.ts
12
+ /**
13
+ * Create a named guard.
14
+ *
15
+ * @param name - Guard name (for debugging/introspection)
16
+ * @param handlerOrOptions - Handler function or options object
17
+ */
18
+ function guard(name, handlerOrOptions) {
19
+ const opts = typeof handlerOrOptions === "function" ? { handler: handlerOrOptions } : handlerOrOptions;
20
+ return {
21
+ _type: "guard",
22
+ name,
23
+ operations: opts.operations,
24
+ handler: opts.handler
25
+ };
26
+ }
27
+
28
+ //#endregion
29
+ //#region src/pipeline/transform.ts
30
+ /**
31
+ * Create a named transform.
32
+ *
33
+ * @param name - Transform name (for debugging/introspection)
34
+ * @param handlerOrOptions - Handler function or options object
35
+ */
36
+ function transform(name, handlerOrOptions) {
37
+ const opts = typeof handlerOrOptions === "function" ? { handler: handlerOrOptions } : handlerOrOptions;
38
+ return {
39
+ _type: "transform",
40
+ name,
41
+ operations: opts.operations,
42
+ handler: opts.handler
43
+ };
44
+ }
45
+
46
+ //#endregion
47
+ //#region src/pipeline/intercept.ts
48
+ /**
49
+ * Create a named interceptor.
50
+ *
51
+ * @param name - Interceptor name (for debugging/introspection)
52
+ * @param handlerOrOptions - Handler function or options object
53
+ */
54
+ function intercept(name, handlerOrOptions) {
55
+ const opts = typeof handlerOrOptions === "function" ? { handler: handlerOrOptions } : handlerOrOptions;
56
+ return {
57
+ _type: "interceptor",
58
+ name,
59
+ operations: opts.operations,
60
+ handler: opts.handler
61
+ };
62
+ }
63
+
64
+ //#endregion
65
+ //#region src/middleware/middleware.ts
66
+ /**
67
+ * Create a named middleware with priority and conditions.
68
+ */
69
+ function middleware(name, options) {
70
+ return {
71
+ name,
72
+ operations: options.operations,
73
+ priority: options.priority ?? 10,
74
+ when: options.when,
75
+ handler: options.handler
76
+ };
77
+ }
78
+ /**
79
+ * Sort named middlewares by priority (ascending — lower runs first).
80
+ * Returns a MiddlewareConfig map keyed by operation, ready to pass to `defineResource()`.
81
+ */
82
+ function sortMiddlewares(middlewares) {
83
+ const sorted = [...middlewares].sort((a, b) => a.priority - b.priority);
84
+ const operations = CRUD_OPERATIONS;
85
+ const result = {};
86
+ for (const op of operations) {
87
+ const applicable = sorted.filter((m) => !m.operations || m.operations.length === 0 || m.operations.includes(op));
88
+ if (applicable.length > 0) result[op] = applicable.map((m) => {
89
+ if (!m.when) return m.handler;
90
+ const wrapped = async (request, reply) => {
91
+ if (await m.when(request)) return m.handler(request, reply);
92
+ };
93
+ return wrapped;
94
+ });
95
+ }
96
+ return result;
97
+ }
98
+
99
+ //#endregion
100
+ //#region src/index.ts
101
+ const version = "__ARC_VERSION__";
102
+
103
+ //#endregion
104
+ export { ArcError, BaseController, CRUD_OPERATIONS, DEFAULT_ID_FIELD, DEFAULT_LIMIT, DEFAULT_MAX_LIMIT, DEFAULT_SORT, DEFAULT_TENANT_FIELD, DEFAULT_UPDATE_METHOD, ForbiddenError, HOOK_OPERATIONS, HOOK_PHASES, MAX_FILTER_DEPTH, MAX_REGEX_LENGTH, MAX_SEARCH_LENGTH, MUTATION_OPERATIONS, MongooseAdapter, NotFoundError, PrismaAdapter, RESERVED_QUERY_PARAMS, ResourceDefinition, SYSTEM_FIELDS, UnauthorizedError, ValidationError, adminOnly, allOf, allowPublic, anyOf, applyFieldReadPermissions, applyFieldWritePermissions, arcLog, assertValidConfig, authenticated, configureArcLogger, createDynamicPermissionMatrix, createMongooseAdapter, createOrgPermissions, createPrismaAdapter, defineResource, denyAll, fields, formatValidationErrors, fullPublic, getControllerScope, guard, intercept, middleware, ownerWithAdminBypass, presets_exports as permissions, pipe, publicRead, publicReadAdminWrite, readOnly, requestContext, requireAuth, requireOrgMembership, requireOrgRole, requireOwnership, requireRoles, requireTeamMembership, sortMiddlewares, transform, validateResourceConfig, version, when };
105
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.mjs","names":[],"sources":["../src/pipeline/guard.ts","../src/pipeline/transform.ts","../src/pipeline/intercept.ts","../src/middleware/middleware.ts","../src/index.ts"],"sourcesContent":["/**\n * guard() — Boolean check that short-circuits on failure.\n *\n * Guards run BEFORE transforms and the handler. If a guard fails (returns false\n * or throws), the request is rejected immediately.\n *\n * @example\n * ```typescript\n * import { guard } from '@classytic/arc';\n *\n * const isActive = guard('isActive', (ctx) => {\n * if (!ctx.user?.isActive) throw new ForbiddenError('Account suspended');\n * return true;\n * });\n *\n * // With operation filter\n * const requireBody = guard('requireBody', {\n * operations: ['create', 'update'],\n * handler: (ctx) => {\n * if (!ctx.body || Object.keys(ctx.body).length === 0) {\n * throw new ValidationError('Request body is required');\n * }\n * return true;\n * },\n * });\n * ```\n */\n\nimport type { PipelineContext, Guard, OperationFilter } from './types.js';\n\ninterface GuardOptions {\n operations?: OperationFilter;\n handler: (ctx: PipelineContext) => boolean | Promise<boolean>;\n}\n\n/**\n * Create a named guard.\n *\n * @param name - Guard name (for debugging/introspection)\n * @param handlerOrOptions - Handler function or options object\n */\nexport function guard(\n name: string,\n handlerOrOptions: ((ctx: PipelineContext) => boolean | Promise<boolean>) | GuardOptions,\n): Guard {\n const opts = typeof handlerOrOptions === 'function'\n ? { handler: handlerOrOptions }\n : handlerOrOptions;\n\n return {\n _type: 'guard' as const,\n name,\n operations: opts.operations,\n handler: opts.handler,\n };\n}\n","/**\n * transform() — Modifies request data before the handler.\n *\n * Transforms run AFTER guards but BEFORE the handler. They can modify\n * the request body, params, or context.\n *\n * @example\n * ```typescript\n * import { transform } from '@classytic/arc';\n *\n * const slugify = transform('slugify', {\n * operations: ['create'],\n * handler: (ctx) => {\n * if (ctx.body?.name && !ctx.body?.slug) {\n * ctx.body.slug = ctx.body.name.toLowerCase().replace(/\\s+/g, '-');\n * }\n * },\n * });\n *\n * const trimStrings = transform('trimStrings', (ctx) => {\n * if (ctx.body && typeof ctx.body === 'object') {\n * for (const [key, value] of Object.entries(ctx.body)) {\n * if (typeof value === 'string') {\n * (ctx.body as Record<string, unknown>)[key] = value.trim();\n * }\n * }\n * }\n * });\n * ```\n */\n\nimport type { PipelineContext, Transform, OperationFilter } from './types.js';\n\ninterface TransformOptions {\n operations?: OperationFilter;\n handler: (ctx: PipelineContext) => PipelineContext | void | Promise<PipelineContext | void>;\n}\n\n/**\n * Create a named transform.\n *\n * @param name - Transform name (for debugging/introspection)\n * @param handlerOrOptions - Handler function or options object\n */\nexport function transform(\n name: string,\n handlerOrOptions: ((ctx: PipelineContext) => PipelineContext | void | Promise<PipelineContext | void>) | TransformOptions,\n): Transform {\n const opts = typeof handlerOrOptions === 'function'\n ? { handler: handlerOrOptions }\n : handlerOrOptions;\n\n return {\n _type: 'transform' as const,\n name,\n operations: opts.operations,\n handler: opts.handler,\n };\n}\n","/**\n * intercept() — Wraps handler execution (before + after pattern).\n *\n * Interceptors wrap the handler like an onion — they can run code before\n * the handler, after the handler, modify the response, measure timing, etc.\n *\n * @example\n * ```typescript\n * import { intercept } from '@classytic/arc';\n *\n * const timing = intercept('timing', async (ctx, next) => {\n * const start = performance.now();\n * const result = await next();\n * result.meta = { ...result.meta, durationMs: Math.round(performance.now() - start) };\n * return result;\n * });\n *\n * const cache = intercept('cache', {\n * operations: ['list', 'get'],\n * handler: async (ctx, next) => {\n * const cached = await redis.get(cacheKey(ctx));\n * if (cached) return JSON.parse(cached);\n * const result = await next();\n * await redis.setex(cacheKey(ctx), 60, JSON.stringify(result));\n * return result;\n * },\n * });\n * ```\n */\n\nimport type { PipelineContext, NextFunction, Interceptor, OperationFilter } from './types.js';\nimport type { IControllerResponse } from '../types/index.js';\n\ninterface InterceptOptions {\n operations?: OperationFilter;\n handler: (ctx: PipelineContext, next: NextFunction) => Promise<IControllerResponse<unknown>>;\n}\n\n/**\n * Create a named interceptor.\n *\n * @param name - Interceptor name (for debugging/introspection)\n * @param handlerOrOptions - Handler function or options object\n */\nexport function intercept(\n name: string,\n handlerOrOptions: ((ctx: PipelineContext, next: NextFunction) => Promise<IControllerResponse<unknown>>) | InterceptOptions,\n): Interceptor {\n const opts = typeof handlerOrOptions === 'function'\n ? { handler: handlerOrOptions }\n : handlerOrOptions;\n\n return {\n _type: 'interceptor' as const,\n name,\n operations: opts.operations,\n handler: opts.handler,\n };\n}\n","/**\n * Named Middleware — Priority-based, conditional middleware execution.\n *\n * Named middleware replaces flat arrays with structured, inspectable middleware\n * that runs in priority order and supports conditional execution.\n *\n * @example\n * ```typescript\n * import { middleware } from '@classytic/arc';\n *\n * const verifyEmail = middleware('verifyEmail', {\n * operations: ['create', 'update'],\n * priority: 5,\n * when: (req) => !req.user?.emailVerified,\n * handler: async (req, reply) => {\n * reply.code(403).send({ error: 'Email verification required' });\n * },\n * });\n *\n * const rateLimit = middleware('rateLimit', {\n * priority: 1,\n * handler: async (req, reply) => {\n * // rate limit logic\n * },\n * });\n *\n * const productResource = defineResource({\n * name: 'product',\n * adapter,\n * middlewares: sortMiddlewares([verifyEmail, rateLimit]),\n * });\n * ```\n */\n\nimport type { MiddlewareConfig, MiddlewareHandler, RequestWithExtras } from '../types/index.js';\nimport { CRUD_OPERATIONS } from '../constants.js';\n\nexport interface NamedMiddleware {\n /** Unique name for debugging/introspection */\n readonly name: string;\n /** Operations this middleware applies to (default: all) */\n readonly operations?: Array<'list' | 'get' | 'create' | 'update' | 'delete' | string>;\n /** Priority — lower numbers run first (default: 10) */\n readonly priority: number;\n /** Conditional execution — return true to run, false to skip */\n readonly when?: (request: RequestWithExtras) => boolean | Promise<boolean>;\n /** The middleware handler */\n readonly handler: MiddlewareHandler;\n}\n\ninterface MiddlewareOptions {\n operations?: NamedMiddleware['operations'];\n priority?: number;\n when?: NamedMiddleware['when'];\n handler: MiddlewareHandler;\n}\n\n/**\n * Create a named middleware with priority and conditions.\n */\nexport function middleware(\n name: string,\n options: MiddlewareOptions,\n): NamedMiddleware {\n return {\n name,\n operations: options.operations,\n priority: options.priority ?? 10,\n when: options.when,\n handler: options.handler,\n };\n}\n\n/**\n * Sort named middlewares by priority (ascending — lower runs first).\n * Returns a MiddlewareConfig map keyed by operation, ready to pass to `defineResource()`.\n */\nexport function sortMiddlewares(middlewares: NamedMiddleware[]): MiddlewareConfig {\n const sorted = [...middlewares].sort((a, b) => a.priority - b.priority);\n\n const operations = CRUD_OPERATIONS;\n const result: MiddlewareConfig = {};\n\n for (const op of operations) {\n const applicable = sorted.filter(\n (m) => !m.operations || m.operations.length === 0 || m.operations.includes(op),\n );\n if (applicable.length > 0) {\n result[op] = applicable.map((m) => {\n if (!m.when) return m.handler;\n // Wrap with conditional check\n const wrapped: MiddlewareHandler = async (request, reply) => {\n const shouldRun = await m.when!(request);\n if (shouldRun) {\n return m.handler(request, reply);\n }\n };\n return wrapped;\n });\n }\n }\n\n return result;\n}\n","/**\n * @classytic/arc\n *\n * Resource-oriented backend framework for Fastify.\n * Production-ready MongoDB support is provided via MongoKit.\n * Prisma/PostgreSQL/MySQL/SQLite adapter support is available as experimental\n * bring-your-own-repository integration via Arc's DataAdapter interface.\n *\n * ## Import Strategy (Tree-Shaking)\n *\n * This main entry exports ONLY the essentials for defining resources.\n * All other features live in dedicated subpaths — Node.js does NOT\n * tree-shake, so barrel re-exports load eagerly at runtime.\n *\n * ```typescript\n * // Main entry — resource definition + permissions + errors\n * import { defineResource, createMongooseAdapter, allowPublic } from '@classytic/arc';\n *\n * // Everything else from dedicated subpaths:\n * import { createApp } from '@classytic/arc/factory';\n * import { createTestApp } from '@classytic/arc/testing';\n * import { eventPlugin } from '@classytic/arc/events';\n * import { beforeCreate } from '@classytic/arc/hooks';\n * import { healthPlugin } from '@classytic/arc/plugins';\n * import { RedisEventTransport } from '@classytic/arc/events/redis';\n * import { tracingPlugin } from '@classytic/arc/plugins/tracing';\n * import { MongoAuditStore } from '@classytic/arc/audit/mongodb';\n * ```\n *\n * ## Subpath Exports\n *\n * | Subpath | Purpose |\n * |---------|---------|\n * | `@classytic/arc` | Core: defineResource, adapters, permissions, errors |\n * | `@classytic/arc/factory` | App creation (createApp, ArcFactory) |\n * | `@classytic/arc/permissions` | Permission functions (also in main) |\n * | `@classytic/arc/adapters` | Database adapters + PrismaQueryParser |\n * | `@classytic/arc/presets` | Preset functions (softDelete, tree, etc.) |\n * | `@classytic/arc/hooks` | Hook helpers (beforeCreate, afterUpdate) |\n * | `@classytic/arc/events` | Event bus (MemoryTransport only) |\n * | `@classytic/arc/events/redis` | Redis Pub/Sub transport (requires ioredis) |\n * | `@classytic/arc/events/redis-stream` | Redis Streams transport (requires ioredis) |\n * | `@classytic/arc/plugins` | Fastify plugins (health, requestId, etc.) |\n * | `@classytic/arc/plugins/tracing` | OpenTelemetry tracing (requires @opentelemetry/*) |\n * | `@classytic/arc/audit` | Audit trail (MemoryStore only) |\n * | `@classytic/arc/audit/mongodb` | MongoDB audit store (requires mongoose) |\n * | `@classytic/arc/idempotency` | Idempotency (MemoryStore only) |\n * | `@classytic/arc/idempotency/redis` | Redis idempotency store (requires ioredis) |\n * | `@classytic/arc/idempotency/mongodb` | MongoDB idempotency store (requires mongoose) |\n * | `@classytic/arc/utils` | Utilities (errors, state machine, circuit breaker) |\n * | `@classytic/arc/org` | Organization/multi-tenant |\n * | `@classytic/arc/auth` | Authentication (JWT, Better Auth) |\n * | `@classytic/arc/testing` | Test utilities, mocks, TestHarness |\n * | `@classytic/arc/schemas` | TypeBox schema helpers |\n * | `@classytic/arc/types` | TypeScript types only |\n * | `@classytic/arc/discovery` | Auto-discovery plugin |\n * | `@classytic/arc/integrations/streamline` | @classytic/streamline adapter |\n * | `@classytic/arc/integrations/websocket` | @fastify/websocket adapter |\n * | `@classytic/arc/integrations/jobs` | BullMQ job queue adapter |\n *\n * @example Basic Resource\n * ```typescript\n * import { defineResource, createMongooseAdapter, allowPublic, requireRoles } from '@classytic/arc';\n *\n * const productResource = defineResource({\n * name: 'product',\n * adapter: createMongooseAdapter({ model: ProductModel, repository: productRepo }),\n * permissions: {\n * list: allowPublic(),\n * create: requireRoles(['admin']),\n * },\n * });\n * ```\n *\n * @example Full Application\n * ```typescript\n * import { createApp } from '@classytic/arc/factory';\n * import { productResource } from './modules/product.resource.js';\n *\n * const app = await createApp({\n * preset: 'production',\n * auth: { type: 'jwt', jwt: { secret: process.env.JWT_SECRET } },\n * plugins: async (fastify) => {\n * await fastify.register(productResource.toPlugin());\n * },\n * });\n * ```\n */\n\n// ============================================================================\n// Adapters (database abstraction — zero external deps at this level)\n// ============================================================================\nexport {\n MongooseAdapter,\n createMongooseAdapter,\n PrismaAdapter,\n createPrismaAdapter,\n} from \"./adapters/index.js\";\nexport type {\n DataAdapter,\n SchemaMetadata,\n FieldMetadata,\n RelationMetadata,\n ValidationResult as AdapterValidationResult,\n MongooseAdapterOptions,\n PrismaAdapterOptions,\n RepositoryLike,\n} from \"./adapters/index.js\";\n\n// ============================================================================\n// Core — defineResource, BaseController\n// ============================================================================\nexport {\n BaseController,\n defineResource,\n ResourceDefinition,\n getControllerScope,\n} from \"./core/index.js\";\nexport type { BaseControllerOptions } from \"./core/index.js\";\n\n/**\n * Note: Arc is database-agnostic\n *\n * Import Repository directly from your database kit:\n * - MongoDB: `import { Repository } from '@classytic/mongokit'`\n *\n * Arc provides adapters (createMongooseAdapter, createPrismaAdapter) that work\n * with any repository implementing the CrudRepository interface.\n */\n\n// ============================================================================\n// Types — re-export all types (zero runtime cost, eliminated at compile time)\n// ============================================================================\nexport type {\n // Base types\n AnyRecord,\n PaginatedResult,\n ApiResponse,\n // Framework-agnostic controller types (MongoKit-compatible)\n IRequestContext,\n IControllerResponse,\n IController,\n ControllerLike,\n // User & Auth\n UserBase,\n UserOrganization,\n JWTPayload,\n // Request context\n RequestContext,\n ArcInternalMetadata,\n OwnershipCheck,\n FastifyRequestExtras,\n RequestWithExtras,\n FastifyWithAuth,\n FastifyWithDecorators,\n RouteHandlerMethod,\n // Service & Repository\n ServiceContext,\n QueryOptions,\n CrudRepository,\n // Controller\n RouteHandler,\n CrudController,\n CrudRouteKey,\n // Schema\n RouteSchemaOptions,\n FieldRule,\n CrudSchemas,\n // Routes\n AdditionalRoute,\n MiddlewareConfig,\n // Presets\n PresetResult,\n PresetFunction,\n // Resource\n ResourceConfig,\n EventDefinition,\n ResourceMetadata,\n // Registry\n RegistryEntry,\n RegistryStats,\n IntrospectionData,\n // Plugin options\n AuthPluginOptions,\n IntrospectionPluginOptions,\n CrudRouterOptions,\n RateLimitConfig,\n ConfigError,\n ValidationResult,\n ValidateOptions,\n HealthCheck,\n HealthOptions,\n GracefulShutdownOptions,\n RequestIdOptions,\n // Utility types for better type inference\n InferDocType,\n InferAdapterDoc,\n InferResourceDoc,\n TypedResourceConfig,\n TypedController,\n TypedRepository,\n} from \"./types/index.js\";\n\n// ============================================================================\n// Constants — single source of truth for defaults and magic values (zero deps)\n// ============================================================================\nexport * from \"./constants.js\";\n\n// ============================================================================\n// Errors — commonly needed alongside defineResource (zero deps, pure classes)\n// ============================================================================\nexport {\n ArcError,\n NotFoundError,\n ValidationError,\n UnauthorizedError,\n ForbiddenError,\n} from \"./utils/errors.js\";\n\n// ============================================================================\n// Validation — resource config validation (zero deps, pure functions)\n// ============================================================================\nexport {\n validateResourceConfig,\n formatValidationErrors,\n assertValidConfig,\n} from \"./core/validateResourceConfig.js\";\n\n// ============================================================================\n// Permission System — commonly used with defineResource (pure functions)\n// ============================================================================\nexport {\n // Permission presets (common patterns in one call)\n permissions,\n publicRead,\n publicReadAdminWrite,\n authenticated,\n adminOnly,\n ownerWithAdminBypass,\n fullPublic,\n readOnly,\n // Low-level permission helpers\n allowPublic,\n requireAuth,\n requireRoles,\n requireOwnership,\n allOf,\n anyOf,\n denyAll,\n when,\n // Organization permissions\n requireOrgMembership,\n requireOrgRole,\n createOrgPermissions,\n createDynamicPermissionMatrix,\n requireTeamMembership,\n // Field-level permissions\n fields,\n applyFieldReadPermissions,\n applyFieldWritePermissions,\n} from \"./permissions/index.js\";\n\nexport type {\n PermissionCheck,\n PermissionContext,\n PermissionResult,\n DynamicPermissionMatrixConfig,\n DynamicPermissionMatrix,\n FieldPermission,\n FieldPermissionMap,\n} from \"./permissions/index.js\";\n\n// ============================================================================\n// Pipeline — functional guard/transform/intercept (zero deps, pure functions)\n// ============================================================================\nexport { guard, transform, intercept, pipe } from \"./pipeline/index.js\";\nexport type {\n PipelineContext,\n PipelineStep,\n PipelineConfig,\n Guard,\n Transform,\n Interceptor,\n} from \"./pipeline/index.js\";\n\n// ============================================================================\n// Middleware — named, priority-based (zero deps, pure functions)\n// ============================================================================\nexport { middleware, sortMiddlewares } from \"./middleware/index.js\";\nexport type { NamedMiddleware } from \"./middleware/index.js\";\n\n// ============================================================================\n// Request Context — AsyncLocalStorage (zero deps)\n// ============================================================================\nexport { requestContext } from \"./context/index.js\";\nexport type { RequestStore } from \"./context/index.js\";\n\n// ============================================================================\n// MOVED TO DEDICATED SUBPATHS (no longer re-exported from main barrel)\n// ============================================================================\n//\n// These were previously re-exported here but pulled in heavy dependencies.\n// Import from their dedicated subpaths instead:\n//\n// Factory (pulls in security plugins):\n// import { createApp, ArcFactory } from '@classytic/arc/factory';\n//\n// Plugins (each plugin is self-contained):\n// import { healthPlugin } from '@classytic/arc/plugins';\n// import { tracingPlugin } from '@classytic/arc/plugins/tracing';\n//\n// Hooks (HookSystem):\n// import { createHookSystem, HookSystem } from '@classytic/arc/hooks';\n//\n// Events (eventPlugin + transports):\n// import { eventPlugin } from '@classytic/arc/events';\n// import { RedisEventTransport } from '@classytic/arc/events/redis';\n//\n// Registry:\n// import { ResourceRegistry } from '@classytic/arc/registry';\n//\n\n// ============================================================================\n// Logger — centralized debug/warning system (zero deps)\n// ============================================================================\nexport { configureArcLogger, arcLog } from \"./logger/index.js\";\nexport type { ArcLoggerOptions, ArcLogWriter, ArcLogger } from \"./logger/index.js\";\n\n// Version from package.json (injected at build time via tsdown define)\nexport const version: string = \"__ARC_VERSION__\";\n"],"mappings":";;;;;;;;;;;;;;;;;AAyCA,SAAgB,MACd,MACA,kBACO;CACP,MAAM,OAAO,OAAO,qBAAqB,aACrC,EAAE,SAAS,kBAAkB,GAC7B;AAEJ,QAAO;EACL,OAAO;EACP;EACA,YAAY,KAAK;EACjB,SAAS,KAAK;EACf;;;;;;;;;;;ACVH,SAAgB,UACd,MACA,kBACW;CACX,MAAM,OAAO,OAAO,qBAAqB,aACrC,EAAE,SAAS,kBAAkB,GAC7B;AAEJ,QAAO;EACL,OAAO;EACP;EACA,YAAY,KAAK;EACjB,SAAS,KAAK;EACf;;;;;;;;;;;ACbH,SAAgB,UACd,MACA,kBACa;CACb,MAAM,OAAO,OAAO,qBAAqB,aACrC,EAAE,SAAS,kBAAkB,GAC7B;AAEJ,QAAO;EACL,OAAO;EACP;EACA,YAAY,KAAK;EACjB,SAAS,KAAK;EACf;;;;;;;;ACGH,SAAgB,WACd,MACA,SACiB;AACjB,QAAO;EACL;EACA,YAAY,QAAQ;EACpB,UAAU,QAAQ,YAAY;EAC9B,MAAM,QAAQ;EACd,SAAS,QAAQ;EAClB;;;;;;AAOH,SAAgB,gBAAgB,aAAkD;CAChF,MAAM,SAAS,CAAC,GAAG,YAAY,CAAC,MAAM,GAAG,MAAM,EAAE,WAAW,EAAE,SAAS;CAEvE,MAAM,aAAa;CACnB,MAAM,SAA2B,EAAE;AAEnC,MAAK,MAAM,MAAM,YAAY;EAC3B,MAAM,aAAa,OAAO,QACvB,MAAM,CAAC,EAAE,cAAc,EAAE,WAAW,WAAW,KAAK,EAAE,WAAW,SAAS,GAAG,CAC/E;AACD,MAAI,WAAW,SAAS,EACtB,QAAO,MAAM,WAAW,KAAK,MAAM;AACjC,OAAI,CAAC,EAAE,KAAM,QAAO,EAAE;GAEtB,MAAM,UAA6B,OAAO,SAAS,UAAU;AAE3D,QADkB,MAAM,EAAE,KAAM,QAAQ,CAEtC,QAAO,EAAE,QAAQ,SAAS,MAAM;;AAGpC,UAAO;IACP;;AAIN,QAAO;;;;;ACmOT,MAAa,UAAkB"}
@@ -0,0 +1,47 @@
1
+ import { t as DomainEvent } from "../EventTransport-BD2U0BTc.mjs";
2
+ import { WebSocketClient, WebSocketMessage } from "./websocket.mjs";
3
+ import { FastifyPluginAsync, FastifyRequest } from "fastify";
4
+
5
+ //#region src/integrations/event-gateway.d.ts
6
+ interface EventGatewayOptions {
7
+ /** Require auth for all real-time connections (default: true) */
8
+ auth?: boolean;
9
+ /** Custom auth function for WebSocket upgrade */
10
+ authenticate?: (request: unknown) => Promise<{
11
+ userId?: string;
12
+ organizationId?: string;
13
+ } | null>;
14
+ /** Filter events by org from request.scope (default: false) */
15
+ orgScoped?: boolean;
16
+ /** Room/subscription authorization policy */
17
+ roomPolicy?: (client: {
18
+ userId?: string;
19
+ organizationId?: string;
20
+ }, room: string) => boolean | Promise<boolean>;
21
+ /** Max message size from WS clients in bytes (default: 16384) */
22
+ maxMessageBytes?: number;
23
+ /** Max subscriptions per client (default: 100) */
24
+ maxSubscriptionsPerClient?: number;
25
+ /** SSE config. Set false to disable SSE. */
26
+ sse?: false | {
27
+ path?: string;
28
+ patterns?: string[];
29
+ heartbeat?: number;
30
+ filter?: (event: DomainEvent<unknown>, request: FastifyRequest) => boolean;
31
+ };
32
+ /** WebSocket config. Set false to disable WebSocket. */
33
+ ws?: false | {
34
+ path?: string;
35
+ resources?: string[];
36
+ heartbeatInterval?: number;
37
+ maxClientsPerRoom?: number;
38
+ exposeStats?: boolean | "authenticated";
39
+ onMessage?: (client: WebSocketClient, message: WebSocketMessage) => void | Promise<void>;
40
+ onConnect?: (client: WebSocketClient) => void | Promise<void>;
41
+ onDisconnect?: (client: WebSocketClient) => void | Promise<void>;
42
+ };
43
+ }
44
+ declare const eventGatewayPlugin: FastifyPluginAsync<EventGatewayOptions>;
45
+ //#endregion
46
+ export { EventGatewayOptions, eventGatewayPlugin as default, eventGatewayPlugin };
47
+ //# sourceMappingURL=event-gateway.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"event-gateway.d.mts","names":[],"sources":["../../src/integrations/event-gateway.ts"],"mappings":";;;;;UA+BiB,mBAAA;EAEf;EAAA,IAAA;EAGE;EADF,YAAA,IACE,OAAA,cACG,OAAA;IAAU,MAAA;IAAiB,cAAA;EAAA;EAIhC;EAFA,SAAA;EAG6B;EAD7B,UAAA,IACE,MAAA;IAAU,MAAA;IAAiB,cAAA;EAAA,GAC3B,IAAA,uBACa,OAAA;EAIf;EAFA,eAAA;EAQM;EANN,yBAAA;EAQM;EALN,GAAA;IAGM,IAAA;IACA,QAAA;IACA,SAAA;IACA,MAAA,IAAU,KAAA,EAAO,WAAA,WAAsB,OAAA,EAAS,cAAA;EAAA;EAOhD;EAHN,EAAA;IAGM,IAAA;IACA,SAAA;IACA,iBAAA;IACA,iBAAA;IACA,WAAA;IACA,SAAA,IACE,MAAA,EAAQ,eAAA,EACR,OAAA,EAAS,gBAAA,YACC,OAAA;IACZ,SAAA,IAAa,MAAA,EAAQ,eAAA,YAA2B,OAAA;IAChD,YAAA,IAAgB,MAAA,EAAQ,eAAA,YAA2B,OAAA;EAAA;AAAA;AAAA,cAgE9C,kBAAA,EAGP,kBAAA,CAAmB,mBAAA"}
@@ -0,0 +1,44 @@
1
+ import fp from "fastify-plugin";
2
+
3
+ //#region src/integrations/event-gateway.ts
4
+ const eventGatewayPluginImpl = async (fastify, opts = {}) => {
5
+ const { auth = true, orgScoped = false, roomPolicy, maxMessageBytes, maxSubscriptionsPerClient, authenticate } = opts;
6
+ if (auth && !authenticate && !fastify.hasDecorator("authenticate")) throw new Error("[arc-event-gateway] auth is true but fastify.authenticate is not registered. Register an auth plugin first, provide a custom authenticate function, or set auth: false.");
7
+ if (opts.sse !== false) {
8
+ const { default: ssePlugin } = await import("../sse-B3c3_yZp.mjs").then((n) => n.r);
9
+ await fastify.register(ssePlugin, {
10
+ path: opts.sse?.path ?? "/events/stream",
11
+ requireAuth: auth,
12
+ patterns: opts.sse?.patterns ?? ["*"],
13
+ heartbeat: opts.sse?.heartbeat ?? 3e4,
14
+ orgScoped,
15
+ filter: opts.sse?.filter
16
+ });
17
+ }
18
+ if (opts.ws !== false) {
19
+ const { websocketPlugin } = await import("./websocket.mjs");
20
+ await fastify.register(websocketPlugin, {
21
+ path: opts.ws?.path ?? "/ws",
22
+ auth,
23
+ authenticate,
24
+ resources: opts.ws?.resources ?? [],
25
+ heartbeatInterval: opts.ws?.heartbeatInterval ?? 3e4,
26
+ maxClientsPerRoom: opts.ws?.maxClientsPerRoom,
27
+ roomPolicy,
28
+ maxMessageBytes,
29
+ maxSubscriptionsPerClient,
30
+ exposeStats: opts.ws?.exposeStats,
31
+ onMessage: opts.ws?.onMessage,
32
+ onConnect: opts.ws?.onConnect,
33
+ onDisconnect: opts.ws?.onDisconnect
34
+ });
35
+ }
36
+ };
37
+ const eventGatewayPlugin = fp(eventGatewayPluginImpl, {
38
+ name: "arc-event-gateway",
39
+ fastify: "5.x"
40
+ });
41
+
42
+ //#endregion
43
+ export { eventGatewayPlugin as default, eventGatewayPlugin };
44
+ //# sourceMappingURL=event-gateway.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"event-gateway.mjs","names":[],"sources":["../../src/integrations/event-gateway.ts"],"sourcesContent":["/**\r\n * @classytic/arc — Event Gateway\r\n *\r\n * Unified real-time configuration point that wires SSE and WebSocket with\r\n * shared auth, org-scoping, and policy enforcement. Replaces the need to\r\n * configure SSE and WebSocket plugins independently.\r\n *\r\n * import { eventGatewayPlugin } from '@classytic/arc/integrations/event-gateway';\r\n *\r\n * await fastify.register(eventGatewayPlugin, {\r\n * auth: true,\r\n * orgScoped: true,\r\n * roomPolicy: (client, room) => ['product', 'order'].includes(room),\r\n * sse: { path: '/api/events', patterns: ['order.*', 'product.*'] },\r\n * ws: { path: '/ws', resources: ['product', 'order'] },\r\n * });\r\n */\r\n\r\nimport type {\r\n FastifyInstance,\r\n FastifyPluginAsync,\r\n FastifyRequest,\r\n} from \"fastify\";\r\nimport fp from \"fastify-plugin\";\r\nimport type { DomainEvent } from \"../events/EventTransport.js\";\r\nimport type { WebSocketClient, WebSocketMessage } from \"./websocket.js\";\r\n\r\n// ============================================================================\r\n// Types\r\n// ============================================================================\r\n\r\nexport interface EventGatewayOptions {\r\n /** Require auth for all real-time connections (default: true) */\r\n auth?: boolean;\r\n /** Custom auth function for WebSocket upgrade */\r\n authenticate?: (\r\n request: unknown,\r\n ) => Promise<{ userId?: string; organizationId?: string } | null>;\r\n /** Filter events by org from request.scope (default: false) */\r\n orgScoped?: boolean;\r\n /** Room/subscription authorization policy */\r\n roomPolicy?: (\r\n client: { userId?: string; organizationId?: string },\r\n room: string,\r\n ) => boolean | Promise<boolean>;\r\n /** Max message size from WS clients in bytes (default: 16384) */\r\n maxMessageBytes?: number;\r\n /** Max subscriptions per client (default: 100) */\r\n maxSubscriptionsPerClient?: number;\r\n\r\n /** SSE config. Set false to disable SSE. */\r\n sse?:\r\n | false\r\n | {\r\n path?: string;\r\n patterns?: string[];\r\n heartbeat?: number;\r\n filter?: (event: DomainEvent<unknown>, request: FastifyRequest) => boolean;\r\n };\r\n\r\n /** WebSocket config. Set false to disable WebSocket. */\r\n ws?:\r\n | false\r\n | {\r\n path?: string;\r\n resources?: string[];\r\n heartbeatInterval?: number;\r\n maxClientsPerRoom?: number;\r\n exposeStats?: boolean | \"authenticated\";\r\n onMessage?: (\r\n client: WebSocketClient,\r\n message: WebSocketMessage,\r\n ) => void | Promise<void>;\r\n onConnect?: (client: WebSocketClient) => void | Promise<void>;\r\n onDisconnect?: (client: WebSocketClient) => void | Promise<void>;\r\n };\r\n}\r\n\r\n// ============================================================================\r\n// Plugin\r\n// ============================================================================\r\n\r\nconst eventGatewayPluginImpl: FastifyPluginAsync<EventGatewayOptions> = async (\r\n fastify: FastifyInstance,\r\n opts: EventGatewayOptions = {},\r\n) => {\r\n const {\r\n auth = true,\r\n orgScoped = false,\r\n roomPolicy,\r\n maxMessageBytes,\r\n maxSubscriptionsPerClient,\r\n authenticate,\r\n } = opts;\r\n\r\n // Fail-closed: validate auth decorator once for both SSE and WebSocket\r\n if (auth && !authenticate && !fastify.hasDecorator(\"authenticate\")) {\r\n throw new Error(\r\n \"[arc-event-gateway] auth is true but fastify.authenticate is not registered. \" +\r\n \"Register an auth plugin first, provide a custom authenticate function, or set auth: false.\",\r\n );\r\n }\r\n\r\n // Register SSE if not disabled\r\n if (opts.sse !== false) {\r\n // Lazy import to avoid pulling SSE into bundles when disabled\r\n const { default: ssePlugin } = await import(\"../plugins/sse.js\");\r\n await fastify.register(ssePlugin, {\r\n path: opts.sse?.path ?? \"/events/stream\",\r\n requireAuth: auth,\r\n patterns: opts.sse?.patterns ?? [\"*\"],\r\n heartbeat: opts.sse?.heartbeat ?? 30000,\r\n orgScoped,\r\n filter: opts.sse?.filter,\r\n });\r\n }\r\n\r\n // Register WebSocket if not disabled\r\n if (opts.ws !== false) {\r\n const { websocketPlugin } = await import(\"./websocket.js\");\r\n await fastify.register(websocketPlugin, {\r\n path: opts.ws?.path ?? \"/ws\",\r\n auth,\r\n authenticate,\r\n resources: opts.ws?.resources ?? [],\r\n heartbeatInterval: opts.ws?.heartbeatInterval ?? 30000,\r\n maxClientsPerRoom: opts.ws?.maxClientsPerRoom,\r\n roomPolicy,\r\n maxMessageBytes,\r\n maxSubscriptionsPerClient,\r\n exposeStats: opts.ws?.exposeStats,\r\n onMessage: opts.ws?.onMessage,\r\n onConnect: opts.ws?.onConnect,\r\n onDisconnect: opts.ws?.onDisconnect,\r\n });\r\n }\r\n};\r\n\r\nexport const eventGatewayPlugin = fp(eventGatewayPluginImpl, {\r\n name: \"arc-event-gateway\",\r\n fastify: \"5.x\",\r\n}) as FastifyPluginAsync<EventGatewayOptions>;\r\n\r\nexport default eventGatewayPlugin;\r\n"],"mappings":";;;AAkFA,MAAM,yBAAkE,OACtE,SACA,OAA4B,EAAE,KAC3B;CACH,MAAM,EACJ,OAAO,MACP,YAAY,OACZ,YACA,iBACA,2BACA,iBACE;AAGJ,KAAI,QAAQ,CAAC,gBAAgB,CAAC,QAAQ,aAAa,eAAe,CAChE,OAAM,IAAI,MACR,0KAED;AAIH,KAAI,KAAK,QAAQ,OAAO;EAEtB,MAAM,EAAE,SAAS,cAAc,MAAM,OAAO;AAC5C,QAAM,QAAQ,SAAS,WAAW;GAChC,MAAM,KAAK,KAAK,QAAQ;GACxB,aAAa;GACb,UAAU,KAAK,KAAK,YAAY,CAAC,IAAI;GACrC,WAAW,KAAK,KAAK,aAAa;GAClC;GACA,QAAQ,KAAK,KAAK;GACnB,CAAC;;AAIJ,KAAI,KAAK,OAAO,OAAO;EACrB,MAAM,EAAE,oBAAoB,MAAM,OAAO;AACzC,QAAM,QAAQ,SAAS,iBAAiB;GACtC,MAAM,KAAK,IAAI,QAAQ;GACvB;GACA;GACA,WAAW,KAAK,IAAI,aAAa,EAAE;GACnC,mBAAmB,KAAK,IAAI,qBAAqB;GACjD,mBAAmB,KAAK,IAAI;GAC5B;GACA;GACA;GACA,aAAa,KAAK,IAAI;GACtB,WAAW,KAAK,IAAI;GACpB,WAAW,KAAK,IAAI;GACpB,cAAc,KAAK,IAAI;GACxB,CAAC;;;AAIN,MAAa,qBAAqB,GAAG,wBAAwB;CAC3D,MAAM;CACN,SAAS;CACV,CAAC"}
@@ -0,0 +1,5 @@
1
+ import { WebSocketClient, WebSocketMessage, WebSocketPluginOptions } from "./websocket.mjs";
2
+ import { EventGatewayOptions } from "./event-gateway.mjs";
3
+ import { StreamlinePluginOptions, WorkflowLike, WorkflowRunLike } from "./streamline.mjs";
4
+ import { JobDefinition, JobDispatchOptions, JobDispatcher, JobMeta, JobsPluginOptions, QueueStats } from "./jobs.mjs";
5
+ export { type EventGatewayOptions, type JobDefinition, type JobDispatchOptions, type JobDispatcher, type JobMeta, type JobsPluginOptions, type QueueStats, type StreamlinePluginOptions, type WebSocketClient, type WebSocketMessage, type WebSocketPluginOptions, type WorkflowLike, type WorkflowRunLike };
@@ -0,0 +1 @@
1
+ export { };
@@ -0,0 +1,104 @@
1
+ import { FastifyPluginAsync } from "fastify";
2
+
3
+ //#region src/integrations/jobs.d.ts
4
+ interface JobDefinition<TData = unknown, TResult = unknown> {
5
+ /** Unique job name */
6
+ name: string;
7
+ /** Job handler function */
8
+ handler: (data: TData, meta: JobMeta) => Promise<TResult>;
9
+ /** Number of retries on failure (default: 3) */
10
+ retries?: number;
11
+ /** Backoff strategy */
12
+ backoff?: {
13
+ type: 'exponential' | 'fixed';
14
+ delay: number;
15
+ };
16
+ /** Job timeout in ms (default: 30000) */
17
+ timeout?: number;
18
+ /** Concurrency per worker (default: 1) */
19
+ concurrency?: number;
20
+ /** Rate limit: max jobs per duration */
21
+ rateLimit?: {
22
+ max: number;
23
+ duration: number;
24
+ };
25
+ /** Dead letter queue name (default: '{name}:dead') */
26
+ deadLetterQueue?: string;
27
+ }
28
+ interface JobMeta {
29
+ jobId: string;
30
+ attemptsMade: number;
31
+ timestamp: number;
32
+ }
33
+ interface JobDispatchOptions {
34
+ /** Delay job execution by ms */
35
+ delay?: number;
36
+ /** Job priority (lower = higher priority) */
37
+ priority?: number;
38
+ /** Unique job ID (for deduplication) */
39
+ jobId?: string;
40
+ /** Remove job after completion */
41
+ removeOnComplete?: boolean | number;
42
+ /** Remove job after failure */
43
+ removeOnFail?: boolean | number;
44
+ }
45
+ interface JobsPluginOptions {
46
+ /** Redis connection options (passed to BullMQ) */
47
+ connection: {
48
+ host: string;
49
+ port: number;
50
+ password?: string;
51
+ db?: number;
52
+ } | unknown;
53
+ /** Job definitions to register */
54
+ jobs: JobDefinition[];
55
+ /** URL prefix for job management endpoints (default: '/jobs') */
56
+ prefix?: string;
57
+ /** Bridge job events to Arc's event bus (default: true) */
58
+ bridgeEvents?: boolean;
59
+ /** Default job options applied to all jobs */
60
+ defaults?: {
61
+ retries?: number;
62
+ backoff?: {
63
+ type: 'exponential' | 'fixed';
64
+ delay: number;
65
+ };
66
+ timeout?: number;
67
+ removeOnComplete?: boolean | number;
68
+ removeOnFail?: boolean | number;
69
+ };
70
+ }
71
+ interface JobDispatcher {
72
+ dispatch<TData = unknown>(name: string, data: TData, options?: JobDispatchOptions): Promise<{
73
+ jobId: string;
74
+ }>;
75
+ getQueue(name: string): unknown | null;
76
+ getStats(): Promise<Record<string, QueueStats>>;
77
+ close(): Promise<void>;
78
+ }
79
+ interface QueueStats {
80
+ waiting: number;
81
+ active: number;
82
+ completed: number;
83
+ failed: number;
84
+ delayed: number;
85
+ }
86
+ /**
87
+ * Define a background job with typed data and configuration.
88
+ *
89
+ * @example
90
+ * const processImage = defineJob({
91
+ * name: 'process-image',
92
+ * handler: async (data: { url: string; width: number }) => {
93
+ * return await sharp(data.url).resize(data.width).toBuffer();
94
+ * },
95
+ * retries: 3,
96
+ * timeout: 60000,
97
+ * });
98
+ */
99
+ declare function defineJob<TData = unknown, TResult = unknown>(definition: JobDefinition<TData, TResult>): JobDefinition<TData, TResult>;
100
+ /** Pluggable BullMQ job queue integration for Arc */
101
+ declare const jobsPlugin: FastifyPluginAsync<JobsPluginOptions>;
102
+ //#endregion
103
+ export { JobDefinition, JobDispatchOptions, JobDispatcher, JobMeta, JobsPluginOptions, QueueStats, jobsPlugin as default, jobsPlugin, defineJob };
104
+ //# sourceMappingURL=jobs.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"jobs.d.mts","names":[],"sources":["../../src/integrations/jobs.ts"],"mappings":";;;UA+CiB,aAAA;EAyBA;EAvBf,IAAA;;EAEA,OAAA,GAAU,IAAA,EAAM,KAAA,EAAO,IAAA,EAAM,OAAA,KAAY,OAAA,CAAQ,OAAA;EAuBjD;EArBA,OAAA;EAyBA;EAvBA,OAAA;IAAY,IAAA;IAA+B,KAAA;EAAA;EA8B5B;EA5Bf,OAAA;;EAEA,WAAA;EA4BA;EA1BA,SAAA;IAAc,GAAA;IAAa,QAAA;EAAA;EA4B3B;EA1BA,eAAA;AAAA;AAAA,UAGe,OAAA;EACf,KAAA;EACA,YAAA;EACA,SAAA;AAAA;AAAA,UAGe,kBAAA;EA0Bb;EAxBF,KAAA;EA0BE;EAxBF,QAAA;EAwBc;EAtBd,KAAA;EA0B4B;EAxB5B,gBAAA;EA2BQ;EAzBR,YAAA;AAAA;AAAA,UAGe,iBAAA;EA0BK;EAxBpB,UAAA;IAAc,IAAA;IAAc,IAAA;IAAc,QAAA;IAAmB,EAAA;EAAA;EAmB3D;EAjBF,IAAA,EAAM,aAAA;EAkBJ;EAhBF,MAAA;EAiBE;EAfF,YAAA;EAgBa;EAdb,QAAA;IACE,OAAA;IACA,OAAA;MAAY,IAAA;MAA+B,KAAA;IAAA;IAC3C,OAAA;IACA,gBAAA;IACA,YAAA;EAAA;AAAA;AAAA,UAIa,aAAA;EACf,QAAA,kBACE,IAAA,UACA,IAAA,EAAM,KAAA,EACN,OAAA,GAAU,kBAAA,GACT,OAAA;IAAU,KAAA;EAAA;EACb,QAAA,CAAS,IAAA;EACT,QAAA,IAAY,OAAA,CAAQ,MAAA,SAAe,UAAA;EACnC,KAAA,IAAS,OAAA;AAAA;AAAA,UAGM,UAAA;EACf,OAAA;EACA,MAAA;EACA,SAAA;EACA,MAAA;EACA,OAAA;AAAA;;;;;;;;;;;;;;iBAoBc,SAAA,oCAAA,CACd,UAAA,EAAY,aAAA,CAAc,KAAA,EAAO,OAAA,IAChC,aAAA,CAAc,KAAA,EAAO,OAAA;;cAoKX,UAAA,EAAY,kBAAA,CAAmB,iBAAA"}
@@ -0,0 +1,124 @@
1
+ //#region src/integrations/jobs.ts
2
+ /**
3
+ * Define a background job with typed data and configuration.
4
+ *
5
+ * @example
6
+ * const processImage = defineJob({
7
+ * name: 'process-image',
8
+ * handler: async (data: { url: string; width: number }) => {
9
+ * return await sharp(data.url).resize(data.width).toBuffer();
10
+ * },
11
+ * retries: 3,
12
+ * timeout: 60000,
13
+ * });
14
+ */
15
+ function defineJob(definition) {
16
+ return definition;
17
+ }
18
+ const jobsPluginImpl = async (fastify, options) => {
19
+ const { connection, jobs, prefix = "/jobs", bridgeEvents = true, defaults = {} } = options;
20
+ let Queue;
21
+ let Worker;
22
+ try {
23
+ const bullmq = await import("bullmq");
24
+ Queue = bullmq.Queue;
25
+ Worker = bullmq.Worker;
26
+ } catch {
27
+ throw new Error("@classytic/arc/integrations/jobs requires \"bullmq\" package.\nInstall it: npm install bullmq");
28
+ }
29
+ const queues = /* @__PURE__ */ new Map();
30
+ const workers = /* @__PURE__ */ new Map();
31
+ for (const job of jobs) {
32
+ const queueName = job.name;
33
+ const queue = new Queue(queueName, { connection });
34
+ queues.set(queueName, queue);
35
+ const worker = new Worker(queueName, async (bullJob) => {
36
+ const meta = {
37
+ jobId: bullJob.id,
38
+ attemptsMade: bullJob.attemptsMade,
39
+ timestamp: Date.now()
40
+ };
41
+ const result = await job.handler(bullJob.data, meta);
42
+ if (bridgeEvents && fastify.events?.publish) await fastify.events.publish(`job.${queueName}.completed`, {
43
+ jobId: bullJob.id,
44
+ data: bullJob.data,
45
+ result
46
+ });
47
+ return result;
48
+ }, {
49
+ connection,
50
+ concurrency: job.concurrency ?? 1,
51
+ limiter: job.rateLimit ? {
52
+ max: job.rateLimit.max,
53
+ duration: job.rateLimit.duration
54
+ } : void 0
55
+ });
56
+ worker.on("failed", async (bullJob, error) => {
57
+ if (bridgeEvents && fastify.events?.publish) await fastify.events.publish(`job.${queueName}.failed`, {
58
+ jobId: bullJob?.id,
59
+ data: bullJob?.data,
60
+ error: error.message,
61
+ attemptsMade: bullJob?.attemptsMade
62
+ });
63
+ });
64
+ workers.set(queueName, worker);
65
+ }
66
+ const dispatcher = {
67
+ async dispatch(name, data, opts = {}) {
68
+ const queue = queues.get(name);
69
+ if (!queue) throw new Error(`Job queue '${name}' not registered. Available: ${Array.from(queues.keys()).join(", ")}`);
70
+ const jobDef = jobs.find((j) => j.name === name);
71
+ return { jobId: (await queue.add(name, data, {
72
+ delay: opts.delay,
73
+ priority: opts.priority,
74
+ jobId: opts.jobId,
75
+ removeOnComplete: opts.removeOnComplete ?? defaults.removeOnComplete ?? 100,
76
+ removeOnFail: opts.removeOnFail ?? defaults.removeOnFail ?? 500,
77
+ attempts: jobDef?.retries ?? defaults.retries ?? 3,
78
+ backoff: jobDef?.backoff ?? defaults.backoff ?? {
79
+ type: "exponential",
80
+ delay: 1e3
81
+ }
82
+ })).id };
83
+ },
84
+ getQueue(name) {
85
+ return queues.get(name) ?? null;
86
+ },
87
+ async getStats() {
88
+ const stats = {};
89
+ for (const [name, queue] of queues) {
90
+ const counts = await queue.getJobCounts();
91
+ stats[name] = {
92
+ waiting: counts.waiting ?? 0,
93
+ active: counts.active ?? 0,
94
+ completed: counts.completed ?? 0,
95
+ failed: counts.failed ?? 0,
96
+ delayed: counts.delayed ?? 0
97
+ };
98
+ }
99
+ return stats;
100
+ },
101
+ async close() {
102
+ const closePromises = [];
103
+ for (const worker of workers.values()) closePromises.push(worker.close());
104
+ for (const queue of queues.values()) closePromises.push(queue.close());
105
+ await Promise.all(closePromises);
106
+ }
107
+ };
108
+ if (!fastify.hasDecorator("jobs")) fastify.decorate("jobs", dispatcher);
109
+ fastify.get(`${prefix}/stats`, async () => {
110
+ return {
111
+ success: true,
112
+ data: await dispatcher.getStats()
113
+ };
114
+ });
115
+ fastify.addHook("onClose", async () => {
116
+ await dispatcher.close();
117
+ });
118
+ };
119
+ /** Pluggable BullMQ job queue integration for Arc */
120
+ const jobsPlugin = jobsPluginImpl;
121
+
122
+ //#endregion
123
+ export { jobsPlugin as default, jobsPlugin, defineJob };
124
+ //# sourceMappingURL=jobs.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"jobs.mjs","names":[],"sources":["../../src/integrations/jobs.ts"],"sourcesContent":["/**\n * @classytic/arc — Job Queue Integration\n *\n * Pluggable adapter for background job processing with BullMQ.\n * Provides a clean defineJob() API, auto-connects to Arc's event bus,\n * and supports retries, delays, priorities, and dead-letter queues.\n *\n * This is a SEPARATE subpath import — only loaded when explicitly used:\n * import { jobsPlugin, defineJob } from '@classytic/arc/integrations/jobs';\n *\n * Requires: bullmq (peer dependency)\n *\n * NOTE: Job processing requires a persistent process and Redis.\n * This does NOT work on serverless platforms.\n *\n * @example\n * ```typescript\n * import { jobsPlugin, defineJob } from '@classytic/arc/integrations/jobs';\n *\n * const sendEmail = defineJob({\n * name: 'send-email',\n * handler: async (data) => {\n * await emailService.send(data.to, data.subject, data.body);\n * },\n * retries: 3,\n * backoff: { type: 'exponential', delay: 1000 },\n * });\n *\n * await fastify.register(jobsPlugin, {\n * connection: { host: 'localhost', port: 6379 },\n * jobs: [sendEmail],\n * });\n *\n * // Dispatch a job from anywhere\n * await fastify.jobs.dispatch('send-email', {\n * to: 'user@example.com',\n * subject: 'Hello',\n * body: 'Welcome!',\n * });\n * ```\n */\nimport type { FastifyInstance, FastifyPluginAsync } from 'fastify';\n\n// ============================================================================\n// Types (no BullMQ import at module level)\n// ============================================================================\n\nexport interface JobDefinition<TData = unknown, TResult = unknown> {\n /** Unique job name */\n name: string;\n /** Job handler function */\n handler: (data: TData, meta: JobMeta) => Promise<TResult>;\n /** Number of retries on failure (default: 3) */\n retries?: number;\n /** Backoff strategy */\n backoff?: { type: 'exponential' | 'fixed'; delay: number };\n /** Job timeout in ms (default: 30000) */\n timeout?: number;\n /** Concurrency per worker (default: 1) */\n concurrency?: number;\n /** Rate limit: max jobs per duration */\n rateLimit?: { max: number; duration: number };\n /** Dead letter queue name (default: '{name}:dead') */\n deadLetterQueue?: string;\n}\n\nexport interface JobMeta {\n jobId: string;\n attemptsMade: number;\n timestamp: number;\n}\n\nexport interface JobDispatchOptions {\n /** Delay job execution by ms */\n delay?: number;\n /** Job priority (lower = higher priority) */\n priority?: number;\n /** Unique job ID (for deduplication) */\n jobId?: string;\n /** Remove job after completion */\n removeOnComplete?: boolean | number;\n /** Remove job after failure */\n removeOnFail?: boolean | number;\n}\n\nexport interface JobsPluginOptions {\n /** Redis connection options (passed to BullMQ) */\n connection: { host: string; port: number; password?: string; db?: number } | unknown;\n /** Job definitions to register */\n jobs: JobDefinition[];\n /** URL prefix for job management endpoints (default: '/jobs') */\n prefix?: string;\n /** Bridge job events to Arc's event bus (default: true) */\n bridgeEvents?: boolean;\n /** Default job options applied to all jobs */\n defaults?: {\n retries?: number;\n backoff?: { type: 'exponential' | 'fixed'; delay: number };\n timeout?: number;\n removeOnComplete?: boolean | number;\n removeOnFail?: boolean | number;\n };\n}\n\nexport interface JobDispatcher {\n dispatch<TData = unknown>(\n name: string,\n data: TData,\n options?: JobDispatchOptions\n ): Promise<{ jobId: string }>;\n getQueue(name: string): unknown | null;\n getStats(): Promise<Record<string, QueueStats>>;\n close(): Promise<void>;\n}\n\nexport interface QueueStats {\n waiting: number;\n active: number;\n completed: number;\n failed: number;\n delayed: number;\n}\n\n// ============================================================================\n// defineJob — declarative job definition\n// ============================================================================\n\n/**\n * Define a background job with typed data and configuration.\n *\n * @example\n * const processImage = defineJob({\n * name: 'process-image',\n * handler: async (data: { url: string; width: number }) => {\n * return await sharp(data.url).resize(data.width).toBuffer();\n * },\n * retries: 3,\n * timeout: 60000,\n * });\n */\nexport function defineJob<TData = unknown, TResult = unknown>(\n definition: JobDefinition<TData, TResult>\n): JobDefinition<TData, TResult> {\n return definition;\n}\n\n// ============================================================================\n// Plugin Implementation\n// ============================================================================\n\nconst jobsPluginImpl: FastifyPluginAsync<JobsPluginOptions> = async (\n fastify: FastifyInstance,\n options: JobsPluginOptions\n) => {\n const {\n connection,\n jobs,\n prefix = '/jobs',\n bridgeEvents = true,\n defaults = {},\n } = options;\n\n // Dynamic import of BullMQ (only when plugin is actually registered)\n let Queue: any;\n let Worker: any;\n\n try {\n const bullmq = await import('bullmq');\n Queue = bullmq.Queue;\n Worker = bullmq.Worker;\n } catch {\n throw new Error(\n '@classytic/arc/integrations/jobs requires \"bullmq\" package.\\n' +\n 'Install it: npm install bullmq'\n );\n }\n\n const queues = new Map<string, InstanceType<typeof Queue>>();\n const workers = new Map<string, InstanceType<typeof Worker>>();\n\n // Register each job as a queue + worker pair\n for (const job of jobs) {\n const queueName = job.name;\n\n // Create queue\n const queue = new Queue(queueName, { connection });\n queues.set(queueName, queue);\n\n // Create worker\n const worker = new Worker(\n queueName,\n async (bullJob: any) => {\n const meta: JobMeta = {\n jobId: bullJob.id,\n attemptsMade: bullJob.attemptsMade,\n timestamp: Date.now(),\n };\n\n const result = await job.handler(bullJob.data, meta);\n\n // Bridge completion event\n if (bridgeEvents && fastify.events?.publish) {\n await fastify.events.publish(`job.${queueName}.completed`, {\n jobId: bullJob.id,\n data: bullJob.data,\n result,\n });\n }\n\n return result;\n },\n {\n connection,\n concurrency: job.concurrency ?? 1,\n limiter: job.rateLimit\n ? { max: job.rateLimit.max, duration: job.rateLimit.duration }\n : undefined,\n }\n );\n\n // Bridge failure event\n worker.on('failed', async (bullJob: any, error: Error) => {\n if (bridgeEvents && fastify.events?.publish) {\n await fastify.events.publish(`job.${queueName}.failed`, {\n jobId: bullJob?.id,\n data: bullJob?.data,\n error: error.message,\n attemptsMade: bullJob?.attemptsMade,\n });\n }\n });\n\n workers.set(queueName, worker);\n }\n\n // Dispatcher interface\n const dispatcher: JobDispatcher = {\n async dispatch(name, data, opts = {}) {\n const queue = queues.get(name);\n if (!queue) {\n throw new Error(`Job queue '${name}' not registered. Available: ${Array.from(queues.keys()).join(', ')}`);\n }\n\n const jobDef = jobs.find((j) => j.name === name);\n const bullJob = await queue.add(name, data, {\n delay: opts.delay,\n priority: opts.priority,\n jobId: opts.jobId,\n removeOnComplete: opts.removeOnComplete ?? defaults.removeOnComplete ?? 100,\n removeOnFail: opts.removeOnFail ?? defaults.removeOnFail ?? 500,\n attempts: jobDef?.retries ?? defaults.retries ?? 3,\n backoff: jobDef?.backoff ?? defaults.backoff ?? { type: 'exponential', delay: 1000 },\n });\n\n return { jobId: bullJob.id };\n },\n\n getQueue(name) {\n return queues.get(name) ?? null;\n },\n\n async getStats() {\n const stats: Record<string, QueueStats> = {};\n for (const [name, queue] of queues) {\n const counts = await queue.getJobCounts();\n stats[name] = {\n waiting: counts.waiting ?? 0,\n active: counts.active ?? 0,\n completed: counts.completed ?? 0,\n failed: counts.failed ?? 0,\n delayed: counts.delayed ?? 0,\n };\n }\n return stats;\n },\n\n async close() {\n const closePromises: Promise<void>[] = [];\n for (const worker of workers.values()) {\n closePromises.push(worker.close());\n }\n for (const queue of queues.values()) {\n closePromises.push(queue.close());\n }\n await Promise.all(closePromises);\n },\n };\n\n // Decorate fastify\n if (!fastify.hasDecorator('jobs')) {\n fastify.decorate('jobs', dispatcher);\n }\n\n // Management endpoints\n fastify.get(`${prefix}/stats`, async () => {\n const stats = await dispatcher.getStats();\n return { success: true, data: stats };\n });\n\n // Graceful shutdown\n fastify.addHook('onClose', async () => {\n await dispatcher.close();\n });\n};\n\n/** Pluggable BullMQ job queue integration for Arc */\nexport const jobsPlugin: FastifyPluginAsync<JobsPluginOptions> = jobsPluginImpl;\nexport default jobsPlugin;\n"],"mappings":";;;;;;;;;;;;;;AA4IA,SAAgB,UACd,YAC+B;AAC/B,QAAO;;AAOT,MAAM,iBAAwD,OAC5D,SACA,YACG;CACH,MAAM,EACJ,YACA,MACA,SAAS,SACT,eAAe,MACf,WAAW,EAAE,KACX;CAGJ,IAAI;CACJ,IAAI;AAEJ,KAAI;EACF,MAAM,SAAS,MAAM,OAAO;AAC5B,UAAQ,OAAO;AACf,WAAS,OAAO;SACV;AACN,QAAM,IAAI,MACR,gGAED;;CAGH,MAAM,yBAAS,IAAI,KAAyC;CAC5D,MAAM,0BAAU,IAAI,KAA0C;AAG9D,MAAK,MAAM,OAAO,MAAM;EACtB,MAAM,YAAY,IAAI;EAGtB,MAAM,QAAQ,IAAI,MAAM,WAAW,EAAE,YAAY,CAAC;AAClD,SAAO,IAAI,WAAW,MAAM;EAG5B,MAAM,SAAS,IAAI,OACjB,WACA,OAAO,YAAiB;GACtB,MAAM,OAAgB;IACpB,OAAO,QAAQ;IACf,cAAc,QAAQ;IACtB,WAAW,KAAK,KAAK;IACtB;GAED,MAAM,SAAS,MAAM,IAAI,QAAQ,QAAQ,MAAM,KAAK;AAGpD,OAAI,gBAAgB,QAAQ,QAAQ,QAClC,OAAM,QAAQ,OAAO,QAAQ,OAAO,UAAU,aAAa;IACzD,OAAO,QAAQ;IACf,MAAM,QAAQ;IACd;IACD,CAAC;AAGJ,UAAO;KAET;GACE;GACA,aAAa,IAAI,eAAe;GAChC,SAAS,IAAI,YACT;IAAE,KAAK,IAAI,UAAU;IAAK,UAAU,IAAI,UAAU;IAAU,GAC5D;GACL,CACF;AAGD,SAAO,GAAG,UAAU,OAAO,SAAc,UAAiB;AACxD,OAAI,gBAAgB,QAAQ,QAAQ,QAClC,OAAM,QAAQ,OAAO,QAAQ,OAAO,UAAU,UAAU;IACtD,OAAO,SAAS;IAChB,MAAM,SAAS;IACf,OAAO,MAAM;IACb,cAAc,SAAS;IACxB,CAAC;IAEJ;AAEF,UAAQ,IAAI,WAAW,OAAO;;CAIhC,MAAM,aAA4B;EAChC,MAAM,SAAS,MAAM,MAAM,OAAO,EAAE,EAAE;GACpC,MAAM,QAAQ,OAAO,IAAI,KAAK;AAC9B,OAAI,CAAC,MACH,OAAM,IAAI,MAAM,cAAc,KAAK,+BAA+B,MAAM,KAAK,OAAO,MAAM,CAAC,CAAC,KAAK,KAAK,GAAG;GAG3G,MAAM,SAAS,KAAK,MAAM,MAAM,EAAE,SAAS,KAAK;AAWhD,UAAO,EAAE,QAVO,MAAM,MAAM,IAAI,MAAM,MAAM;IAC1C,OAAO,KAAK;IACZ,UAAU,KAAK;IACf,OAAO,KAAK;IACZ,kBAAkB,KAAK,oBAAoB,SAAS,oBAAoB;IACxE,cAAc,KAAK,gBAAgB,SAAS,gBAAgB;IAC5D,UAAU,QAAQ,WAAW,SAAS,WAAW;IACjD,SAAS,QAAQ,WAAW,SAAS,WAAW;KAAE,MAAM;KAAe,OAAO;KAAM;IACrF,CAAC,EAEsB,IAAI;;EAG9B,SAAS,MAAM;AACb,UAAO,OAAO,IAAI,KAAK,IAAI;;EAG7B,MAAM,WAAW;GACf,MAAM,QAAoC,EAAE;AAC5C,QAAK,MAAM,CAAC,MAAM,UAAU,QAAQ;IAClC,MAAM,SAAS,MAAM,MAAM,cAAc;AACzC,UAAM,QAAQ;KACZ,SAAS,OAAO,WAAW;KAC3B,QAAQ,OAAO,UAAU;KACzB,WAAW,OAAO,aAAa;KAC/B,QAAQ,OAAO,UAAU;KACzB,SAAS,OAAO,WAAW;KAC5B;;AAEH,UAAO;;EAGT,MAAM,QAAQ;GACZ,MAAM,gBAAiC,EAAE;AACzC,QAAK,MAAM,UAAU,QAAQ,QAAQ,CACnC,eAAc,KAAK,OAAO,OAAO,CAAC;AAEpC,QAAK,MAAM,SAAS,OAAO,QAAQ,CACjC,eAAc,KAAK,MAAM,OAAO,CAAC;AAEnC,SAAM,QAAQ,IAAI,cAAc;;EAEnC;AAGD,KAAI,CAAC,QAAQ,aAAa,OAAO,CAC/B,SAAQ,SAAS,QAAQ,WAAW;AAItC,SAAQ,IAAI,GAAG,OAAO,SAAS,YAAY;AAEzC,SAAO;GAAE,SAAS;GAAM,MADV,MAAM,WAAW,UAAU;GACJ;GACrC;AAGF,SAAQ,QAAQ,WAAW,YAAY;AACrC,QAAM,WAAW,OAAO;GACxB;;;AAIJ,MAAa,aAAoD"}
@@ -0,0 +1,61 @@
1
+ import { FastifyPluginAsync } from "fastify";
2
+
3
+ //#region src/integrations/streamline.d.ts
4
+ /** Minimal workflow interface — matches @classytic/streamline's createWorkflow() return */
5
+ interface WorkflowLike {
6
+ definition: {
7
+ id: string;
8
+ name?: string;
9
+ steps: Record<string, unknown>;
10
+ };
11
+ engine: {
12
+ start(input: unknown, meta?: unknown): Promise<WorkflowRunLike>;
13
+ execute(runId: string): Promise<WorkflowRunLike>;
14
+ resume(runId: string, payload?: unknown): Promise<WorkflowRunLike>;
15
+ cancel(runId: string): Promise<WorkflowRunLike>;
16
+ pause?(runId: string): Promise<WorkflowRunLike>;
17
+ rewindTo?(runId: string, stepId: string): Promise<WorkflowRunLike>;
18
+ get(runId: string): Promise<WorkflowRunLike | null>;
19
+ shutdown?(): void;
20
+ };
21
+ start(input: unknown, meta?: unknown): Promise<WorkflowRunLike>;
22
+ resume(runId: string, payload?: unknown): Promise<WorkflowRunLike>;
23
+ cancel(runId: string): Promise<WorkflowRunLike>;
24
+ get(runId: string): Promise<WorkflowRunLike | null>;
25
+ shutdown?(): void;
26
+ }
27
+ interface WorkflowRunLike {
28
+ _id: string;
29
+ workflowId: string;
30
+ status: string;
31
+ context?: unknown;
32
+ input?: unknown;
33
+ steps?: Record<string, unknown>;
34
+ error?: unknown;
35
+ createdAt?: Date;
36
+ updatedAt?: Date;
37
+ [key: string]: unknown;
38
+ }
39
+ interface StreamlinePluginOptions {
40
+ /** Array of workflows created with createWorkflow() */
41
+ workflows: WorkflowLike[];
42
+ /** URL prefix for workflow endpoints (default: '/workflows') */
43
+ prefix?: string;
44
+ /** Require authentication for all workflow endpoints (default: true) */
45
+ auth?: boolean;
46
+ /** Connect workflow events to Arc's event bus (default: true) */
47
+ bridgeEvents?: boolean;
48
+ /** Custom permission check for workflow operations */
49
+ permissions?: {
50
+ start?: (request: unknown) => boolean | Promise<boolean>;
51
+ resume?: (request: unknown) => boolean | Promise<boolean>;
52
+ cancel?: (request: unknown) => boolean | Promise<boolean>;
53
+ list?: (request: unknown) => boolean | Promise<boolean>;
54
+ get?: (request: unknown) => boolean | Promise<boolean>;
55
+ };
56
+ }
57
+ /** Pluggable streamline integration for Arc */
58
+ declare const streamlinePlugin: FastifyPluginAsync<StreamlinePluginOptions>;
59
+ //#endregion
60
+ export { StreamlinePluginOptions, WorkflowLike, WorkflowRunLike, streamlinePlugin as default, streamlinePlugin };
61
+ //# sourceMappingURL=streamline.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"streamline.d.mts","names":[],"sources":["../../src/integrations/streamline.ts"],"mappings":";;;;UAsCiB,YAAA;EACf,UAAA;IAAc,EAAA;IAAY,IAAA;IAAe,KAAA,EAAO,MAAA;EAAA;EAChD,MAAA;IACE,KAAA,CAAM,KAAA,WAAgB,IAAA,aAAiB,OAAA,CAAQ,eAAA;IAC/C,OAAA,CAAQ,KAAA,WAAgB,OAAA,CAAQ,eAAA;IAChC,MAAA,CAAO,KAAA,UAAe,OAAA,aAAoB,OAAA,CAAQ,eAAA;IAClD,MAAA,CAAO,KAAA,WAAgB,OAAA,CAAQ,eAAA;IAC/B,KAAA,EAAO,KAAA,WAAgB,OAAA,CAAQ,eAAA;IAC/B,QAAA,EAAU,KAAA,UAAe,MAAA,WAAiB,OAAA,CAAQ,eAAA;IAClD,GAAA,CAAI,KAAA,WAAgB,OAAA,CAAQ,eAAA;IAC5B,QAAA;EAAA;EAEF,KAAA,CAAM,KAAA,WAAgB,IAAA,aAAiB,OAAA,CAAQ,eAAA;EAC/C,MAAA,CAAO,KAAA,UAAe,OAAA,aAAoB,OAAA,CAAQ,eAAA;EAClD,MAAA,CAAO,KAAA,WAAgB,OAAA,CAAQ,eAAA;EAC/B,GAAA,CAAI,KAAA,WAAgB,OAAA,CAAQ,eAAA;EAC5B,QAAA;AAAA;AAAA,UAGe,eAAA;EACf,GAAA;EACA,UAAA;EACA,MAAA;EACA,OAAA;EACA,KAAA;EACA,KAAA,GAAQ,MAAA;EACR,KAAA;EACA,SAAA,GAAY,IAAA;EACZ,SAAA,GAAY,IAAA;EAAA,CACX,GAAA;AAAA;AAAA,UAGc,uBAAA;EAtBb;EAwBF,SAAA,EAAW,YAAA;EAtBL;EAwBN,MAAA;EAxBuC;EA0BvC,IAAA;EAzBA;EA2BA,YAAA;EA3BsB;EA6BtB,WAAA;IACE,KAAA,IAAS,OAAA,wBAA+B,OAAA;IACxC,MAAA,IAAU,OAAA,wBAA+B,OAAA;IACzC,MAAA,IAAU,OAAA,wBAA+B,OAAA;IACzC,IAAA,IAAQ,OAAA,wBAA+B,OAAA;IACvC,GAAA,IAAO,OAAA,wBAA+B,OAAA;EAAA;AAAA;;cAyL7B,gBAAA,EAAkB,kBAAA,CAAmB,uBAAA"}