@cqrs-ddd/pipeline-idempotency 0.5.1 → 0.6.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/errors/idempotency-conflict.error.d.ts +1 -6
- package/dist/errors/idempotency-conflict.error.d.ts.map +1 -1
- package/dist/errors/idempotency-conflict.error.js +1 -7
- package/dist/errors/idempotency-conflict.error.js.map +1 -1
- package/dist/helpers/partitioned-key.d.ts.map +1 -1
- package/dist/helpers/partitioned-key.js +1 -8
- package/dist/helpers/partitioned-key.js.map +1 -1
- package/dist/http.d.ts +4 -7
- package/dist/http.d.ts.map +1 -1
- package/dist/http.js +22 -9
- package/dist/http.js.map +1 -1
- package/dist/idempotency.behavior.d.ts +6 -34
- package/dist/idempotency.behavior.d.ts.map +1 -1
- package/dist/idempotency.behavior.js +59 -108
- package/dist/idempotency.behavior.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/stores/postgres.store.d.ts +4 -6
- package/dist/stores/postgres.store.d.ts.map +1 -1
- package/dist/stores/postgres.store.js +4 -11
- package/dist/stores/postgres.store.js.map +1 -1
- package/package.json +6 -6
|
@@ -13,17 +13,12 @@ export type IdempotencyConflictReason = 'in_progress' | 'key_reuse' | 'replay_sc
|
|
|
13
13
|
* `toHttpResponse` of `@cqrs-ddd/pipeline-idempotency/http` maps it to its HTTP status.
|
|
14
14
|
*/
|
|
15
15
|
export declare class IdempotencyConflictError extends Error {
|
|
16
|
-
/** The
|
|
16
|
+
/** The stored key in conflict. It holds the tenant and the principal: log it, do not send it. */
|
|
17
17
|
readonly key: string;
|
|
18
18
|
/** The request that was rejected, e.g. `CreateUserCommand`. */
|
|
19
19
|
readonly requestName: string;
|
|
20
20
|
/** Why the request was rejected. */
|
|
21
21
|
readonly reason: IdempotencyConflictReason;
|
|
22
|
-
/**
|
|
23
|
-
* Suggested HTTP status: `409` for `in_progress` and `replay_scope`, `422` for
|
|
24
|
-
* `key_reuse`.
|
|
25
|
-
*/
|
|
26
|
-
readonly statusCode: number;
|
|
27
22
|
constructor(params: {
|
|
28
23
|
key: string;
|
|
29
24
|
requestName: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"idempotency-conflict.error.d.ts","sourceRoot":"","sources":["../../src/errors/idempotency-conflict.error.ts"],"names":[],"mappings":"AAEA,8CAA8C;AAC9C,MAAM,MAAM,yBAAyB,GACjC,aAAa,GACb,WAAW,GACX,cAAc,CAAC;AA0BnB;;;;;;;;;;;GAWG;AACH,qBAAa,wBAAyB,SAAQ,KAAK;IACjD,
|
|
1
|
+
{"version":3,"file":"idempotency-conflict.error.d.ts","sourceRoot":"","sources":["../../src/errors/idempotency-conflict.error.ts"],"names":[],"mappings":"AAEA,8CAA8C;AAC9C,MAAM,MAAM,yBAAyB,GACjC,aAAa,GACb,WAAW,GACX,cAAc,CAAC;AA0BnB;;;;;;;;;;;GAWG;AACH,qBAAa,wBAAyB,SAAQ,KAAK;IACjD,iGAAiG;IACjG,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,+DAA+D;IAC/D,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,oCAAoC;IACpC,QAAQ,CAAC,MAAM,EAAE,yBAAyB,CAAC;IAE3C,YAAY,MAAM,EAAE;QAClB,GAAG,EAAE,MAAM,CAAC;QACZ,WAAW,EAAE,MAAM,CAAC;QACpB,MAAM,EAAE,yBAAyB,CAAC;KACnC,EAMA;CACF"}
|
|
@@ -25,24 +25,18 @@ function conflictMessage(params) {
|
|
|
25
25
|
* `toHttpResponse` of `@cqrs-ddd/pipeline-idempotency/http` maps it to its HTTP status.
|
|
26
26
|
*/
|
|
27
27
|
export class IdempotencyConflictError extends Error {
|
|
28
|
-
/** The
|
|
28
|
+
/** The stored key in conflict. It holds the tenant and the principal: log it, do not send it. */
|
|
29
29
|
key;
|
|
30
30
|
/** The request that was rejected, e.g. `CreateUserCommand`. */
|
|
31
31
|
requestName;
|
|
32
32
|
/** Why the request was rejected. */
|
|
33
33
|
reason;
|
|
34
|
-
/**
|
|
35
|
-
* Suggested HTTP status: `409` for `in_progress` and `replay_scope`, `422` for
|
|
36
|
-
* `key_reuse`.
|
|
37
|
-
*/
|
|
38
|
-
statusCode;
|
|
39
34
|
constructor(params) {
|
|
40
35
|
super(conflictMessage(params));
|
|
41
36
|
this.name = 'IdempotencyConflictError';
|
|
42
37
|
this.key = params.key;
|
|
43
38
|
this.requestName = params.requestName;
|
|
44
39
|
this.reason = params.reason;
|
|
45
|
-
this.statusCode = params.reason === 'key_reuse' ? 422 : 409;
|
|
46
40
|
}
|
|
47
41
|
}
|
|
48
42
|
//# sourceMappingURL=idempotency-conflict.error.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"idempotency-conflict.error.js","sourceRoot":"","sources":["../../src/errors/idempotency-conflict.error.ts"],"names":[],"mappings":"AAAA,sEAAsE;AAQtE,SAAS,eAAe,CAAC,MAIxB;IACC,QAAQ,MAAM,CAAC,MAAM,EAAE,CAAC;QACtB,KAAK,aAAa;YAChB,OAAO,CACL,mCAAmC,MAAM,CAAC,GAAG,kBAAkB;gBAC/D,gBAAgB,MAAM,CAAC,WAAW,EAAE,CACrC,CAAC;QACJ,KAAK,cAAc;YACjB,OAAO,CACL,oBAAoB,MAAM,CAAC,GAAG,aAAa,MAAM,CAAC,WAAW,YAAY;gBACzE,4EAA4E,CAC7E,CAAC;QACJ;YACE,OAAO,CACL,oBAAoB,MAAM,CAAC,GAAG,yBAAyB;gBACvD,GAAG,MAAM,CAAC,WAAW,2BAA2B,CACjD,CAAC;IACN,CAAC;AACH,CAAC;AAED;;;;;;;;;;;GAWG;AACH,MAAM,OAAO,wBAAyB,SAAQ,KAAK;IACjD,
|
|
1
|
+
{"version":3,"file":"idempotency-conflict.error.js","sourceRoot":"","sources":["../../src/errors/idempotency-conflict.error.ts"],"names":[],"mappings":"AAAA,sEAAsE;AAQtE,SAAS,eAAe,CAAC,MAIxB;IACC,QAAQ,MAAM,CAAC,MAAM,EAAE,CAAC;QACtB,KAAK,aAAa;YAChB,OAAO,CACL,mCAAmC,MAAM,CAAC,GAAG,kBAAkB;gBAC/D,gBAAgB,MAAM,CAAC,WAAW,EAAE,CACrC,CAAC;QACJ,KAAK,cAAc;YACjB,OAAO,CACL,oBAAoB,MAAM,CAAC,GAAG,aAAa,MAAM,CAAC,WAAW,YAAY;gBACzE,4EAA4E,CAC7E,CAAC;QACJ;YACE,OAAO,CACL,oBAAoB,MAAM,CAAC,GAAG,yBAAyB;gBACvD,GAAG,MAAM,CAAC,WAAW,2BAA2B,CACjD,CAAC;IACN,CAAC;AACH,CAAC;AAED;;;;;;;;;;;GAWG;AACH,MAAM,OAAO,wBAAyB,SAAQ,KAAK;IACjD,iGAAiG;IACxF,GAAG,CAAS;IACrB,+DAA+D;IACtD,WAAW,CAAS;IAC7B,oCAAoC;IAC3B,MAAM,CAA4B;IAE3C,YAAY,MAIX;QACC,KAAK,CAAC,eAAe,CAAC,MAAM,CAAC,CAAC,CAAC;QAC/B,IAAI,CAAC,IAAI,GAAG,0BAA0B,CAAC;QACvC,IAAI,CAAC,GAAG,GAAG,MAAM,CAAC,GAAG,CAAC;QACtB,IAAI,CAAC,WAAW,GAAG,MAAM,CAAC,WAAW,CAAC;QACtC,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC;IAC9B,CAAC;CACF"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"partitioned-key.d.ts","sourceRoot":"","sources":["../../src/helpers/partitioned-key.ts"],"names":[],"mappings":"AAEA,OAAO,EACL,KAAK,gBAAgB,
|
|
1
|
+
{"version":3,"file":"partitioned-key.d.ts","sourceRoot":"","sources":["../../src/helpers/partitioned-key.ts"],"names":[],"mappings":"AAEA,OAAO,EACL,KAAK,gBAAgB,EAErB,KAAK,sBAAsB,EAE5B,MAAM,oBAAoB,CAAC;AAG5B,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,gDAAgD,CAAC;AAE5F;;;;;;;GAOG;AACH,MAAM,MAAM,2BAA2B,GAAG,CACxC,OAAO,EAAE,gBAAgB,KACtB,MAAM,GAAG,SAAS,MAAM,EAAE,GAAG,SAAS,CAAC;AAE5C;;;;GAIG;AACH,MAAM,MAAM,2BAA2B,GAAG,CACxC,OAAO,EAAE,gBAAgB,KACtB,MAAM,GAAG,SAAS,CAAC;AAExB,kEAAkE;AAClE,MAAM,WAAW,gCACf,SAAQ,sBAAsB;IAC9B,iEAAiE;IACjE,SAAS,EAAE,2BAA2B,CAAC;IAEvC,kEAAkE;IAClE,SAAS,EAAE,2BAA2B,CAAC;IAEvC;;;OAGG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;IAEhB;;;;OAIG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IAEjB;;;;;;;;;;OAUG;IACH,kBAAkB,CAAC,EAAE,OAAO,GAAG,MAAM,CAAC;CACvC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2CG;AACH,wBAAgB,sCAAsC,CACpD,OAAO,EAAE,gCAAgC,GAAG;IAAE,kBAAkB,CAAC,EAAE,OAAO,CAAA;CAAE,GAC3E,CAAC,OAAO,EAAE,gBAAgB,KAAK,MAAM,CAAC;AACzC,wBAAgB,sCAAsC,CACpD,OAAO,EAAE,gCAAgC,GACxC,qBAAqB,CAAC"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/* Copyright (C) 2026-present Aristotelis — see repository license. */
|
|
2
|
-
import { tenantSegments, } from '@cqrs-ddd/pipeline';
|
|
2
|
+
import { principalSegments, tenantSegments, } from '@cqrs-ddd/pipeline';
|
|
3
3
|
import { joinKeySegments } from '@cqrs-ddd/safe-stringify';
|
|
4
4
|
import { MissingIdempotencyPartitionError } from '../errors/missing-partition.error.js';
|
|
5
5
|
export function createPartitionedIdempotencyKeyFactory(options) {
|
|
@@ -27,11 +27,4 @@ export function createPartitionedIdempotencyKeyFactory(options) {
|
|
|
27
27
|
};
|
|
28
28
|
return keyFor;
|
|
29
29
|
}
|
|
30
|
-
/** Normalizes a principal to non-empty trimmed segments, or `undefined`. */
|
|
31
|
-
function principalSegments(value) {
|
|
32
|
-
const segments = (typeof value === 'string' ? [value] : (value ?? [])).map((segment) => (typeof segment === 'string' ? segment.trim() : ''));
|
|
33
|
-
return segments.length > 0 && segments.every((segment) => segment !== '')
|
|
34
|
-
? segments
|
|
35
|
-
: undefined;
|
|
36
|
-
}
|
|
37
30
|
//# sourceMappingURL=partitioned-key.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"partitioned-key.js","sourceRoot":"","sources":["../../src/helpers/partitioned-key.ts"],"names":[],"mappings":"AAAA,sEAAsE;AAEtE,OAAO,
|
|
1
|
+
{"version":3,"file":"partitioned-key.js","sourceRoot":"","sources":["../../src/helpers/partitioned-key.ts"],"names":[],"mappings":"AAAA,sEAAsE;AAEtE,OAAO,EAEL,iBAAiB,EAEjB,cAAc,GACf,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAC;AAC3D,OAAO,EAAE,gCAAgC,EAAE,MAAM,sCAAsC,CAAC;AA8GxF,MAAM,UAAU,sCAAsC,CACpD,OAAyC;IAEzC,MAAM,kBAAkB,GAAG,OAAO,CAAC,kBAAkB,IAAI,OAAO,CAAC;IAEjE,MAAM,MAAM,GAA0B,CAAC,OAAO,EAAE,EAAE;QAChD,MAAM,MAAM,GAAG,cAAc,CAC3B,OAAO,EACP,OAAO,EACP,gCAAgC,CACjC,CAAC;QAEF,MAAM,SAAS,GAAG,iBAAiB,CAAC,OAAO,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC,CAAC;QAChE,IAAI,CAAC,SAAS,EAAE,CAAC;YACf,MAAM,IAAI,gCAAgC,CACxC,OAAO,CAAC,WAAW,EACnB,WAAW,EACX,uEAAuE;gBACrE,sEAAsE,CACzE,CAAC;QACJ,CAAC;QAED,MAAM,SAAS,GAAG,OAAO,CAAC,SAAS,CAAC,OAAO,CAAC,EAAE,IAAI,EAAE,CAAC;QACrD,IAAI,CAAC,SAAS,EAAE,CAAC;YACf,IAAI,kBAAkB,KAAK,MAAM;gBAAE,OAAO,SAAS,CAAC;YACpD,MAAM,IAAI,gCAAgC,CACxC,OAAO,CAAC,WAAW,EACnB,WAAW,EACX,0EAA0E,CAC3E,CAAC;QACJ,CAAC;QAED,OAAO,eAAe,CAAC;YACrB,GAAG,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;YAC7C,GAAG,MAAM;YACT,GAAG,SAAS;YACZ,OAAO,CAAC,MAAM,IAAI,OAAO,CAAC,WAAW;YACrC,SAAS;SACV,CAAC,CAAC;IACL,CAAC,CAAC;IAEF,OAAO,MAAM,CAAC;AAChB,CAAC"}
|
package/dist/http.d.ts
CHANGED
|
@@ -1,14 +1,11 @@
|
|
|
1
|
+
import type { HttpResponse } from '@cqrs-ddd/pipeline';
|
|
1
2
|
import type { IdempotencyConflictError } from './errors/idempotency-conflict.error.js';
|
|
2
|
-
|
|
3
|
-
export interface HttpResponse {
|
|
4
|
-
status: number;
|
|
5
|
-
body: Record<string, unknown>;
|
|
6
|
-
headers: Record<string, string>;
|
|
7
|
-
}
|
|
3
|
+
export type { HttpResponse };
|
|
8
4
|
/**
|
|
9
5
|
* The HTTP answer for an {@link IdempotencyConflictError}: `409 Conflict` for an
|
|
10
6
|
* operation in progress or a different replay scope, `422 Unprocessable Entity` for a
|
|
11
|
-
* key reused with another payload.
|
|
7
|
+
* key reused with another payload. The body carries the `reason` and never the stored
|
|
8
|
+
* key, which holds the tenant and the principal.
|
|
12
9
|
*
|
|
13
10
|
* @example
|
|
14
11
|
* ```ts
|
package/dist/http.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"http.d.ts","sourceRoot":"","sources":["../src/http.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,
|
|
1
|
+
{"version":3,"file":"http.d.ts","sourceRoot":"","sources":["../src/http.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AACvD,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,wCAAwC,CAAC;AAEvF,YAAY,EAAE,YAAY,EAAE,CAAC;AAqB7B;;;;;;;;;;;GAWG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,wBAAwB,GAAG,YAAY,CAO5E"}
|
package/dist/http.js
CHANGED
|
@@ -1,8 +1,26 @@
|
|
|
1
1
|
/* Copyright (C) 2026-present Aristotelis — see repository license. */
|
|
2
|
+
const ANSWERS = {
|
|
3
|
+
in_progress: {
|
|
4
|
+
statusCode: 409,
|
|
5
|
+
error: 'Conflict',
|
|
6
|
+
message: 'A request with this idempotency key is already in progress',
|
|
7
|
+
},
|
|
8
|
+
replay_scope: {
|
|
9
|
+
statusCode: 409,
|
|
10
|
+
error: 'Conflict',
|
|
11
|
+
message: 'This idempotency key holds a response authorized under a different scope; it cannot be replayed',
|
|
12
|
+
},
|
|
13
|
+
key_reuse: {
|
|
14
|
+
statusCode: 422,
|
|
15
|
+
error: 'Unprocessable Entity',
|
|
16
|
+
message: 'This idempotency key was already used with a different payload',
|
|
17
|
+
},
|
|
18
|
+
};
|
|
2
19
|
/**
|
|
3
20
|
* The HTTP answer for an {@link IdempotencyConflictError}: `409 Conflict` for an
|
|
4
21
|
* operation in progress or a different replay scope, `422 Unprocessable Entity` for a
|
|
5
|
-
* key reused with another payload.
|
|
22
|
+
* key reused with another payload. The body carries the `reason` and never the stored
|
|
23
|
+
* key, which holds the tenant and the principal.
|
|
6
24
|
*
|
|
7
25
|
* @example
|
|
8
26
|
* ```ts
|
|
@@ -11,15 +29,10 @@
|
|
|
11
29
|
* ```
|
|
12
30
|
*/
|
|
13
31
|
export function toHttpResponse(error) {
|
|
32
|
+
const answer = ANSWERS[error.reason];
|
|
14
33
|
return {
|
|
15
|
-
status:
|
|
16
|
-
body: {
|
|
17
|
-
statusCode: error.statusCode,
|
|
18
|
-
error: error.statusCode === 409 ? 'Conflict' : 'Unprocessable Entity',
|
|
19
|
-
message: error.message,
|
|
20
|
-
idempotencyKey: error.key,
|
|
21
|
-
reason: error.reason,
|
|
22
|
-
},
|
|
34
|
+
status: answer.statusCode,
|
|
35
|
+
body: { ...answer, reason: error.reason },
|
|
23
36
|
headers: {},
|
|
24
37
|
};
|
|
25
38
|
}
|
package/dist/http.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"http.js","sourceRoot":"","sources":["../src/http.ts"],"names":[],"mappings":"AAAA,sEAAsE;
|
|
1
|
+
{"version":3,"file":"http.js","sourceRoot":"","sources":["../src/http.ts"],"names":[],"mappings":"AAAA,sEAAsE;AAOtE,MAAM,OAAO,GAAG;IACd,WAAW,EAAE;QACX,UAAU,EAAE,GAAG;QACf,KAAK,EAAE,UAAU;QACjB,OAAO,EAAE,4DAA4D;KACtE;IACD,YAAY,EAAE;QACZ,UAAU,EAAE,GAAG;QACf,KAAK,EAAE,UAAU;QACjB,OAAO,EACL,iGAAiG;KACpG;IACD,SAAS,EAAE;QACT,UAAU,EAAE,GAAG;QACf,KAAK,EAAE,sBAAsB;QAC7B,OAAO,EAAE,gEAAgE;KAC1E;CACO,CAAC;AAEX;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,cAAc,CAAC,KAA+B;IAC5D,MAAM,MAAM,GAAG,OAAO,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;IACrC,OAAO;QACL,MAAM,EAAE,MAAM,CAAC,UAAU;QACzB,IAAI,EAAE,EAAE,GAAG,MAAM,EAAE,MAAM,EAAE,KAAK,CAAC,MAAM,EAAE;QACzC,OAAO,EAAE,EAAE;KACZ,CAAC;AACJ,CAAC"}
|
|
@@ -2,48 +2,24 @@ import { type IPipelineBehavior, type IPipelineBehaviorContract, type IPipelineB
|
|
|
2
2
|
import type { IdempotencyBehaviorOptions } from './interfaces/idempotency-options.interface.js';
|
|
3
3
|
import type { IdempotencyStore } from './interfaces/idempotency-store.interface.js';
|
|
4
4
|
/**
|
|
5
|
-
*
|
|
5
|
+
* The `context.items` entry holding the active idempotency key string.
|
|
6
6
|
*
|
|
7
|
-
*
|
|
8
|
-
* ```ts
|
|
9
|
-
* const key = context.items.get(IDEMPOTENCY_KEY_ITEM) as string | undefined;
|
|
10
|
-
* ```
|
|
11
|
-
*/
|
|
12
|
-
export declare const IDEMPOTENCY_KEY_ITEM: unique symbol;
|
|
13
|
-
/**
|
|
14
|
-
* Typed token for {@link IDEMPOTENCY_KEY_ITEM}: the active idempotency key. Reads and writes the same
|
|
15
|
-
* `context.items` entry through `getPipelineItem` / `requirePipelineItem`.
|
|
7
|
+
* Read it with `getPipelineItem` or `requirePipelineItem`.
|
|
16
8
|
*/
|
|
17
9
|
export declare const IDEMPOTENCY_KEY_ITEM_TOKEN: PipelineItemToken<string>;
|
|
18
10
|
/**
|
|
19
|
-
*
|
|
11
|
+
* The `context.items` entry to `true` when the response was replayed
|
|
20
12
|
* from a previously-stored record (the handler did not run this time).
|
|
21
13
|
*
|
|
22
|
-
*
|
|
23
|
-
* ```ts
|
|
24
|
-
* const wasReplayed = context.items.get(IDEMPOTENCY_REPLAYED_ITEM) === true;
|
|
25
|
-
* ```
|
|
26
|
-
*/
|
|
27
|
-
export declare const IDEMPOTENCY_REPLAYED_ITEM: unique symbol;
|
|
28
|
-
/**
|
|
29
|
-
* Typed token for {@link IDEMPOTENCY_REPLAYED_ITEM}: the idempotent replay flag. Reads and writes the same
|
|
30
|
-
* `context.items` entry through `getPipelineItem` / `requirePipelineItem`.
|
|
14
|
+
* Read it with `getPipelineItem` or `requirePipelineItem`.
|
|
31
15
|
*/
|
|
32
16
|
export declare const IDEMPOTENCY_REPLAYED_ITEM_TOKEN: PipelineItemToken<boolean>;
|
|
33
17
|
/**
|
|
34
|
-
*
|
|
18
|
+
* The `context.items` entry to `true` when a handler completed after
|
|
35
19
|
* its claim had expired or been replaced. The successful handler result is returned,
|
|
36
20
|
* but this execution is not allowed to overwrite the newer owner's replay state.
|
|
37
21
|
*
|
|
38
|
-
*
|
|
39
|
-
* ```ts
|
|
40
|
-
* const ownershipLost = context.items.get(IDEMPOTENCY_OWNERSHIP_LOST_ITEM) === true;
|
|
41
|
-
* ```
|
|
42
|
-
*/
|
|
43
|
-
export declare const IDEMPOTENCY_OWNERSHIP_LOST_ITEM: unique symbol;
|
|
44
|
-
/**
|
|
45
|
-
* Typed token for {@link IDEMPOTENCY_OWNERSHIP_LOST_ITEM}: the idempotency ownership-lost flag. Reads and writes the same
|
|
46
|
-
* `context.items` entry through `getPipelineItem` / `requirePipelineItem`.
|
|
22
|
+
* Read it with `getPipelineItem` or `requirePipelineItem`.
|
|
47
23
|
*/
|
|
48
24
|
export declare const IDEMPOTENCY_OWNERSHIP_LOST_ITEM_TOKEN: PipelineItemToken<boolean>;
|
|
49
25
|
/**
|
|
@@ -105,10 +81,6 @@ export declare class IdempotencyBehavior implements IPipelineBehavior, IPipeline
|
|
|
105
81
|
* and the request happens here, before the key is claimed.
|
|
106
82
|
*/
|
|
107
83
|
private prepareClaim;
|
|
108
|
-
/**
|
|
109
|
-
* The first claim failed. Replay or refuse against the record found; when
|
|
110
|
-
* that record disappeared or expired meanwhile, claim the key once more.
|
|
111
|
-
*/
|
|
112
84
|
private replayOrReclaim;
|
|
113
85
|
/** Runs the handler under this execution's claim, then completes the claim. */
|
|
114
86
|
private run;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"idempotency.behavior.d.ts","sourceRoot":"","sources":["../src/idempotency.behavior.ts"],"names":[],"mappings":"AAGA,OAAO,
|
|
1
|
+
{"version":3,"file":"idempotency.behavior.d.ts","sourceRoot":"","sources":["../src/idempotency.behavior.ts"],"names":[],"mappings":"AAGA,OAAO,EAGL,KAAK,iBAAiB,EACtB,KAAK,yBAAyB,EAC9B,KAAK,gCAAgC,EACrC,KAAK,gBAAgB,EACrB,KAAK,YAAY,EACjB,0BAA0B,EAC1B,KAAK,iBAAiB,EACtB,KAAK,cAAc,EAEpB,MAAM,oBAAoB,CAAC;AAU5B,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,+CAA+C,CAAC;AAMhG,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,6CAA6C,CAAC;AAEpF;;;;GAIG;AACH,eAAO,MAAM,0BAA0B,EAAE,iBAAiB,CAAC,MAAM,CACb,CAAC;AAErD;;;;;GAKG;AACH,eAAO,MAAM,+BAA+B,EAAE,iBAAiB,CAAC,OAAO,CACb,CAAC;AAE3D;;;;;;GAMG;AACH,eAAO,MAAM,qCAAqC,EAAE,iBAAiB,CAAC,OAAO,CACb,CAAC;AA0BjE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8CG;AACH,qBAAa,mBACX,YACE,iBAAiB,EACjB,gCAAgC,CAAC,0BAA0B,CAAC;IAwC5D,OAAO,CAAC,QAAQ,CAAC,KAAK;IAtCxB,MAAM,CAAC,QAAQ,CAAC,CAAC,0BAA0B,CAAC,EAAE,yBAAyB,CAgCrE;IAEF,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAiB;IACxC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAA6B;IAEtD,YACmB,KAAK,EAAE,gBAAgB,EACxC,QAAQ,CAAC,EAAE,0BAA0B,EACrC,MAAM,CAAC,EAAE,cAAc,EAexB;IAEK,MAAM,CACV,OAAO,EAAE,gBAAgB,EACzB,IAAI,EAAE,YAAY,GACjB,OAAO,CAAC,OAAO,CAAC,CA6BlB;IAED;;;OAGG;IACH,OAAO,CAAC,YAAY;YA0BN,eAAe;IAyB7B,+EAA+E;YACjE,GAAG;IAqBjB,uEAAuE;IACvE,OAAO,CAAC,QAAQ;IAsBhB;;;OAGG;YACW,QAAQ;IA4CtB;;;;OAIG;YACW,gBAAgB;IAiB9B,4EAA4E;IAC5E,OAAO,CAAC,YAAY;IA4CpB;;;OAGG;YACW,OAAO;IAYrB,2EAA2E;IAC3E,uBAAuB,CACrB,OAAO,CAAC,EAAE,0BAA0B,GACnC,0BAA0B,CAG5B;CACF"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/* Copyright (C) 2026-present Aristotelis — see repository license. */
|
|
2
2
|
import { randomUUID } from 'node:crypto';
|
|
3
|
-
import { createPipelineItem, PIPELINE_BEHAVIOR_CONTRACT, setPipelineItem, } from '@cqrs-ddd/pipeline';
|
|
3
|
+
import { behaviorDiagnostic, createPipelineItem, PIPELINE_BEHAVIOR_CONTRACT, setPipelineItem, } from '@cqrs-ddd/pipeline';
|
|
4
4
|
import { untyped } from '@cqrs-ddd/untyped';
|
|
5
5
|
import { DEFAULT_IDEMPOTENCY_TTL_MS } from './constants/tokens.js';
|
|
6
6
|
import { IdempotencyCompletionError } from './errors/idempotency-completion.error.js';
|
|
@@ -8,50 +8,26 @@ import { IdempotencyConflictError, } from './errors/idempotency-conflict.error.j
|
|
|
8
8
|
import { fingerprintValue } from './helpers/fingerprint.js';
|
|
9
9
|
import { toJsonSnapshot } from './helpers/json-snapshot.js';
|
|
10
10
|
/**
|
|
11
|
-
*
|
|
11
|
+
* The `context.items` entry holding the active idempotency key string.
|
|
12
12
|
*
|
|
13
|
-
*
|
|
14
|
-
* ```ts
|
|
15
|
-
* const key = context.items.get(IDEMPOTENCY_KEY_ITEM) as string | undefined;
|
|
16
|
-
* ```
|
|
17
|
-
*/
|
|
18
|
-
export const IDEMPOTENCY_KEY_ITEM = Symbol('IDEMPOTENCY_KEY_ITEM');
|
|
19
|
-
/**
|
|
20
|
-
* Typed token for {@link IDEMPOTENCY_KEY_ITEM}: the active idempotency key. Reads and writes the same
|
|
21
|
-
* `context.items` entry through `getPipelineItem` / `requirePipelineItem`.
|
|
13
|
+
* Read it with `getPipelineItem` or `requirePipelineItem`.
|
|
22
14
|
*/
|
|
23
|
-
export const IDEMPOTENCY_KEY_ITEM_TOKEN = createPipelineItem('IDEMPOTENCY_KEY_ITEM'
|
|
15
|
+
export const IDEMPOTENCY_KEY_ITEM_TOKEN = createPipelineItem('IDEMPOTENCY_KEY_ITEM');
|
|
24
16
|
/**
|
|
25
|
-
*
|
|
17
|
+
* The `context.items` entry to `true` when the response was replayed
|
|
26
18
|
* from a previously-stored record (the handler did not run this time).
|
|
27
19
|
*
|
|
28
|
-
*
|
|
29
|
-
* ```ts
|
|
30
|
-
* const wasReplayed = context.items.get(IDEMPOTENCY_REPLAYED_ITEM) === true;
|
|
31
|
-
* ```
|
|
20
|
+
* Read it with `getPipelineItem` or `requirePipelineItem`.
|
|
32
21
|
*/
|
|
33
|
-
export const
|
|
22
|
+
export const IDEMPOTENCY_REPLAYED_ITEM_TOKEN = createPipelineItem('IDEMPOTENCY_REPLAYED_ITEM');
|
|
34
23
|
/**
|
|
35
|
-
*
|
|
36
|
-
* `context.items` entry through `getPipelineItem` / `requirePipelineItem`.
|
|
37
|
-
*/
|
|
38
|
-
export const IDEMPOTENCY_REPLAYED_ITEM_TOKEN = createPipelineItem('IDEMPOTENCY_REPLAYED_ITEM', IDEMPOTENCY_REPLAYED_ITEM);
|
|
39
|
-
/**
|
|
40
|
-
* Unique symbol key set on `context.items` to `true` when a handler completed after
|
|
24
|
+
* The `context.items` entry to `true` when a handler completed after
|
|
41
25
|
* its claim had expired or been replaced. The successful handler result is returned,
|
|
42
26
|
* but this execution is not allowed to overwrite the newer owner's replay state.
|
|
43
27
|
*
|
|
44
|
-
*
|
|
45
|
-
* ```ts
|
|
46
|
-
* const ownershipLost = context.items.get(IDEMPOTENCY_OWNERSHIP_LOST_ITEM) === true;
|
|
47
|
-
* ```
|
|
48
|
-
*/
|
|
49
|
-
export const IDEMPOTENCY_OWNERSHIP_LOST_ITEM = Symbol('IDEMPOTENCY_OWNERSHIP_LOST_ITEM');
|
|
50
|
-
/**
|
|
51
|
-
* Typed token for {@link IDEMPOTENCY_OWNERSHIP_LOST_ITEM}: the idempotency ownership-lost flag. Reads and writes the same
|
|
52
|
-
* `context.items` entry through `getPipelineItem` / `requirePipelineItem`.
|
|
28
|
+
* Read it with `getPipelineItem` or `requirePipelineItem`.
|
|
53
29
|
*/
|
|
54
|
-
export const IDEMPOTENCY_OWNERSHIP_LOST_ITEM_TOKEN = createPipelineItem('IDEMPOTENCY_OWNERSHIP_LOST_ITEM'
|
|
30
|
+
export const IDEMPOTENCY_OWNERSHIP_LOST_ITEM_TOKEN = createPipelineItem('IDEMPOTENCY_OWNERSHIP_LOST_ITEM');
|
|
55
31
|
const DEFAULT_SCOPE = ['command'];
|
|
56
32
|
/** Whether this request kind is configured for idempotency. */
|
|
57
33
|
function inScope(options, requestKind) {
|
|
@@ -107,42 +83,26 @@ function inScope(options, requestKind) {
|
|
|
107
83
|
export class IdempotencyBehavior {
|
|
108
84
|
store;
|
|
109
85
|
static [PIPELINE_BEHAVIOR_CONTRACT] = {
|
|
110
|
-
order: (
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
return undefined;
|
|
114
|
-
}
|
|
115
|
-
return {
|
|
116
|
-
after: ['@cqrs-ddd/pipeline-casl:CaslBehavior', 'CaslBehavior'],
|
|
117
|
-
};
|
|
118
|
-
},
|
|
86
|
+
order: ({ effectiveOptions, requestKind }) => inScope(effectiveOptions, requestKind)
|
|
87
|
+
? { after: ['@cqrs-ddd/pipeline-casl:CaslBehavior', 'CaslBehavior'] }
|
|
88
|
+
: undefined,
|
|
119
89
|
validate: (context) => {
|
|
120
90
|
const options = context.effectiveOptions;
|
|
121
|
-
if (!inScope(options, context.requestKind))
|
|
91
|
+
if (!inScope(options, context.requestKind))
|
|
122
92
|
return undefined;
|
|
123
|
-
|
|
124
|
-
if (
|
|
125
|
-
context.
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
behaviorName: IdempotencyBehavior.name,
|
|
131
|
-
message: 'Explicit IdempotencyBehavior intent requires an explicit `keyFactory` for deduplication',
|
|
132
|
-
fix: 'Provide keyFactory in the idempotent({ keyFactory }) entry ' +
|
|
133
|
-
'or use createPartitionedIdempotencyKeyFactory(...).',
|
|
134
|
-
},
|
|
135
|
-
];
|
|
93
|
+
const isDeclaredByHandler = context.declarationSource !== 'global';
|
|
94
|
+
if (isDeclaredByHandler && !options?.keyFactory) {
|
|
95
|
+
return behaviorDiagnostic(context, IdempotencyBehavior.name, {
|
|
96
|
+
message: 'Explicit IdempotencyBehavior intent requires an explicit `keyFactory` for deduplication',
|
|
97
|
+
fix: 'Provide keyFactory in the idempotent({ keyFactory }) entry ' +
|
|
98
|
+
'or use createPartitionedIdempotencyKeyFactory(...).',
|
|
99
|
+
});
|
|
136
100
|
}
|
|
137
101
|
if (options?.keyFactory && typeof options.keyFactory !== 'function') {
|
|
138
|
-
return
|
|
139
|
-
{
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
message: `IdempotencyBehavior keyFactory must be a callable function, received ${typeof options.keyFactory}`,
|
|
143
|
-
fix: 'Pass a valid function (ctx) => string as the keyFactory option of the idempotent({ keyFactory }) entry.',
|
|
144
|
-
},
|
|
145
|
-
];
|
|
102
|
+
return behaviorDiagnostic(context, IdempotencyBehavior.name, {
|
|
103
|
+
message: `IdempotencyBehavior keyFactory must be a callable function, received ${typeof options.keyFactory}`,
|
|
104
|
+
fix: 'Pass a valid function (ctx) => string as the keyFactory option of the idempotent({ keyFactory }) entry.',
|
|
105
|
+
});
|
|
146
106
|
}
|
|
147
107
|
return undefined;
|
|
148
108
|
},
|
|
@@ -183,51 +143,35 @@ export class IdempotencyBehavior {
|
|
|
183
143
|
* and the request happens here, before the key is claimed.
|
|
184
144
|
*/
|
|
185
145
|
prepareClaim(context, options, key) {
|
|
186
|
-
const ttl = options.ttl ?? DEFAULT_IDEMPOTENCY_TTL_MS;
|
|
187
|
-
if (!Number.isSafeInteger(ttl) || ttl <= 0) {
|
|
188
|
-
throw new TypeError('Idempotency ttl must be a positive safe integer in milliseconds.');
|
|
189
|
-
}
|
|
190
|
-
const fingerprint = (options.fingerprint ?? true)
|
|
191
|
-
? fingerprintValue(context.request)
|
|
192
|
-
: undefined;
|
|
193
|
-
// Resolved before the claim, so a replayScopeFactory that throws rejects the
|
|
194
|
-
// operation before any key is claimed.
|
|
195
|
-
const replayScope = options.replayScopeFactory?.(context);
|
|
196
|
-
const scopeRequired = options.replayScopeFactory !== undefined;
|
|
197
|
-
const claimId = randomUUID();
|
|
198
|
-
const claim = {
|
|
199
|
-
key,
|
|
200
|
-
status: 'in_progress',
|
|
201
|
-
requestName: context.requestName,
|
|
202
|
-
claimId,
|
|
203
|
-
fingerprint,
|
|
204
|
-
replayScope,
|
|
205
|
-
createdAt: new Date().toISOString(),
|
|
206
|
-
};
|
|
207
146
|
return {
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
147
|
+
ttl: ttlOf(options),
|
|
148
|
+
scopeRequired: options.replayScopeFactory !== undefined,
|
|
149
|
+
releaseOnError: options.releaseOnError ?? true,
|
|
150
|
+
claim: {
|
|
151
|
+
key,
|
|
152
|
+
status: 'in_progress',
|
|
153
|
+
requestName: context.requestName,
|
|
154
|
+
claimId: randomUUID(),
|
|
155
|
+
fingerprint: (options.fingerprint ?? true)
|
|
156
|
+
? fingerprintValue(context.request)
|
|
157
|
+
: undefined,
|
|
158
|
+
// Resolved before the claim, so a replayScopeFactory that throws rejects
|
|
159
|
+
// the operation before any key is claimed.
|
|
160
|
+
replayScope: options.replayScopeFactory?.(context),
|
|
161
|
+
createdAt: new Date().toISOString(),
|
|
162
|
+
},
|
|
216
163
|
};
|
|
217
164
|
}
|
|
218
|
-
/**
|
|
219
|
-
* The first claim failed. Replay or refuse against the record found; when
|
|
220
|
-
* that record disappeared or expired meanwhile, claim the key once more.
|
|
221
|
-
*/
|
|
222
165
|
async replayOrReclaim(context, attempt, next) {
|
|
223
|
-
const
|
|
166
|
+
const { key } = attempt.claim;
|
|
167
|
+
const existing = await this.store.get(key);
|
|
224
168
|
if (existing) {
|
|
225
169
|
return this.replayOrConflict(context, attempt, existing);
|
|
226
170
|
}
|
|
227
171
|
// The record disappeared or expired after the failed claim. Give this
|
|
228
172
|
// request one bounded opportunity to claim the now-available key instead
|
|
229
173
|
// of reporting a false in-progress conflict.
|
|
230
|
-
const claimed = await this.store.setIfAbsent(
|
|
174
|
+
const claimed = await this.store.setIfAbsent(key, attempt.claim, attempt.ttl);
|
|
231
175
|
if (!claimed) {
|
|
232
176
|
return this.replayOrConflict(context, attempt);
|
|
233
177
|
}
|
|
@@ -240,8 +184,8 @@ export class IdempotencyBehavior {
|
|
|
240
184
|
response = await next();
|
|
241
185
|
}
|
|
242
186
|
catch (error) {
|
|
243
|
-
if (attempt.
|
|
244
|
-
await this.release(attempt.key, attempt.claimId);
|
|
187
|
+
if (attempt.releaseOnError) {
|
|
188
|
+
await this.release(attempt.claim.key, attempt.claim.claimId);
|
|
245
189
|
}
|
|
246
190
|
throw error;
|
|
247
191
|
}
|
|
@@ -251,7 +195,7 @@ export class IdempotencyBehavior {
|
|
|
251
195
|
}
|
|
252
196
|
/** The replayable JSON snapshot of a successful handler's response. */
|
|
253
197
|
snapshot(context, attempt, response) {
|
|
254
|
-
const { key, claimId } = attempt;
|
|
198
|
+
const { key, claimId } = attempt.claim;
|
|
255
199
|
try {
|
|
256
200
|
return toJsonSnapshot(response);
|
|
257
201
|
}
|
|
@@ -269,7 +213,7 @@ export class IdempotencyBehavior {
|
|
|
269
213
|
* flags the context when a newer claim replaced it.
|
|
270
214
|
*/
|
|
271
215
|
async complete(context, attempt, responseSnapshot) {
|
|
272
|
-
const { key, claimId } = attempt;
|
|
216
|
+
const { key, claimId } = attempt.claim;
|
|
273
217
|
// The handler has already succeeded. Completion must be conditional on
|
|
274
218
|
// still owning the claim; a stale execution must never overwrite a newer
|
|
275
219
|
// execution that reclaimed the key after this claim's TTL elapsed.
|
|
@@ -301,15 +245,15 @@ export class IdempotencyBehavior {
|
|
|
301
245
|
* completed record whose replay scope does not match the caller's.
|
|
302
246
|
*/
|
|
303
247
|
async replayOrConflict(context, attempt, knownExisting) {
|
|
304
|
-
const existing = knownExisting ?? (await this.store.get(attempt.key));
|
|
248
|
+
const existing = knownExisting ?? (await this.store.get(attempt.claim.key));
|
|
305
249
|
this.refuseReplay(context, attempt, existing);
|
|
306
250
|
setPipelineItem(context, IDEMPOTENCY_REPLAYED_ITEM_TOKEN, true);
|
|
307
|
-
this.logger.debug?.(`Replaying idempotent response for ${context.requestName} (key: ${attempt.key})`, IdempotencyBehavior.name);
|
|
251
|
+
this.logger.debug?.(`Replaying idempotent response for ${context.requestName} (key: ${attempt.claim.key})`, IdempotencyBehavior.name);
|
|
308
252
|
return existing.response;
|
|
309
253
|
}
|
|
310
254
|
/** Throws the conflict that forbids replaying `existing` to this caller. */
|
|
311
255
|
refuseReplay(context, attempt, existing) {
|
|
312
|
-
const { key, fingerprint } = attempt;
|
|
256
|
+
const { key, fingerprint, replayScope } = attempt.claim;
|
|
313
257
|
// A second failed claim followed by another disappearing record indicates
|
|
314
258
|
// repeated contention. The retry is deliberately bounded to avoid a hot
|
|
315
259
|
// loop if a backend is unstable or claims are being rapidly replaced.
|
|
@@ -330,7 +274,7 @@ export class IdempotencyBehavior {
|
|
|
330
274
|
// record is retained: re-executing a completed operation would duplicate it.
|
|
331
275
|
if (attempt.scopeRequired &&
|
|
332
276
|
(existing.replayScope === undefined ||
|
|
333
|
-
existing.replayScope !==
|
|
277
|
+
existing.replayScope !== replayScope)) {
|
|
334
278
|
this.logger.warn?.(`Refusing to replay ${context.requestName} (key: ${key}): the stored ` +
|
|
335
279
|
'response was authorized under a different scope than this caller.', IdempotencyBehavior.name);
|
|
336
280
|
throw conflict(context, key, 'replay_scope');
|
|
@@ -356,6 +300,13 @@ export class IdempotencyBehavior {
|
|
|
356
300
|
return { ...this.defaults, ...options };
|
|
357
301
|
}
|
|
358
302
|
}
|
|
303
|
+
function ttlOf(options) {
|
|
304
|
+
const ttl = options.ttl ?? DEFAULT_IDEMPOTENCY_TTL_MS;
|
|
305
|
+
if (!Number.isSafeInteger(ttl) || ttl <= 0) {
|
|
306
|
+
throw new TypeError('Idempotency ttl must be a positive safe integer in milliseconds.');
|
|
307
|
+
}
|
|
308
|
+
return ttl;
|
|
309
|
+
}
|
|
359
310
|
function conflict(context, key, reason) {
|
|
360
311
|
return new IdempotencyConflictError({
|
|
361
312
|
key,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"idempotency.behavior.js","sourceRoot":"","sources":["../src/idempotency.behavior.ts"],"names":[],"mappings":"AAAA,sEAAsE;AAEtE,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EACL,kBAAkB,EAMlB,0BAA0B,
|
|
1
|
+
{"version":3,"file":"idempotency.behavior.js","sourceRoot":"","sources":["../src/idempotency.behavior.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
|
@@ -11,7 +11,7 @@ export { buildIdempotencyAttributes } from './helpers/build-attributes.js';
|
|
|
11
11
|
export { fingerprintValue } from './helpers/fingerprint.js';
|
|
12
12
|
export { type IdempotencyIntentOptions, idempotent, } from './helpers/idempotency.intent.js';
|
|
13
13
|
export { createPartitionedIdempotencyKeyFactory, type IdempotencyOperationFactory, type IdempotencyPrincipalFactory, type PartitionedIdempotencyKeyOptions, } from './helpers/partitioned-key.js';
|
|
14
|
-
export {
|
|
14
|
+
export { IDEMPOTENCY_KEY_ITEM_TOKEN, IDEMPOTENCY_OWNERSHIP_LOST_ITEM_TOKEN, IDEMPOTENCY_REPLAYED_ITEM_TOKEN, IdempotencyBehavior, } from './idempotency.behavior.js';
|
|
15
15
|
export type { IdempotencyBehaviorOptions, IdempotencyKeyFactory, IdempotencyReplayScopeFactory, } from './interfaces/idempotency-options.interface.js';
|
|
16
16
|
export type { IdempotencyRecord, IdempotencyRequestKind, IdempotencyStatus, JsonValue, } from './interfaces/idempotency-record.interface.js';
|
|
17
17
|
export type { IdempotencyStore, MaybePromise, } from './interfaces/idempotency-store.interface.js';
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAEA;;;;GAIG;AAEH,OAAO,EAAE,0BAA0B,EAAE,MAAM,uBAAuB,CAAC;AACnE,OAAO,EACL,0BAA0B,EAC1B,KAAK,4BAA4B,GAClC,MAAM,0CAA0C,CAAC;AAClD,OAAO,EACL,wBAAwB,EACxB,KAAK,yBAAyB,GAC/B,MAAM,wCAAwC,CAAC;AAChD,OAAO,EACL,KAAK,6BAA6B,EAClC,gCAAgC,GACjC,MAAM,qCAAqC,CAAC;AAC7C,OAAO,EAAE,0BAA0B,EAAE,MAAM,+BAA+B,CAAC;AAC3E,OAAO,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AAC5D,OAAO,EACL,KAAK,wBAAwB,EAC7B,UAAU,GACX,MAAM,iCAAiC,CAAC;AACzC,OAAO,EACL,sCAAsC,EACtC,KAAK,2BAA2B,EAChC,KAAK,2BAA2B,EAChC,KAAK,gCAAgC,GACtC,MAAM,8BAA8B,CAAC;AACtC,OAAO,EACL,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAEA;;;;GAIG;AAEH,OAAO,EAAE,0BAA0B,EAAE,MAAM,uBAAuB,CAAC;AACnE,OAAO,EACL,0BAA0B,EAC1B,KAAK,4BAA4B,GAClC,MAAM,0CAA0C,CAAC;AAClD,OAAO,EACL,wBAAwB,EACxB,KAAK,yBAAyB,GAC/B,MAAM,wCAAwC,CAAC;AAChD,OAAO,EACL,KAAK,6BAA6B,EAClC,gCAAgC,GACjC,MAAM,qCAAqC,CAAC;AAC7C,OAAO,EAAE,0BAA0B,EAAE,MAAM,+BAA+B,CAAC;AAC3E,OAAO,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AAC5D,OAAO,EACL,KAAK,wBAAwB,EAC7B,UAAU,GACX,MAAM,iCAAiC,CAAC;AACzC,OAAO,EACL,sCAAsC,EACtC,KAAK,2BAA2B,EAChC,KAAK,2BAA2B,EAChC,KAAK,gCAAgC,GACtC,MAAM,8BAA8B,CAAC;AACtC,OAAO,EACL,0BAA0B,EAC1B,qCAAqC,EACrC,+BAA+B,EAC/B,mBAAmB,GACpB,MAAM,2BAA2B,CAAC;AACnC,YAAY,EACV,0BAA0B,EAC1B,qBAAqB,EACrB,6BAA6B,GAC9B,MAAM,+CAA+C,CAAC;AACvD,YAAY,EACV,iBAAiB,EACjB,sBAAsB,EACtB,iBAAiB,EACjB,SAAS,GACV,MAAM,8CAA8C,CAAC;AACtD,YAAY,EACV,gBAAgB,EAChB,YAAY,GACb,MAAM,6CAA6C,CAAC;AACrD,OAAO,EACL,sBAAsB,EACtB,KAAK,6BAA6B,GACnC,MAAM,0BAA0B,CAAC;AAClC,OAAO,EACL,yBAAyB,EACzB,wBAAwB,EACxB,KAAK,+BAA+B,EACpC,KAAK,qBAAqB,EAC1B,KAAK,uBAAuB,EAC5B,KAAK,eAAe,GACrB,MAAM,4BAA4B,CAAC;AACpC,OAAO,EACL,KAAK,eAAe,EACpB,qBAAqB,EACrB,KAAK,4BAA4B,GAClC,MAAM,yBAAyB,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -12,7 +12,7 @@ export { buildIdempotencyAttributes } from './helpers/build-attributes.js';
|
|
|
12
12
|
export { fingerprintValue } from './helpers/fingerprint.js';
|
|
13
13
|
export { idempotent, } from './helpers/idempotency.intent.js';
|
|
14
14
|
export { createPartitionedIdempotencyKeyFactory, } from './helpers/partitioned-key.js';
|
|
15
|
-
export {
|
|
15
|
+
export { IDEMPOTENCY_KEY_ITEM_TOKEN, IDEMPOTENCY_OWNERSHIP_LOST_ITEM_TOKEN, IDEMPOTENCY_REPLAYED_ITEM_TOKEN, IdempotencyBehavior, } from './idempotency.behavior.js';
|
|
16
16
|
export { MemoryIdempotencyStore, } from './stores/memory.store.js';
|
|
17
17
|
export { createIdempotencyTableSql, PostgresIdempotencyStore, } from './stores/postgres.store.js';
|
|
18
18
|
export { RedisIdempotencyStore, } from './stores/redis.store.js';
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,sEAAsE;AAEtE;;;;GAIG;AAEH,OAAO,EAAE,0BAA0B,EAAE,MAAM,uBAAuB,CAAC;AACnE,OAAO,EACL,0BAA0B,GAE3B,MAAM,0CAA0C,CAAC;AAClD,OAAO,EACL,wBAAwB,GAEzB,MAAM,wCAAwC,CAAC;AAChD,OAAO,EAEL,gCAAgC,GACjC,MAAM,qCAAqC,CAAC;AAC7C,OAAO,EAAE,0BAA0B,EAAE,MAAM,+BAA+B,CAAC;AAC3E,OAAO,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AAC5D,OAAO,EAEL,UAAU,GACX,MAAM,iCAAiC,CAAC;AACzC,OAAO,EACL,sCAAsC,GAIvC,MAAM,8BAA8B,CAAC;AACtC,OAAO,EACL,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,sEAAsE;AAEtE;;;;GAIG;AAEH,OAAO,EAAE,0BAA0B,EAAE,MAAM,uBAAuB,CAAC;AACnE,OAAO,EACL,0BAA0B,GAE3B,MAAM,0CAA0C,CAAC;AAClD,OAAO,EACL,wBAAwB,GAEzB,MAAM,wCAAwC,CAAC;AAChD,OAAO,EAEL,gCAAgC,GACjC,MAAM,qCAAqC,CAAC;AAC7C,OAAO,EAAE,0BAA0B,EAAE,MAAM,+BAA+B,CAAC;AAC3E,OAAO,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AAC5D,OAAO,EAEL,UAAU,GACX,MAAM,iCAAiC,CAAC;AACzC,OAAO,EACL,sCAAsC,GAIvC,MAAM,8BAA8B,CAAC;AACtC,OAAO,EACL,0BAA0B,EAC1B,qCAAqC,EACrC,+BAA+B,EAC/B,mBAAmB,GACpB,MAAM,2BAA2B,CAAC;AAgBnC,OAAO,EACL,sBAAsB,GAEvB,MAAM,0BAA0B,CAAC;AAClC,OAAO,EACL,yBAAyB,EACzB,wBAAwB,GAKzB,MAAM,4BAA4B,CAAC;AACpC,OAAO,EAEL,qBAAqB,GAEtB,MAAM,yBAAyB,CAAC"}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type PostgresQueryableLike as Queryable } from '@cqrs-ddd/pipeline';
|
|
1
2
|
import type { IdempotencyRecord } from '../interfaces/idempotency-record.interface.js';
|
|
2
3
|
import type { IdempotencyStore } from '../interfaces/idempotency-store.interface.js';
|
|
3
4
|
/** A single returned row, keyed by column name. */
|
|
@@ -10,13 +11,10 @@ export interface PostgresQueryResultLike {
|
|
|
10
11
|
rowCount?: number | null;
|
|
11
12
|
}
|
|
12
13
|
/**
|
|
13
|
-
* Minimal structural shape of a `pg` `Pool` / `Client
|
|
14
|
-
*
|
|
15
|
-
* Add it in your app: `pnpm add pg`.
|
|
14
|
+
* Minimal structural shape of a `pg` `Pool` / `Client` whose results this store
|
|
15
|
+
* reads. A real `Pool` or `Client` satisfies it. Add it in your app: `pnpm add pg`.
|
|
16
16
|
*/
|
|
17
|
-
export
|
|
18
|
-
query(text: string, values?: unknown[]): Promise<PostgresQueryResultLike>;
|
|
19
|
-
}
|
|
17
|
+
export type PostgresQueryableLike = Queryable<PostgresQueryResultLike>;
|
|
20
18
|
/** Options for {@link PostgresIdempotencyStore}. */
|
|
21
19
|
export interface PostgresIdempotencyStoreOptions {
|
|
22
20
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"postgres.store.d.ts","sourceRoot":"","sources":["../../src/stores/postgres.store.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EACV,iBAAiB,EAElB,MAAM,+CAA+C,CAAC;AACvD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,8CAA8C,CAAC;AAErF,mDAAmD;AACnD,MAAM,WAAW,eAAe;IAC9B,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC;CAC3B;AAED,uDAAuD;AACvD,MAAM,WAAW,uBAAuB;IACtC,IAAI,EAAE,eAAe,EAAE,CAAC;IACxB,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC1B;AAED
|
|
1
|
+
{"version":3,"file":"postgres.store.d.ts","sourceRoot":"","sources":["../../src/stores/postgres.store.ts"],"names":[],"mappings":"AAEA,OAAO,EAEL,KAAK,qBAAqB,IAAI,SAAS,EACxC,MAAM,oBAAoB,CAAC;AAC5B,OAAO,KAAK,EACV,iBAAiB,EAElB,MAAM,+CAA+C,CAAC;AACvD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,8CAA8C,CAAC;AAErF,mDAAmD;AACnD,MAAM,WAAW,eAAe;IAC9B,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC;CAC3B;AAED,uDAAuD;AACvD,MAAM,WAAW,uBAAuB;IACtC,IAAI,EAAE,eAAe,EAAE,CAAC;IACxB,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC1B;AAED;;;GAGG;AACH,MAAM,MAAM,qBAAqB,GAAG,SAAS,CAAC,uBAAuB,CAAC,CAAC;AAEvE,oDAAoD;AACpD,MAAM,WAAW,+BAA+B;IAC9C;;;OAGG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAKD;;;;;;;;;;GAUG;AACH,wBAAgB,yBAAyB,CAAC,KAAK,SAAqB,GAAG,MAAM,CAe5E;AA6CD;;;;;;;;;;;;;;;;;;;GAmBG;AACH,qBAAa,wBAAyB,YAAW,gBAAgB;IAI7D,OAAO,CAAC,QAAQ,CAAC,EAAE;IAHrB,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAS;IAE/B,YACmB,EAAE,EAAE,qBAAqB,EAC1C,OAAO,GAAE,+BAAoC,EAG9C;IAEK,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,iBAAiB,GAAG,SAAS,CAAC,CAW7D;IAEK,WAAW,CACf,GAAG,EAAE,MAAM,EACX,MAAM,EAAE,iBAAiB,EACzB,KAAK,EAAE,MAAM,GACZ,OAAO,CAAC,OAAO,CAAC,CAoBlB;IAEK,eAAe,CACnB,GAAG,EAAE,MAAM,EACX,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,iBAAiB,EACzB,KAAK,EAAE,MAAM,GACZ,OAAO,CAAC,OAAO,CAAC,CAoBlB;IAEK,aAAa,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAUlE;IAEK,GAAG,CACP,GAAG,EAAE,MAAM,EACX,MAAM,EAAE,iBAAiB,EACzB,KAAK,EAAE,MAAM,GACZ,OAAO,CAAC,IAAI,CAAC,CAiBf;IAEK,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAEvC;IAED,OAAO,CAAC,QAAQ;CAajB"}
|
|
@@ -1,13 +1,6 @@
|
|
|
1
1
|
/* Copyright (C) 2026-present Aristotelis — see repository license. */
|
|
2
|
-
|
|
3
|
-
const
|
|
4
|
-
function assertSafeTable(table) {
|
|
5
|
-
if (!SAFE_IDENTIFIER.test(table)) {
|
|
6
|
-
throw new Error(`Invalid idempotency table name "${table}". ` +
|
|
7
|
-
'Use an unquoted identifier like "idempotency_keys" or "schema.idempotency_keys".');
|
|
8
|
-
}
|
|
9
|
-
return table;
|
|
10
|
-
}
|
|
2
|
+
import { assertSafeTable, } from '@cqrs-ddd/pipeline';
|
|
3
|
+
const safeTable = (table) => assertSafeTable(table, 'idempotency', 'idempotency_keys');
|
|
11
4
|
/**
|
|
12
5
|
* SQL that creates the idempotency table and its `expires_at` index. Run it
|
|
13
6
|
* from an application migration, not at request time.
|
|
@@ -20,7 +13,7 @@ function assertSafeTable(table) {
|
|
|
20
13
|
* @returns SQL statements required by {@link PostgresIdempotencyStore}.
|
|
21
14
|
*/
|
|
22
15
|
export function createIdempotencyTableSql(table = 'idempotency_keys') {
|
|
23
|
-
const name =
|
|
16
|
+
const name = safeTable(table);
|
|
24
17
|
return `CREATE TABLE IF NOT EXISTS ${name} (
|
|
25
18
|
key TEXT PRIMARY KEY,
|
|
26
19
|
status TEXT NOT NULL,
|
|
@@ -97,7 +90,7 @@ export class PostgresIdempotencyStore {
|
|
|
97
90
|
table;
|
|
98
91
|
constructor(db, options = {}) {
|
|
99
92
|
this.db = db;
|
|
100
|
-
this.table =
|
|
93
|
+
this.table = safeTable(options.table ?? 'idempotency_keys');
|
|
101
94
|
}
|
|
102
95
|
async get(key) {
|
|
103
96
|
const result = await this.db.query(`SELECT status, request_name, claim_id, fingerprint, replay_scope, response,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"postgres.store.js","sourceRoot":"","sources":["../../src/stores/postgres.store.ts"],"names":[],"mappings":"AAAA,sEAAsE;
|
|
1
|
+
{"version":3,"file":"postgres.store.js","sourceRoot":"","sources":["../../src/stores/postgres.store.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cqrs-ddd/pipeline-idempotency",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.0",
|
|
4
4
|
"description": "Idempotency for @cqrs-ddd/pipeline: deduplicates commands by an operation key and replays completed responses within the caller's scope",
|
|
5
5
|
"author": "Aristotelis",
|
|
6
6
|
"license": "SEE LICENSE IN LICENSE",
|
|
@@ -48,16 +48,16 @@
|
|
|
48
48
|
"access": "public"
|
|
49
49
|
},
|
|
50
50
|
"peerDependencies": {
|
|
51
|
-
"@cqrs-ddd/pipeline": "^0.
|
|
51
|
+
"@cqrs-ddd/pipeline": "^0.6.0"
|
|
52
52
|
},
|
|
53
53
|
"devDependencies": {
|
|
54
|
+
"@cqrs-ddd/pipeline": "0.6.0",
|
|
54
55
|
"@vitest/coverage-v8": "^5.0.5",
|
|
55
|
-
"vitest": "^5.0.5"
|
|
56
|
-
"@cqrs-ddd/pipeline": "0.5.1"
|
|
56
|
+
"vitest": "^5.0.5"
|
|
57
57
|
},
|
|
58
58
|
"dependencies": {
|
|
59
|
-
"@cqrs-ddd/
|
|
60
|
-
"@cqrs-ddd/
|
|
59
|
+
"@cqrs-ddd/safe-stringify": "^0.6.0",
|
|
60
|
+
"@cqrs-ddd/untyped": "^0.6.0"
|
|
61
61
|
},
|
|
62
62
|
"scripts": {
|
|
63
63
|
"build": "tsc -p tsconfig.build.json",
|