@nestjs-redisx/idempotency 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (31) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +52 -0
  3. package/dist/idempotency/api/decorators/idempotent.decorator.d.ts +12 -0
  4. package/dist/idempotency/api/decorators/idempotent.decorator.d.ts.map +1 -0
  5. package/dist/idempotency/api/interceptors/idempotency.interceptor.d.ts +19 -0
  6. package/dist/idempotency/api/interceptors/idempotency.interceptor.d.ts.map +1 -0
  7. package/dist/idempotency/application/ports/idempotency-service.port.d.ts +45 -0
  8. package/dist/idempotency/application/ports/idempotency-service.port.d.ts.map +1 -0
  9. package/dist/idempotency/application/ports/idempotency-store.port.d.ts +67 -0
  10. package/dist/idempotency/application/ports/idempotency-store.port.d.ts.map +1 -0
  11. package/dist/idempotency/application/services/idempotency.service.d.ts +27 -0
  12. package/dist/idempotency/application/services/idempotency.service.d.ts.map +1 -0
  13. package/dist/idempotency/infrastructure/adapters/redis-idempotency-store.adapter.d.ts +22 -0
  14. package/dist/idempotency/infrastructure/adapters/redis-idempotency-store.adapter.d.ts.map +1 -0
  15. package/dist/idempotency/infrastructure/scripts/lua-scripts.d.ts +24 -0
  16. package/dist/idempotency/infrastructure/scripts/lua-scripts.d.ts.map +1 -0
  17. package/dist/idempotency.plugin.d.ts +45 -0
  18. package/dist/idempotency.plugin.d.ts.map +1 -0
  19. package/dist/index.d.ts +10 -0
  20. package/dist/index.d.ts.map +1 -0
  21. package/dist/index.js +459 -0
  22. package/dist/index.js.map +1 -0
  23. package/dist/index.mjs +446 -0
  24. package/dist/index.mjs.map +1 -0
  25. package/dist/shared/constants/index.d.ts +7 -0
  26. package/dist/shared/constants/index.d.ts.map +1 -0
  27. package/dist/shared/errors/index.d.ts +39 -0
  28. package/dist/shared/errors/index.d.ts.map +1 -0
  29. package/dist/shared/types/index.d.ts +79 -0
  30. package/dist/shared/types/index.d.ts.map +1 -0
  31. package/package.json +77 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 NestJS RedisX Contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,52 @@
1
+ <p align="center">
2
+ <img src="https://raw.githubusercontent.com/nestjs-redisx/nestjs-redisx/main/website/public/images/logo.png" alt="NestJS RedisX" />
3
+ </p>
4
+
5
+ # @nestjs-redisx/idempotency
6
+
7
+ [![npm](https://img.shields.io/npm/v/@nestjs-redisx/idempotency)](https://www.npmjs.com/package/@nestjs-redisx/idempotency)
8
+ [![npm downloads](https://img.shields.io/npm/dm/@nestjs-redisx/idempotency)](https://www.npmjs.com/package/@nestjs-redisx/idempotency)
9
+ [![license](https://img.shields.io/npm/l/@nestjs-redisx/idempotency)](https://opensource.org/licenses/MIT)
10
+
11
+ HTTP idempotency plugin for NestJS RedisX. Prevents duplicate request processing using the `@Idempotent` decorator with fingerprint validation and automatic response replay.
12
+
13
+ ## Installation
14
+
15
+ ```bash
16
+ npm install @nestjs-redisx/core @nestjs-redisx/idempotency ioredis
17
+ ```
18
+
19
+ ## Quick Example
20
+
21
+ ```typescript
22
+ import { RedisModule } from '@nestjs-redisx/core';
23
+ import { IdempotencyPlugin, Idempotent } from '@nestjs-redisx/idempotency';
24
+
25
+ @Module({
26
+ imports: [
27
+ RedisModule.forRoot({
28
+ clients: { host: 'localhost', port: 6379 },
29
+ plugins: [new IdempotencyPlugin({ defaultTtl: 86400 })],
30
+ }),
31
+ ],
32
+ })
33
+ export class AppModule {}
34
+
35
+ @Controller('payments')
36
+ export class PaymentsController {
37
+ @Post()
38
+ @Idempotent({ ttl: 3600 })
39
+ async createPayment(@Body() dto: CreatePaymentDto) {
40
+ // Executes once per Idempotency-Key header, replays cached response after
41
+ return this.paymentsService.create(dto);
42
+ }
43
+ }
44
+ ```
45
+
46
+ ## Documentation
47
+
48
+ Full documentation: [nestjs-redisx.dev/en/reference/idempotency/](https://nestjs-redisx.dev/en/reference/idempotency/)
49
+
50
+ ## License
51
+
52
+ MIT
@@ -0,0 +1,12 @@
1
+ import { ExecutionContext } from '@nestjs/common';
2
+ export declare const IDEMPOTENT_OPTIONS: unique symbol;
3
+ export interface IIdempotentOptions {
4
+ ttl?: number;
5
+ keyExtractor?: (context: ExecutionContext) => string | Promise<string>;
6
+ fingerprintFields?: ('method' | 'path' | 'body' | 'query')[];
7
+ validateFingerprint?: boolean;
8
+ cacheHeaders?: string[];
9
+ skip?: (context: ExecutionContext) => boolean | Promise<boolean>;
10
+ }
11
+ export declare function Idempotent(options?: IIdempotentOptions): MethodDecorator;
12
+ //# sourceMappingURL=idempotent.decorator.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"idempotent.decorator.d.ts","sourceRoot":"","sources":["../../../../src/idempotency/api/decorators/idempotent.decorator.ts"],"names":[],"mappings":"AAAA,OAAO,EAAiD,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAIjG,eAAO,MAAM,kBAAkB,eAAmC,CAAC;AAEnE,MAAM,WAAW,kBAAkB;IACjC,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,YAAY,CAAC,EAAE,CAAC,OAAO,EAAE,gBAAgB,KAAK,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IACvE,iBAAiB,CAAC,EAAE,CAAC,QAAQ,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC,EAAE,CAAC;IAC7D,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAC9B,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;IACxB,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,gBAAgB,KAAK,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;CAClE;AAED,wBAAgB,UAAU,CAAC,OAAO,GAAE,kBAAuB,GAAG,eAAe,CAE5E"}
@@ -0,0 +1,19 @@
1
+ import { NestInterceptor, ExecutionContext, CallHandler } from '@nestjs/common';
2
+ import { Reflector } from '@nestjs/core';
3
+ import { Observable } from 'rxjs';
4
+ import { IIdempotencyPluginOptions } from '../../../shared/types';
5
+ import { IIdempotencyService } from '../../application/ports/idempotency-service.port';
6
+ export declare class IdempotencyInterceptor implements NestInterceptor {
7
+ private readonly idempotencyService;
8
+ private readonly config;
9
+ private readonly reflector;
10
+ constructor(idempotencyService: IIdempotencyService, config: IIdempotencyPluginOptions, reflector: Reflector);
11
+ intercept(context: ExecutionContext, next: CallHandler): Promise<Observable<unknown>>;
12
+ private getOptions;
13
+ private extractKey;
14
+ private generateFingerprint;
15
+ private hash;
16
+ private replayResponse;
17
+ private extractHeaders;
18
+ }
19
+ //# sourceMappingURL=idempotency.interceptor.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"idempotency.interceptor.d.ts","sourceRoot":"","sources":["../../../../src/idempotency/api/interceptors/idempotency.interceptor.ts"],"names":[],"mappings":"AAEA,OAAO,EAAc,eAAe,EAAE,gBAAgB,EAAE,WAAW,EAAU,MAAM,gBAAgB,CAAC;AACpG,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AACzC,OAAO,EAAE,UAAU,EAAM,MAAM,MAAM,CAAC;AAKtC,OAAO,EAAE,yBAAyB,EAAsB,MAAM,uBAAuB,CAAC;AACtF,OAAO,EAAE,mBAAmB,EAAE,MAAM,kDAAkD,CAAC;AAavF,qBACa,sBAAuB,YAAW,eAAe;IAE7B,OAAO,CAAC,QAAQ,CAAC,kBAAkB;IAC5B,OAAO,CAAC,QAAQ,CAAC,MAAM;IACxC,OAAO,CAAC,QAAQ,CAAC,SAAS;gBAFC,kBAAkB,EAAE,mBAAmB,EAChC,MAAM,EAAE,yBAAyB,EAClD,SAAS,EAAE,SAAS;IAGpD,SAAS,CAAC,OAAO,EAAE,gBAAgB,EAAE,IAAI,EAAE,WAAW,GAAG,OAAO,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC;IAsD3F,OAAO,CAAC,UAAU;YAIJ,UAAU;YAWV,mBAAmB;IAmBjC,OAAO,CAAC,IAAI;IAIZ,OAAO,CAAC,cAAc;IActB,OAAO,CAAC,cAAc;CAWvB"}
@@ -0,0 +1,45 @@
1
+ import { IIdempotencyRecord, IIdempotencyCheckResult, IIdempotencyResponse, IIdempotencyOptions } from '../../../shared/types';
2
+ /**
3
+ * Service port for idempotency operations
4
+ */
5
+ export interface IIdempotencyService {
6
+ /**
7
+ * Check if key exists and acquire lock if not.
8
+ *
9
+ * @param key - Idempotency key
10
+ * @param fingerprint - Request fingerprint hash
11
+ * @param options - Operation options
12
+ * @returns Check result with isNew flag and optional record
13
+ */
14
+ checkAndLock(key: string, fingerprint: string, options?: IIdempotencyOptions): Promise<IIdempotencyCheckResult>;
15
+ /**
16
+ * Store successful response.
17
+ *
18
+ * @param key - Idempotency key
19
+ * @param response - Response to store
20
+ * @param options - Operation options
21
+ */
22
+ complete(key: string, response: IIdempotencyResponse, options?: IIdempotencyOptions): Promise<void>;
23
+ /**
24
+ * Mark request as failed.
25
+ *
26
+ * @param key - Idempotency key
27
+ * @param error - Error message
28
+ */
29
+ fail(key: string, error: string): Promise<void>;
30
+ /**
31
+ * Get existing record by key.
32
+ *
33
+ * @param key - Idempotency key
34
+ * @returns Record or null if not found
35
+ */
36
+ get(key: string): Promise<IIdempotencyRecord | null>;
37
+ /**
38
+ * Delete record (for testing/admin).
39
+ *
40
+ * @param key - Idempotency key
41
+ * @returns True if deleted, false if not found
42
+ */
43
+ delete(key: string): Promise<boolean>;
44
+ }
45
+ //# sourceMappingURL=idempotency-service.port.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"idempotency-service.port.d.ts","sourceRoot":"","sources":["../../../../src/idempotency/application/ports/idempotency-service.port.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,uBAAuB,EAAE,oBAAoB,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAC;AAE/H;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAClC;;;;;;;OAOG;IACH,YAAY,CAAC,GAAG,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,mBAAmB,GAAG,OAAO,CAAC,uBAAuB,CAAC,CAAC;IAEhH;;;;;;OAMG;IACH,QAAQ,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,oBAAoB,EAAE,OAAO,CAAC,EAAE,mBAAmB,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAEpG;;;;;OAKG;IACH,IAAI,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAEhD;;;;;OAKG;IACH,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,kBAAkB,GAAG,IAAI,CAAC,CAAC;IAErD;;;;;OAKG;IACH,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;CACvC"}
@@ -0,0 +1,67 @@
1
+ import { IIdempotencyRecord } from '../../../shared/types';
2
+ /**
3
+ * Result of check and lock operation
4
+ */
5
+ export interface ICheckAndLockResult {
6
+ /** Status of the check */
7
+ status: 'new' | 'processing' | 'completed' | 'failed' | 'fingerprint_mismatch';
8
+ /** Record if exists */
9
+ record?: IIdempotencyRecord;
10
+ }
11
+ /**
12
+ * Data for completing a request
13
+ */
14
+ export interface ICompleteData {
15
+ /** HTTP status code */
16
+ statusCode: number;
17
+ /** Response body (JSON string) */
18
+ response: string;
19
+ /** Response headers (JSON string) */
20
+ headers?: string;
21
+ /** Completion timestamp (ms) */
22
+ completedAt: number;
23
+ }
24
+ /**
25
+ * Store port for Redis operations
26
+ */
27
+ export interface IIdempotencyStore {
28
+ /**
29
+ * Atomic check and lock operation using Lua script.
30
+ *
31
+ * @param key - Redis key
32
+ * @param fingerprint - Request fingerprint hash
33
+ * @param lockTimeoutMs - Lock timeout in milliseconds
34
+ * @returns Check result
35
+ */
36
+ checkAndLock(key: string, fingerprint: string, lockTimeoutMs: number): Promise<ICheckAndLockResult>;
37
+ /**
38
+ * Mark record as completed with response data.
39
+ *
40
+ * @param key - Redis key
41
+ * @param data - Completion data
42
+ * @param ttlSeconds - TTL in seconds
43
+ */
44
+ complete(key: string, data: ICompleteData, ttlSeconds: number): Promise<void>;
45
+ /**
46
+ * Mark record as failed.
47
+ *
48
+ * @param key - Redis key
49
+ * @param error - Error message
50
+ */
51
+ fail(key: string, error: string): Promise<void>;
52
+ /**
53
+ * Get record by key.
54
+ *
55
+ * @param key - Redis key
56
+ * @returns Record or null if not found
57
+ */
58
+ get(key: string): Promise<IIdempotencyRecord | null>;
59
+ /**
60
+ * Delete record.
61
+ *
62
+ * @param key - Redis key
63
+ * @returns True if deleted, false if not found
64
+ */
65
+ delete(key: string): Promise<boolean>;
66
+ }
67
+ //# sourceMappingURL=idempotency-store.port.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"idempotency-store.port.d.ts","sourceRoot":"","sources":["../../../../src/idempotency/application/ports/idempotency-store.port.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAC;AAE3D;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAClC,0BAA0B;IAC1B,MAAM,EAAE,KAAK,GAAG,YAAY,GAAG,WAAW,GAAG,QAAQ,GAAG,sBAAsB,CAAC;IAE/E,uBAAuB;IACvB,MAAM,CAAC,EAAE,kBAAkB,CAAC;CAC7B;AAED;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B,uBAAuB;IACvB,UAAU,EAAE,MAAM,CAAC;IAEnB,kCAAkC;IAClC,QAAQ,EAAE,MAAM,CAAC;IAEjB,qCAAqC;IACrC,OAAO,CAAC,EAAE,MAAM,CAAC;IAEjB,gCAAgC;IAChC,WAAW,EAAE,MAAM,CAAC;CACrB;AAED;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC;;;;;;;OAOG;IACH,YAAY,CAAC,GAAG,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,GAAG,OAAO,CAAC,mBAAmB,CAAC,CAAC;IAEpG;;;;;;OAMG;IACH,QAAQ,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAE9E;;;;;OAKG;IACH,IAAI,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAEhD;;;;;OAKG;IACH,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,kBAAkB,GAAG,IAAI,CAAC,CAAC;IAErD;;;;;OAKG;IACH,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;CACvC"}
@@ -0,0 +1,27 @@
1
+ import { IIdempotencyPluginOptions, IIdempotencyRecord, IIdempotencyCheckResult, IIdempotencyResponse, IIdempotencyOptions } from '../../../shared/types';
2
+ import { IIdempotencyService } from '../ports/idempotency-service.port';
3
+ import { IIdempotencyStore } from '../ports/idempotency-store.port';
4
+ interface IMetricsService {
5
+ incrementCounter(name: string, labels?: Record<string, string>, value?: number): void;
6
+ observeHistogram(name: string, value: number, labels?: Record<string, string>): void;
7
+ }
8
+ /**
9
+ * Idempotency service implementation
10
+ */
11
+ export declare class IdempotencyService implements IIdempotencyService {
12
+ private readonly config;
13
+ private readonly store;
14
+ private readonly metrics?;
15
+ constructor(config: IIdempotencyPluginOptions, store: IIdempotencyStore, metrics?: IMetricsService | undefined);
16
+ checkAndLock(key: string, fingerprint: string, options?: IIdempotencyOptions): Promise<IIdempotencyCheckResult>;
17
+ private recordDuration;
18
+ complete(key: string, response: IIdempotencyResponse, options?: IIdempotencyOptions): Promise<void>;
19
+ fail(key: string, error: string): Promise<void>;
20
+ get(key: string): Promise<IIdempotencyRecord | null>;
21
+ delete(key: string): Promise<boolean>;
22
+ private waitForCompletion;
23
+ private buildKey;
24
+ private sleep;
25
+ }
26
+ export {};
27
+ //# sourceMappingURL=idempotency.service.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"idempotency.service.d.ts","sourceRoot":"","sources":["../../../../src/idempotency/application/services/idempotency.service.ts"],"names":[],"mappings":"AAOA,OAAO,EAAE,yBAAyB,EAAE,kBAAkB,EAAE,uBAAuB,EAAE,oBAAoB,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAC;AAC1J,OAAO,EAAE,mBAAmB,EAAE,MAAM,mCAAmC,CAAC;AACxE,OAAO,EAAE,iBAAiB,EAAE,MAAM,iCAAiC,CAAC;AAKpE,UAAU,eAAe;IACvB,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACtF,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,IAAI,CAAC;CACtF;AAED;;GAEG;AACH,qBACa,kBAAmB,YAAW,mBAAmB;IAG1D,OAAO,CAAC,QAAQ,CAAC,MAAM;IAEvB,OAAO,CAAC,QAAQ,CAAC,KAAK;IACe,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAC;gBAH7C,MAAM,EAAE,yBAAyB,EAEjC,KAAK,EAAE,iBAAiB,EACa,OAAO,CAAC,EAAE,eAAe,YAAA;IAG3E,YAAY,CAAC,GAAG,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,OAAO,GAAE,mBAAwB,GAAG,OAAO,CAAC,uBAAuB,CAAC;IAiCzH,OAAO,CAAC,cAAc;IAKhB,QAAQ,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,oBAAoB,EAAE,OAAO,GAAE,mBAAwB,GAAG,OAAO,CAAC,IAAI,CAAC;IAgBvG,IAAI,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAK/C,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,kBAAkB,GAAG,IAAI,CAAC;IAKpD,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;YAK7B,iBAAiB;IAoB/B,OAAO,CAAC,QAAQ;IAKhB,OAAO,CAAC,KAAK;CAGd"}
@@ -0,0 +1,22 @@
1
+ import { OnModuleInit } from '@nestjs/common';
2
+ import { IRedisDriver } from '@nestjs-redisx/core';
3
+ import { IIdempotencyRecord } from '../../../shared/types';
4
+ import { IIdempotencyStore, ICheckAndLockResult, ICompleteData } from '../../application/ports/idempotency-store.port';
5
+ /**
6
+ * Redis-based idempotency store implementation
7
+ */
8
+ export declare class RedisIdempotencyStoreAdapter implements IIdempotencyStore, OnModuleInit {
9
+ private readonly driver;
10
+ private checkAndLockSha;
11
+ constructor(driver: IRedisDriver);
12
+ /**
13
+ * Pre-load Lua script on module initialization
14
+ */
15
+ onModuleInit(): Promise<void>;
16
+ checkAndLock(key: string, fingerprint: string, lockTimeoutMs: number): Promise<ICheckAndLockResult>;
17
+ complete(key: string, data: ICompleteData, ttlSeconds: number): Promise<void>;
18
+ fail(key: string, error: string): Promise<void>;
19
+ get(key: string): Promise<IIdempotencyRecord | null>;
20
+ delete(key: string): Promise<boolean>;
21
+ }
22
+ //# sourceMappingURL=redis-idempotency-store.adapter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"redis-idempotency-store.adapter.d.ts","sourceRoot":"","sources":["../../../../src/idempotency/infrastructure/adapters/redis-idempotency-store.adapter.ts"],"names":[],"mappings":"AAAA,OAAO,EAAsB,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAClE,OAAO,EAAE,YAAY,EAAgB,MAAM,qBAAqB,CAAC;AAEjE,OAAO,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAC;AAC3D,OAAO,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,aAAa,EAAE,MAAM,gDAAgD,CAAC;AAGvH;;GAEG;AACH,qBACa,4BAA6B,YAAW,iBAAiB,EAAE,YAAY;IAGhD,OAAO,CAAC,QAAQ,CAAC,MAAM;IAFzD,OAAO,CAAC,eAAe,CAAuB;gBAEK,MAAM,EAAE,YAAY;IAEvE;;OAEG;IACG,YAAY,IAAI,OAAO,CAAC,IAAI,CAAC;IAI7B,YAAY,CAAC,GAAG,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,GAAG,OAAO,CAAC,mBAAmB,CAAC;IAqCnG,QAAQ,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAW7E,IAAI,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAQ/C,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,kBAAkB,GAAG,IAAI,CAAC;IAmBpD,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;CAI5C"}
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Inline Lua scripts for idempotency operations.
3
+ *
4
+ * Scripts are stored as inline strings to avoid issues with file reading
5
+ * after build (dist directory doesn't contain .lua files).
6
+ */
7
+ /**
8
+ * Check and Lock Lua script for idempotency
9
+ *
10
+ * This script atomically checks if an idempotency key exists and locks it if new.
11
+ *
12
+ * KEYS[1] = idempotency key
13
+ * ARGV[1] = fingerprint
14
+ * ARGV[2] = lock timeout (ms)
15
+ * ARGV[3] = current timestamp (ms)
16
+ *
17
+ * Returns:
18
+ * - ['new'] - new request, lock acquired
19
+ * - ['fingerprint_mismatch'] - same key, different fingerprint
20
+ * - ['processing'] - another request is processing
21
+ * - [status, statusCode, response, headers, error] - completed/failed record
22
+ */
23
+ export declare const CHECK_AND_LOCK_SCRIPT: string;
24
+ //# sourceMappingURL=lua-scripts.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lua-scripts.d.ts","sourceRoot":"","sources":["../../../../src/idempotency/infrastructure/scripts/lua-scripts.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH;;;;;;;;;;;;;;;GAeG;AACH,eAAO,MAAM,qBAAqB,QAuD1B,CAAC"}
@@ -0,0 +1,45 @@
1
+ /**
2
+ * Idempotency plugin for NestJS RedisX.
3
+ * Provides request deduplication with response replay for idempotent operations.
4
+ */
5
+ import { Provider } from '@nestjs/common';
6
+ import { IRedisXPlugin } from '@nestjs-redisx/core';
7
+ import { IIdempotencyPluginOptions } from './shared/types';
8
+ /**
9
+ * Idempotency plugin for NestJS RedisX.
10
+ *
11
+ * Provides request deduplication with response replay:
12
+ * - Prevents duplicate processing of same request
13
+ * - Replays successful responses
14
+ * - Handles concurrent requests
15
+ * - Validates request fingerprints
16
+ *
17
+ * @example
18
+ * ```typescript
19
+ * @Module({
20
+ * imports: [
21
+ * RedisModule.forRoot({
22
+ * clients: { host: 'localhost', port: 6379 },
23
+ * plugins: [
24
+ * new IdempotencyPlugin({
25
+ * defaultTtl: 86400,
26
+ * headerName: 'Idempotency-Key',
27
+ * validateFingerprint: true,
28
+ * }),
29
+ * ],
30
+ * }),
31
+ * ],
32
+ * })
33
+ * export class AppModule {}
34
+ * ```
35
+ */
36
+ export declare class IdempotencyPlugin implements IRedisXPlugin {
37
+ private readonly options;
38
+ readonly name = "idempotency";
39
+ readonly version = "0.1.0";
40
+ readonly description = "Request deduplication with response replay for idempotent operations";
41
+ constructor(options?: IIdempotencyPluginOptions);
42
+ getProviders(): Provider[];
43
+ getExports(): Array<string | symbol | Provider>;
44
+ }
45
+ //# sourceMappingURL=idempotency.plugin.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"idempotency.plugin.d.ts","sourceRoot":"","sources":["../src/idempotency.plugin.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAE1C,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAMpD,OAAO,EAAE,yBAAyB,EAAE,MAAM,gBAAgB,CAAC;AAa3D;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,qBAAa,iBAAkB,YAAW,aAAa;IAKzC,OAAO,CAAC,QAAQ,CAAC,OAAO;IAJpC,QAAQ,CAAC,IAAI,iBAAiB;IAC9B,QAAQ,CAAC,OAAO,WAAW;IAC3B,QAAQ,CAAC,WAAW,0EAA0E;gBAEjE,OAAO,GAAE,yBAA8B;IAEpE,YAAY,IAAI,QAAQ,EAAE;IAuB1B,UAAU,IAAI,KAAK,CAAC,MAAM,GAAG,MAAM,GAAG,QAAQ,CAAC;CAGhD"}
@@ -0,0 +1,10 @@
1
+ export { IdempotencyPlugin } from './idempotency.plugin';
2
+ export { IdempotencyService } from './idempotency/application/services/idempotency.service';
3
+ export type { IIdempotencyService } from './idempotency/application/ports/idempotency-service.port';
4
+ export type { IIdempotencyStore } from './idempotency/application/ports/idempotency-store.port';
5
+ export { Idempotent, type IIdempotentOptions, IDEMPOTENT_OPTIONS } from './idempotency/api/decorators/idempotent.decorator';
6
+ export { IdempotencyInterceptor } from './idempotency/api/interceptors/idempotency.interceptor';
7
+ export type { IIdempotencyPluginOptions, IIdempotencyRecord, IIdempotencyCheckResult, IIdempotencyResponse, IIdempotencyOptions } from './shared/types';
8
+ export { IdempotencyError, IdempotencyKeyRequiredError, IdempotencyFingerprintMismatchError, IdempotencyTimeoutError, IdempotencyFailedError, IdempotencyRecordNotFoundError } from './shared/errors';
9
+ export { IDEMPOTENCY_PLUGIN_OPTIONS, IDEMPOTENCY_SERVICE, IDEMPOTENCY_STORE } from './shared/constants';
10
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AAGzD,OAAO,EAAE,kBAAkB,EAAE,MAAM,wDAAwD,CAAC;AAG5F,YAAY,EAAE,mBAAmB,EAAE,MAAM,0DAA0D,CAAC;AACpG,YAAY,EAAE,iBAAiB,EAAE,MAAM,wDAAwD,CAAC;AAGhG,OAAO,EAAE,UAAU,EAAE,KAAK,kBAAkB,EAAE,kBAAkB,EAAE,MAAM,mDAAmD,CAAC;AAG5H,OAAO,EAAE,sBAAsB,EAAE,MAAM,wDAAwD,CAAC;AAGhG,YAAY,EAAE,yBAAyB,EAAE,kBAAkB,EAAE,uBAAuB,EAAE,oBAAoB,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;AAGxJ,OAAO,EAAE,gBAAgB,EAAE,2BAA2B,EAAE,mCAAmC,EAAE,uBAAuB,EAAE,sBAAsB,EAAE,8BAA8B,EAAE,MAAM,iBAAiB,CAAC;AAGtM,OAAO,EAAE,0BAA0B,EAAE,mBAAmB,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC"}