@voyantjs/hono 0.80.2 → 0.80.4

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.
@@ -1,5 +1,5 @@
1
1
  import type { MiddlewareHandler } from "hono";
2
- import { type DbFactory, type VoyantBindings, type VoyantVariables } from "../types.js";
2
+ import { type DbFactory, type VoyantBindings, type VoyantDb, type VoyantVariables } from "../types.js";
3
3
  /**
4
4
  * Twenty-four hours, in milliseconds. Default TTL for stored idempotency
5
5
  * keys. Tunable per middleware instance.
@@ -22,6 +22,13 @@ export interface IdempotencyKeyOptions {
22
22
  * working through a deprecation window.
23
23
  */
24
24
  required?: boolean;
25
+ /**
26
+ * Query parameters that affect the response contract and should be included
27
+ * in the idempotency fingerprint alongside the request body. Reusing the
28
+ * same key with different fingerprinted query values returns 409 instead of
29
+ * replaying a response captured under different request semantics.
30
+ */
31
+ fingerprintSearchParams?: readonly string[];
25
32
  /**
26
33
  * Optional callback that, given the response body that's about to be
27
34
  * stored, returns a `referenceId` (typically the booking id). Used for
@@ -57,9 +64,11 @@ declare module "hono" {
57
64
  * the `db` middleware that ships with `createApp`) — caller is responsible
58
65
  * for ordering this middleware after `db`.
59
66
  */
60
- export declare function idempotencyKey<TBindings extends VoyantBindings = VoyantBindings>(options?: IdempotencyKeyOptions): MiddlewareHandler<{
67
+ export declare function idempotencyKey<TBindings extends object = VoyantBindings, TVariables extends {
68
+ db: VoyantDb;
69
+ } = VoyantVariables>(options?: IdempotencyKeyOptions): MiddlewareHandler<{
61
70
  Bindings: TBindings;
62
- Variables: VoyantVariables;
71
+ Variables: TVariables;
63
72
  }>;
64
73
  /**
65
74
  * Sweep expired idempotency rows. Call from a daily cron.
@@ -1 +1 @@
1
- {"version":3,"file":"idempotency-key.d.ts","sourceRoot":"","sources":["../../src/middleware/idempotency-key.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,MAAM,CAAA;AAE7C,OAAO,EACL,KAAK,SAAS,EAEd,KAAK,cAAc,EAEnB,KAAK,eAAe,EACrB,MAAM,aAAa,CAAA;AAEpB;;;GAGG;AACH,eAAO,MAAM,0BAA0B,QAAsB,CAAA;AAmB7D,MAAM,WAAW,qBAAqB;IACpC;;;OAGG;IACH,KAAK,CAAC,EAAE,MAAM,CAAA;IACd;;;OAGG;IACH,KAAK,CAAC,EAAE,MAAM,CAAA;IACd;;;;OAIG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB;;;;OAIG;IACH,kBAAkB,CAAC,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,MAAM,GAAG,IAAI,GAAG,SAAS,CAAA;CAClE;AAED,UAAU,sBAAsB;IAC9B,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,mBAAmB,CAAC,EAAE,OAAO,CAAA;CAC9B;AAED,OAAO,QAAQ,MAAM,CAAC;IACpB,UAAU,kBAAmB,SAAQ,sBAAsB;KAAG;CAC/D;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,cAAc,CAAC,SAAS,SAAS,cAAc,GAAG,cAAc,EAC9E,OAAO,GAAE,qBAA0B,GAClC,iBAAiB,CAAC;IACnB,QAAQ,EAAE,SAAS,CAAA;IACnB,SAAS,EAAE,eAAe,CAAA;CAC3B,CAAC,CAoID;AAiBD;;;;;;GAMG;AACH,wBAAsB,2BAA2B,CAAC,SAAS,SAAS,cAAc,EAChF,SAAS,EAAE,SAAS,CAAC,SAAS,CAAC,EAC/B,GAAG,EAAE,SAAS,GACb,OAAO,CAAC;IAAE,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC,CAW9B"}
1
+ {"version":3,"file":"idempotency-key.d.ts","sourceRoot":"","sources":["../../src/middleware/idempotency-key.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,MAAM,CAAA;AAE7C,OAAO,EACL,KAAK,SAAS,EAEd,KAAK,cAAc,EACnB,KAAK,QAAQ,EACb,KAAK,eAAe,EACrB,MAAM,aAAa,CAAA;AAEpB;;;GAGG;AACH,eAAO,MAAM,0BAA0B,QAAsB,CAAA;AAmB7D,MAAM,WAAW,qBAAqB;IACpC;;;OAGG;IACH,KAAK,CAAC,EAAE,MAAM,CAAA;IACd;;;OAGG;IACH,KAAK,CAAC,EAAE,MAAM,CAAA;IACd;;;;OAIG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB;;;;;OAKG;IACH,uBAAuB,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;IAC3C;;;;OAIG;IACH,kBAAkB,CAAC,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,MAAM,GAAG,IAAI,GAAG,SAAS,CAAA;CAClE;AAED,UAAU,sBAAsB;IAC9B,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,mBAAmB,CAAC,EAAE,OAAO,CAAA;CAC9B;AAED,OAAO,QAAQ,MAAM,CAAC;IACpB,UAAU,kBAAmB,SAAQ,sBAAsB;KAAG;CAC/D;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,cAAc,CAC5B,SAAS,SAAS,MAAM,GAAG,cAAc,EACzC,UAAU,SAAS;IAAE,EAAE,EAAE,QAAQ,CAAA;CAAE,GAAG,eAAe,EAErD,OAAO,GAAE,qBAA0B,GAClC,iBAAiB,CAAC;IACnB,QAAQ,EAAE,SAAS,CAAA;IACnB,SAAS,EAAE,UAAU,CAAA;CACtB,CAAC,CAuID;AAgCD;;;;;;GAMG;AACH,wBAAsB,2BAA2B,CAAC,SAAS,SAAS,cAAc,EAChF,SAAS,EAAE,SAAS,CAAC,SAAS,CAAC,EAC/B,GAAG,EAAE,SAAS,GACb,OAAO,CAAC;IAAE,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC,CAW9B"}
@@ -56,9 +56,10 @@ export function idempotencyKey(options = {}) {
56
56
  if (headerKey.length > 255) {
57
57
  return c.json({ error: `${HEADER_NAME} must be 255 characters or fewer` }, 400);
58
58
  }
59
- const scope = options.scope ?? `${c.req.method} ${new URL(c.req.url).pathname}`;
59
+ const requestUrl = new URL(c.req.url);
60
+ const scope = options.scope ?? `${c.req.method} ${requestUrl.pathname}`;
60
61
  const rawBody = await c.req.text();
61
- const bodyHash = await sha256Hex(rawBody);
62
+ const bodyHash = await sha256Hex(buildFingerprintInput(rawBody, requestUrl, options.fingerprintSearchParams));
62
63
  // The `db` middleware always unwraps a `DisposableDb` to a plain
63
64
  // `VoyantDb` before storing on context, so this cast is safe.
64
65
  const db = c.get("db");
@@ -152,6 +153,15 @@ export function idempotencyKey(options = {}) {
152
153
  }
153
154
  };
154
155
  }
156
+ function buildFingerprintInput(rawBody, url, searchParamKeys) {
157
+ if (!searchParamKeys?.length)
158
+ return rawBody;
159
+ const queryFingerprint = searchParamKeys.map((key) => [key, url.searchParams.getAll(key).sort()]);
160
+ return JSON.stringify({
161
+ body: rawBody,
162
+ query: queryFingerprint,
163
+ });
164
+ }
155
165
  function pickStringField(body, keys) {
156
166
  if (!body || typeof body !== "object")
157
167
  return null;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@voyantjs/hono",
3
- "version": "0.80.2",
3
+ "version": "0.80.4",
4
4
  "license": "Apache-2.0",
5
5
  "type": "module",
6
6
  "exports": {
@@ -99,18 +99,18 @@
99
99
  "drizzle-orm": "^0.45.2",
100
100
  "hono": "^4.12.10",
101
101
  "zod": "^4.3.6",
102
- "@voyantjs/core": "0.80.2",
103
- "@voyantjs/db": "0.80.2",
104
- "@voyantjs/types": "0.80.2",
105
- "@voyantjs/utils": "0.80.2",
106
- "@voyantjs/workflows": "0.80.2"
102
+ "@voyantjs/core": "0.80.4",
103
+ "@voyantjs/db": "0.80.4",
104
+ "@voyantjs/types": "0.80.4",
105
+ "@voyantjs/utils": "0.80.4",
106
+ "@voyantjs/workflows": "0.80.4"
107
107
  },
108
108
  "devDependencies": {
109
109
  "@cloudflare/workers-types": "^4.20260426.1",
110
110
  "typescript": "^6.0.2",
111
111
  "vitest": "^4.1.2",
112
112
  "@voyantjs/voyant-typescript-config": "0.1.0",
113
- "@voyantjs/workflows-orchestrator": "0.80.2"
113
+ "@voyantjs/workflows-orchestrator": "0.80.4"
114
114
  },
115
115
  "files": [
116
116
  "dist"