@classytic/arc 2.41.0 → 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.
- package/dist/{EventTransport-CbUKDzZZ.d.mts → EventTransport-CiicKz2Z.d.mts} +1 -1
- package/dist/{EventTransport-n7qO5Cz3.mjs → EventTransport-tzA7CAB4.mjs} +22 -17
- package/dist/{HookSystem-CxvZBo77.mjs → HookSystem-B6UaKRbF.mjs} +2 -2
- package/dist/{QueryCache-DFA-QrU9.d.mts → QueryCache--5uAuYPW.d.mts} +7 -3
- package/dist/{ResourceRegistry-Cu7uTJp9.mjs → ResourceRegistry-CuCARu2B.mjs} +44 -13
- package/dist/actionPermissions-BotX1YOa.mjs +776 -0
- package/dist/audit/index.d.mts +43 -5
- package/dist/audit/index.mjs +48 -26
- package/dist/auth/audit.mjs +1 -1
- package/dist/auth/index.d.mts +43 -13
- package/dist/auth/index.mjs +130 -155
- package/dist/auth/redis-session.mjs +1 -1
- package/dist/{authorizationDecision-2YY_Kaj1.mjs → authorizationDecision-DQYrkl5f.mjs} +7 -102
- package/dist/{base-CxrV1LmG.d.mts → base-CZyixEQR.d.mts} +27 -5
- package/dist/boundedMap-BULzfXYU.mjs +57 -0
- package/dist/{buildHandler-CUhjGHGZ.mjs → buildHandler-CRQ8EcX5.mjs} +4 -4
- package/dist/cache/index.d.mts +22 -34
- package/dist/cache/index.mjs +4 -4
- package/dist/{caching-DsWDjIxx.mjs → caching-Cc2M1DwO.mjs} +3 -3
- package/dist/claimed-B10RTfkM.mjs +8 -0
- package/dist/cleanup/index.d.mts +6 -10
- package/dist/cleanup/index.mjs +27 -73
- package/dist/cli/commands/describe.d.mts +4 -2
- package/dist/cli/commands/describe.mjs +7 -4
- package/dist/cli/commands/docs.mjs +3 -3
- package/dist/cli/commands/doctor.mjs +1 -1
- package/dist/cli/commands/generate.mjs +2 -2
- package/dist/cli/commands/init.mjs +2 -2
- package/dist/cli/commands/introspect.mjs +2 -2
- package/dist/cli/index.mjs +3 -3
- package/dist/{constants-BxLgNFF7.mjs → constants-Cu7Mx7_U.mjs} +4 -2
- package/dist/context/index.d.mts +2 -5
- package/dist/context/index.mjs +18 -38
- package/dist/core/index.d.mts +3 -3
- package/dist/core/index.mjs +8 -8
- package/dist/{core-iNGqYerO.mjs → core-Cb0LjccA.mjs} +5 -4
- package/dist/{createActionRouter-BBlbPs3k.mjs → createActionRouter-xjqonMRO.mjs} +87 -39
- package/dist/{createAggregationRouter-Dv5SuK2x.mjs → createAggregationRouter-zRYWw_5D.mjs} +11 -8
- package/dist/{preloadResources-Bbp4QJ8V.mjs → createApp-D-953ZAq.mjs} +299 -390
- package/dist/{createMcpServer-CbDWkZ1T.d.mts → createMcpServer-C9cxS8zt.d.mts} +1 -1
- package/dist/{defineAggregation-UyeurGkp.mjs → defineAggregation-Bm8RjMbq.mjs} +1 -1
- package/dist/{defineResource-DzGT-2LP.mjs → defineResource-DIC7JZGU.mjs} +118 -1024
- package/dist/discovery/index.mjs +1 -1
- package/dist/docs/index.d.mts +10 -4
- package/dist/docs/index.mjs +4 -4
- package/dist/{elevation-BXCfVcNG.d.mts → elevation-BW2J3tNj.d.mts} +1 -2
- package/dist/{elevation-BF-vhGxY.mjs → elevation-DG8SrLN-.mjs} +41 -12
- package/dist/encryption/index.mjs +3 -3
- package/dist/{errorHandler-CIMkCOWm.mjs → errorHandler-BnfDviL1.mjs} +26 -5
- package/dist/{errors-C_YhELhe.mjs → errors-CtrywMHP.mjs} +58 -23
- package/dist/{errors-BGdVwP25.d.mts → errors-JYc9vwqE.d.mts} +9 -0
- package/dist/{eventPlugin-B52abJ8W.d.mts → eventPlugin-BtmA4-3P.d.mts} +65 -9
- package/dist/{eventPlugin-CmXaPWLv.mjs → eventPlugin-Q6BhzHzs.mjs} +82 -21
- package/dist/{subscribe-helpers-AVYPgqTl.mjs → eventTypes-Cdhm6ZlV.mjs} +151 -17
- package/dist/events/index.d.mts +33 -8
- package/dist/events/index.mjs +49 -53
- package/dist/events/transports/redis-stream-entry.d.mts +1 -1
- package/dist/events/transports/redis-stream-entry.mjs +139 -157
- package/dist/events/transports/redis.d.mts +1 -1
- package/dist/events/transports/redis.mjs +2 -2
- package/dist/factory/index.d.mts +32 -17
- package/dist/factory/index.mjs +213 -32
- package/dist/{fastify-augmentation-Cq2nHVaW.d.mts → fastify-augmentation-DhUJ-mvo.d.mts} +11 -25
- package/dist/{fields-I_EeRWND.d.mts → fields-BBSpf8pS.d.mts} +1 -1
- package/dist/{fields-BTyPLHK0.mjs → fields-ToPYpMqS.mjs} +1 -1
- package/dist/filter-merge-IpGywvym.mjs +99 -0
- package/dist/{generate-D_YmiCNf.mjs → generate-DRTkLlIl.mjs} +2 -2
- package/dist/{headers-CNECDE6q.mjs → headers-BbkIF6vA.mjs} +2 -2
- package/dist/health-D-Ft6JM6.mjs +22 -0
- package/dist/{health-CxQzbJmr.mjs → health-DqbsG-_w.mjs} +115 -55
- package/dist/hooks/index.d.mts +2 -2
- package/dist/hooks/index.mjs +2 -2
- package/dist/idempotency/index.d.mts +31 -7
- package/dist/idempotency/index.mjs +155 -98
- package/dist/idempotency/redis.d.mts +1 -1
- package/dist/idempotency/redis.mjs +48 -11
- package/dist/{index-BHdZD8zK.d.mts → index-Bxriq3so.d.mts} +84 -21
- package/dist/{preloadResources-CjS2OfpU.d.mts → index-C8zVGYsk.d.mts} +37 -38
- package/dist/{index-D4u76eQW.d.mts → index-CRoPtISf.d.mts} +23 -73
- package/dist/{index-CbJiYrfv.d.mts → index-Chgz3d0i.d.mts} +1 -1
- package/dist/index-JdbZAIsm.d.mts +510 -0
- package/dist/{index-DHbyPo5j.d.mts → index-mqaqZNpe.d.mts} +253 -254
- package/dist/index.d.mts +8 -8
- package/dist/index.mjs +11 -11
- package/dist/{init-Y2MlSN9n.mjs → init-B_oUYDXI.mjs} +1 -1
- package/dist/integrations/event-gateway.d.mts +8 -3
- package/dist/integrations/event-gateway.mjs +4 -4
- package/dist/integrations/index.d.mts +3 -3
- package/dist/integrations/index.mjs +1 -1
- package/dist/integrations/jobs/index.d.mts +1 -1
- package/dist/integrations/jobs/index.mjs +18 -5
- package/dist/integrations/mcp/index.d.mts +72 -59
- package/dist/integrations/mcp/index.mjs +19 -4
- package/dist/integrations/mcp/testing.d.mts +2 -2
- package/dist/integrations/mcp/testing.mjs +2 -2
- package/dist/integrations/streamline.d.mts +78 -136
- package/dist/integrations/streamline.mjs +116 -54
- package/dist/integrations/webhooks.d.mts +60 -120
- package/dist/integrations/webhooks.mjs +36 -80
- package/dist/integrations/websocket-pushref-redis.d.mts +1 -1
- package/dist/integrations/websocket-pushref-redis.mjs +1 -1
- package/dist/integrations/websocket-redis.d.mts +1 -1
- package/dist/integrations/websocket-redis.mjs +1 -1
- package/dist/integrations/websocket.d.mts +3 -3
- package/dist/integrations/websocket.mjs +2 -2
- package/dist/{interface-DEWNOMza.d.mts → interface-C0395yUN.d.mts} +13 -1
- package/dist/{jsonBody-Cq9KYl2Z.mjs → jsonBody-B4YVGcql.mjs} +2 -2
- package/dist/{keys-Cmv4cBRa.mjs → keys-B2KtgMLJ.mjs} +7 -18
- package/dist/{loadResources-CblF7xt2.mjs → loadResources-Dn4lalw2.mjs} +3 -3
- package/dist/{loadResourcesFromEntry-99fIMC3p.mjs → loadResourcesFromEntry-3sLYbuse.mjs} +2 -2
- package/dist/logger/index.d.mts +1 -8
- package/dist/logger/index.mjs +3 -153
- package/dist/logger-DXjfIqjV.mjs +160 -0
- package/dist/{mcpPlugin-xF3Gw-HW.mjs → mcpPlugin-C08HalN5.mjs} +433 -170
- package/dist/{memory-CpDZ9NPw.mjs → memory-9Cp7611C.mjs} +36 -34
- package/dist/{metrics-BaKnq8ag.mjs → metrics-CWI11eRu.mjs} +39 -72
- package/dist/middleware/index.d.mts +2 -2
- package/dist/middleware/index.mjs +3 -3
- package/dist/migrations/index.mjs +2 -2
- package/dist/{module-BkQEesNQ.mjs → module-BSvOQC7E.mjs} +8 -8
- package/dist/multiTenant-cY0FkDiA.d.mts +104 -0
- package/dist/{multipartBody-C92aJ8am.mjs → multipartBody-CXM2a-Dy.mjs} +46 -7
- package/dist/{openapi-C3LFRzPq.mjs → openapi-DT_G4VhY.mjs} +48 -10
- package/dist/outbox-admin/index.d.mts +3 -3
- package/dist/outbox-admin/index.mjs +6 -18
- package/dist/{outbox-module-dsCyLxXH.mjs → outbox-module-Bjgzl6tg.mjs} +203 -33
- package/dist/{outbox-module-BHdUnuuK.d.mts → outbox-module-gZCHoE4H.d.mts} +79 -6
- package/dist/permissions/index.d.mts +5 -4
- package/dist/permissions/index.mjs +8 -6
- package/dist/{permissions-DiHXy8Ck.mjs → permissions-Cm0uYHvd.mjs} +149 -33
- package/dist/{pipe-CBVmUJ-j.mjs → pipe-BqeGT6UO.mjs} +2 -2
- package/dist/pipeline/index.d.mts +2 -2
- package/dist/pipeline/index.mjs +2 -2
- package/dist/plan-FVR6LP59.mjs +565 -0
- package/dist/{BaseController-DYJRZ0EJ.mjs → plugin-BccET1tL.mjs} +1256 -344
- package/dist/plugins/index.d.mts +14 -11
- package/dist/plugins/index.mjs +44 -22
- package/dist/plugins/response-cache.mjs +8 -14
- package/dist/plugins/tracing-entry.mjs +2 -2
- package/dist/{pluralize-2_U6QZQg.mjs → pluralize-CVIOcgMF.mjs} +1 -1
- package/dist/presets/filesUpload.d.mts +2 -2
- package/dist/presets/filesUpload.mjs +5 -5
- package/dist/presets/index.d.mts +26 -31
- package/dist/presets/index.mjs +4 -4
- package/dist/presets/multiTenant.d.mts +2 -107
- package/dist/presets/multiTenant.mjs +22 -162
- package/dist/presets/search.d.mts +2 -2
- package/dist/presets/search.mjs +2 -2
- package/dist/{presets-BdlugYTc.mjs → presets-Cpvj6fGO.mjs} +99 -107
- package/dist/{projection-Uvgp-EEj.mjs → projection-Ce4aQhQk.mjs} +2 -2
- package/dist/{pushref-registry-Dov9l4qE.d.mts → pushref-registry-UxCkoA4C.d.mts} +1 -1
- package/dist/{queryCachePlugin-BzqxconG.d.mts → queryCachePlugin-C2lZjTxk.d.mts} +1 -1
- package/dist/{queryCachePlugin-B5F09bYr.mjs → queryCachePlugin-CFWNH8aT.mjs} +12 -14
- package/dist/{rateLimitKey-CK-EU2L8.mjs → rateLimitKey-C4O-kPm0.mjs} +1 -1
- package/dist/{realtime-FnnD0n3Z.mjs → realtime-DmPE9S54.mjs} +25 -21
- package/dist/redis-stream-BMk-bpQS.d.mts +219 -0
- package/dist/{redis-Bzi1y3F9.d.mts → redis-vLvtcGy_.d.mts} +13 -24
- package/dist/redisShapes-CQksY67q.d.mts +55 -0
- package/dist/registry/index.d.mts +9 -4
- package/dist/registry/index.mjs +4 -4
- package/dist/{registry-D3y8VEJ1.mjs → registry-Bcz9G1bh.mjs} +10 -8
- package/dist/{renewingLease-B3VYh5aU.mjs → renewingLease-qODa7KJQ.mjs} +1 -1
- package/dist/{replyHelpers-DPHsdw-5.mjs → replyHelpers-CG_VtVgS.mjs} +2 -2
- package/dist/{requestContext-cSVKnYPk.mjs → requestContext-Dsu9P1bV.mjs} +1 -1
- package/dist/requirement-CbiLdQ1n.d.mts +22 -0
- package/dist/requirement-KDdoRcOP.mjs +35 -0
- package/dist/resourceRateLimit-Wf6d6FL9.d.mts +30 -0
- package/dist/resourceRateLimit-dSjVr1pk.mjs +180 -0
- package/dist/{rolldown-runtime-DjDuyglE.mjs → rolldown-runtime-D1w9Y1VJ.mjs} +1 -1
- package/dist/routeValidator-D36SW8qt.mjs +70 -0
- package/dist/{runtimeCapabilities-DSA_dH1_.mjs → runtimeCapabilities-0tccjRlj.mjs} +1 -1
- package/dist/{schedules-BktJb3Y3.mjs → schedules-C-OpmHBq.mjs} +46 -37
- package/dist/{schemaConverter-BLCSmNbr.mjs → schemaConverter-BORek3O-.mjs} +2 -2
- package/dist/{schemaIR-K16Bb5BX.mjs → schemaIR-C7Vbp71I.mjs} +2 -2
- package/dist/schemas/index.d.mts +10 -0
- package/dist/schemas/index.mjs +8 -3
- package/dist/scim/index.mjs +2 -2
- package/dist/scope/index.d.mts +2 -2
- package/dist/scope/index.mjs +6 -6
- package/dist/{scope-jSxuBTFM.mjs → scope-DQthe88b.mjs} +4 -4
- package/dist/shutdown-MqA81z_P.mjs +59 -0
- package/dist/{simpleEqualityMatcher-BiOnyflP.mjs → simpleEqualityMatcher-auW8QjXN.mjs} +1 -1
- package/dist/{sse-CH7Slxc1.mjs → sse-CYzQBVFe.mjs} +11 -15
- package/dist/{sseStream-CN-Lh1Tu.mjs → sseStream-B5_Afiqn.mjs} +2 -2
- package/dist/{store-helpers-9DX9-1j_.mjs → store-helpers-BOzk-CuX.mjs} +1 -1
- package/dist/{streaming-dvppnxsr.mjs → streaming-DjTQhZrN.mjs} +1 -1
- package/dist/subscriberIdentity-CW39uA8G.mjs +28 -0
- package/dist/tenancy--ed9eH_z.mjs +43 -0
- package/dist/tenantPreparation-BKon6Yox.mjs +67 -0
- package/dist/testing/cleanupStoreContract.mjs +1 -1
- package/dist/testing/index.d.mts +193 -175
- package/dist/testing/index.mjs +286 -212
- package/dist/testing/outboxStoreContract.mjs +137 -2
- package/dist/testing/requiredPorts.d.mts +17 -0
- package/dist/testing/requiredPorts.mjs +65 -0
- package/dist/testing/storageContract.mjs +1 -1
- package/dist/timing-qczZ0YC-.mjs +46 -0
- package/dist/{transactionContext-DCWMI6hV.mjs → transactionContext-DjFkrNG6.mjs} +1 -1
- package/dist/{typeGuards-D7tHFdH6.mjs → typeGuards-CySF12G-.mjs} +1 -1
- package/dist/types/index.d.mts +4 -4
- package/dist/types/index.mjs +1 -1
- package/dist/types/storage.mjs +1 -1
- package/dist/{types-CWQn9-IC.d.mts → types-B4Aq4Sdj.d.mts} +22 -9
- package/dist/{types-DfVDvkdP.d.mts → types-BJ-2XqVF.d.mts} +189 -204
- package/dist/{types-CDy3-zw1.mjs → types-Bk9NOgOC.mjs} +4 -4
- package/dist/usage/index.d.mts +2 -3
- package/dist/usage/index.mjs +5 -5
- package/dist/{userHelpers-DDMOZ_Pm.mjs → userHelpers-aklawRaK.mjs} +1 -1
- package/dist/utils/index.d.mts +84 -223
- package/dist/utils/index.mjs +13 -12
- package/dist/{utils-B-zj_b__.mjs → utils-ec1oTo1n.mjs} +315 -404
- package/dist/{validate-R0G_JrnK.mjs → validate-9yzHC1e2.mjs} +6 -14
- package/dist/{versioning-D6JIK8Tk.d.mts → versioning-Bsb51gGT.d.mts} +28 -13
- package/dist/{versioning-DuVWcu-F.mjs → versioning-fhhWHV74.mjs} +4 -4
- package/dist/{websocket-BOXVggMf.d.mts → websocket-Cs4awVyD.d.mts} +1 -1
- package/dist/{workScope-BhsWde4k.mjs → workScope-zpPyge4t.mjs} +2 -2
- package/package.json +578 -574
- package/skills/arc/SKILL.md +13 -4
- package/skills/arc/references/auth.md +8 -5
- package/skills/arc/references/integrations.md +1 -1
- package/skills/arc/references/mcp.md +8 -6
- package/skills/arc/references/multi-tenancy.md +19 -14
- package/skills/arc/references/production.md +27 -10
- package/dist/actionPermissions-DPwPWPOV.mjs +0 -350
- package/dist/index-CFfLKMH4.d.mts +0 -290
- package/dist/interface-BIpgzbZl.mjs +0 -17
- package/dist/redis-stream-BPKscsTB.d.mts +0 -260
- package/dist/registerRoute-DRsSHPSM.mjs +0 -364
- package/dist/sync/changelog.d.mts +0 -3
- package/dist/sync/changelog.mjs +0 -3
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
|
|
2
|
-
import { a as EventTransport, n as DomainEvent } from "../EventTransport-
|
|
3
|
-
import {
|
|
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
|
|
53
|
-
*
|
|
54
|
-
*
|
|
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
|
-
*
|
|
60
|
-
*
|
|
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
|
|
72
|
-
*
|
|
73
|
-
*
|
|
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
|
-
*
|
|
87
|
-
*
|
|
88
|
-
*
|
|
89
|
-
*
|
|
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
|
-
*
|
|
94
|
-
*
|
|
95
|
-
*
|
|
96
|
-
*
|
|
97
|
-
*
|
|
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
|
-
*
|
|
135
|
-
*
|
|
136
|
-
* v1.<timestampMs>.<deliveryId>.<body>
|
|
103
|
+
* Sign a v1 webhook. Signed string: `v1.<timestampMs>.<deliveryId>.<body>`.
|
|
137
104
|
*
|
|
138
|
-
*
|
|
139
|
-
*
|
|
140
|
-
*
|
|
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
|
-
*
|
|
223
|
-
*
|
|
224
|
-
*
|
|
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
|
-
* ⚠
|
|
231
|
-
*
|
|
232
|
-
*
|
|
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
|
-
* @
|
|
237
|
-
*
|
|
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
|
|
282
|
-
*
|
|
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)
|
|
298
|
-
*
|
|
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
|
-
*
|
|
308
|
-
*
|
|
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
|
-
*
|
|
319
|
-
*
|
|
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
|
-
*
|
|
324
|
-
*
|
|
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
|
|
338
|
-
*
|
|
339
|
-
*
|
|
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
|
-
*
|
|
367
|
-
*
|
|
368
|
-
*
|
|
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.
|
|
2
|
-
import { n as declareRuntimeCapability } from "../runtimeCapabilities-
|
|
3
|
-
import { i as exponentialBackoff, t as createOutboxModule } from "../outbox-module-
|
|
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
|
-
*
|
|
25
|
+
* Sign a v1 webhook. Signed string: `v1.<timestampMs>.<deliveryId>.<body>`.
|
|
26
26
|
*
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
*
|
|
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
|
|
103
|
-
*
|
|
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
|
-
*
|
|
107
|
-
*
|
|
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
|
-
*
|
|
140
|
-
*
|
|
141
|
-
*
|
|
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
|
-
*
|
|
145
|
-
*
|
|
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
|
-
*
|
|
148
|
-
*
|
|
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
|
-
|
|
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
|
|
380
|
-
*
|
|
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
|
-
*
|
|
397
|
-
*
|
|
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
|
-
*
|
|
402
|
-
*
|
|
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
|
|
468
|
-
*
|
|
469
|
-
*
|
|
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
|
-
*
|
|
502
|
-
*
|
|
503
|
-
*
|
|
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-
|
|
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,5 +1,5 @@
|
|
|
1
1
|
|
|
2
|
-
import { f as WebSocketAdapter } from "../websocket-
|
|
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
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-
|
|
3
|
-
import { a as PushRefRegistry, c as SerializedEntry, i as Principal, o as PushRefRegistryOptions, r as MemoryPushRefStore, s as PushRefStore } from "../pushref-registry-
|
|
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.
|
|
2
|
-
import { n as declareRuntimeCapability } from "../runtimeCapabilities-
|
|
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.
|
|
2
|
-
import { t as ArcError } from "./errors-
|
|
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.
|
|
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
|
-
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
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
|
|
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 };
|