@rotorsoft/act-http 1.8.3 → 1.8.5
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/README.md +31 -11
- package/dist/.tsbuildinfo +1 -1
- package/dist/@types/api/idempotency.d.ts +10 -3
- package/dist/@types/api/idempotency.d.ts.map +1 -1
- package/dist/@types/hono/index.d.ts.map +1 -1
- package/dist/@types/openapi/index.d.ts.map +1 -1
- package/dist/@types/receiver/check.d.ts +8 -4
- package/dist/@types/receiver/check.d.ts.map +1 -1
- package/dist/@types/receiver/express/index.d.ts +23 -2
- package/dist/@types/receiver/express/index.d.ts.map +1 -1
- package/dist/@types/receiver/fastify/index.d.ts +22 -3
- package/dist/@types/receiver/fastify/index.d.ts.map +1 -1
- package/dist/@types/receiver/finalize.d.ts +34 -0
- package/dist/@types/receiver/finalize.d.ts.map +1 -0
- package/dist/@types/receiver/hono/index.d.ts +15 -0
- package/dist/@types/receiver/hono/index.d.ts.map +1 -1
- package/dist/@types/receiver/start.d.ts.map +1 -1
- package/dist/@types/receiver/trpc/index.d.ts.map +1 -1
- package/dist/api/index.cjs +9 -1
- package/dist/api/index.cjs.map +1 -1
- package/dist/api/index.js +1 -1
- package/dist/{chunk-4CGAUB5H.js → chunk-DMDU25DN.js} +23 -2
- package/dist/chunk-DMDU25DN.js.map +1 -0
- package/dist/{chunk-OQKYUDEK.js → chunk-FIYLGJAE.js} +10 -2
- package/dist/{chunk-OQKYUDEK.js.map → chunk-FIYLGJAE.js.map} +1 -1
- package/dist/{chunk-K4HAOBRF.js → chunk-M7XFCGYR.js} +23 -5
- package/dist/chunk-M7XFCGYR.js.map +1 -0
- package/dist/hono/index.cjs +26 -2
- package/dist/hono/index.cjs.map +1 -1
- package/dist/hono/index.js +18 -2
- package/dist/hono/index.js.map +1 -1
- package/dist/openapi/index.cjs +22 -2
- package/dist/openapi/index.cjs.map +1 -1
- package/dist/openapi/index.js +22 -2
- package/dist/openapi/index.js.map +1 -1
- package/dist/receiver/express/index.cjs +28 -1
- package/dist/receiver/express/index.cjs.map +1 -1
- package/dist/receiver/express/index.js +11 -3
- package/dist/receiver/express/index.js.map +1 -1
- package/dist/receiver/fastify/index.cjs +31 -1
- package/dist/receiver/fastify/index.cjs.map +1 -1
- package/dist/receiver/fastify/index.js +14 -3
- package/dist/receiver/fastify/index.js.map +1 -1
- package/dist/receiver/hono/index.cjs +39 -2
- package/dist/receiver/hono/index.cjs.map +1 -1
- package/dist/receiver/hono/index.js +2 -2
- package/dist/receiver/index.cjs +41 -2
- package/dist/receiver/index.cjs.map +1 -1
- package/dist/receiver/index.js +4 -2
- package/dist/receiver/index.js.map +1 -1
- package/dist/receiver/trpc/index.cjs +48 -6
- package/dist/receiver/trpc/index.cjs.map +1 -1
- package/dist/receiver/trpc/index.js +31 -8
- package/dist/receiver/trpc/index.js.map +1 -1
- package/dist/trpc/index.cjs +9 -1
- package/dist/trpc/index.cjs.map +1 -1
- package/dist/trpc/index.js +1 -1
- package/package.json +4 -4
- package/dist/chunk-4CGAUB5H.js.map +0 -1
- package/dist/chunk-K4HAOBRF.js.map +0 -1
|
@@ -23,9 +23,16 @@ export type IdempotencyResult<T> = {
|
|
|
23
23
|
};
|
|
24
24
|
/**
|
|
25
25
|
* Wrap an action handler so the framework honors `Idempotency-Key`
|
|
26
|
-
* dedup. Acquires the key via {@link IdempotencyStore.claim}
|
|
27
|
-
* the handler exactly when the claim
|
|
28
|
-
* handler entirely on a duplicate.
|
|
26
|
+
* dedup. Acquires the key via {@link IdempotencyStore.claim} (a
|
|
27
|
+
* *tentative* reservation), runs the handler exactly when the claim
|
|
28
|
+
* was fresh, and skips the handler entirely on a duplicate.
|
|
29
|
+
*
|
|
30
|
+
* The claim is finalized two-phase: on handler success the key is
|
|
31
|
+
* {@link IdempotencyStore.commit committed} so later retries dedup;
|
|
32
|
+
* on handler failure it is {@link IdempotencyStore.release released}
|
|
33
|
+
* and the rejection re-thrown, so the sender's retry re-processes
|
|
34
|
+
* instead of the delivery being permanently dropped. The tentative
|
|
35
|
+
* claim still dedups a concurrent duplicate mid-flight.
|
|
29
36
|
*
|
|
30
37
|
* Reuses the contract `@rotorsoft/act-ops/idempotency` already
|
|
31
38
|
* defines for the receiver-side `Idempotency-Key` story. A single
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"idempotency.d.ts","sourceRoot":"","sources":["../../../src/api/idempotency.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,gCAAgC,CAAC;AAEvE;;;;;;;;;;;;;;;GAeG;AACH,MAAM,MAAM,iBAAiB,CAAC,CAAC,IAC3B;IAAE,OAAO,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,CAAC,CAAA;CAAE,GAC7B;IAAE,OAAO,EAAE,IAAI,CAAA;CAAE,CAAC;AAEtB
|
|
1
|
+
{"version":3,"file":"idempotency.d.ts","sourceRoot":"","sources":["../../../src/api/idempotency.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,gCAAgC,CAAC;AAEvE;;;;;;;;;;;;;;;GAeG;AACH,MAAM,MAAM,iBAAiB,CAAC,CAAC,IAC3B;IAAE,OAAO,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,CAAC,CAAA;CAAE,GAC7B;IAAE,OAAO,EAAE,IAAI,CAAA;CAAE,CAAC;AAEtB;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAsB,eAAe,CAAC,CAAC,EACrC,KAAK,EAAE,gBAAgB,EACvB,GAAG,EAAE,MAAM,EACX,OAAO,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GACxB,OAAO,CAAC,iBAAiB,CAAC,CAAC,CAAC,CAAC,CAc/B"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/hono/index.ts"],"names":[],"mappings":"AAgDA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,gBAAgB,CAAC;AACpD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,gCAAgC,CAAC;AACvE,OAAO,KAAK,EAAE,OAAO,EAAE,iBAAiB,EAAE,MAAM,MAAM,CAAC;AACvD,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAE5B,OAAO,EACL,KAAK,cAAc,EAMnB,KAAK,UAAU,EAGhB,MAAM,iBAAiB,CAAC;AAEzB;;;;;GAKG;AACH,MAAM,MAAM,sBAAsB,GAAG;IACnC,KAAK,EAAE,KAAK,CAAC;CACd,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,MAAM,MAAM,WAAW,GAAG;IACxB,QAAQ,CAAC,KAAK,EAAE,cAAc,CAAC;IAC/B,QAAQ,CAAC,MAAM,EAAE,CACf,WAAW,EAAE,MAAM,EACnB,KAAK,EAAE,OAAO,EACd,CAAC,EAAE,OAAO,KACP,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IAC9B,QAAQ,CAAC,eAAe,CAAC,EAAE,CACzB,WAAW,EAAE,MAAM,EACnB,KAAK,EAAE,OAAO,EACd,CAAC,EAAE,OAAO,KACP,MAAM,GAAG,SAAS,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAAC;IACtD,QAAQ,CAAC,WAAW,CAAC,EAAE;QACrB,QAAQ,CAAC,KAAK,EAAE,gBAAgB,CAAC;QACjC,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,EAAE,OAAO,KAAK,MAAM,GAAG,SAAS,CAAC;KACvD,CAAC;IACF;;;;;;;;;;;;;;;OAeG;IACH,QAAQ,CAAC,GAAG,CAAC,EAAE,UAAU,CAAC;IAC1B,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;CAC5B,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,aAAa,CAC3B,SAAS,EAAE,cAAc,GACxB,iBAAiB,CAAC;IAAE,SAAS,EAAE,sBAAsB,CAAA;CAAE,CAAC,CAe1D;AAKD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH;;;;;;;;GAQG;AACH,KAAK,UAAU,GAAG;IAChB,QAAQ,CAAC,QAAQ,EAAE;QACjB,OAAO,EAAE,MAAM,CACb,MAAM,EACN;YACE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;YACtB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;SAC3C,CACF,CAAC;KACH,CAAC;IACF,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;CACxE,CAAC;AAEF,wBAAgB,IAAI,CAAC,IAAI,SAAS,UAAU,GAAG,UAAU,EACvD,GAAG,EAAE,IAAI,EACT,OAAO,EAAE,WAAW,GACnB,IAAI,CAAC;IAAE,SAAS,EAAE,sBAAsB,CAAA;CAAE,CAAC,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/hono/index.ts"],"names":[],"mappings":"AAgDA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,gBAAgB,CAAC;AACpD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,gCAAgC,CAAC;AACvE,OAAO,KAAK,EAAE,OAAO,EAAE,iBAAiB,EAAE,MAAM,MAAM,CAAC;AACvD,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAE5B,OAAO,EACL,KAAK,cAAc,EAMnB,KAAK,UAAU,EAGhB,MAAM,iBAAiB,CAAC;AAEzB;;;;;GAKG;AACH,MAAM,MAAM,sBAAsB,GAAG;IACnC,KAAK,EAAE,KAAK,CAAC;CACd,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,MAAM,MAAM,WAAW,GAAG;IACxB,QAAQ,CAAC,KAAK,EAAE,cAAc,CAAC;IAC/B,QAAQ,CAAC,MAAM,EAAE,CACf,WAAW,EAAE,MAAM,EACnB,KAAK,EAAE,OAAO,EACd,CAAC,EAAE,OAAO,KACP,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IAC9B,QAAQ,CAAC,eAAe,CAAC,EAAE,CACzB,WAAW,EAAE,MAAM,EACnB,KAAK,EAAE,OAAO,EACd,CAAC,EAAE,OAAO,KACP,MAAM,GAAG,SAAS,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAAC;IACtD,QAAQ,CAAC,WAAW,CAAC,EAAE;QACrB,QAAQ,CAAC,KAAK,EAAE,gBAAgB,CAAC;QACjC,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,EAAE,OAAO,KAAK,MAAM,GAAG,SAAS,CAAC;KACvD,CAAC;IACF;;;;;;;;;;;;;;;OAeG;IACH,QAAQ,CAAC,GAAG,CAAC,EAAE,UAAU,CAAC;IAC1B,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;CAC5B,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,aAAa,CAC3B,SAAS,EAAE,cAAc,GACxB,iBAAiB,CAAC;IAAE,SAAS,EAAE,sBAAsB,CAAA;CAAE,CAAC,CAe1D;AAKD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH;;;;;;;;GAQG;AACH,KAAK,UAAU,GAAG;IAChB,QAAQ,CAAC,QAAQ,EAAE;QACjB,OAAO,EAAE,MAAM,CACb,MAAM,EACN;YACE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;YACtB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;SAC3C,CACF,CAAC;KACH,CAAC;IACF,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;CACxE,CAAC;AAEF,wBAAgB,IAAI,CAAC,IAAI,SAAS,UAAU,GAAG,UAAU,EACvD,GAAG,EAAE,IAAI,EACT,OAAO,EAAE,WAAW,GACnB,IAAI,CAAC;IAAE,SAAS,EAAE,sBAAsB,CAAA;CAAE,CAAC,CA4J7C"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/openapi/index.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/openapi/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -2,11 +2,15 @@ import type { IdempotencyStore } from "@rotorsoft/act-ops/idempotency";
|
|
|
2
2
|
import { type VerifyOptions } from "./verify.js";
|
|
3
3
|
/**
|
|
4
4
|
* Failure reasons returned by {@link checkWebhook}. The shape splits
|
|
5
|
-
* `missing-key`
|
|
6
|
-
* verification failures (authentication errors, HTTP 401)
|
|
7
|
-
* maps to its own telemetry bucket.
|
|
5
|
+
* client/configuration errors (`missing-key`, `empty-body` — HTTP 400)
|
|
6
|
+
* from the five verification failures (authentication errors, HTTP 401)
|
|
7
|
+
* so each maps to its own telemetry bucket. `empty-body` is the
|
|
8
|
+
* misconfigured-raw-parser signal: `secret` is set but the resolved
|
|
9
|
+
* body is empty, so hashing it would compute an HMAC over `${ts}.` and
|
|
10
|
+
* reject every otherwise-valid signed request with a misleading
|
|
11
|
+
* bad-signature — a distinct config error is far easier to diagnose.
|
|
8
12
|
*/
|
|
9
|
-
export type CheckFailureReason = "missing-key" | "missing-signature" | "missing-timestamp" | "stale" | "future" | "bad-signature";
|
|
13
|
+
export type CheckFailureReason = "missing-key" | "empty-body" | "missing-signature" | "missing-timestamp" | "stale" | "future" | "bad-signature";
|
|
10
14
|
/**
|
|
11
15
|
* Outcome of {@link checkWebhook}. Either the request passed every
|
|
12
16
|
* configured check and carries a usable idempotency key, or it
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"check.d.ts","sourceRoot":"","sources":["../../../src/receiver/check.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,gCAAgC,CAAC;AAEvE,OAAO,EAAE,KAAK,aAAa,EAAiB,MAAM,aAAa,CAAC;AAEhE
|
|
1
|
+
{"version":3,"file":"check.d.ts","sourceRoot":"","sources":["../../../src/receiver/check.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,gCAAgC,CAAC;AAEvE,OAAO,EAAE,KAAK,aAAa,EAAiB,MAAM,aAAa,CAAC;AAEhE;;;;;;;;;GASG;AACH,MAAM,MAAM,kBAAkB,GAC1B,aAAa,GACb,YAAY,GACZ,mBAAmB,GACnB,mBAAmB,GACnB,OAAO,GACP,QAAQ,GACR,eAAe,CAAC;AAEpB;;;;;GAKG;AACH,MAAM,MAAM,WAAW,GACnB;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,GAAG,GAAG,GAAG,CAAC;IAAC,MAAM,EAAE,kBAAkB,CAAA;CAAE,GAC5D;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,GAAG,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,OAAO,CAAA;CAAE,CAAC;AAEhD,wCAAwC;AACxC,MAAM,MAAM,mBAAmB,GAAG;IAChC,uEAAuE;IACvE,KAAK,EAAE,gBAAgB,CAAC;IACxB;;;;;OAKG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB;;;;OAIG;IACH,MAAM,CAAC,EAAE,aAAa,CAAC;CACxB,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAsB,YAAY,CAChC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,SAAS,CAAC,EACtD,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,mBAAmB,GAC3B,OAAO,CAAC,WAAW,CAAC,CAyBtB"}
|
|
@@ -32,16 +32,37 @@
|
|
|
32
32
|
* On failure: responds with the framework-idiomatic JSON shape
|
|
33
33
|
* `{ error: <reason> }` at status 400 (missing-key) or 401
|
|
34
34
|
* (verification failures), and does not call `next()`. On success:
|
|
35
|
-
* attaches `req.idempotency = { key, deduped }` and
|
|
35
|
+
* attaches `req.idempotency = { key, deduped, commit, release }` and
|
|
36
|
+
* calls `next()`.
|
|
37
|
+
*
|
|
38
|
+
* **Two-phase dedup**: the claim is *tentative*. Express runs the
|
|
39
|
+
* middleware to completion before the route handler, so — unlike the
|
|
40
|
+
* Hono adapter — it can't observe the handler's outcome to finalize
|
|
41
|
+
* automatically. The route handler **must** call
|
|
42
|
+
* `req.idempotency.commit()` on success or `req.idempotency.release()`
|
|
43
|
+
* on a transient failure. Skipping both leaves the claim tentative:
|
|
44
|
+
* it dedups concurrent duplicates but expires on TTL, so the delivery
|
|
45
|
+
* is never permanently lost — it just isn't durably deduped either.
|
|
36
46
|
*
|
|
37
47
|
* **Raw body requirement**: when `secret` is configured, mount
|
|
38
48
|
* `express.raw({ type: "application/json" })` (or whatever
|
|
39
49
|
* content-type your webhooks use) ahead of the receiver middleware.
|
|
40
50
|
* The middleware reads `req.body` as a `Buffer | string` and converts
|
|
41
|
-
* to a UTF-8 string for hashing. Skip when unsigned.
|
|
51
|
+
* to a UTF-8 string for hashing. Skip when unsigned. If the raw parser
|
|
52
|
+
* isn't mounted (the default `express.json()` ate the bytes, leaving a
|
|
53
|
+
* parsed object the adapter can't hash), the middleware short-circuits
|
|
54
|
+
* with `400 { error: "empty-body" }` instead of hashing an empty string
|
|
55
|
+
* and rejecting every valid request with a misleading `401
|
|
56
|
+
* bad-signature`.
|
|
42
57
|
*/
|
|
43
58
|
import type { RequestHandler } from "express";
|
|
44
59
|
import { type CheckWebhookOptions } from "../check.js";
|
|
60
|
+
import { type Finalizers } from "../finalize.js";
|
|
61
|
+
/** Shape attached to `req.idempotency` by the Express adapter. */
|
|
62
|
+
export type ExpressIdempotency = {
|
|
63
|
+
key: string;
|
|
64
|
+
deduped: boolean;
|
|
65
|
+
} & Finalizers;
|
|
45
66
|
/**
|
|
46
67
|
* Build an Express middleware that verifies the request signature
|
|
47
68
|
* (when `secret` is set), enforces `Idempotency-Key`, and claims the
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/receiver/express/index.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/receiver/express/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwDG;AACH,OAAO,KAAK,EAAyB,cAAc,EAAY,MAAM,SAAS,CAAC;AAC/E,OAAO,EAAE,KAAK,mBAAmB,EAAgB,MAAM,aAAa,CAAC;AACrE,OAAO,EAAE,KAAK,UAAU,EAAmB,MAAM,gBAAgB,CAAC;AAElE,kEAAkE;AAClE,MAAM,MAAM,kBAAkB,GAAG;IAC/B,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,EAAE,OAAO,CAAC;CAClB,GAAG,UAAU,CAAC;AAEf;;;;GAIG;AACH,wBAAgB,iBAAiB,CAC/B,OAAO,EAAE,mBAAmB,GAC3B,cAAc,CA6BhB"}
|
|
@@ -33,18 +33,37 @@
|
|
|
33
33
|
*
|
|
34
34
|
* On failure: replies with `{ error: <reason> }` at status 400
|
|
35
35
|
* (missing-key) or 401 (verification failures). On success: attaches
|
|
36
|
-
* `request.idempotency = { key, deduped }` and lets
|
|
37
|
-
* run.
|
|
36
|
+
* `request.idempotency = { key, deduped, commit, release }` and lets
|
|
37
|
+
* the route handler run.
|
|
38
|
+
*
|
|
39
|
+
* **Two-phase dedup**: the claim is *tentative*. A Fastify
|
|
40
|
+
* `preHandler` runs to completion before the route handler, so — like
|
|
41
|
+
* the Express adapter — it can't finalize automatically. The route
|
|
42
|
+
* handler **must** call `request.idempotency.commit()` on success or
|
|
43
|
+
* `request.idempotency.release()` on a transient failure. Skipping
|
|
44
|
+
* both leaves the claim tentative: it dedups concurrent duplicates but
|
|
45
|
+
* expires on TTL, so a delivery is never permanently lost — it just
|
|
46
|
+
* isn't durably deduped either.
|
|
38
47
|
*
|
|
39
48
|
* **Raw body requirement**: when `secret` is configured, register a
|
|
40
49
|
* content-type parser that preserves the raw body string. Fastify's
|
|
41
50
|
* default JSON parser eats the bytes — register a custom parser via
|
|
42
51
|
* `app.addContentTypeParser("application/json", { parseAs: "string" }, …)`
|
|
43
52
|
* and stash the string on `request.rawBody` (Fastify pattern). The
|
|
44
|
-
* middleware reads `request.rawBody` for hashing. Skip when unsigned.
|
|
53
|
+
* middleware reads `request.rawBody` for hashing. Skip when unsigned. If
|
|
54
|
+
* `request.rawBody` is empty because the parser wasn't registered, the
|
|
55
|
+
* middleware short-circuits with `400 { error: "empty-body" }` instead
|
|
56
|
+
* of hashing an empty string and rejecting every valid request with a
|
|
57
|
+
* misleading `401 bad-signature`.
|
|
45
58
|
*/
|
|
46
59
|
import type { FastifyReply, FastifyRequest } from "fastify";
|
|
47
60
|
import { type CheckWebhookOptions } from "../check.js";
|
|
61
|
+
import { type Finalizers } from "../finalize.js";
|
|
62
|
+
/** Shape attached to `request.idempotency` by the Fastify adapter. */
|
|
63
|
+
export type FastifyIdempotency = {
|
|
64
|
+
key: string;
|
|
65
|
+
deduped: boolean;
|
|
66
|
+
} & Finalizers;
|
|
48
67
|
/**
|
|
49
68
|
* Build a Fastify `preHandler` hook that verifies the request
|
|
50
69
|
* signature (when `secret` is set), enforces `Idempotency-Key`, and
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/receiver/fastify/index.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/receiver/fastify/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAyDG;AACH,OAAO,KAAK,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAC5D,OAAO,EAAE,KAAK,mBAAmB,EAAgB,MAAM,aAAa,CAAC;AACrE,OAAO,EAAE,KAAK,UAAU,EAAmB,MAAM,gBAAgB,CAAC;AAElE,sEAAsE;AACtE,MAAM,MAAM,kBAAkB,GAAG;IAC/B,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,EAAE,OAAO,CAAC;CAClB,GAAG,UAAU,CAAC;AAOf;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAC/B,OAAO,EAAE,mBAAmB,GAC3B,CAAC,OAAO,EAAE,cAAc,EAAE,KAAK,EAAE,YAAY,KAAK,OAAO,CAAC,IAAI,CAAC,CA4BjE"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type { IdempotencyStore } from "@rotorsoft/act-ops/idempotency";
|
|
2
|
+
/**
|
|
3
|
+
* The two-phase finalizers a receiver adapter binds to a single
|
|
4
|
+
* inbound delivery. The claim `checkWebhook` makes is *tentative*;
|
|
5
|
+
* exactly one of these promotes or drops it once the business handler
|
|
6
|
+
* resolves its outcome.
|
|
7
|
+
*/
|
|
8
|
+
export type Finalizers = {
|
|
9
|
+
/**
|
|
10
|
+
* Promote the tentative claim to a durable record so every later
|
|
11
|
+
* retry of this key dedups. Call on handler success.
|
|
12
|
+
*/
|
|
13
|
+
commit: () => void | Promise<void>;
|
|
14
|
+
/**
|
|
15
|
+
* Drop the tentative claim so the sender's retry re-processes.
|
|
16
|
+
* Call on transient handler failure.
|
|
17
|
+
*/
|
|
18
|
+
release: () => void | Promise<void>;
|
|
19
|
+
};
|
|
20
|
+
/**
|
|
21
|
+
* Build the {@link Finalizers} for one delivery.
|
|
22
|
+
*
|
|
23
|
+
* Guarantees:
|
|
24
|
+
*
|
|
25
|
+
* - **Deduped deliveries are inert.** When `deduped` is `true` the key
|
|
26
|
+
* was already committed by an earlier delivery; both finalizers are
|
|
27
|
+
* no-ops so a duplicate can never release someone else's committed
|
|
28
|
+
* claim.
|
|
29
|
+
* - **Finalize-once.** After the first `commit` or `release` fires,
|
|
30
|
+
* further calls are no-ops — an adapter that both auto-finalizes and
|
|
31
|
+
* lets the operator call `commit()`/`release()` can't double-fire.
|
|
32
|
+
*/
|
|
33
|
+
export declare function make_finalizers(store: IdempotencyStore, key: string, deduped: boolean): Finalizers;
|
|
34
|
+
//# sourceMappingURL=finalize.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"finalize.d.ts","sourceRoot":"","sources":["../../../src/receiver/finalize.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,gCAAgC,CAAC;AAEvE;;;;;GAKG;AACH,MAAM,MAAM,UAAU,GAAG;IACvB;;;OAGG;IACH,MAAM,EAAE,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACnC;;;OAGG;IACH,OAAO,EAAE,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACrC,CAAC;AAEF;;;;;;;;;;;;GAYG;AACH,wBAAgB,eAAe,CAC7B,KAAK,EAAE,gBAAgB,EACvB,GAAG,EAAE,MAAM,EACX,OAAO,EAAE,OAAO,GACf,UAAU,CAcZ"}
|
|
@@ -31,6 +31,14 @@
|
|
|
31
31
|
* success: stashes `c.set("idempotency", { key, deduped })` and
|
|
32
32
|
* continues with `await next()`.
|
|
33
33
|
*
|
|
34
|
+
* **Two-phase dedup**: the claim `checkWebhook` makes is *tentative*.
|
|
35
|
+
* Because Hono middleware wraps the downstream chain, this adapter
|
|
36
|
+
* finalizes the claim automatically after `await next()`: a downstream
|
|
37
|
+
* 2xx **commits** the key (later retries dedup); a 5xx or a thrown
|
|
38
|
+
* handler **releases** it (the sender's retry re-processes instead of
|
|
39
|
+
* being silently dropped). Operators who need explicit control can
|
|
40
|
+
* still call `c.get("idempotency").commit()` / `.release()`.
|
|
41
|
+
*
|
|
34
42
|
* **Raw body**: Hono exposes `await c.req.text()` natively, which
|
|
35
43
|
* the middleware reads when `secret` is configured. No extra setup
|
|
36
44
|
* needed.
|
|
@@ -42,11 +50,18 @@ import { type CheckWebhookOptions } from "../check.js";
|
|
|
42
50
|
* generic on the returned {@link MiddlewareHandler} threads it
|
|
43
51
|
* through so route handlers downstream of `app.post(..., webhookMiddleware(...), handler)`
|
|
44
52
|
* see `c.get("idempotency")` typed without a manual cast.
|
|
53
|
+
*
|
|
54
|
+
* `commit` / `release` finalize the tentative claim — call one after
|
|
55
|
+
* the handler resolves its outcome. This adapter also finalizes
|
|
56
|
+
* automatically based on the response status, so explicit calls are
|
|
57
|
+
* only needed when the auto-detection doesn't fit.
|
|
45
58
|
*/
|
|
46
59
|
export type WebhookVariables = {
|
|
47
60
|
idempotency: {
|
|
48
61
|
key: string;
|
|
49
62
|
deduped: boolean;
|
|
63
|
+
commit: () => void | Promise<void>;
|
|
64
|
+
release: () => void | Promise<void>;
|
|
50
65
|
};
|
|
51
66
|
};
|
|
52
67
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/receiver/hono/index.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/receiver/hono/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4CG;AACH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,MAAM,CAAC;AAC9C,OAAO,EAAE,KAAK,mBAAmB,EAAgB,MAAM,aAAa,CAAC;AAGrE;;;;;;;;;;GAUG;AACH,MAAM,MAAM,gBAAgB,GAAG;IAC7B,WAAW,EAAE;QACX,GAAG,EAAE,MAAM,CAAC;QACZ,OAAO,EAAE,OAAO,CAAC;QACjB,MAAM,EAAE,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;QACnC,OAAO,EAAE,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;KACrC,CAAC;CACH,CAAC;AAEF;;;;GAIG;AACH,wBAAgB,iBAAiB,CAC/B,OAAO,EAAE,mBAAmB,GAC3B,iBAAiB,CAAC;IAAE,SAAS,EAAE,gBAAgB,CAAA;CAAE,CAAC,CA4BpD"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"start.d.ts","sourceRoot":"","sources":["../../../src/receiver/start.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAEV,eAAe,EAEf,eAAe,EAEhB,MAAM,6BAA6B,CAAC;AAIrC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4CG;AACH,wBAAgB,QAAQ,CAAC,OAAO,EAAE,eAAe,GAAG,eAAe,
|
|
1
|
+
{"version":3,"file":"start.d.ts","sourceRoot":"","sources":["../../../src/receiver/start.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAEV,eAAe,EAEf,eAAe,EAEhB,MAAM,6BAA6B,CAAC;AAIrC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4CG;AACH,wBAAgB,QAAQ,CAAC,OAAO,EAAE,eAAe,GAAG,eAAe,CAkGlE"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/receiver/trpc/index.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/receiver/trpc/index.ts"],"names":[],"mappings":"AAgDA,OAAO,EAAE,KAAK,mBAAmB,EAAgB,MAAM,aAAa,CAAC;AAGrE;;;;;;;;;;;;GAYG;AAEH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,mBAAmB,GAAG,GAAG,CA0DnE"}
|
package/dist/api/index.cjs
CHANGED
|
@@ -80,7 +80,15 @@ async function withIdempotency(store, key, handler) {
|
|
|
80
80
|
if (!fresh) {
|
|
81
81
|
return { deduped: true };
|
|
82
82
|
}
|
|
83
|
-
|
|
83
|
+
let result;
|
|
84
|
+
try {
|
|
85
|
+
result = await handler();
|
|
86
|
+
} catch (err) {
|
|
87
|
+
await store.release(key);
|
|
88
|
+
throw err;
|
|
89
|
+
}
|
|
90
|
+
await store.commit(key);
|
|
91
|
+
return { deduped: false, result };
|
|
84
92
|
}
|
|
85
93
|
|
|
86
94
|
// ../../node_modules/.pnpm/zod@4.4.3/node_modules/zod/v4/classic/external.js
|