@classytic/arc 2.41.1 → 2.43.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 (230) hide show
  1. package/dist/{EventTransport-CbUKDzZZ.d.mts → EventTransport-CiicKz2Z.d.mts} +1 -1
  2. package/dist/{EventTransport-DSqD_Xsg.mjs → EventTransport-tzA7CAB4.mjs} +22 -17
  3. package/dist/{HookSystem-CMF94SEA.mjs → HookSystem-B6UaKRbF.mjs} +2 -2
  4. package/dist/{QueryCache-DFA-QrU9.d.mts → QueryCache--5uAuYPW.d.mts} +7 -3
  5. package/dist/{ResourceRegistry-MGpXoxHZ.mjs → ResourceRegistry-CuCARu2B.mjs} +44 -13
  6. package/dist/actionPermissions-BotX1YOa.mjs +776 -0
  7. package/dist/audit/index.d.mts +43 -5
  8. package/dist/audit/index.mjs +48 -26
  9. package/dist/auth/audit.mjs +1 -1
  10. package/dist/auth/index.d.mts +43 -13
  11. package/dist/auth/index.mjs +130 -155
  12. package/dist/auth/redis-session.mjs +1 -1
  13. package/dist/{authorizationDecision-DawKGoXb.mjs → authorizationDecision-DQYrkl5f.mjs} +7 -102
  14. package/dist/{base-CxrV1LmG.d.mts → base-CZyixEQR.d.mts} +27 -5
  15. package/dist/boundedMap-BULzfXYU.mjs +57 -0
  16. package/dist/{buildHandler-CvxKOc0m.mjs → buildHandler-CRQ8EcX5.mjs} +4 -4
  17. package/dist/cache/index.d.mts +22 -34
  18. package/dist/cache/index.mjs +4 -4
  19. package/dist/{caching-DbCBxlgw.mjs → caching-Cc2M1DwO.mjs} +3 -3
  20. package/dist/claimed-B10RTfkM.mjs +8 -0
  21. package/dist/cleanup/index.d.mts +6 -10
  22. package/dist/cleanup/index.mjs +27 -73
  23. package/dist/cli/commands/describe.d.mts +4 -2
  24. package/dist/cli/commands/describe.mjs +7 -4
  25. package/dist/cli/commands/docs.mjs +3 -3
  26. package/dist/cli/commands/doctor.mjs +1 -1
  27. package/dist/cli/commands/generate.mjs +2 -2
  28. package/dist/cli/commands/init.mjs +2 -2
  29. package/dist/cli/commands/introspect.mjs +2 -2
  30. package/dist/cli/index.mjs +3 -3
  31. package/dist/{constants-D5Ubn1fe.mjs → constants-Cu7Mx7_U.mjs} +4 -2
  32. package/dist/context/index.d.mts +2 -5
  33. package/dist/context/index.mjs +18 -38
  34. package/dist/core/index.d.mts +3 -3
  35. package/dist/core/index.mjs +8 -8
  36. package/dist/{core-5lazhva9.mjs → core-Cb0LjccA.mjs} +5 -4
  37. package/dist/{createActionRouter-DNqoaK81.mjs → createActionRouter-xjqonMRO.mjs} +87 -39
  38. package/dist/{createAggregationRouter-BcbQJf10.mjs → createAggregationRouter-zRYWw_5D.mjs} +11 -8
  39. package/dist/{preloadResources-f3boenVZ.mjs → createApp-D-953ZAq.mjs} +299 -390
  40. package/dist/{createMcpServer-CbDWkZ1T.d.mts → createMcpServer-C9cxS8zt.d.mts} +1 -1
  41. package/dist/{defineAggregation-D_eJjm5A.mjs → defineAggregation-Bm8RjMbq.mjs} +1 -1
  42. package/dist/{defineResource-DxkMTjRn.mjs → defineResource-DIC7JZGU.mjs} +118 -1024
  43. package/dist/discovery/index.mjs +1 -1
  44. package/dist/docs/index.d.mts +10 -4
  45. package/dist/docs/index.mjs +4 -4
  46. package/dist/{elevation-BXCfVcNG.d.mts → elevation-BW2J3tNj.d.mts} +1 -2
  47. package/dist/{elevation-CkvG0LG2.mjs → elevation-DG8SrLN-.mjs} +41 -12
  48. package/dist/encryption/index.mjs +3 -3
  49. package/dist/{errorHandler-D1GHKGlV.mjs → errorHandler-BnfDviL1.mjs} +26 -5
  50. package/dist/{errors-BH7YU_9O.mjs → errors-CtrywMHP.mjs} +58 -23
  51. package/dist/{errors-BGdVwP25.d.mts → errors-JYc9vwqE.d.mts} +9 -0
  52. package/dist/{eventPlugin-B52abJ8W.d.mts → eventPlugin-BtmA4-3P.d.mts} +65 -9
  53. package/dist/{eventPlugin-zZY-unhr.mjs → eventPlugin-Q6BhzHzs.mjs} +82 -21
  54. package/dist/{subscribe-helpers-DatDkcuG.mjs → eventTypes-Cdhm6ZlV.mjs} +151 -17
  55. package/dist/events/index.d.mts +33 -8
  56. package/dist/events/index.mjs +49 -53
  57. package/dist/events/transports/redis-stream-entry.d.mts +1 -1
  58. package/dist/events/transports/redis-stream-entry.mjs +139 -157
  59. package/dist/events/transports/redis.d.mts +1 -1
  60. package/dist/events/transports/redis.mjs +2 -2
  61. package/dist/factory/index.d.mts +32 -17
  62. package/dist/factory/index.mjs +213 -32
  63. package/dist/{fastify-augmentation-Cq2nHVaW.d.mts → fastify-augmentation-DhUJ-mvo.d.mts} +11 -25
  64. package/dist/{fields-I_EeRWND.d.mts → fields-BBSpf8pS.d.mts} +1 -1
  65. package/dist/{fields-BHrkoF5y.mjs → fields-ToPYpMqS.mjs} +1 -1
  66. package/dist/filter-merge-IpGywvym.mjs +99 -0
  67. package/dist/{generate-BLRf2m3y.mjs → generate-DRTkLlIl.mjs} +2 -2
  68. package/dist/{headers-BIXWKrzQ.mjs → headers-BbkIF6vA.mjs} +2 -2
  69. package/dist/health-D-Ft6JM6.mjs +22 -0
  70. package/dist/{health-ZRHeDHXH.mjs → health-DqbsG-_w.mjs} +115 -55
  71. package/dist/hooks/index.d.mts +2 -2
  72. package/dist/hooks/index.mjs +2 -2
  73. package/dist/idempotency/index.d.mts +31 -7
  74. package/dist/idempotency/index.mjs +155 -98
  75. package/dist/idempotency/redis.d.mts +1 -1
  76. package/dist/idempotency/redis.mjs +48 -11
  77. package/dist/{index-BHdZD8zK.d.mts → index-Bxriq3so.d.mts} +84 -21
  78. package/dist/{preloadResources-CjS2OfpU.d.mts → index-C8zVGYsk.d.mts} +37 -38
  79. package/dist/{index-D4u76eQW.d.mts → index-CRoPtISf.d.mts} +23 -73
  80. package/dist/{index-CbJiYrfv.d.mts → index-Chgz3d0i.d.mts} +1 -1
  81. package/dist/index-JdbZAIsm.d.mts +510 -0
  82. package/dist/{index-DHbyPo5j.d.mts → index-mqaqZNpe.d.mts} +253 -254
  83. package/dist/index.d.mts +8 -8
  84. package/dist/index.mjs +11 -11
  85. package/dist/{init-CpLkjU18.mjs → init-B_oUYDXI.mjs} +1 -1
  86. package/dist/integrations/event-gateway.d.mts +8 -3
  87. package/dist/integrations/event-gateway.mjs +4 -4
  88. package/dist/integrations/index.d.mts +3 -3
  89. package/dist/integrations/index.mjs +1 -1
  90. package/dist/integrations/jobs/index.d.mts +1 -1
  91. package/dist/integrations/jobs/index.mjs +18 -5
  92. package/dist/integrations/mcp/index.d.mts +72 -59
  93. package/dist/integrations/mcp/index.mjs +19 -4
  94. package/dist/integrations/mcp/testing.d.mts +2 -2
  95. package/dist/integrations/mcp/testing.mjs +2 -2
  96. package/dist/integrations/streamline.d.mts +78 -136
  97. package/dist/integrations/streamline.mjs +116 -54
  98. package/dist/integrations/webhooks.d.mts +60 -120
  99. package/dist/integrations/webhooks.mjs +36 -80
  100. package/dist/integrations/websocket-pushref-redis.d.mts +1 -1
  101. package/dist/integrations/websocket-pushref-redis.mjs +1 -1
  102. package/dist/integrations/websocket-redis.d.mts +1 -1
  103. package/dist/integrations/websocket-redis.mjs +1 -1
  104. package/dist/integrations/websocket.d.mts +3 -3
  105. package/dist/integrations/websocket.mjs +2 -2
  106. package/dist/{interface-DEWNOMza.d.mts → interface-C0395yUN.d.mts} +13 -1
  107. package/dist/{jsonBody-ELR9lfeU.mjs → jsonBody-B4YVGcql.mjs} +2 -2
  108. package/dist/{keys-cpZCtcIE.mjs → keys-B2KtgMLJ.mjs} +7 -18
  109. package/dist/{loadResources-BPfWiI5s.mjs → loadResources-Dn4lalw2.mjs} +3 -3
  110. package/dist/{loadResourcesFromEntry-ixtfL3J2.mjs → loadResourcesFromEntry-3sLYbuse.mjs} +2 -2
  111. package/dist/logger/index.d.mts +1 -8
  112. package/dist/logger/index.mjs +3 -153
  113. package/dist/logger-DXjfIqjV.mjs +160 -0
  114. package/dist/{mcpPlugin-BX5PujI0.mjs → mcpPlugin-C08HalN5.mjs} +433 -170
  115. package/dist/{memory-Iotz0cCx.mjs → memory-9Cp7611C.mjs} +36 -34
  116. package/dist/{metrics-NMsmpRmY.mjs → metrics-CWI11eRu.mjs} +39 -72
  117. package/dist/middleware/index.d.mts +2 -2
  118. package/dist/middleware/index.mjs +3 -3
  119. package/dist/migrations/index.mjs +2 -2
  120. package/dist/{module-DpLJG2_N.mjs → module-BSvOQC7E.mjs} +8 -8
  121. package/dist/multiTenant-cY0FkDiA.d.mts +104 -0
  122. package/dist/{multipartBody-DtRWM6_U.mjs → multipartBody-CXM2a-Dy.mjs} +46 -7
  123. package/dist/{openapi-CmFf3yq_.mjs → openapi-DT_G4VhY.mjs} +48 -10
  124. package/dist/outbox-admin/index.d.mts +3 -3
  125. package/dist/outbox-admin/index.mjs +6 -18
  126. package/dist/{outbox-module-D2z5WkDY.mjs → outbox-module-Bjgzl6tg.mjs} +203 -33
  127. package/dist/{outbox-module-BHdUnuuK.d.mts → outbox-module-gZCHoE4H.d.mts} +79 -6
  128. package/dist/permissions/index.d.mts +5 -4
  129. package/dist/permissions/index.mjs +8 -6
  130. package/dist/{permissions-D8TBH44E.mjs → permissions-Cm0uYHvd.mjs} +149 -33
  131. package/dist/{pipe-FMWNheSO.mjs → pipe-BqeGT6UO.mjs} +2 -2
  132. package/dist/pipeline/index.d.mts +2 -2
  133. package/dist/pipeline/index.mjs +2 -2
  134. package/dist/plan-FVR6LP59.mjs +565 -0
  135. package/dist/{BaseController-CYEwjyjN.mjs → plugin-BccET1tL.mjs} +1256 -345
  136. package/dist/plugins/index.d.mts +14 -11
  137. package/dist/plugins/index.mjs +44 -22
  138. package/dist/plugins/response-cache.mjs +8 -14
  139. package/dist/plugins/tracing-entry.mjs +2 -2
  140. package/dist/{pluralize-CXWZIzTv.mjs → pluralize-CVIOcgMF.mjs} +1 -1
  141. package/dist/presets/filesUpload.d.mts +2 -2
  142. package/dist/presets/filesUpload.mjs +5 -5
  143. package/dist/presets/index.d.mts +26 -31
  144. package/dist/presets/index.mjs +4 -4
  145. package/dist/presets/multiTenant.d.mts +2 -107
  146. package/dist/presets/multiTenant.mjs +22 -162
  147. package/dist/presets/search.d.mts +2 -2
  148. package/dist/presets/search.mjs +2 -2
  149. package/dist/{presets-ZJSPgmT2.mjs → presets-Cpvj6fGO.mjs} +99 -107
  150. package/dist/{projection-BzIgzoKZ.mjs → projection-Ce4aQhQk.mjs} +2 -2
  151. package/dist/{pushref-registry-Dov9l4qE.d.mts → pushref-registry-UxCkoA4C.d.mts} +1 -1
  152. package/dist/{queryCachePlugin-BzqxconG.d.mts → queryCachePlugin-C2lZjTxk.d.mts} +1 -1
  153. package/dist/{queryCachePlugin-DZHPOm9p.mjs → queryCachePlugin-CFWNH8aT.mjs} +12 -14
  154. package/dist/{rateLimitKey-BvOvcBDi.mjs → rateLimitKey-C4O-kPm0.mjs} +1 -1
  155. package/dist/{realtime-8tIQWZz4.mjs → realtime-DmPE9S54.mjs} +25 -21
  156. package/dist/redis-stream-BMk-bpQS.d.mts +219 -0
  157. package/dist/{redis-Bzi1y3F9.d.mts → redis-vLvtcGy_.d.mts} +13 -24
  158. package/dist/redisShapes-CQksY67q.d.mts +55 -0
  159. package/dist/registry/index.d.mts +9 -4
  160. package/dist/registry/index.mjs +4 -4
  161. package/dist/{registry-DNkgGQcK.mjs → registry-Bcz9G1bh.mjs} +10 -8
  162. package/dist/{renewingLease-yF8BdW4b.mjs → renewingLease-qODa7KJQ.mjs} +1 -1
  163. package/dist/{replyHelpers-CNdvMfM4.mjs → replyHelpers-CG_VtVgS.mjs} +2 -2
  164. package/dist/{requestContext-cDkAUr-P.mjs → requestContext-Dsu9P1bV.mjs} +1 -1
  165. package/dist/requirement-CbiLdQ1n.d.mts +22 -0
  166. package/dist/requirement-KDdoRcOP.mjs +35 -0
  167. package/dist/resourceRateLimit-Wf6d6FL9.d.mts +30 -0
  168. package/dist/resourceRateLimit-dSjVr1pk.mjs +180 -0
  169. package/dist/{rolldown-runtime-eysyzFAF.mjs → rolldown-runtime-D1w9Y1VJ.mjs} +1 -1
  170. package/dist/routeValidator-D36SW8qt.mjs +70 -0
  171. package/dist/{runtimeCapabilities-CkhS6jn6.mjs → runtimeCapabilities-0tccjRlj.mjs} +1 -1
  172. package/dist/{schedules-j6uXXAeJ.mjs → schedules-C-OpmHBq.mjs} +46 -37
  173. package/dist/{schemaConverter-BVA7cFRG.mjs → schemaConverter-BORek3O-.mjs} +2 -2
  174. package/dist/{schemaIR-CTvPDoPB.mjs → schemaIR-C7Vbp71I.mjs} +2 -2
  175. package/dist/schemas/index.d.mts +10 -0
  176. package/dist/schemas/index.mjs +8 -3
  177. package/dist/scim/index.mjs +2 -2
  178. package/dist/scope/index.d.mts +2 -2
  179. package/dist/scope/index.mjs +6 -6
  180. package/dist/{scope-BUi1xV1I.mjs → scope-DQthe88b.mjs} +4 -4
  181. package/dist/shutdown-MqA81z_P.mjs +59 -0
  182. package/dist/{simpleEqualityMatcher-C9bzq2gO.mjs → simpleEqualityMatcher-auW8QjXN.mjs} +1 -1
  183. package/dist/{sse-BeqVKQKu.mjs → sse-CYzQBVFe.mjs} +11 -15
  184. package/dist/{sseStream-Ppzo-WUH.mjs → sseStream-B5_Afiqn.mjs} +2 -2
  185. package/dist/{store-helpers-dW71XVWL.mjs → store-helpers-BOzk-CuX.mjs} +1 -1
  186. package/dist/{streaming-IcRFjWQU.mjs → streaming-DjTQhZrN.mjs} +1 -1
  187. package/dist/subscriberIdentity-CW39uA8G.mjs +28 -0
  188. package/dist/tenancy--ed9eH_z.mjs +43 -0
  189. package/dist/tenantPreparation-BKon6Yox.mjs +67 -0
  190. package/dist/testing/cleanupStoreContract.mjs +1 -1
  191. package/dist/testing/index.d.mts +193 -175
  192. package/dist/testing/index.mjs +286 -212
  193. package/dist/testing/outboxStoreContract.mjs +137 -2
  194. package/dist/testing/requiredPorts.d.mts +17 -0
  195. package/dist/testing/requiredPorts.mjs +65 -0
  196. package/dist/testing/storageContract.mjs +1 -1
  197. package/dist/timing-qczZ0YC-.mjs +46 -0
  198. package/dist/{transactionContext-CPbdlnVw.mjs → transactionContext-DjFkrNG6.mjs} +1 -1
  199. package/dist/{typeGuards-BLjPgVBm.mjs → typeGuards-CySF12G-.mjs} +1 -1
  200. package/dist/types/index.d.mts +4 -4
  201. package/dist/types/index.mjs +1 -1
  202. package/dist/types/storage.mjs +1 -1
  203. package/dist/{types-CWQn9-IC.d.mts → types-B4Aq4Sdj.d.mts} +22 -9
  204. package/dist/{types-DfVDvkdP.d.mts → types-BJ-2XqVF.d.mts} +189 -204
  205. package/dist/{types-CgenB1gv.mjs → types-Bk9NOgOC.mjs} +4 -4
  206. package/dist/usage/index.d.mts +2 -3
  207. package/dist/usage/index.mjs +5 -5
  208. package/dist/{userHelpers-ChRvkuSZ.mjs → userHelpers-aklawRaK.mjs} +1 -1
  209. package/dist/utils/index.d.mts +84 -223
  210. package/dist/utils/index.mjs +13 -12
  211. package/dist/{utils-CxcctrAh.mjs → utils-ec1oTo1n.mjs} +315 -404
  212. package/dist/{validate-DrYWnOzI.mjs → validate-9yzHC1e2.mjs} +6 -14
  213. package/dist/{versioning-D6JIK8Tk.d.mts → versioning-Bsb51gGT.d.mts} +28 -13
  214. package/dist/{versioning-DKkT4hNJ.mjs → versioning-fhhWHV74.mjs} +4 -4
  215. package/dist/{websocket-BOXVggMf.d.mts → websocket-Cs4awVyD.d.mts} +1 -1
  216. package/dist/{workScope-CxKqoIFn.mjs → workScope-zpPyge4t.mjs} +2 -2
  217. package/package.json +16 -12
  218. package/skills/arc/SKILL.md +13 -4
  219. package/skills/arc/references/auth.md +8 -5
  220. package/skills/arc/references/integrations.md +1 -1
  221. package/skills/arc/references/mcp.md +8 -6
  222. package/skills/arc/references/multi-tenancy.md +19 -14
  223. package/skills/arc/references/production.md +27 -10
  224. package/dist/actionPermissions-BoPGQ5-4.mjs +0 -350
  225. package/dist/index-CFfLKMH4.d.mts +0 -290
  226. package/dist/interface-YzIbF7Ii.mjs +0 -17
  227. package/dist/redis-stream-BPKscsTB.d.mts +0 -260
  228. package/dist/registerRoute-Bsnk6NiZ.mjs +0 -364
  229. package/dist/sync/changelog.d.mts +0 -3
  230. package/dist/sync/changelog.mjs +0 -3
@@ -1,6 +1,6 @@
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
@@ -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. */
@@ -112,16 +88,9 @@ type WebhookSubscriptionPublic = Omit<WebhookSubscription, "secret">;
112
88
  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>`
@@ -204,6 +170,13 @@ type VerifyWebhookResult = {
204
170
  * ```
205
171
  */
206
172
  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
+ */
207
180
  declare function defaultWebhookUrlPolicy(url: URL): void;
208
181
  /** Options for `verifySignature` — customize for non-Arc webhook senders */
209
182
  interface VerifySignatureOptions {
@@ -219,47 +192,25 @@ 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
216
  declare function verifySignature(body: string | Buffer, secret: string, signature: string | undefined, options?: VerifySignatureOptions): boolean;
@@ -278,10 +229,8 @@ 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
235
  declare class WebhookDeliveryError extends Error {
287
236
  readonly status?: number;
@@ -294,9 +243,8 @@ declare class WebhookDeliveryError extends Error {
294
243
  }
295
244
  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,25 +252,20 @@ 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
270
  declare function createWebhookDeliveryTransport(options: WebhookDeliveryTransportOptions): EventTransport;
328
271
  interface WebhookDeliveryPolicyOptions {
@@ -334,10 +277,9 @@ 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
284
  declare function webhookDeliveryFailurePolicy(options?: WebhookDeliveryPolicyOptions): OutboxFailurePolicy;
343
285
  interface DurableWebhookModuleOptions extends Omit<OutboxModuleOptions, "name" | "transport" | "failurePolicy">, Pick<WebhookDeliveryTransportOptions, "fetch" | "timeout" | "validateUrl" | "resolveHeaders"> {
@@ -363,11 +305,9 @@ 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
312
  declare function createDurableWebhookModule(options: DurableWebhookModuleOptions): ReturnType<typeof createOutboxModule>;
373
313
  //#endregion
@@ -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.43.0 | MIT | https://classytic.com */
2
+ import { n as declareRuntimeCapability } from "../runtimeCapabilities-0tccjRlj.mjs";
3
+ import { i as exponentialBackoff, t as createOutboxModule } from "../outbox-module-Bjgzl6tg.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
@@ -1,4 +1,4 @@
1
- /*! @classytic/arc v2.41.1 | MIT | https://classytic.com */
1
+ /*! @classytic/arc v2.43.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.
@@ -1,5 +1,5 @@
1
1
 
2
- import { f as WebSocketAdapter } from "../websocket-BOXVggMf.mjs";
2
+ import { f as WebSocketAdapter } from "../websocket-Cs4awVyD.mjs";
3
3
  //#region src/integrations/websocket-redis.d.ts
4
4
  interface RedisLike {
5
5
  publish(channel: string, message: string): Promise<number>;
@@ -1,4 +1,4 @@
1
- /*! @classytic/arc v2.41.1 | MIT | https://classytic.com */
1
+ /*! @classytic/arc v2.43.0 | MIT | https://classytic.com */
2
2
  //#region src/integrations/websocket-redis.ts
3
3
  /**
4
4
  * Redis Pub/Sub adapter for cross-instance WebSocket broadcast.
@@ -1,4 +1,4 @@
1
1
 
2
- import { a as GeoRoomOptions, c as WebSocketClient, d as LocalWebSocketAdapter, f as WebSocketAdapter, i as GeoRoom, l as WebSocketMessage, n as GeoPoint, o as RoomManager, r as GeoPublishOpts, s as AuthResult, t as websocketPlugin, u as WebSocketPluginOptions } from "../websocket-BOXVggMf.mjs";
3
- import { a as PushRefRegistry, c as SerializedEntry, i as Principal, o as PushRefRegistryOptions, r as MemoryPushRefStore, s as PushRefStore } from "../pushref-registry-Dov9l4qE.mjs";
4
- export { type AuthResult, type GeoPoint, type GeoPublishOpts, GeoRoom, type GeoRoomOptions, LocalWebSocketAdapter, MemoryPushRefStore, type Principal, PushRefRegistry, type PushRefRegistryOptions, type PushRefStore, RoomManager, type SerializedEntry, type WebSocketAdapter, type WebSocketClient, type WebSocketMessage, type WebSocketPluginOptions, websocketPlugin };
2
+ import { a as GeoRoomOptions, c as WebSocketClient, d as LocalWebSocketAdapter, f as WebSocketAdapter, i as GeoRoom, l as WebSocketMessage, n as GeoPoint, o as RoomManager, r as GeoPublishOpts, s as AuthResult, t as websocketPlugin, u as WebSocketPluginOptions } from "../websocket-Cs4awVyD.mjs";
3
+ import { a as PushRefRegistry, c as SerializedEntry, i as Principal, o as PushRefRegistryOptions, r as MemoryPushRefStore, s as PushRefStore, u as DeadQueueEntry } from "../pushref-registry-UxCkoA4C.mjs";
4
+ export { type AuthResult, type DeadQueueEntry, type GeoPoint, type GeoPublishOpts, GeoRoom, type GeoRoomOptions, LocalWebSocketAdapter, MemoryPushRefStore, type Principal, PushRefRegistry, type PushRefRegistryOptions, type PushRefStore, RoomManager, type SerializedEntry, type WebSocketAdapter, type WebSocketClient, type WebSocketMessage, type WebSocketPluginOptions, websocketPlugin };
@@ -1,5 +1,5 @@
1
- /*! @classytic/arc v2.41.1 | MIT | https://classytic.com */
2
- import { n as declareRuntimeCapability } from "../runtimeCapabilities-CkhS6jn6.mjs";
1
+ /*! @classytic/arc v2.43.0 | MIT | https://classytic.com */
2
+ import { n as declareRuntimeCapability } from "../runtimeCapabilities-0tccjRlj.mjs";
3
3
  import { randomUUID } from "node:crypto";
4
4
  import sjson from "secure-json-parse";
5
5
  import fp from "fastify-plugin";
@@ -9,12 +9,20 @@
9
9
  interface IdempotencyResult {
10
10
  /** The idempotency key */
11
11
  key: string;
12
+ /**
13
+ * `fingerprintRequest` of the body the key was first used with. A retry under the same key
14
+ * with a different body is refused (422), never answered with this response. Absent on
15
+ * entries written before 2.43 — those replay without the check.
16
+ */
17
+ requestFingerprint?: string;
12
18
  /** HTTP status code of the cached response */
13
19
  statusCode: number;
14
20
  /** Response headers to replay */
15
21
  headers: Record<string, string>;
16
22
  /** Response body */
17
23
  body: unknown;
24
+ /** Binary responses are stored as base64 so JSON-backed stores preserve their bytes. */
25
+ bodyEncoding?: "base64";
18
26
  /** When this entry was created */
19
27
  createdAt: Date;
20
28
  /** When this entry expires */
@@ -33,6 +41,8 @@ interface IdempotencyLock {
33
41
  interface IdempotencyStore {
34
42
  /** Store name for logging */
35
43
  readonly name: string;
44
+ /** false when a conditional completion method cannot be atomic with this backing client. */
45
+ readonly supportsFencedCompletion?: boolean;
36
46
  /**
37
47
  * Get a cached result for an idempotency key.
38
48
  * Returns undefined if not found or expired.
@@ -43,6 +53,8 @@ interface IdempotencyStore {
43
53
  * TTL is handled by the store implementation.
44
54
  */
45
55
  set(key: string, result: Omit<IdempotencyResult, "key">): Promise<void>;
56
+ /** Atomically record only while requestId owns an unexpired lock. false means ownership lost. */
57
+ setIfLocked?(key: string, requestId: string, result: Omit<IdempotencyResult, "key">): Promise<boolean>;
46
58
  /**
47
59
  * Try to acquire a lock for processing a key.
48
60
  * Returns true if lock acquired, false if already locked.
@@ -73,6 +85,6 @@ interface IdempotencyStore {
73
85
  /**
74
86
  * Helper to create a result object
75
87
  */
76
- declare function createIdempotencyResult(statusCode: number, body: unknown, headers: Record<string, string>, ttlMs: number): Omit<IdempotencyResult, "key">;
88
+ declare function createIdempotencyResult(statusCode: number, body: unknown, headers: Record<string, string>, ttlMs: number, requestFingerprint?: string): Omit<IdempotencyResult, "key">;
77
89
  //#endregion
78
90
  export { createIdempotencyResult as i, IdempotencyResult as n, IdempotencyStore as r, IdempotencyLock as t };
@@ -1,5 +1,5 @@
1
- /*! @classytic/arc v2.41.1 | MIT | https://classytic.com */
2
- import { t as ArcError } from "./errors-BH7YU_9O.mjs";
1
+ /*! @classytic/arc v2.43.0 | MIT | https://classytic.com */
2
+ import { t as ArcError } from "./errors-CtrywMHP.mjs";
3
3
  import sjson from "secure-json-parse";
4
4
  //#region src/utils/jsonBody.ts
5
5
  /**
@@ -1,4 +1,5 @@
1
- /*! @classytic/arc v2.41.1 | MIT | https://classytic.com */
1
+ /*! @classytic/arc v2.43.0 | MIT | https://classytic.com */
2
+ import { contentHash } from "@classytic/repo-core/hash";
2
3
  //#region src/cache/keys.ts
3
4
  /**
4
5
  * Cache Key Utilities
@@ -20,25 +21,13 @@ function tagVersionKey(tag) {
20
21
  return `arc:tagver:${tag}`;
21
22
  }
22
23
  /**
23
- * Stable hash for query params.
24
- * Sorts keys recursively, serializes to JSON, then applies djb2 hash.
25
- * Returns hex string.
24
+ * Digest of the query params for a cache key — repo-core's canonical serialiser (escaped,
25
+ * type-preserving, key-order-free) under SHA-256. Two different queries never share a key: a
26
+ * delimiter inside a value, `1` vs `"1"`, two dates or two regexes all stay distinct, and a
27
+ * cryptographic digest leaves no crafted collision onto another caller's cached result.
26
28
  */
27
29
  function hashParams(params) {
28
- return djb2(stableStringify(params)).toString(36);
29
- }
30
- function stableStringify(value) {
31
- if (value === null || value === void 0) return "";
32
- if (typeof value !== "object") return String(value);
33
- if (Array.isArray(value)) return `[${value.map(stableStringify).join(",")}]`;
34
- if (value instanceof RegExp) return `/${value.source}/${value.flags}`;
35
- if (value instanceof Date) return `d${value.getTime()}`;
36
- return "{" + Object.keys(value).sort().map((k) => `${k}:${stableStringify(value[k])}`).join(",") + "}";
37
- }
38
- function djb2(str) {
39
- let hash = 5381;
40
- for (let i = 0; i < str.length; i++) hash = (hash << 5) + hash + str.charCodeAt(i) >>> 0;
41
- return hash;
30
+ return contentHash(params, { encoding: "base64url" });
42
31
  }
43
32
  //#endregion
44
33
  export { versionKey as i, hashParams as n, tagVersionKey as r, buildQueryKey as t };