@classytic/arc 2.41.1 → 2.44.0

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 (248) hide show
  1. package/dist/{EventTransport-CbUKDzZZ.d.mts → EventTransport-CiicKz2Z.d.mts} +1 -1
  2. package/dist/{EventTransport-DSqD_Xsg.mjs → EventTransport-DZWDsaPa.mjs} +22 -17
  3. package/dist/{HookSystem-CMF94SEA.mjs → HookSystem-yjEgn7Cm.mjs} +2 -2
  4. package/dist/{QueryCache-DFA-QrU9.d.mts → QueryCache--5uAuYPW.d.mts} +7 -3
  5. package/dist/{ResourceRegistry-MGpXoxHZ.mjs → ResourceRegistry-DJxHUpE4.mjs} +44 -13
  6. package/dist/acquire-BozJ-9yt.mjs +35 -0
  7. package/dist/actionPermissions-5N04zBL4.mjs +772 -0
  8. package/dist/audit/index.d.mts +46 -8
  9. package/dist/audit/index.mjs +48 -26
  10. package/dist/auth/audit.d.mts +8 -8
  11. package/dist/auth/audit.mjs +1 -1
  12. package/dist/auth/index.d.mts +46 -16
  13. package/dist/auth/index.mjs +134 -156
  14. package/dist/auth/redis-session.d.mts +4 -5
  15. package/dist/auth/redis-session.mjs +1 -1
  16. package/dist/{authorizationDecision-DawKGoXb.mjs → authorizationDecision-wX3SA9w_.mjs} +7 -102
  17. package/dist/{base-CxrV1LmG.d.mts → base-CZyixEQR.d.mts} +27 -5
  18. package/dist/boundedMap-DWiKW7lP.mjs +57 -0
  19. package/dist/{buildHandler-CvxKOc0m.mjs → buildHandler-Bswu_vNb.mjs} +4 -4
  20. package/dist/cache/index.d.mts +31 -43
  21. package/dist/cache/index.mjs +4 -4
  22. package/dist/{caching-DbCBxlgw.mjs → caching-Drp68DIS.mjs} +3 -3
  23. package/dist/claimed-DYvQoZLv.mjs +8 -0
  24. package/dist/cleanup/index.d.mts +18 -22
  25. package/dist/cleanup/index.mjs +27 -73
  26. package/dist/cli/commands/describe.d.mts +7 -5
  27. package/dist/cli/commands/describe.mjs +7 -4
  28. package/dist/cli/commands/docs.d.mts +2 -3
  29. package/dist/cli/commands/docs.mjs +3 -3
  30. package/dist/cli/commands/doctor.d.mts +2 -3
  31. package/dist/cli/commands/doctor.mjs +1 -1
  32. package/dist/cli/commands/generate.mjs +2 -3
  33. package/dist/cli/commands/init.mjs +2 -3
  34. package/dist/cli/commands/introspect.d.mts +2 -3
  35. package/dist/cli/commands/introspect.mjs +2 -2
  36. package/dist/cli/index.mjs +3 -3
  37. package/dist/{constants-D5Ubn1fe.mjs → constants-B2QHb6u6.mjs} +4 -2
  38. package/dist/context/index.d.mts +11 -14
  39. package/dist/context/index.mjs +18 -38
  40. package/dist/core/index.d.mts +3 -3
  41. package/dist/core/index.mjs +8 -8
  42. package/dist/{core-5lazhva9.mjs → core-COcqo0Cp.mjs} +15 -5
  43. package/dist/{createActionRouter-DNqoaK81.mjs → createActionRouter-zG13rpPJ.mjs} +97 -39
  44. package/dist/{createAggregationRouter-BcbQJf10.mjs → createAggregationRouter-CWRz96Vr.mjs} +11 -8
  45. package/dist/{preloadResources-f3boenVZ.mjs → createApp-BGLYtErt.mjs} +305 -394
  46. package/dist/{createMcpServer-CbDWkZ1T.d.mts → createMcpServer-DSztofa9.d.mts} +1 -1
  47. package/dist/{defineAggregation-D_eJjm5A.mjs → defineAggregation--3xguLkk.mjs} +1 -1
  48. package/dist/{defineResource-DxkMTjRn.mjs → defineResource-gUp9ug66.mjs} +118 -1024
  49. package/dist/discovery/index.d.mts +6 -7
  50. package/dist/discovery/index.mjs +1 -1
  51. package/dist/docs/index.d.mts +11 -5
  52. package/dist/docs/index.mjs +4 -4
  53. package/dist/{elevation-CkvG0LG2.mjs → elevation-BG4gnMsJ.mjs} +41 -12
  54. package/dist/{elevation-BXCfVcNG.d.mts → elevation-BW2J3tNj.d.mts} +1 -2
  55. package/dist/encryption/index.d.mts +5 -5
  56. package/dist/encryption/index.mjs +3 -3
  57. package/dist/{errorHandler-D1GHKGlV.mjs → errorHandler-COHw3N1b.mjs} +26 -5
  58. package/dist/{errors-BH7YU_9O.mjs → errors-CW4anJBy.mjs} +58 -23
  59. package/dist/{errors-BGdVwP25.d.mts → errors-JYc9vwqE.d.mts} +9 -0
  60. package/dist/{eventPlugin-B52abJ8W.d.mts → eventPlugin-BtmA4-3P.d.mts} +65 -9
  61. package/dist/{eventPlugin-zZY-unhr.mjs → eventPlugin-BweHAVws.mjs} +82 -21
  62. package/dist/{subscribe-helpers-DatDkcuG.mjs → eventTypes-N4-omQFR.mjs} +151 -17
  63. package/dist/events/index.d.mts +43 -18
  64. package/dist/events/index.mjs +49 -53
  65. package/dist/events/transports/redis-stream-entry.d.mts +1 -1
  66. package/dist/events/transports/redis-stream-entry.mjs +139 -157
  67. package/dist/events/transports/redis.d.mts +5 -6
  68. package/dist/events/transports/redis.mjs +2 -2
  69. package/dist/factory/index.d.mts +42 -27
  70. package/dist/factory/index.mjs +213 -32
  71. package/dist/{fastify-augmentation-Cq2nHVaW.d.mts → fastify-augmentation-DRlCIWE9.d.mts} +11 -25
  72. package/dist/{fields-BHrkoF5y.mjs → fields-A5crhsJb.mjs} +1 -1
  73. package/dist/{fields-I_EeRWND.d.mts → fields-BBSpf8pS.d.mts} +1 -1
  74. package/dist/filter-merge-DGM-GQ1J.mjs +99 -0
  75. package/dist/{generate-BLRf2m3y.mjs → generate-BXKo2Gc8.mjs} +2 -2
  76. package/dist/{headers-BIXWKrzQ.mjs → headers-DKZQKL-Z.mjs} +2 -2
  77. package/dist/health-BYAwA6YS.mjs +22 -0
  78. package/dist/{health-ZRHeDHXH.mjs → health-CrqUp3sz.mjs} +115 -55
  79. package/dist/hooks/index.d.mts +2 -2
  80. package/dist/hooks/index.mjs +2 -3
  81. package/dist/idempotency/index.d.mts +36 -10
  82. package/dist/idempotency/index.mjs +230 -111
  83. package/dist/idempotency/redis.d.mts +1 -1
  84. package/dist/idempotency/redis.mjs +77 -12
  85. package/dist/{index-D4u76eQW.d.mts → index-Beg3aNnK.d.mts} +23 -101
  86. package/dist/{preloadResources-CjS2OfpU.d.mts → index-BkV55WlV.d.mts} +37 -38
  87. package/dist/{index-BHdZD8zK.d.mts → index-Bxriq3so.d.mts} +84 -21
  88. package/dist/index-CUJvoyw3.d.mts +510 -0
  89. package/dist/{index-CbJiYrfv.d.mts → index-Chgz3d0i.d.mts} +1 -1
  90. package/dist/{index-DHbyPo5j.d.mts → index-WH7xgvxS.d.mts} +304 -254
  91. package/dist/index.d.mts +9 -9
  92. package/dist/index.mjs +11 -11
  93. package/dist/{init-CpLkjU18.mjs → init-BPzK5Lnr.mjs} +1 -1
  94. package/dist/integrations/event-gateway.d.mts +11 -7
  95. package/dist/integrations/event-gateway.mjs +4 -4
  96. package/dist/integrations/index.d.mts +3 -3
  97. package/dist/integrations/index.mjs +1 -1
  98. package/dist/integrations/jobs/index.d.mts +1 -1
  99. package/dist/integrations/jobs/index.mjs +18 -5
  100. package/dist/integrations/mcp/index.d.mts +102 -89
  101. package/dist/integrations/mcp/index.mjs +19 -4
  102. package/dist/integrations/mcp/testing.d.mts +8 -9
  103. package/dist/integrations/mcp/testing.mjs +2 -2
  104. package/dist/integrations/streamline.d.mts +85 -144
  105. package/dist/integrations/streamline.mjs +131 -55
  106. package/dist/integrations/webhooks.d.mts +83 -143
  107. package/dist/integrations/webhooks.mjs +36 -80
  108. package/dist/integrations/websocket-pushref-redis.d.mts +5 -6
  109. package/dist/integrations/websocket-pushref-redis.mjs +1 -1
  110. package/dist/integrations/websocket-redis.d.mts +5 -6
  111. package/dist/integrations/websocket-redis.mjs +1 -1
  112. package/dist/integrations/websocket.d.mts +3 -3
  113. package/dist/integrations/websocket.mjs +2 -2
  114. package/dist/{interface-DEWNOMza.d.mts → interface-JJzwH4Ro.d.mts} +34 -2
  115. package/dist/{jsonBody-ELR9lfeU.mjs → jsonBody-YIq-vGLv.mjs} +2 -2
  116. package/dist/{keys-cpZCtcIE.mjs → keys-CpoiZ8hU.mjs} +7 -18
  117. package/dist/{loadResources-BPfWiI5s.mjs → loadResources-6jO52-RR.mjs} +3 -3
  118. package/dist/{loadResourcesFromEntry-ixtfL3J2.mjs → loadResourcesFromEntry-MP8xEppu.mjs} +2 -2
  119. package/dist/logger/index.d.mts +9 -17
  120. package/dist/logger/index.mjs +2 -153
  121. package/dist/logger-D4V1zGLI.mjs +160 -0
  122. package/dist/{mcpPlugin-BX5PujI0.mjs → mcpPlugin-srPc7ItN.mjs} +446 -170
  123. package/dist/{memory-Iotz0cCx.mjs → memory-CTa5pOCW.mjs} +36 -34
  124. package/dist/{metrics-NMsmpRmY.mjs → metrics-DsZrvnpB.mjs} +39 -72
  125. package/dist/middleware/index.d.mts +5 -5
  126. package/dist/middleware/index.mjs +3 -3
  127. package/dist/migrations/index.d.mts +13 -13
  128. package/dist/migrations/index.mjs +2 -2
  129. package/dist/{module-DpLJG2_N.mjs → module--L0iBIZz.mjs} +8 -8
  130. package/dist/mongodb-memory-server-CHhYSq5y.mjs +35539 -0
  131. package/dist/mongoose-CYJCLyi-.mjs +67451 -0
  132. package/dist/multiTenant-CAtYlW6H.d.mts +104 -0
  133. package/dist/{multipartBody-DtRWM6_U.mjs → multipartBody-Cc7eZ9cq.mjs} +46 -7
  134. package/dist/node-D8UZv_8c.mjs +41236 -0
  135. package/dist/{openapi-CmFf3yq_.mjs → openapi-CMegfsm8.mjs} +48 -10
  136. package/dist/outbox-admin/index.d.mts +5 -5
  137. package/dist/outbox-admin/index.mjs +6 -18
  138. package/dist/{outbox-module-D2z5WkDY.mjs → outbox-module-Ryk270jd.mjs} +203 -33
  139. package/dist/{outbox-module-BHdUnuuK.d.mts → outbox-module-gZCHoE4H.d.mts} +79 -6
  140. package/dist/permissions/index.d.mts +5 -4
  141. package/dist/permissions/index.mjs +8 -6
  142. package/dist/{permissions-D8TBH44E.mjs → permissions-C5ijIz5Q.mjs} +149 -33
  143. package/dist/{pipe-FMWNheSO.mjs → pipe-D81zMtWs.mjs} +2 -2
  144. package/dist/pipeline/index.d.mts +7 -7
  145. package/dist/pipeline/index.mjs +2 -2
  146. package/dist/plan-CJGMKoui.mjs +565 -0
  147. package/dist/{BaseController-CYEwjyjN.mjs → plugin-DALd9dcF.mjs} +1269 -346
  148. package/dist/plugins/index.d.mts +17 -14
  149. package/dist/plugins/index.mjs +44 -22
  150. package/dist/plugins/response-cache.d.mts +5 -6
  151. package/dist/plugins/response-cache.mjs +8 -14
  152. package/dist/plugins/tracing-entry.mjs +2 -2
  153. package/dist/{pluralize-CXWZIzTv.mjs → pluralize-COUNYo3d.mjs} +1 -1
  154. package/dist/presets/filesUpload.d.mts +8 -8
  155. package/dist/presets/filesUpload.mjs +5 -5
  156. package/dist/presets/index.d.mts +35 -40
  157. package/dist/presets/index.mjs +4 -4
  158. package/dist/presets/multiTenant.d.mts +2 -107
  159. package/dist/presets/multiTenant.mjs +22 -162
  160. package/dist/presets/search.d.mts +9 -10
  161. package/dist/presets/search.mjs +2 -2
  162. package/dist/{presets-ZJSPgmT2.mjs → presets-CONf2Qem.mjs} +99 -107
  163. package/dist/{projection-BzIgzoKZ.mjs → projection-wFoLqdss.mjs} +2 -2
  164. package/dist/{pushref-registry-Dov9l4qE.d.mts → pushref-registry-UxCkoA4C.d.mts} +1 -1
  165. package/dist/{queryCachePlugin-DZHPOm9p.mjs → queryCachePlugin-BATECpyh.mjs} +12 -14
  166. package/dist/{queryCachePlugin-BzqxconG.d.mts → queryCachePlugin-C2lZjTxk.d.mts} +1 -1
  167. package/dist/{rateLimitKey-BvOvcBDi.mjs → rateLimitKey-DVEZ_UpV.mjs} +1 -1
  168. package/dist/{realtime-8tIQWZz4.mjs → realtime-xyubMKSB.mjs} +25 -21
  169. package/dist/{redis-Bzi1y3F9.d.mts → redis-BDgv9Glw.d.mts} +17 -24
  170. package/dist/redis-stream-BMk-bpQS.d.mts +219 -0
  171. package/dist/redisShapes-CQksY67q.d.mts +55 -0
  172. package/dist/registry/index.d.mts +15 -10
  173. package/dist/registry/index.mjs +4 -4
  174. package/dist/{registry-DNkgGQcK.mjs → registry-Bdzg-hdm.mjs} +13 -10
  175. package/dist/{renewingLease-yF8BdW4b.mjs → renewingLease-BFJxGPuh.mjs} +1 -1
  176. package/dist/{replyHelpers-CNdvMfM4.mjs → replyHelpers-Bnc6lJyy.mjs} +2 -2
  177. package/dist/{requestContext-cDkAUr-P.mjs → requestContext-C10UeFe5.mjs} +1 -1
  178. package/dist/requirement-Bu50QALH.mjs +35 -0
  179. package/dist/requirement-CbiLdQ1n.d.mts +22 -0
  180. package/dist/resourceRateLimit-Wf6d6FL9.d.mts +30 -0
  181. package/dist/resourceRateLimit-urWPJxrt.mjs +180 -0
  182. package/dist/rolldown-runtime-MYrLNGnq.mjs +45 -0
  183. package/dist/routeValidator-DHtpzrzk.mjs +70 -0
  184. package/dist/{runtimeCapabilities-CkhS6jn6.mjs → runtimeCapabilities-DIMKMV2G.mjs} +1 -1
  185. package/dist/{schedules-j6uXXAeJ.mjs → schedules-CKTs5ys8.mjs} +46 -37
  186. package/dist/{schemaConverter-BVA7cFRG.mjs → schemaConverter-BVjkQ5DD.mjs} +2 -2
  187. package/dist/{schemaIR-CTvPDoPB.mjs → schemaIR-BA0Hb21Y.mjs} +2 -2
  188. package/dist/schemas/index.d.mts +20 -10
  189. package/dist/schemas/index.mjs +8 -3
  190. package/dist/scim/index.d.mts +13 -13
  191. package/dist/scim/index.mjs +2 -2
  192. package/dist/scope/index.d.mts +4 -4
  193. package/dist/scope/index.mjs +6 -6
  194. package/dist/{scope-BUi1xV1I.mjs → scope-DINaj7Ax.mjs} +4 -4
  195. package/dist/shutdown-LpLMH8T1.mjs +59 -0
  196. package/dist/{simpleEqualityMatcher-C9bzq2gO.mjs → simpleEqualityMatcher-DbwIM1f1.mjs} +1 -1
  197. package/dist/{sse-BeqVKQKu.mjs → sse-BzJFPX_1.mjs} +11 -15
  198. package/dist/{sseStream-Ppzo-WUH.mjs → sseStream-DQChzGTs.mjs} +2 -2
  199. package/dist/{store-helpers-dW71XVWL.mjs → store-helpers-Coj9RhOZ.mjs} +1 -1
  200. package/dist/{streaming-IcRFjWQU.mjs → streaming-JzB5CAga.mjs} +1 -1
  201. package/dist/subscriberIdentity-DeyppIif.mjs +28 -0
  202. package/dist/tenancy-DxV8OuX2.mjs +43 -0
  203. package/dist/tenantPreparation-CBvvAcAj.mjs +67 -0
  204. package/dist/testing/cleanupStoreContract.d.mts +4 -5
  205. package/dist/testing/cleanupStoreContract.mjs +1 -1
  206. package/dist/testing/index.d.mts +215 -197
  207. package/dist/testing/index.mjs +289 -214
  208. package/dist/testing/outboxStoreContract.d.mts +4 -5
  209. package/dist/testing/outboxStoreContract.mjs +137 -2
  210. package/dist/testing/requiredPorts.d.mts +16 -0
  211. package/dist/testing/requiredPorts.mjs +65 -0
  212. package/dist/testing/storageContract.d.mts +4 -5
  213. package/dist/testing/storageContract.mjs +1 -1
  214. package/dist/timing-CntavTlR.mjs +46 -0
  215. package/dist/{transactionContext-CPbdlnVw.mjs → transactionContext-orvnGu4F.mjs} +1 -1
  216. package/dist/{typeGuards-BLjPgVBm.mjs → typeGuards-kEaP_WmO.mjs} +1 -1
  217. package/dist/types/index.d.mts +4 -4
  218. package/dist/types/index.mjs +1 -1
  219. package/dist/types/storage.mjs +1 -1
  220. package/dist/{types-DfVDvkdP.d.mts → types-BJ-2XqVF.d.mts} +189 -204
  221. package/dist/{types-CWQn9-IC.d.mts → types-D3Ixsh00.d.mts} +22 -9
  222. package/dist/{types-CgenB1gv.mjs → types-fvO6Ovzx.mjs} +4 -4
  223. package/dist/usage/index.d.mts +5 -6
  224. package/dist/usage/index.mjs +5 -5
  225. package/dist/{userHelpers-ChRvkuSZ.mjs → userHelpers-C76zD5pa.mjs} +1 -1
  226. package/dist/utils/index.d.mts +125 -264
  227. package/dist/utils/index.mjs +13 -12
  228. package/dist/{utils-CxcctrAh.mjs → utils-BpkVZ8rB.mjs} +315 -404
  229. package/dist/{validate-DrYWnOzI.mjs → validate-CWp-lsrm.mjs} +6 -14
  230. package/dist/{versioning-D6JIK8Tk.d.mts → versioning-Bsb51gGT.d.mts} +28 -13
  231. package/dist/{versioning-DKkT4hNJ.mjs → versioning-CHhL9P3o.mjs} +4 -4
  232. package/dist/{websocket-BOXVggMf.d.mts → websocket-Cs4awVyD.d.mts} +1 -1
  233. package/dist/{workScope-CxKqoIFn.mjs → workScope-n9a7d_lZ.mjs} +2 -2
  234. package/package.json +57 -59
  235. package/skills/arc/SKILL.md +13 -4
  236. package/skills/arc/references/auth.md +8 -5
  237. package/skills/arc/references/integrations.md +1 -1
  238. package/skills/arc/references/mcp.md +8 -6
  239. package/skills/arc/references/multi-tenancy.md +19 -14
  240. package/skills/arc/references/production.md +27 -10
  241. package/dist/actionPermissions-BoPGQ5-4.mjs +0 -350
  242. package/dist/index-CFfLKMH4.d.mts +0 -290
  243. package/dist/interface-YzIbF7Ii.mjs +0 -17
  244. package/dist/redis-stream-BPKscsTB.d.mts +0 -260
  245. package/dist/registerRoute-Bsnk6NiZ.mjs +0 -364
  246. package/dist/rolldown-runtime-eysyzFAF.mjs +0 -15
  247. package/dist/sync/changelog.d.mts +0 -3
  248. package/dist/sync/changelog.mjs +0 -3
@@ -1,10 +1,10 @@
1
1
 
2
- import { a as EventTransport, n as DomainEvent } from "../EventTransport-CbUKDzZZ.mjs";
3
- import { n as OutboxModuleOptions, r as createOutboxModule } from "../outbox-module-BHdUnuuK.mjs";
2
+ import { a as EventTransport, n as DomainEvent } from "../EventTransport-CiicKz2Z.mjs";
3
+ import { i as createOutboxModule, r as OutboxModuleOptions } from "../outbox-module-gZCHoE4H.mjs";
4
4
  import { FastifyPluginAsync } from "fastify";
5
5
  import { OutboxFailurePolicy, OutboxStore } from "@classytic/primitives/outbox";
6
6
  //#region src/integrations/webhooks.d.ts
7
- interface WebhookSubscription {
7
+ export interface WebhookSubscription {
8
8
  /** Unique subscription ID */
9
9
  id: string;
10
10
  /** Delivery URL */
@@ -16,7 +16,7 @@ interface WebhookSubscription {
16
16
  /** Optional metadata */
17
17
  metadata?: Record<string, unknown>;
18
18
  }
19
- interface WebhookDeliveryRecord {
19
+ export interface WebhookDeliveryRecord {
20
20
  subscriptionId: string;
21
21
  eventType: string;
22
22
  success: boolean;
@@ -27,13 +27,13 @@ interface WebhookDeliveryRecord {
27
27
  attempts?: number;
28
28
  }
29
29
  /** Pluggable persistence — memory for dev, bring your own DB for prod */
30
- interface WebhookStore {
30
+ export interface WebhookStore {
31
31
  readonly name: string;
32
32
  getAll(): Promise<WebhookSubscription[]>;
33
33
  save(sub: WebhookSubscription): Promise<void>;
34
34
  remove(id: string): Promise<void>;
35
35
  }
36
- interface WebhookPluginOptions {
36
+ export interface WebhookPluginOptions {
37
37
  /** Custom store for persistent subscriptions (default: in-memory) */
38
38
  store?: WebhookStore;
39
39
  /** Custom fetch (for testing) */
@@ -42,24 +42,15 @@ interface WebhookPluginOptions {
42
42
  timeout?: number;
43
43
  /** Max delivery log entries kept in memory (default: 1000) */
44
44
  maxLogEntries?: number;
45
- /**
46
- * Max concurrent deliveries per event (default: 5). Set to 1 for
47
- * sequential. Must be >= 1 — the plugin throws at registration otherwise
48
- * (a zero would make the delivery loop spin forever without progress).
49
- */
45
+ /** Max concurrent deliveries per event, integer >= 1 (default: 5). */
50
46
  concurrency?: number;
51
47
  /**
52
- * In-process retry for failed deliveries. Default: single attempt.
53
- * Retries fire on network errors, timeouts, 429 and 5xx — NOT on other
54
- * 4xx (the receiver rejected the payload; repeating it can't succeed).
55
- * Each attempt is re-signed with a fresh timestamp so the receiver's
56
- * replay-tolerance window applies per attempt; `x-arc-webhook-delivery`
57
- * stays constant, which is exactly what receiver-side dedup keys on.
48
+ * In-process retry (default: single attempt) on network errors, timeouts,
49
+ * 429 and 5xx — not other 4xx. Each attempt is re-signed with a fresh
50
+ * timestamp; `x-arc-webhook-delivery` stays constant for receiver dedup.
58
51
  *
59
- * This is BEST-EFFORT: attempts live in this process, so a crash or
60
- * deploy drops the remaining ones. Guaranteed delivery is a durable-job
61
- * concern (queue keyed by subscription + delivery id) — pair the events
62
- * outbox with a worker if you need it.
52
+ * BEST-EFFORT: a crash or deploy drops remaining attempts. Use `durable`
53
+ * for guaranteed delivery.
63
54
  */
64
55
  retry?: {
65
56
  /** Total attempts including the first. Must be an integer >= 1. */
@@ -68,39 +59,24 @@ interface WebhookPluginOptions {
68
59
  backoffMs?: number;
69
60
  };
70
61
  /**
71
- * URL policy hook — the SSRF seam for tenant-registered URLs. Throw to
72
- * reject. Pass {@link defaultWebhookUrlPolicy} to require HTTPS and block
73
- * localhost / link-local / private-network / cloud-metadata destinations,
74
- * or supply your own (allowlists, per-tenant rules).
75
- *
76
- * Enforcement points when set:
77
- * - `register()` — new subscriptions are validated BEFORE saving.
78
- * - Plugin init — every PERSISTED subscription is re-validated; failures
79
- * are excluded from dispatch and logged (they stay in the store for
80
- * the host to fix), so pre-policy rows and out-of-band store writes
81
- * can't bypass the policy.
82
- * - Delivery — `fetch` runs with `redirect: "manual"`, so an allowed
83
- * public URL can't 302 into a blocked private/metadata target; a 3xx
84
- * records as a failed delivery.
62
+ * URL policy — the SSRF seam for tenant-registered URLs; throw to reject.
63
+ * Pass {@link defaultWebhookUrlPolicy} or your own. No policy applies by
64
+ * default (internal webhooks to private services are legitimate).
85
65
  *
86
- * Arc deliberately ships no DEFAULT policy — deployments differ (internal
87
- * webhooks to private services are legitimate), and DNS-rebinding
88
- * protection belongs at the actual outbound network boundary, not in the
89
- * framework.
66
+ * When set it is enforced:
67
+ * - on `register()`, before saving;
68
+ * - at init on every PERSISTED subscription — failures are excluded from
69
+ * dispatch and logged, so pre-policy / out-of-band rows can't bypass it;
70
+ * - at delivery via `redirect: "manual"`, so an allowed URL can't 302 into
71
+ * a blocked target (a 3xx records as failed).
90
72
  */
91
73
  validateUrl?: (url: URL, subscription: WebhookSubscription) => void | Promise<void>;
92
74
  /**
93
- * DURABLE delivery — the outbox→worker composition. When set, a matching
94
- * event no longer POSTs inline: each (event × subscription) pair becomes ONE
95
- * durable row in the given outbox store, and `createDurableWebhookModule`'s
96
- * relay claims, delivers, retries with backoff, and dead-letters — all
97
- * surviving crash and deploy, which the in-process `retry` cannot.
98
- *
99
- * The row id is deterministic (`<event id>:wh:<subscription id>`) and the
100
- * save carries it as `dedupeKey`, so an at-least-once upstream (Redis
101
- * Streams, an events outbox) redelivering the same event enqueues each
102
- * delivery exactly once. Mutually exclusive with `retry` — the retry axis
103
- * moves to the relay's failure policy.
75
+ * Durable delivery: each (event × subscription) becomes one outbox row that
76
+ * `createDurableWebhookModule`'s relay delivers, retries and dead-letters,
77
+ * surviving crash and deploy. The row id `<event id>:wh:<subscription id>`
78
+ * doubles as `dedupeKey`, so an at-least-once upstream enqueues each
79
+ * delivery once. Mutually exclusive with `retry`.
104
80
  */
105
81
  durable?: {
106
82
  /** Outbox store the delivery rows are written to — share it with the module. */
@@ -108,20 +84,13 @@ interface WebhookPluginOptions {
108
84
  };
109
85
  }
110
86
  /** `WebhookSubscription` as returned by `list()` — secret withheld. */
111
- type WebhookSubscriptionPublic = Omit<WebhookSubscription, "secret">;
112
- interface WebhookManager {
87
+ export type WebhookSubscriptionPublic = Omit<WebhookSubscription, "secret">;
88
+ export interface WebhookManager {
113
89
  register(sub: WebhookSubscription): Promise<void> | void;
114
90
  unregister(id: string): Promise<void> | void;
115
- /**
116
- * Enumerate subscriptions WITHOUT their secrets — safe to surface in
117
- * admin UIs / API responses. Use {@link WebhookManager.getSecret} for
118
- * the privileged retrieval path.
119
- */
91
+ /** Subscriptions WITHOUT secrets — safe for admin UIs / API responses. */
120
92
  list(): WebhookSubscriptionPublic[];
121
- /**
122
- * Privileged secret retrieval — separate from `list()` so enumerating
123
- * subscriptions never leaks signing material into logs or UIs.
124
- */
93
+ /** Privileged secret retrieval, kept out of `list()` so enumeration never leaks it. */
125
94
  getSecret(id: string): string | undefined;
126
95
  deliveryLog(limit?: number): WebhookDeliveryRecord[];
127
96
  }
@@ -131,14 +100,11 @@ declare module "fastify" {
131
100
  }
132
101
  }
133
102
  /**
134
- * Signed-string layout for the v1 webhook contract:
135
- *
136
- * v1.<timestampMs>.<deliveryId>.<body>
103
+ * Sign a v1 webhook. Signed string: `v1.<timestampMs>.<deliveryId>.<body>`.
137
104
  *
138
- * Binding the timestamp bounds replay to the verifier's tolerance window,
139
- * and binding the delivery id lets receivers deduplicate redeliveries on an
140
- * AUTHENTICATED value (the legacy `x-webhook-id` header was unsigned — an
141
- * attacker replaying a captured request could change it and defeat dedup).
105
+ * The timestamp bounds replay to the verifier's tolerance window; the
106
+ * delivery id is signed so receivers dedupe on an AUTHENTICATED value (an
107
+ * unsigned id header could be altered on replay to defeat dedup).
142
108
  *
143
109
  * Wire headers:
144
110
  * - `x-arc-webhook-signature: v1=<hex hmac-sha256>`
@@ -147,11 +113,11 @@ declare module "fastify" {
147
113
  * `meta.id` — STABLE across retries of the same event, so dedup on it
148
114
  * collapses redeliveries)
149
115
  */
150
- declare function signWebhook(body: string, secret: string, meta: {
116
+ export declare function signWebhook(body: string, secret: string, meta: {
151
117
  timestamp: number;
152
118
  deliveryId: string;
153
119
  }): string;
154
- interface VerifyWebhookInput {
120
+ export interface VerifyWebhookInput {
155
121
  /** Raw request body — the exact bytes the sender signed (`req.rawBody`). */
156
122
  body: string | Buffer;
157
123
  /** Shared secret. */
@@ -170,7 +136,7 @@ interface VerifyWebhookInput {
170
136
  /** Injectable clock for tests. @default Date.now */
171
137
  now?: () => number;
172
138
  }
173
- type VerifyWebhookResult = {
139
+ export type VerifyWebhookResult = {
174
140
  valid: true;
175
141
  /** The AUTHENTICATED delivery id — dedup redeliveries on this. */
176
142
  deliveryId: string;
@@ -203,10 +169,17 @@ type VerifyWebhookResult = {
203
169
  * });
204
170
  * ```
205
171
  */
206
- declare function verifyWebhook(input: VerifyWebhookInput): VerifyWebhookResult;
207
- declare function defaultWebhookUrlPolicy(url: URL): void;
172
+ export declare function verifyWebhook(input: VerifyWebhookInput): VerifyWebhookResult;
173
+ /**
174
+ * Opt-in URL policy: requires HTTPS and rejects loopback, link-local,
175
+ * private-network and cloud-metadata hosts. Pass as `validateUrl`.
176
+ *
177
+ * Validates the URL LITERAL only — no DNS resolution. Rebinding protection
178
+ * belongs at the outbound network boundary (egress proxy / network policy).
179
+ */
180
+ export declare function defaultWebhookUrlPolicy(url: URL): void;
208
181
  /** Options for `verifySignature` — customize for non-Arc webhook senders */
209
- interface VerifySignatureOptions {
182
+ export interface VerifySignatureOptions {
210
183
  /**
211
184
  * Expected prefix before the hex digest (default: `'sha256='`).
212
185
  * Set to `''` for bare hex signatures.
@@ -219,56 +192,34 @@ interface VerifySignatureOptions {
219
192
  algorithm?: string;
220
193
  }
221
194
  /**
222
- * Verify an INBOUND third-party webhook signature using timing-safe
223
- * comparison — for webhooks arc RECEIVES (GitHub, Stripe, ...), not for
224
- * arc-to-arc delivery. Arc's own outbound contract is v1
225
- * (`x-arc-webhook-*`) — receivers of arc webhooks use {@link verifyWebhook}.
226
- *
227
- * Defaults to the common `sha256=<hex>` format; configurable for any HMAC
228
- * scheme via options.
195
+ * Timing-safe verification of an INBOUND third-party webhook (GitHub,
196
+ * Stripe, ...). Defaults to `sha256=<hex>`; prefix/algorithm configurable.
197
+ * Receivers of arc's own webhooks use {@link verifyWebhook}.
229
198
  *
230
- * ⚠ The `body` argument must be the exact bytes the sender signed. Fastify
231
- * parses JSON bodies by default and the re-serialised object will NOT match
232
- * the original signature. Register `fastify-raw-body` (arc's createApp does
233
- * this automatically with `global: false`) and opt the route in via
234
- * `config: { rawBody: true }`, then pass `req.rawBody` here.
199
+ * ⚠ `body` must be the exact signed bytes: opt the route into
200
+ * `config: { rawBody: true }` (arc registers fastify-raw-body with
201
+ * `global: false`) and pass `req.rawBody` — re-serialised JSON won't match.
235
202
  *
236
- * @param body - Raw request body (string or Buffer — must be the exact bytes the sender signed)
237
- * @param secret - Shared secret between sender and receiver
238
- * @param signature - The signature header value (e.g. `req.headers['x-webhook-signature']`)
239
- * @param options - Override prefix/algorithm for non-Arc senders
240
- * @returns `true` if valid, `false` otherwise — never throws
203
+ * @returns `true` if valid, `false` otherwise. Throws `TypeError` only when
204
+ * `body` is not a string/Buffer.
241
205
  *
242
206
  * @example
243
207
  * ```typescript
244
- * import { verifySignature } from '@classytic/arc/integrations/webhooks';
245
- *
246
- * // `config: { rawBody: true }` is REQUIRED — arc registers
247
- * // fastify-raw-body with `global: false`, so routes opt in per-route;
248
- * // without it `req.rawBody` is undefined and verifySignature throws a
249
- * // TypeError.
250
- *
251
- * // GitHub-style sender (sha256=<hex>)
252
208
  * fastify.post('/webhooks/github', { config: { rawBody: true } }, async (req, reply) => {
253
209
  * const sig = req.headers['x-hub-signature-256'] as string;
254
210
  * if (!verifySignature(req.rawBody, secret, sig)) {
255
211
  * return reply.status(401).send({ error: 'Invalid signature' });
256
212
  * }
257
213
  * });
258
- *
259
- * // Stripe-style (bare hex, different header)
260
- * const valid = verifySignature(body, stripeSecret, req.headers['stripe-signature'], {
261
- * prefix: '',
262
- * });
263
214
  * ```
264
215
  */
265
- declare function verifySignature(body: string | Buffer, secret: string, signature: string | undefined, options?: VerifySignatureOptions): boolean;
216
+ export declare function verifySignature(body: string | Buffer, secret: string, signature: string | undefined, options?: VerifySignatureOptions): boolean;
266
217
  declare const webhookPlugin: FastifyPluginAsync<WebhookPluginOptions>;
267
218
  declare const _default: FastifyPluginAsync<WebhookPluginOptions>;
268
219
  /** Event type carried by durable webhook delivery rows. */
269
- declare const WEBHOOK_DELIVERY_EVENT = "arc.webhook.delivery";
220
+ export declare const WEBHOOK_DELIVERY_EVENT = "arc.webhook.delivery";
270
221
  /** Payload of a {@link WEBHOOK_DELIVERY_EVENT} row. */
271
- interface WebhookDeliveryPayload {
222
+ export interface WebhookDeliveryPayload {
272
223
  subscriptionId: string;
273
224
  /** The original event, verbatim — what the receiver is signed and sent. */
274
225
  event: {
@@ -278,12 +229,10 @@ interface WebhookDeliveryPayload {
278
229
  };
279
230
  }
280
231
  /**
281
- * A failed webhook POST, classified for the failure policy: `transient`
282
- * failures (network error, timeout, 429, 5xx) are worth retrying; the rest
283
- * (4xx rejection, 3xx under `redirect: "manual"`, URL-policy rejection,
284
- * malformed row) are final for this payload and dead-letter immediately.
232
+ * A failed webhook POST. `transient` (network, timeout, 429, 5xx) retries;
233
+ * anything else (4xx, 3xx, URL-policy rejection, malformed row) dead-letters.
285
234
  */
286
- declare class WebhookDeliveryError extends Error {
235
+ export declare class WebhookDeliveryError extends Error {
287
236
  readonly status?: number;
288
237
  readonly transient: boolean;
289
238
  constructor(message: string, options: {
@@ -292,11 +241,10 @@ declare class WebhookDeliveryError extends Error {
292
241
  cause?: unknown;
293
242
  });
294
243
  }
295
- interface WebhookDeliveryTransportOptions {
244
+ export interface WebhookDeliveryTransportOptions {
296
245
  /**
297
- * Resolve a subscription (WITH secret) at delivery time — fresh URL and
298
- * secret apply to every attempt, and an unregistered id is an acknowledged
299
- * no-op (the delivery is moot, not an error).
246
+ * Resolve a subscription (WITH secret) per attempt. `undefined` (unregistered)
247
+ * acknowledges the delivery as moot.
300
248
  */
301
249
  getSubscription: (id: string) => WebhookSubscription | undefined | Promise<WebhookSubscription | undefined>;
302
250
  /** Custom fetch (for testing). */
@@ -304,28 +252,23 @@ interface WebhookDeliveryTransportOptions {
304
252
  /** Per-POST timeout in ms (default: 10000). */
305
253
  timeout?: number;
306
254
  /**
307
- * URL policy — same seam as {@link WebhookPluginOptions.validateUrl}, but
308
- * enforced at DELIVERY time (covers rows enqueued before a policy existed
309
- * and out-of-band store writes). When set, `redirect: "manual"` applies so
310
- * an allowed URL can't 302 into a blocked target; a rejection is permanent.
255
+ * Same seam as {@link WebhookPluginOptions.validateUrl}, enforced at
256
+ * DELIVERY time (with `redirect: "manual"`); a rejection is permanent.
311
257
  */
312
258
  validateUrl?: (url: URL, subscription: WebhookSubscription) => void | Promise<void>;
313
259
  /** Resolve private auth headers at delivery time; reserved Arc headers cannot be replaced. */
314
260
  resolveHeaders?: (subscription: WebhookSubscription, event: WebhookDeliveryPayload["event"]) => Record<string, string> | Promise<Record<string, string>>;
315
261
  }
316
262
  /**
317
- * Publish-only `EventTransport` whose `publish()` IS the webhook POST —
318
- * plugged into an outbox relay, delivery inherits the relay's guarantees.
319
- * Throws {@link WebhookDeliveryError} on failure so the relay observes it
320
- * (fail → policy → retryAt/deadLetter); returns normally on 2xx and on
321
- * unregistered subscriptions (acknowledge).
263
+ * Publish-only `EventTransport` whose `publish()` IS the webhook POST, for an
264
+ * outbox relay. Throws {@link WebhookDeliveryError} on failure; returns on
265
+ * 2xx or an unregistered subscription.
322
266
  *
323
- * The wire contract is IDENTICAL to inline dispatch: v1 signing, and
324
- * `x-arc-webhook-delivery` carries the ORIGINAL event's `meta.id` — stable
325
- * across retries, so receiver-side dedup is unchanged.
267
+ * Same wire contract as inline dispatch: `x-arc-webhook-delivery` is the
268
+ * ORIGINAL event's `meta.id`, stable across retries.
326
269
  */
327
- declare function createWebhookDeliveryTransport(options: WebhookDeliveryTransportOptions): EventTransport;
328
- interface WebhookDeliveryPolicyOptions {
270
+ export declare function createWebhookDeliveryTransport(options: WebhookDeliveryTransportOptions): EventTransport;
271
+ export interface WebhookDeliveryPolicyOptions {
329
272
  /** Total attempts before dead-lettering (default: 8). */
330
273
  maxAttempts?: number;
331
274
  /** First retry delay in ms (default: 5000). */
@@ -334,13 +277,12 @@ interface WebhookDeliveryPolicyOptions {
334
277
  maxMs?: number;
335
278
  }
336
279
  /**
337
- * Default failure policy for durable webhook delivery: permanent failures
338
- * ({@link WebhookDeliveryError} with `transient: false`) dead-letter on the
339
- * first attempt — repeating a rejected payload can't succeed; transient ones
340
- * retry on exponential backoff until `maxAttempts`, then dead-letter.
280
+ * Default durable-delivery policy: non-transient {@link WebhookDeliveryError}s
281
+ * dead-letter immediately; others retry with exponential backoff until
282
+ * `maxAttempts`.
341
283
  */
342
- declare function webhookDeliveryFailurePolicy(options?: WebhookDeliveryPolicyOptions): OutboxFailurePolicy;
343
- interface DurableWebhookModuleOptions extends Omit<OutboxModuleOptions, "name" | "transport" | "failurePolicy">, Pick<WebhookDeliveryTransportOptions, "fetch" | "timeout" | "validateUrl" | "resolveHeaders"> {
284
+ export declare function webhookDeliveryFailurePolicy(options?: WebhookDeliveryPolicyOptions): OutboxFailurePolicy;
285
+ export interface DurableWebhookModuleOptions extends Omit<OutboxModuleOptions, "name" | "transport" | "failurePolicy">, Pick<WebhookDeliveryTransportOptions, "fetch" | "timeout" | "validateUrl" | "resolveHeaders"> {
344
286
  /** Module name / `dependsOn` key. Default `'webhook-delivery'`. */
345
287
  name?: string;
346
288
  /** Retry/backoff tuning for the default failure policy. */
@@ -363,12 +305,10 @@ interface DurableWebhookModuleOptions extends Omit<OutboxModuleOptions, "name" |
363
305
  * });
364
306
  * ```
365
307
  *
366
- * The relay arm is `kind: 'relay'`, so `role: 'relay'` deployments run
367
- * delivery and `role: 'api'` deployments only enqueue. The delivering process
368
- * must ALSO register `webhookPlugin` (with the shared subscription store) —
369
- * subscriptions resolve through `fastify.webhooks` at delivery time, which is
370
- * boot-checked, not discovered on the first row.
308
+ * `role: 'relay'` deployments deliver; `role: 'api'` only enqueues. The
309
+ * delivering process must ALSO register `webhookPlugin` with the shared
310
+ * subscription store (boot-checked).
371
311
  */
372
- declare function createDurableWebhookModule(options: DurableWebhookModuleOptions): ReturnType<typeof createOutboxModule>;
312
+ export declare function createDurableWebhookModule(options: DurableWebhookModuleOptions): ReturnType<typeof createOutboxModule>;
373
313
  //#endregion
374
- export { DurableWebhookModuleOptions, VerifySignatureOptions, VerifyWebhookInput, VerifyWebhookResult, WEBHOOK_DELIVERY_EVENT, WebhookDeliveryError, WebhookDeliveryPayload, WebhookDeliveryPolicyOptions, WebhookDeliveryRecord, WebhookDeliveryTransportOptions, WebhookManager, WebhookPluginOptions, WebhookStore, WebhookSubscription, WebhookSubscriptionPublic, createDurableWebhookModule, createWebhookDeliveryTransport, _default as default, defaultWebhookUrlPolicy, signWebhook, verifySignature, verifyWebhook, webhookDeliveryFailurePolicy, webhookPlugin };
314
+ export { _default as default, webhookPlugin };
@@ -1,9 +1,9 @@
1
- /*! @classytic/arc v2.41.1 | MIT | https://classytic.com */
2
- import { n as declareRuntimeCapability } from "../runtimeCapabilities-CkhS6jn6.mjs";
3
- import { i as exponentialBackoff, t as createOutboxModule } from "../outbox-module-D2z5WkDY.mjs";
1
+ /*! @classytic/arc v2.44.0 | MIT | https://classytic.com */
2
+ import { n as declareRuntimeCapability } from "../runtimeCapabilities-DIMKMV2G.mjs";
3
+ import { i as exponentialBackoff, t as createOutboxModule } from "../outbox-module-Ryk270jd.mjs";
4
4
  import { createHmac, timingSafeEqual } from "node:crypto";
5
5
  import fp from "fastify-plugin";
6
- import { createChildEvent } from "@classytic/primitives/events";
6
+ import { createChildEvent, matchEventPattern } from "@classytic/primitives/events";
7
7
  //#region src/integrations/webhooks.ts
8
8
  /**
9
9
  * Outbound webhooks — auto-dispatch arc events to registered endpoints with
@@ -22,14 +22,11 @@ import { createChildEvent } from "@classytic/primitives/events";
22
22
  * ```
23
23
  */
24
24
  /**
25
- * Signed-string layout for the v1 webhook contract:
25
+ * Sign a v1 webhook. Signed string: `v1.<timestampMs>.<deliveryId>.<body>`.
26
26
  *
27
- * v1.<timestampMs>.<deliveryId>.<body>
28
- *
29
- * Binding the timestamp bounds replay to the verifier's tolerance window,
30
- * and binding the delivery id lets receivers deduplicate redeliveries on an
31
- * AUTHENTICATED value (the legacy `x-webhook-id` header was unsigned — an
32
- * attacker replaying a captured request could change it and defeat dedup).
27
+ * The timestamp bounds replay to the verifier's tolerance window; the
28
+ * delivery id is signed so receivers dedupe on an AUTHENTICATED value (an
29
+ * unsigned id header could be altered on replay to defeat dedup).
33
30
  *
34
31
  * Wire headers:
35
32
  * - `x-arc-webhook-signature: v1=<hex hmac-sha256>`
@@ -98,18 +95,14 @@ function verifyWebhook(input) {
98
95
  timestamp
99
96
  };
100
97
  }
98
+ const BLOCKED_V4 = /^(?:127\.|10\.|192\.168\.|172\.(?:1[6-9]|2\d|3[01])\.|169\.254\.|0\.)/;
101
99
  /**
102
- * Opt-in URL policy for tenant-registered webhooks: requires HTTPS and
103
- * rejects loopback, link-local, private-network, and cloud-metadata
104
- * destinations. Pass as `validateUrl: defaultWebhookUrlPolicy`.
100
+ * Opt-in URL policy: requires HTTPS and rejects loopback, link-local,
101
+ * private-network and cloud-metadata hosts. Pass as `validateUrl`.
105
102
  *
106
- * Scope note: this validates the URL LITERAL at registration time. It does
107
- * not resolve DNS — rebinding/level-2 SSRF protection belongs at the actual
108
- * outbound network boundary (egress proxy, network policy), where the real
109
- * connection target is known. Internal deployments that legitimately POST
110
- * to private services should write their own narrower policy instead.
103
+ * Validates the URL LITERAL only — no DNS resolution. Rebinding protection
104
+ * belongs at the outbound network boundary (egress proxy / network policy).
111
105
  */
112
- const BLOCKED_V4 = /^(?:127\.|10\.|192\.168\.|172\.(?:1[6-9]|2\d|3[01])\.|169\.254\.|0\.)/;
113
106
  function defaultWebhookUrlPolicy(url) {
114
107
  if (url.protocol !== "https:") throw new Error(`Webhook URL must use https (got ${url.protocol}//)`);
115
108
  let host = url.hostname.toLowerCase();
@@ -136,47 +129,25 @@ function decodeMappedV4(host) {
136
129
  return `${hi >> 8}.${hi & 255}.${lo >> 8}.${lo & 255}`;
137
130
  }
138
131
  /**
139
- * Verify an INBOUND third-party webhook signature using timing-safe
140
- * comparison — for webhooks arc RECEIVES (GitHub, Stripe, ...), not for
141
- * arc-to-arc delivery. Arc's own outbound contract is v1
142
- * (`x-arc-webhook-*`) — receivers of arc webhooks use {@link verifyWebhook}.
132
+ * Timing-safe verification of an INBOUND third-party webhook (GitHub,
133
+ * Stripe, ...). Defaults to `sha256=<hex>`; prefix/algorithm configurable.
134
+ * Receivers of arc's own webhooks use {@link verifyWebhook}.
143
135
  *
144
- * Defaults to the common `sha256=<hex>` format; configurable for any HMAC
145
- * scheme via options.
136
+ * ⚠ `body` must be the exact signed bytes: opt the route into
137
+ * `config: { rawBody: true }` (arc registers fastify-raw-body with
138
+ * `global: false`) and pass `req.rawBody` — re-serialised JSON won't match.
146
139
  *
147
- * ⚠ The `body` argument must be the exact bytes the sender signed. Fastify
148
- * parses JSON bodies by default and the re-serialised object will NOT match
149
- * the original signature. Register `fastify-raw-body` (arc's createApp does
150
- * this automatically with `global: false`) and opt the route in via
151
- * `config: { rawBody: true }`, then pass `req.rawBody` here.
152
- *
153
- * @param body - Raw request body (string or Buffer — must be the exact bytes the sender signed)
154
- * @param secret - Shared secret between sender and receiver
155
- * @param signature - The signature header value (e.g. `req.headers['x-webhook-signature']`)
156
- * @param options - Override prefix/algorithm for non-Arc senders
157
- * @returns `true` if valid, `false` otherwise — never throws
140
+ * @returns `true` if valid, `false` otherwise. Throws `TypeError` only when
141
+ * `body` is not a string/Buffer.
158
142
  *
159
143
  * @example
160
144
  * ```typescript
161
- * import { verifySignature } from '@classytic/arc/integrations/webhooks';
162
- *
163
- * // `config: { rawBody: true }` is REQUIRED — arc registers
164
- * // fastify-raw-body with `global: false`, so routes opt in per-route;
165
- * // without it `req.rawBody` is undefined and verifySignature throws a
166
- * // TypeError.
167
- *
168
- * // GitHub-style sender (sha256=<hex>)
169
145
  * fastify.post('/webhooks/github', { config: { rawBody: true } }, async (req, reply) => {
170
146
  * const sig = req.headers['x-hub-signature-256'] as string;
171
147
  * if (!verifySignature(req.rawBody, secret, sig)) {
172
148
  * return reply.status(401).send({ error: 'Invalid signature' });
173
149
  * }
174
150
  * });
175
- *
176
- * // Stripe-style (bare hex, different header)
177
- * const valid = verifySignature(body, stripeSecret, req.headers['stripe-signature'], {
178
- * prefix: '',
179
- * });
180
151
  * ```
181
152
  */
182
153
  function verifySignature(body, secret, signature, options) {
@@ -197,16 +168,9 @@ function verifySignature(body, secret, signature, options) {
197
168
  return false;
198
169
  }
199
170
  }
171
+ /** The transports' own matcher — a local copy missed `order:*` (the `:` convention). */
200
172
  function matchesPattern(patterns, eventType) {
201
- for (const pattern of patterns) {
202
- if (pattern === "*") return true;
203
- if (pattern === eventType) return true;
204
- if (pattern.endsWith(".*")) {
205
- const prefix = pattern.slice(0, -2);
206
- if (eventType.startsWith(`${prefix}.`)) return true;
207
- }
208
- }
209
- return false;
173
+ return patterns.some((pattern) => matchEventPattern(pattern, eventType));
210
174
  }
211
175
  var MemoryWebhookStore = class {
212
176
  name = "memory";
@@ -376,10 +340,8 @@ var webhooks_default = fp(webhookPlugin, {
376
340
  /** Event type carried by durable webhook delivery rows. */
377
341
  const WEBHOOK_DELIVERY_EVENT = "arc.webhook.delivery";
378
342
  /**
379
- * A failed webhook POST, classified for the failure policy: `transient`
380
- * failures (network error, timeout, 429, 5xx) are worth retrying; the rest
381
- * (4xx rejection, 3xx under `redirect: "manual"`, URL-policy rejection,
382
- * malformed row) are final for this payload and dead-letter immediately.
343
+ * A failed webhook POST. `transient` (network, timeout, 429, 5xx) retries;
344
+ * anything else (4xx, 3xx, URL-policy rejection, malformed row) dead-letters.
383
345
  */
384
346
  var WebhookDeliveryError = class extends Error {
385
347
  status;
@@ -392,15 +354,12 @@ var WebhookDeliveryError = class extends Error {
392
354
  }
393
355
  };
394
356
  /**
395
- * Publish-only `EventTransport` whose `publish()` IS the webhook POST —
396
- * plugged into an outbox relay, delivery inherits the relay's guarantees.
397
- * Throws {@link WebhookDeliveryError} on failure so the relay observes it
398
- * (fail → policy → retryAt/deadLetter); returns normally on 2xx and on
399
- * unregistered subscriptions (acknowledge).
357
+ * Publish-only `EventTransport` whose `publish()` IS the webhook POST, for an
358
+ * outbox relay. Throws {@link WebhookDeliveryError} on failure; returns on
359
+ * 2xx or an unregistered subscription.
400
360
  *
401
- * The wire contract is IDENTICAL to inline dispatch: v1 signing, and
402
- * `x-arc-webhook-delivery` carries the ORIGINAL event's `meta.id` — stable
403
- * across retries, so receiver-side dedup is unchanged.
361
+ * Same wire contract as inline dispatch: `x-arc-webhook-delivery` is the
362
+ * ORIGINAL event's `meta.id`, stable across retries.
404
363
  */
405
364
  function createWebhookDeliveryTransport(options) {
406
365
  const fetchFn = options.fetch ?? globalThis.fetch;
@@ -464,10 +423,9 @@ function createWebhookDeliveryTransport(options) {
464
423
  };
465
424
  }
466
425
  /**
467
- * Default failure policy for durable webhook delivery: permanent failures
468
- * ({@link WebhookDeliveryError} with `transient: false`) dead-letter on the
469
- * first attempt — repeating a rejected payload can't succeed; transient ones
470
- * retry on exponential backoff until `maxAttempts`, then dead-letter.
426
+ * Default durable-delivery policy: non-transient {@link WebhookDeliveryError}s
427
+ * dead-letter immediately; others retry with exponential backoff until
428
+ * `maxAttempts`.
471
429
  */
472
430
  function webhookDeliveryFailurePolicy(options = {}) {
473
431
  const maxAttempts = options.maxAttempts ?? 8;
@@ -498,11 +456,9 @@ function webhookDeliveryFailurePolicy(options = {}) {
498
456
  * });
499
457
  * ```
500
458
  *
501
- * The relay arm is `kind: 'relay'`, so `role: 'relay'` deployments run
502
- * delivery and `role: 'api'` deployments only enqueue. The delivering process
503
- * must ALSO register `webhookPlugin` (with the shared subscription store) —
504
- * subscriptions resolve through `fastify.webhooks` at delivery time, which is
505
- * boot-checked, not discovered on the first row.
459
+ * `role: 'relay'` deployments deliver; `role: 'api'` only enqueues. The
460
+ * delivering process must ALSO register `webhookPlugin` with the shared
461
+ * subscription store (boot-checked).
506
462
  */
507
463
  function createDurableWebhookModule(options) {
508
464
  const { name, delivery, failurePolicy, fetch, timeout, validateUrl, resolveHeaders, ...outboxOptions } = options;
@@ -1,5 +1,5 @@
1
1
 
2
- import { c as SerializedEntry, i as Principal, n as FencedWriteOutcome, s as PushRefStore, t as EnvelopeSnapshot } from "../pushref-registry-Dov9l4qE.mjs";
2
+ import { c as SerializedEntry, i as Principal, n as FencedWriteOutcome, s as PushRefStore, t as EnvelopeSnapshot } from "../pushref-registry-UxCkoA4C.mjs";
3
3
  //#region src/integrations/websocket-pushref-redis.d.ts
4
4
  /**
5
5
  * Minimal `ioredis` shape — typed structurally so this file doesn't
@@ -7,7 +7,7 @@ import { c as SerializedEntry, i as Principal, n as FencedWriteOutcome, s as Pus
7
7
  * `optional: true` in package.json; hosts who don't use this subpath
8
8
  * never need to install ioredis.
9
9
  */
10
- interface RedisLike {
10
+ export interface RedisLike {
11
11
  get(key: string): Promise<string | null>;
12
12
  set(key: string, value: string, ...args: unknown[]): Promise<unknown>;
13
13
  del(key: string): Promise<number>;
@@ -15,7 +15,7 @@ interface RedisLike {
15
15
  scan(cursor: string | number, matchKw: "MATCH", pattern: string, countKw: "COUNT", count: number): Promise<[string, string[]]>;
16
16
  eval(script: string, numKeys: number, ...args: (string | number)[]): Promise<unknown>;
17
17
  }
18
- interface RedisPushRefStoreOptions {
18
+ export interface RedisPushRefStoreOptions {
19
19
  /** Redis key namespace. Default `arc:pushref:`. */
20
20
  keyPrefix?: string;
21
21
  /**
@@ -25,7 +25,7 @@ interface RedisPushRefStoreOptions {
25
25
  */
26
26
  graceMs?: number;
27
27
  }
28
- declare class RedisPushRefStore implements PushRefStore {
28
+ export declare class RedisPushRefStore implements PushRefStore {
29
29
  private redis;
30
30
  private prefix;
31
31
  private graceMs;
@@ -67,5 +67,4 @@ declare class RedisPushRefStore implements PushRefStore {
67
67
  gc(_now: number): Promise<number>;
68
68
  size(): Promise<number>;
69
69
  }
70
- //#endregion
71
- export { RedisLike, RedisPushRefStore, RedisPushRefStoreOptions };
70
+ //#endregion
@@ -1,4 +1,4 @@
1
- /*! @classytic/arc v2.41.1 | MIT | https://classytic.com */
1
+ /*! @classytic/arc v2.44.0 | MIT | https://classytic.com */
2
2
  //#region src/integrations/websocket-pushref-redis.ts
3
3
  /**
4
4
  * Lua script for atomic claim. Runs server-side, single-roundtrip.