@nestjs-redisx/idempotency 1.6.0 → 1.6.1

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 (25) hide show
  1. package/dist/idempotency/api/filters/idempotency-exception.filter.d.ts +1 -0
  2. package/dist/idempotency/api/filters/idempotency-exception.filter.d.ts.map +1 -1
  3. package/dist/idempotency/api/interceptors/idempotency.interceptor.d.ts.map +1 -1
  4. package/dist/idempotency/application/ports/idempotency-service.port.d.ts +1 -1
  5. package/dist/idempotency/application/ports/idempotency-service.port.d.ts.map +1 -1
  6. package/dist/idempotency/application/ports/idempotency-store.port.d.ts +4 -2
  7. package/dist/idempotency/application/ports/idempotency-store.port.d.ts.map +1 -1
  8. package/dist/idempotency/application/services/idempotency.service.d.ts +15 -1
  9. package/dist/idempotency/application/services/idempotency.service.d.ts.map +1 -1
  10. package/dist/idempotency/infrastructure/adapters/redis-idempotency-store.adapter.d.ts +2 -2
  11. package/dist/idempotency/infrastructure/adapters/redis-idempotency-store.adapter.d.ts.map +1 -1
  12. package/dist/idempotency/infrastructure/scripts/lua-scripts.d.ts +1 -0
  13. package/dist/idempotency/infrastructure/scripts/lua-scripts.d.ts.map +1 -1
  14. package/dist/idempotency.plugin.d.ts.map +1 -1
  15. package/dist/index.d.ts +1 -1
  16. package/dist/index.d.ts.map +1 -1
  17. package/dist/index.js +106 -34
  18. package/dist/index.js.map +1 -1
  19. package/dist/index.mjs +106 -35
  20. package/dist/index.mjs.map +1 -1
  21. package/dist/shared/errors/index.d.ts +7 -0
  22. package/dist/shared/errors/index.d.ts.map +1 -1
  23. package/dist/shared/types/index.d.ts +13 -1
  24. package/dist/shared/types/index.d.ts.map +1 -1
  25. package/package.json +2 -2
@@ -1 +1 @@
1
- {"version":3,"sources":["../package.json","../src/shared/errors/index.ts","../src/idempotency/api/filters/idempotency-exception.filter.ts","../src/shared/constants/index.ts","../src/idempotency/api/decorators/idempotent.decorator.ts","../src/idempotency/api/interceptors/idempotency.interceptor.ts","../src/idempotency/application/services/idempotency.service.ts","../src/idempotency/infrastructure/scripts/lua-scripts.ts","../src/idempotency/infrastructure/adapters/redis-idempotency-store.adapter.ts","../src/idempotency.plugin.ts"],"names":["Inject","HttpAdapterHost","Injectable","Reflector"],"mappings":"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","file":"index.mjs","sourcesContent":["{\n \"name\": \"@nestjs-redisx/idempotency\",\n \"version\": \"1.6.0\",\n \"description\": \"Idempotency plugin for NestJS RedisX with request deduplication and response replay\",\n \"author\": \"NestJS RedisX Team\",\n \"license\": \"MIT\",\n \"main\": \"dist/index.js\",\n \"types\": \"dist/index.d.ts\",\n \"exports\": {\n \".\": {\n \"types\": \"./dist/index.d.ts\",\n \"require\": \"./dist/index.js\",\n \"import\": \"./dist/index.js\",\n \"default\": \"./dist/index.js\"\n }\n },\n \"files\": [\n \"dist\",\n \"README.md\",\n \"LICENSE\"\n ],\n \"scripts\": {\n \"build\": \"tsup\",\n \"test\": \"SKIP_INTEGRATION=true vitest run\",\n \"test:watch\": \"SKIP_INTEGRATION=true vitest\",\n \"test:cov\": \"SKIP_INTEGRATION=true vitest run --coverage\",\n \"test:integration\": \"SKIP_INTEGRATION=false vitest run test/integration/idempotency.integration.spec.ts\",\n \"test:integration:watch\": \"SKIP_INTEGRATION=false vitest watch test/integration/idempotency.integration.spec.ts\",\n \"docker:up\": \"cd ../.. && docker-compose up -d redis\",\n \"docker:down\": \"cd ../.. && docker-compose down\",\n \"docker:logs\": \"cd ../.. && docker-compose logs -f redis\",\n \"test:docker\": \"npm run docker:up && sleep 3 && npm run test:integration; TEST_EXIT=$?; npm run docker:down; exit $TEST_EXIT\",\n \"test:all\": \"npm run test:cov && npm run test:integration\",\n \"lint\": \"eslint \\\"{src,test}/**/*.ts\\\"\",\n \"format\": \"prettier --write \\\"{src,test}/**/*.ts\\\"\"\n },\n \"keywords\": [\n \"nestjs\",\n \"redis\",\n \"idempotency\",\n \"idempotent\",\n \"deduplication\",\n \"request-replay\",\n \"http-idempotency\"\n ],\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"https://github.com/nestjs-redisx/nestjs-redisx.git\",\n \"directory\": \"packages/idempotency\"\n },\n \"homepage\": \"https://nestjs-redisx.dev/en/reference/idempotency/\",\n \"bugs\": {\n \"url\": \"https://github.com/nestjs-redisx/nestjs-redisx/issues\"\n },\n \"peerDependencies\": {\n \"@nestjs-redisx/core\": \"^1.0.0\",\n \"@nestjs/common\": \"^10.0.0 || ^11.0.0\",\n \"@nestjs/core\": \"^10.0.0 || ^11.0.0\",\n \"reflect-metadata\": \"^0.2.0\",\n \"rxjs\": \"^7.8.0\"\n },\n \"devDependencies\": {\n \"@nestjs/testing\": \"^9.0.0 || ^10.0.0 || ^11.0.0\",\n \"@types/node\": \"^20.0.0\",\n \"@typescript-eslint/eslint-plugin\": \"^6.0.0\",\n \"@typescript-eslint/parser\": \"^6.0.0\",\n \"@vitest/coverage-v8\": \"^1.6.0\",\n \"eslint\": \"^8.0.0\",\n \"prettier\": \"^3.0.0\",\n \"tsup\": \"^8.0.0\",\n \"typescript\": \"^5.3.0\",\n \"vitest\": \"^1.6.0\"\n },\n \"publishConfig\": {\n \"access\": \"public\"\n },\n \"engines\": {\n \"node\": \">=18.0.0\"\n }\n}\n","import { RedisXError, ErrorCode } from '@nestjs-redisx/core';\n\n/**\n * Base class for all idempotency-related errors\n */\nexport class IdempotencyError extends RedisXError {\n constructor(\n message: string,\n code: ErrorCode,\n public readonly idempotencyKey: string,\n cause?: Error,\n ) {\n super(message, code, cause, { idempotencyKey });\n }\n}\n\n/**\n * Thrown when Idempotency-Key header is required but not provided\n */\nexport class IdempotencyKeyRequiredError extends IdempotencyError {\n constructor() {\n super('Idempotency-Key header is required', ErrorCode.IDEMPOTENCY_KEY_INVALID, '');\n }\n}\n\n/**\n * Thrown when request fingerprint doesn't match the stored one\n */\nexport class IdempotencyFingerprintMismatchError extends IdempotencyError {\n constructor(key: string) {\n super(`Request body does not match previous request with idempotency key \"${key}\"`, ErrorCode.IDEMPOTENCY_KEY_INVALID, key);\n }\n}\n\n/**\n * Thrown when timeout waiting for concurrent request to complete\n */\nexport class IdempotencyTimeoutError extends IdempotencyError {\n constructor(key: string) {\n super(`Timeout waiting for concurrent request with idempotency key \"${key}\"`, ErrorCode.OP_TIMEOUT, key);\n }\n}\n\n/**\n * Thrown when previous request with same key failed\n */\nexport class IdempotencyFailedError extends IdempotencyError {\n constructor(key: string, error?: string) {\n super(`Previous request with idempotency key \"${key}\" failed${error ? `: ${error}` : ''}`, ErrorCode.IDEMPOTENCY_PREVIOUS_FAILED, key);\n }\n}\n\n/**\n * Thrown when idempotency record not found in Redis\n */\nexport class IdempotencyRecordNotFoundError extends IdempotencyError {\n constructor(key: string) {\n super(`Idempotency record not found for key \"${key}\"`, ErrorCode.OP_KEY_NOT_FOUND, key);\n }\n}\n","import { Catch, ExceptionFilter, ArgumentsHost, HttpStatus, Inject } from '@nestjs/common';\nimport { HttpAdapterHost } from '@nestjs/core';\n\nimport { IdempotencyError, IdempotencyKeyRequiredError, IdempotencyFingerprintMismatchError, IdempotencyTimeoutError, IdempotencyFailedError } from '../../../shared/errors';\n\n/**\n * Exception filter for idempotency errors.\n *\n * Without this filter the idempotency errors (which extend `RedisXError`, not\n * `HttpException`) would surface as `500 Internal Server Error`. That is wrong\n * for situations that are part of normal idempotent-request handling — a reused\n * key with a different body, a still-in-progress concurrent request, or a key\n * whose previous attempt failed. This filter maps each error to an appropriate\n * 4xx status while leaving genuinely unexpected errors as 500.\n */\n@Catch(IdempotencyError)\nexport class IdempotencyExceptionFilter implements ExceptionFilter {\n constructor(@Inject(HttpAdapterHost) private readonly adapterHost: HttpAdapterHost) {}\n\n catch(exception: IdempotencyError, host: ArgumentsHost): void {\n const httpAdapter = this.adapterHost.httpAdapter;\n if (!httpAdapter) {\n throw new Error('IdempotencyExceptionFilter: HttpAdapterHost is not initialized. Ensure the NestJS application has bootstrapped with an HTTP adapter before handling requests.');\n }\n\n const response = host.switchToHttp().getResponse();\n const status = this.resolveStatus(exception);\n\n const body = {\n statusCode: status,\n message: exception.message,\n error: this.reasonPhrase(status),\n idempotencyKey: exception.idempotencyKey || undefined,\n };\n\n httpAdapter.reply(response, body, status);\n }\n\n /**\n * Maps an idempotency error to its HTTP status code.\n *\n * - Missing/invalid key -> 400 Bad Request\n * - Reused key with a different request -> 422 Unprocessable Entity\n * - Concurrent request still in progress (timeout) -> 409 Conflict\n * - Previous request with the same key failed -> 409 Conflict\n * - Anything else (unexpected) -> 500 Internal Server Error\n */\n private resolveStatus(exception: IdempotencyError): HttpStatus {\n if (exception instanceof IdempotencyKeyRequiredError) {\n return HttpStatus.BAD_REQUEST;\n }\n if (exception instanceof IdempotencyFingerprintMismatchError) {\n return HttpStatus.UNPROCESSABLE_ENTITY;\n }\n if (exception instanceof IdempotencyTimeoutError) {\n return HttpStatus.CONFLICT;\n }\n if (exception instanceof IdempotencyFailedError) {\n return HttpStatus.CONFLICT;\n }\n return HttpStatus.INTERNAL_SERVER_ERROR;\n }\n\n private reasonPhrase(status: HttpStatus): string {\n switch (status) {\n case HttpStatus.BAD_REQUEST:\n return 'Bad Request';\n case HttpStatus.CONFLICT:\n return 'Conflict';\n case HttpStatus.UNPROCESSABLE_ENTITY:\n return 'Unprocessable Entity';\n default:\n return 'Internal Server Error';\n }\n }\n}\n","/**\n * Injection tokens for idempotency plugin.\n */\nexport const IDEMPOTENCY_PLUGIN_OPTIONS = Symbol.for('IDEMPOTENCY_PLUGIN_OPTIONS');\nexport const IDEMPOTENCY_SERVICE = Symbol.for('IDEMPOTENCY_SERVICE');\nexport const IDEMPOTENCY_STORE = Symbol.for('IDEMPOTENCY_STORE');\n\n/**\n * Plugin-specific Redis driver token.\n * Resolves to the named client specified in plugin options.\n */\nexport const IDEMPOTENCY_REDIS_DRIVER = Symbol.for('IDEMPOTENCY_REDIS_DRIVER');\n","import { applyDecorators, SetMetadata, UseInterceptors, ExecutionContext } from '@nestjs/common';\n\nimport { IdempotencyInterceptor } from '../interceptors/idempotency.interceptor';\n\nexport const IDEMPOTENT_OPTIONS = Symbol.for('IDEMPOTENT_OPTIONS');\n\nexport interface IIdempotentOptions {\n ttl?: number;\n keyExtractor?: (context: ExecutionContext) => string | Promise<string>;\n fingerprintFields?: ('method' | 'path' | 'body' | 'query')[];\n validateFingerprint?: boolean;\n cacheHeaders?: string[];\n skip?: (context: ExecutionContext) => boolean | Promise<boolean>;\n}\n\nexport function Idempotent(options: IIdempotentOptions = {}): MethodDecorator {\n return applyDecorators(SetMetadata(IDEMPOTENT_OPTIONS, options), UseInterceptors(IdempotencyInterceptor));\n}\n","import { createHash } from 'crypto';\n\nimport { Injectable, NestInterceptor, ExecutionContext, CallHandler, Inject, Logger } from '@nestjs/common';\nimport { HttpAdapterHost, Reflector } from '@nestjs/core';\nimport { Observable, of } from 'rxjs';\nimport { tap } from 'rxjs/operators';\n\nimport { IDEMPOTENCY_SERVICE, IDEMPOTENCY_PLUGIN_OPTIONS } from '../../../shared/constants';\nimport { IdempotencyFingerprintMismatchError, IdempotencyFailedError } from '../../../shared/errors';\nimport { IIdempotencyPluginOptions, IIdempotencyRecord } from '../../../shared/types';\nimport { IIdempotencyService } from '../../application/ports/idempotency-service.port';\nimport { IDEMPOTENT_OPTIONS, IIdempotentOptions } from '../decorators/idempotent.decorator';\n\n@Injectable()\nexport class IdempotencyInterceptor implements NestInterceptor {\n private readonly logger = new Logger(IdempotencyInterceptor.name);\n\n constructor(\n @Inject(IDEMPOTENCY_SERVICE) private readonly idempotencyService: IIdempotencyService,\n @Inject(IDEMPOTENCY_PLUGIN_OPTIONS) private readonly config: IIdempotencyPluginOptions,\n @Inject(Reflector) private readonly reflector: Reflector,\n @Inject(HttpAdapterHost) private readonly adapterHost: HttpAdapterHost,\n ) {}\n\n async intercept(context: ExecutionContext, next: CallHandler): Promise<Observable<unknown>> {\n const options = this.getOptions(context);\n const response = context.switchToHttp().getResponse();\n\n const key = await this.extractKey(context, options);\n\n if (!key) {\n return next.handle();\n }\n\n if (options.skip && (await options.skip(context))) {\n return next.handle();\n }\n\n const fingerprint = await this.generateFingerprint(context, options);\n\n let checkResult: Awaited<ReturnType<typeof this.idempotencyService.checkAndLock>>;\n try {\n checkResult = await this.idempotencyService.checkAndLock(key, fingerprint, {\n ttl: options.ttl,\n });\n } catch (error) {\n // The idempotency store is unavailable (e.g. Redis is down). Honor the\n // configured errorPolicy: 'fail-open' proceeds without idempotency\n // protection, 'fail-closed' (default) rejects by propagating the error.\n if ((this.config.errorPolicy ?? 'fail-closed') === 'fail-open') {\n this.logger.warn(`Idempotency store unavailable for key \"${key}\"; proceeding without idempotency (fail-open): ${(error as Error).message}`);\n return next.handle();\n }\n throw error;\n }\n\n if (!checkResult.isNew) {\n if (checkResult.fingerprintMismatch) {\n throw new IdempotencyFingerprintMismatchError(key);\n }\n\n const record = checkResult.record!;\n\n if (record.status === 'failed') {\n throw new IdempotencyFailedError(key, record.error);\n }\n\n return this.replayResponse(response, record);\n }\n\n return next.handle().pipe(\n tap({\n next: (data) => {\n void this.idempotencyService.complete(\n key,\n {\n statusCode: response.statusCode,\n body: data,\n headers: this.extractHeaders(response, options),\n },\n { ttl: options.ttl },\n );\n },\n error: (error) => {\n void this.idempotencyService.fail(key, error.message);\n },\n }),\n );\n }\n\n private getOptions(context: ExecutionContext): IIdempotentOptions {\n return this.reflector.get<IIdempotentOptions>(IDEMPOTENT_OPTIONS, context.getHandler()) ?? {};\n }\n\n private async extractKey(context: ExecutionContext, options: IIdempotentOptions): Promise<string | null> {\n if (options.keyExtractor) {\n return options.keyExtractor(context);\n }\n\n const request = context.switchToHttp().getRequest();\n const headerName = this.config.headerName ?? 'Idempotency-Key';\n\n return request.headers[headerName.toLowerCase()] ?? null;\n }\n\n private async generateFingerprint(context: ExecutionContext, options: IIdempotentOptions): Promise<string> {\n if (this.config.fingerprintGenerator) {\n return this.config.fingerprintGenerator(context);\n }\n\n const request = context.switchToHttp().getRequest();\n const fields = options.fingerprintFields ?? this.config.fingerprintFields ?? ['method', 'path', 'body'];\n\n const parts: string[] = [];\n\n if (fields.includes('method')) parts.push(request.method);\n if (fields.includes('path')) parts.push(this.getRequestPath(request));\n if (fields.includes('body')) parts.push(JSON.stringify(request.body ?? {}));\n if (fields.includes('query')) parts.push(JSON.stringify(request.query ?? {}));\n\n const data = parts.join('|');\n return this.hash(data);\n }\n\n private getRequestPath(request: unknown): string {\n const httpAdapter = this.adapterHost.httpAdapter;\n const req = request as { path?: string; url?: string };\n return httpAdapter?.getRequestUrl?.(request) ?? req.url ?? req.path ?? '';\n }\n\n private hash(data: string): string {\n return createHash('sha256').update(data).digest('hex');\n }\n\n private replayResponse(response: unknown, record: IIdempotencyRecord): Observable<unknown> {\n const httpAdapter = this.adapterHost.httpAdapter;\n if (!httpAdapter) {\n throw new Error('IdempotencyInterceptor: HttpAdapterHost is not initialized. Ensure the NestJS application has bootstrapped with an HTTP adapter before handling requests.');\n }\n\n httpAdapter.status(response, record.statusCode ?? 200);\n\n if (record.headers) {\n const headers = JSON.parse(record.headers);\n for (const [key, value] of Object.entries(headers)) {\n httpAdapter.setHeader(response, key, value as string);\n }\n }\n\n const body = record.response ? JSON.parse(record.response) : null;\n return of(body);\n }\n\n private extractHeaders(response: { getHeader?: (name: string) => string | number | string[] | undefined }, options: IIdempotentOptions): Record<string, string> | undefined {\n if (!options.cacheHeaders?.length) return undefined;\n\n const headers: Record<string, string> = {};\n for (const name of options.cacheHeaders) {\n const value = response.getHeader?.(name);\n if (value) headers[name] = String(value);\n }\n\n return Object.keys(headers).length > 0 ? headers : undefined;\n }\n}\n","import { Injectable, Inject, Optional } from '@nestjs/common';\n\nimport { IDEMPOTENCY_PLUGIN_OPTIONS, IDEMPOTENCY_STORE } from '../../../shared/constants';\n\n/** Polling interval when waiting for an in-flight idempotent request to complete. */\nconst POLL_INTERVAL_MS = 100;\nimport { IdempotencyRecordNotFoundError, IdempotencyTimeoutError } from '../../../shared/errors';\nimport { IIdempotencyPluginOptions, IIdempotencyRecord, IIdempotencyCheckResult, IIdempotencyResponse, IIdempotencyOptions } from '../../../shared/types';\nimport { IIdempotencyService } from '../ports/idempotency-service.port';\nimport { IIdempotencyStore } from '../ports/idempotency-store.port';\n\n// Optional metrics integration\nconst METRICS_SERVICE = Symbol.for('METRICS_SERVICE');\n\ninterface IMetricsService {\n incrementCounter(name: string, labels?: Record<string, string>, value?: number): void;\n observeHistogram(name: string, value: number, labels?: Record<string, string>): void;\n}\n\n/**\n * Idempotency service implementation\n */\n@Injectable()\nexport class IdempotencyService implements IIdempotencyService {\n constructor(\n @Inject(IDEMPOTENCY_PLUGIN_OPTIONS)\n private readonly config: IIdempotencyPluginOptions,\n @Inject(IDEMPOTENCY_STORE)\n private readonly store: IIdempotencyStore,\n @Optional() @Inject(METRICS_SERVICE) private readonly metrics?: IMetricsService,\n ) {}\n\n async checkAndLock(key: string, fingerprint: string, options: IIdempotencyOptions = {}): Promise<IIdempotencyCheckResult> {\n const startTime = Date.now();\n const fullKey = this.buildKey(key);\n const lockTimeout = options.lockTimeout ?? this.config.lockTimeout ?? 30000;\n\n const result = await this.store.checkAndLock(fullKey, fingerprint, lockTimeout);\n\n if (result.status === 'new') {\n this.metrics?.incrementCounter('redisx_idempotency_requests_total', { status: 'new' });\n this.recordDuration(startTime);\n return { isNew: true };\n }\n\n if (result.status === 'fingerprint_mismatch') {\n this.metrics?.incrementCounter('redisx_idempotency_requests_total', { status: 'mismatch' });\n this.recordDuration(startTime);\n return { isNew: false, fingerprintMismatch: true };\n }\n\n if (result.status === 'processing') {\n // Wait for completion\n const record = await this.waitForCompletion(fullKey);\n this.metrics?.incrementCounter('redisx_idempotency_requests_total', { status: 'replay' });\n this.recordDuration(startTime);\n return { isNew: false, record };\n }\n\n // completed or failed - replay from cache\n this.metrics?.incrementCounter('redisx_idempotency_requests_total', { status: 'replay' });\n this.recordDuration(startTime);\n return { isNew: false, record: result.record };\n }\n\n private recordDuration(startTime: number): void {\n const duration = (Date.now() - startTime) / 1000;\n this.metrics?.observeHistogram('redisx_idempotency_duration_seconds', duration);\n }\n\n async complete(key: string, response: IIdempotencyResponse, options: IIdempotencyOptions = {}): Promise<void> {\n const fullKey = this.buildKey(key);\n const ttl = options.ttl ?? this.config.defaultTtl ?? 86400;\n\n await this.store.complete(\n fullKey,\n {\n statusCode: response.statusCode,\n response: JSON.stringify(response.body),\n headers: response.headers ? JSON.stringify(response.headers) : undefined,\n completedAt: Date.now(),\n },\n ttl,\n );\n }\n\n async fail(key: string, error: string): Promise<void> {\n const fullKey = this.buildKey(key);\n // Failed records are short-lived: keep them only for the lock window so a\n // retry returns the failure briefly, then a fresh attempt is allowed.\n const ttlSeconds = Math.ceil((this.config.lockTimeout ?? 30000) / 1000);\n await this.store.fail(fullKey, error, ttlSeconds);\n }\n\n async get(key: string): Promise<IIdempotencyRecord | null> {\n const fullKey = this.buildKey(key);\n return this.store.get(fullKey);\n }\n\n async delete(key: string): Promise<boolean> {\n const fullKey = this.buildKey(key);\n return this.store.delete(fullKey);\n }\n\n private async waitForCompletion(key: string): Promise<IIdempotencyRecord> {\n const waitTimeout = this.config.waitTimeout ?? 60000;\n const startTime = Date.now();\n while (Date.now() - startTime < waitTimeout) {\n const record = await this.store.get(key);\n\n if (!record) {\n throw new IdempotencyRecordNotFoundError(key);\n }\n\n if (record.status === 'completed' || record.status === 'failed') {\n return record;\n }\n\n await this.sleep(POLL_INTERVAL_MS);\n }\n\n throw new IdempotencyTimeoutError(key);\n }\n\n private buildKey(key: string): string {\n const prefix = this.config.keyPrefix ?? 'idempotency:';\n return `${prefix}${key}`;\n }\n\n private sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n }\n}\n","/**\n * Inline Lua scripts for idempotency operations.\n *\n * Scripts are stored as inline strings to avoid issues with file reading\n * after build (dist directory doesn't contain .lua files).\n */\n\n/**\n * Check and Lock Lua script for idempotency\n *\n * This script atomically checks if an idempotency key exists and locks it if new.\n *\n * KEYS[1] = idempotency key\n * ARGV[1] = fingerprint\n * ARGV[2] = lock timeout (ms)\n * ARGV[3] = current timestamp (ms)\n *\n * Returns:\n * - ['new'] - new request, lock acquired\n * - ['fingerprint_mismatch'] - same key, different fingerprint\n * - ['processing'] - another request is processing\n * - [status, statusCode, response, headers, error] - completed/failed record\n */\nexport const CHECK_AND_LOCK_SCRIPT = `\nlocal key = KEYS[1]\nlocal fingerprint = ARGV[1]\nlocal lock_timeout = tonumber(ARGV[2])\nlocal now = tonumber(ARGV[3])\n\n-- Check if key exists\nlocal existing = redis.call('HGETALL', key)\n\nif #existing == 0 then\n -- New request - create lock\n redis.call('HMSET', key,\n 'fingerprint', fingerprint,\n 'status', 'processing',\n 'startedAt', now\n )\n redis.call('PEXPIRE', key, lock_timeout)\n return {'new'}\nend\n\n-- Convert to table\nlocal record = {}\nfor i = 1, #existing, 2 do\n record[existing[i]] = existing[i + 1]\nend\n\n-- Check fingerprint\nif record.fingerprint ~= fingerprint then\n return {'fingerprint_mismatch'}\nend\n\n-- Check status\nif record.status == 'processing' then\n -- Check if lock expired (stale)\n local started = tonumber(record.startedAt)\n if now - started > lock_timeout then\n -- Stale lock - take over\n redis.call('HMSET', key,\n 'status', 'processing',\n 'startedAt', now\n )\n redis.call('PEXPIRE', key, lock_timeout)\n return {'new'}\n end\n return {'processing'}\nend\n\n-- Completed or failed - return record\nreturn {\n record.status,\n record.statusCode or '',\n record.response or '',\n record.headers or '',\n record.error or ''\n}\n`.trim();\n","import { Injectable, Inject, OnModuleInit } from '@nestjs/common';\nimport { IRedisDriver } from '@nestjs-redisx/core';\n\nimport { IDEMPOTENCY_REDIS_DRIVER } from '../../../shared/constants';\nimport { IIdempotencyRecord } from '../../../shared/types';\nimport { IIdempotencyStore, ICheckAndLockResult, ICompleteData } from '../../application/ports/idempotency-store.port';\nimport { CHECK_AND_LOCK_SCRIPT } from '../scripts/lua-scripts';\n\n/**\n * Redis-based idempotency store implementation\n */\n@Injectable()\nexport class RedisIdempotencyStoreAdapter implements IIdempotencyStore, OnModuleInit {\n private checkAndLockSha: string | null = null;\n\n constructor(@Inject(IDEMPOTENCY_REDIS_DRIVER) private readonly driver: IRedisDriver) {}\n\n /**\n * Pre-load Lua script on module initialization\n */\n async onModuleInit(): Promise<void> {\n this.checkAndLockSha = await this.driver.scriptLoad(CHECK_AND_LOCK_SCRIPT);\n }\n\n async checkAndLock(key: string, fingerprint: string, lockTimeoutMs: number): Promise<ICheckAndLockResult> {\n const now = Date.now();\n const rawResult = await this.driver.evalsha(this.checkAndLockSha!, [key], [fingerprint, lockTimeoutMs, now]);\n\n // Normalize result: node-redis may return Buffer/null elements\n const result = (rawResult as unknown[]).map((v) => (v === null || v === undefined ? '' : String(v)));\n\n const status = result[0];\n\n if (status === 'new') {\n return { status: 'new' };\n }\n\n if (status === 'fingerprint_mismatch') {\n return { status: 'fingerprint_mismatch' };\n }\n\n if (status === 'processing') {\n return { status: 'processing' };\n }\n\n // completed or failed\n return {\n status: status as 'completed' | 'failed',\n record: {\n key,\n fingerprint,\n status: status as 'completed' | 'failed',\n statusCode: result[1] ? parseInt(result[1], 10) : undefined,\n response: result[2] || undefined,\n headers: result[3] || undefined,\n error: result[4] || undefined,\n startedAt: 0, // Not returned from Lua\n },\n };\n }\n\n async complete(key: string, data: ICompleteData, ttlSeconds: number): Promise<void> {\n await this.driver.hmset(key, {\n status: 'completed',\n statusCode: String(data.statusCode),\n response: data.response,\n headers: data.headers || '',\n completedAt: String(data.completedAt),\n });\n await this.driver.expire(key, ttlSeconds);\n }\n\n async fail(key: string, error: string, ttlSeconds: number): Promise<void> {\n await this.driver.hmset(key, {\n status: 'failed',\n error,\n completedAt: String(Date.now()),\n });\n // Set an explicit expiry so the failed record does not rely on the leftover\n // lock TTL; once it expires a fresh attempt with the same key is allowed.\n await this.driver.expire(key, ttlSeconds);\n }\n\n async get(key: string): Promise<IIdempotencyRecord | null> {\n const data = await this.driver.hgetall(key);\n if (!data || Object.keys(data).length === 0) {\n return null;\n }\n\n return {\n key,\n fingerprint: data.fingerprint!,\n status: data.status! as 'processing' | 'completed' | 'failed',\n statusCode: data.statusCode ? parseInt(data.statusCode, 10) : undefined,\n response: data.response || undefined,\n headers: data.headers || undefined,\n startedAt: parseInt(data.startedAt!, 10),\n completedAt: data.completedAt ? parseInt(data.completedAt, 10) : undefined,\n error: data.error || undefined,\n };\n }\n\n async delete(key: string): Promise<boolean> {\n const result = await this.driver.del(key);\n return result > 0;\n }\n}\n","/**\n * Idempotency plugin for NestJS RedisX.\n * Provides request deduplication with response replay for idempotent operations.\n */\n\nimport { DynamicModule, ForwardReference, Provider, Type } from '@nestjs/common';\nimport { APP_FILTER, Reflector } from '@nestjs/core';\nimport { IRedisXPlugin, IPluginAsyncOptions, CLIENT_MANAGER, REDIS_CLIENTS_INITIALIZATION, RedisClientManager } from '@nestjs-redisx/core';\n\nimport { version } from '../package.json';\nimport { IdempotencyExceptionFilter } from './idempotency/api/filters/idempotency-exception.filter';\nimport { IdempotencyInterceptor } from './idempotency/api/interceptors/idempotency.interceptor';\nimport { IdempotencyService } from './idempotency/application/services/idempotency.service';\nimport { RedisIdempotencyStoreAdapter } from './idempotency/infrastructure/adapters/redis-idempotency-store.adapter';\nimport { IDEMPOTENCY_PLUGIN_OPTIONS, IDEMPOTENCY_REDIS_DRIVER, IDEMPOTENCY_SERVICE, IDEMPOTENCY_STORE } from './shared/constants';\nimport { IIdempotencyPluginOptions } from './shared/types';\n\nconst DEFAULT_IDEMPOTENCY_CONFIG: Required<Omit<IIdempotencyPluginOptions, 'isGlobal' | 'client' | 'fingerprintGenerator'>> = {\n defaultTtl: 86400,\n keyPrefix: 'idempotency:',\n headerName: 'Idempotency-Key',\n lockTimeout: 30000,\n waitTimeout: 60000,\n validateFingerprint: true,\n fingerprintFields: ['method', 'path', 'body'],\n errorPolicy: 'fail-closed',\n};\n\n/**\n * Idempotency plugin for NestJS RedisX.\n *\n * Provides request deduplication with response replay:\n * - Prevents duplicate processing of same request\n * - Replays successful responses\n * - Handles concurrent requests\n * - Validates request fingerprints\n *\n * @example\n * ```typescript\n * @Module({\n * imports: [\n * RedisModule.forRoot({\n * clients: { host: 'localhost', port: 6379 },\n * plugins: [\n * new IdempotencyPlugin({\n * defaultTtl: 86400,\n * headerName: 'Idempotency-Key',\n * validateFingerprint: true,\n * }),\n * ],\n * }),\n * ],\n * })\n * export class AppModule {}\n * ```\n */\nexport class IdempotencyPlugin implements IRedisXPlugin {\n readonly name = 'idempotency';\n readonly version: string = version;\n readonly description = 'Request deduplication with response replay for idempotent operations';\n\n private asyncOptions?: IPluginAsyncOptions<IIdempotencyPluginOptions>;\n\n constructor(private readonly options: IIdempotencyPluginOptions = {}) {}\n\n static registerAsync(asyncOptions: IPluginAsyncOptions<IIdempotencyPluginOptions>): IdempotencyPlugin {\n const plugin = new IdempotencyPlugin();\n plugin.asyncOptions = asyncOptions;\n return plugin;\n }\n\n private static mergeDefaults(options: IIdempotencyPluginOptions): IIdempotencyPluginOptions {\n return {\n client: options.client,\n defaultTtl: options.defaultTtl ?? DEFAULT_IDEMPOTENCY_CONFIG.defaultTtl,\n keyPrefix: options.keyPrefix ?? DEFAULT_IDEMPOTENCY_CONFIG.keyPrefix,\n headerName: options.headerName ?? DEFAULT_IDEMPOTENCY_CONFIG.headerName,\n lockTimeout: options.lockTimeout ?? DEFAULT_IDEMPOTENCY_CONFIG.lockTimeout,\n waitTimeout: options.waitTimeout ?? DEFAULT_IDEMPOTENCY_CONFIG.waitTimeout,\n validateFingerprint: options.validateFingerprint ?? DEFAULT_IDEMPOTENCY_CONFIG.validateFingerprint,\n fingerprintFields: options.fingerprintFields ?? DEFAULT_IDEMPOTENCY_CONFIG.fingerprintFields,\n errorPolicy: options.errorPolicy ?? DEFAULT_IDEMPOTENCY_CONFIG.errorPolicy,\n fingerprintGenerator: options.fingerprintGenerator,\n };\n }\n\n getImports(): Array<Type<unknown> | DynamicModule | ForwardReference> {\n return this.asyncOptions?.imports ?? [];\n }\n\n getProviders(): Provider[] {\n const optionsProvider: Provider = this.asyncOptions\n ? {\n provide: IDEMPOTENCY_PLUGIN_OPTIONS,\n useFactory: async (...args: unknown[]) => {\n const userOptions = await this.asyncOptions!.useFactory(...args);\n return IdempotencyPlugin.mergeDefaults(userOptions);\n },\n inject: this.asyncOptions.inject || [],\n }\n : {\n provide: IDEMPOTENCY_PLUGIN_OPTIONS,\n useValue: IdempotencyPlugin.mergeDefaults(this.options),\n };\n\n return [\n optionsProvider,\n // Plugin-specific Redis driver (resolves named client)\n {\n provide: IDEMPOTENCY_REDIS_DRIVER,\n useFactory: async (manager: RedisClientManager, _init: void, options: IIdempotencyPluginOptions) => {\n const clientName = options.client ?? 'default';\n try {\n return await manager.getClient(clientName);\n } catch {\n throw new Error(`IdempotencyPlugin: Redis client \"${clientName}\" not found. ` + `Available clients are configured in RedisModule.forRoot({ clients: { ... } }). ` + `Either add a \"${clientName}\" client or remove the \"client\" option to use the default connection.`);\n }\n },\n inject: [CLIENT_MANAGER, REDIS_CLIENTS_INITIALIZATION, IDEMPOTENCY_PLUGIN_OPTIONS],\n },\n { provide: IDEMPOTENCY_STORE, useClass: RedisIdempotencyStoreAdapter },\n { provide: IDEMPOTENCY_SERVICE, useClass: IdempotencyService },\n // Reflector is needed for @Idempotent decorator metadata\n Reflector,\n IdempotencyInterceptor,\n // Global exception filter to map idempotency errors to proper HTTP codes\n // (otherwise they surface as 500 since they are not HttpExceptions)\n { provide: APP_FILTER, useClass: IdempotencyExceptionFilter },\n ];\n }\n\n getExports(): Array<string | symbol | Provider> {\n return [IDEMPOTENCY_PLUGIN_OPTIONS, IDEMPOTENCY_SERVICE, IdempotencyInterceptor];\n }\n}\n"]}
1
+ {"version":3,"sources":["../package.json","../src/shared/errors/index.ts","../src/idempotency/api/filters/idempotency-exception.filter.ts","../src/shared/constants/index.ts","../src/idempotency/api/decorators/idempotent.decorator.ts","../src/idempotency/api/interceptors/idempotency.interceptor.ts","../src/idempotency/application/services/idempotency.service.ts","../src/idempotency/infrastructure/scripts/lua-scripts.ts","../src/idempotency/infrastructure/adapters/redis-idempotency-store.adapter.ts","../src/idempotency.plugin.ts"],"names":["Inject","HttpAdapterHost","Injectable","Reflector"],"mappings":"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","file":"index.mjs","sourcesContent":["{\n \"name\": \"@nestjs-redisx/idempotency\",\n \"version\": \"1.6.1\",\n \"description\": \"Idempotency plugin for NestJS RedisX with request deduplication and response replay\",\n \"author\": \"NestJS RedisX Team\",\n \"license\": \"MIT\",\n \"main\": \"dist/index.js\",\n \"types\": \"dist/index.d.ts\",\n \"exports\": {\n \".\": {\n \"types\": \"./dist/index.d.ts\",\n \"require\": \"./dist/index.js\",\n \"import\": \"./dist/index.js\",\n \"default\": \"./dist/index.js\"\n }\n },\n \"files\": [\n \"dist\",\n \"README.md\",\n \"LICENSE\"\n ],\n \"scripts\": {\n \"build\": \"tsup\",\n \"test\": \"SKIP_INTEGRATION=true vitest run\",\n \"test:watch\": \"SKIP_INTEGRATION=true vitest\",\n \"test:cov\": \"SKIP_INTEGRATION=true vitest run --coverage\",\n \"test:integration\": \"SKIP_INTEGRATION=false vitest run test/integration --pool=forks --poolOptions.forks.singleFork=true\",\n \"test:integration:watch\": \"SKIP_INTEGRATION=false vitest watch test/integration/idempotency.integration.spec.ts\",\n \"docker:up\": \"cd ../.. && docker-compose up -d redis\",\n \"docker:down\": \"cd ../.. && docker-compose down\",\n \"docker:logs\": \"cd ../.. && docker-compose logs -f redis\",\n \"test:docker\": \"npm run docker:up && sleep 3 && npm run test:integration; TEST_EXIT=$?; npm run docker:down; exit $TEST_EXIT\",\n \"test:all\": \"npm run test:cov && npm run test:integration\",\n \"lint\": \"eslint \\\"{src,test}/**/*.ts\\\"\",\n \"format\": \"prettier --write \\\"{src,test}/**/*.ts\\\"\"\n },\n \"keywords\": [\n \"nestjs\",\n \"redis\",\n \"idempotency\",\n \"idempotent\",\n \"deduplication\",\n \"request-replay\",\n \"http-idempotency\"\n ],\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"https://github.com/nestjs-redisx/nestjs-redisx.git\",\n \"directory\": \"packages/idempotency\"\n },\n \"homepage\": \"https://nestjs-redisx.dev/en/reference/idempotency/\",\n \"bugs\": {\n \"url\": \"https://github.com/nestjs-redisx/nestjs-redisx/issues\"\n },\n \"peerDependencies\": {\n \"@nestjs-redisx/core\": \"^1.0.0\",\n \"@nestjs/common\": \"^10.0.0 || ^11.0.0\",\n \"@nestjs/core\": \"^10.0.0 || ^11.0.0\",\n \"reflect-metadata\": \"^0.2.0\",\n \"rxjs\": \"^7.8.0\"\n },\n \"devDependencies\": {\n \"@nestjs/testing\": \"^9.0.0 || ^10.0.0 || ^11.0.0\",\n \"@types/node\": \"^20.0.0\",\n \"@typescript-eslint/eslint-plugin\": \"^6.0.0\",\n \"@typescript-eslint/parser\": \"^6.0.0\",\n \"@vitest/coverage-v8\": \"^1.6.0\",\n \"eslint\": \"^8.0.0\",\n \"prettier\": \"^3.0.0\",\n \"tsup\": \"^8.0.0\",\n \"typescript\": \"^5.3.0\",\n \"vitest\": \"^1.6.0\"\n },\n \"publishConfig\": {\n \"access\": \"public\"\n },\n \"engines\": {\n \"node\": \">=18.0.0\"\n }\n}\n","import { RedisXError, ErrorCode } from '@nestjs-redisx/core';\n\n/**\n * Base class for all idempotency-related errors\n */\nexport class IdempotencyError extends RedisXError {\n constructor(\n message: string,\n code: ErrorCode,\n public readonly idempotencyKey: string,\n cause?: Error,\n ) {\n super(message, code, cause, { idempotencyKey });\n }\n}\n\n/**\n * Thrown when Idempotency-Key header is required but not provided\n */\nexport class IdempotencyKeyRequiredError extends IdempotencyError {\n constructor() {\n super('Idempotency-Key header is required', ErrorCode.IDEMPOTENCY_KEY_INVALID, '');\n }\n}\n\n/**\n * Thrown when request fingerprint doesn't match the stored one\n */\nexport class IdempotencyFingerprintMismatchError extends IdempotencyError {\n constructor(key: string) {\n super(`Request body does not match previous request with idempotency key \"${key}\"`, ErrorCode.IDEMPOTENCY_KEY_INVALID, key);\n }\n}\n\n/**\n * Thrown when timeout waiting for concurrent request to complete\n */\nexport class IdempotencyTimeoutError extends IdempotencyError {\n constructor(key: string) {\n super(`Timeout waiting for concurrent request with idempotency key \"${key}\"`, ErrorCode.OP_TIMEOUT, key);\n }\n}\n\n/**\n * Thrown when previous request with same key failed\n */\nexport class IdempotencyFailedError extends IdempotencyError {\n constructor(key: string, error?: string) {\n super(`Previous request with idempotency key \"${key}\" failed${error ? `: ${error}` : ''}`, ErrorCode.IDEMPOTENCY_PREVIOUS_FAILED, key);\n }\n}\n\n/**\n * Thrown when idempotency record not found in Redis\n */\nexport class IdempotencyRecordNotFoundError extends IdempotencyError {\n constructor(key: string) {\n super(`Idempotency record not found for key \"${key}\"`, ErrorCode.OP_KEY_NOT_FOUND, key);\n }\n}\n\n/**\n * Thrown when the plugin is configured with invalid values (e.g. non-positive\n * timeouts, which would silently break record TTL semantics).\n */\nexport class IdempotencyConfigError extends IdempotencyError {\n constructor(message: string) {\n super(message, ErrorCode.CFG_INVALID, '');\n }\n}\n","import { Catch, ExceptionFilter, ArgumentsHost, HttpStatus, Inject } from '@nestjs/common';\nimport { HttpAdapterHost } from '@nestjs/core';\n\nimport { IdempotencyError, IdempotencyKeyRequiredError, IdempotencyFingerprintMismatchError, IdempotencyTimeoutError, IdempotencyFailedError, IdempotencyRecordNotFoundError } from '../../../shared/errors';\n\n/**\n * Exception filter for idempotency errors.\n *\n * Without this filter the idempotency errors (which extend `RedisXError`, not\n * `HttpException`) would surface as `500 Internal Server Error`. That is wrong\n * for situations that are part of normal idempotent-request handling — a reused\n * key with a different body, a still-in-progress concurrent request, or a key\n * whose previous attempt failed. This filter maps each error to an appropriate\n * 4xx status while leaving genuinely unexpected errors as 500.\n */\n@Catch(IdempotencyError)\nexport class IdempotencyExceptionFilter implements ExceptionFilter {\n constructor(@Inject(HttpAdapterHost) private readonly adapterHost: HttpAdapterHost) {}\n\n catch(exception: IdempotencyError, host: ArgumentsHost): void {\n const httpAdapter = this.adapterHost.httpAdapter;\n if (!httpAdapter) {\n throw new Error('IdempotencyExceptionFilter: HttpAdapterHost is not initialized. Ensure the NestJS application has bootstrapped with an HTTP adapter before handling requests.');\n }\n\n const response = host.switchToHttp().getResponse();\n const status = this.resolveStatus(exception);\n\n const body = {\n statusCode: status,\n message: exception.message,\n error: this.reasonPhrase(status),\n idempotencyKey: exception.idempotencyKey || undefined,\n };\n\n httpAdapter.reply(response, body, status);\n }\n\n /**\n * Maps an idempotency error to its HTTP status code.\n *\n * - Missing/invalid key -> 400 Bad Request\n * - Reused key with a different request -> 422 Unprocessable Entity\n * - Concurrent request still in progress (timeout) -> 409 Conflict\n * - Previous request with the same key failed -> 409 Conflict\n * - In-flight record vanished mid-wait -> 409 Conflict (retryable)\n * - Anything else (unexpected) -> 500 Internal Server Error\n */\n private resolveStatus(exception: IdempotencyError): HttpStatus {\n if (exception instanceof IdempotencyKeyRequiredError) {\n return HttpStatus.BAD_REQUEST;\n }\n if (exception instanceof IdempotencyFingerprintMismatchError) {\n return HttpStatus.UNPROCESSABLE_ENTITY;\n }\n if (exception instanceof IdempotencyTimeoutError) {\n return HttpStatus.CONFLICT;\n }\n if (exception instanceof IdempotencyFailedError) {\n return HttpStatus.CONFLICT;\n }\n if (exception instanceof IdempotencyRecordNotFoundError) {\n // Should be unreachable since waiters take over vanished records, but if\n // it ever surfaces the client should get a retryable 409, not a 500.\n return HttpStatus.CONFLICT;\n }\n return HttpStatus.INTERNAL_SERVER_ERROR;\n }\n\n private reasonPhrase(status: HttpStatus): string {\n switch (status) {\n case HttpStatus.BAD_REQUEST:\n return 'Bad Request';\n case HttpStatus.CONFLICT:\n return 'Conflict';\n case HttpStatus.UNPROCESSABLE_ENTITY:\n return 'Unprocessable Entity';\n default:\n return 'Internal Server Error';\n }\n }\n}\n","/**\n * Injection tokens for idempotency plugin.\n */\nexport const IDEMPOTENCY_PLUGIN_OPTIONS = Symbol.for('IDEMPOTENCY_PLUGIN_OPTIONS');\nexport const IDEMPOTENCY_SERVICE = Symbol.for('IDEMPOTENCY_SERVICE');\nexport const IDEMPOTENCY_STORE = Symbol.for('IDEMPOTENCY_STORE');\n\n/**\n * Plugin-specific Redis driver token.\n * Resolves to the named client specified in plugin options.\n */\nexport const IDEMPOTENCY_REDIS_DRIVER = Symbol.for('IDEMPOTENCY_REDIS_DRIVER');\n","import { applyDecorators, SetMetadata, UseInterceptors, ExecutionContext } from '@nestjs/common';\n\nimport { IdempotencyInterceptor } from '../interceptors/idempotency.interceptor';\n\nexport const IDEMPOTENT_OPTIONS = Symbol.for('IDEMPOTENT_OPTIONS');\n\nexport interface IIdempotentOptions {\n ttl?: number;\n keyExtractor?: (context: ExecutionContext) => string | Promise<string>;\n fingerprintFields?: ('method' | 'path' | 'body' | 'query')[];\n validateFingerprint?: boolean;\n cacheHeaders?: string[];\n skip?: (context: ExecutionContext) => boolean | Promise<boolean>;\n}\n\nexport function Idempotent(options: IIdempotentOptions = {}): MethodDecorator {\n return applyDecorators(SetMetadata(IDEMPOTENT_OPTIONS, options), UseInterceptors(IdempotencyInterceptor));\n}\n","import { createHash } from 'crypto';\n\nimport { Injectable, NestInterceptor, ExecutionContext, CallHandler, Inject, Logger } from '@nestjs/common';\nimport { HttpAdapterHost, Reflector } from '@nestjs/core';\nimport { Observable, of } from 'rxjs';\nimport { tap } from 'rxjs/operators';\n\nimport { IDEMPOTENCY_SERVICE, IDEMPOTENCY_PLUGIN_OPTIONS } from '../../../shared/constants';\nimport { IdempotencyFingerprintMismatchError, IdempotencyFailedError } from '../../../shared/errors';\nimport { IIdempotencyPluginOptions, IIdempotencyRecord } from '../../../shared/types';\nimport { IIdempotencyService } from '../../application/ports/idempotency-service.port';\nimport { IDEMPOTENT_OPTIONS, IIdempotentOptions } from '../decorators/idempotent.decorator';\n\n/**\n * Per-request marker recording that idempotency has already been handled for\n * this request. The interceptor can legitimately be bound more than once\n * (global APP_INTERCEPTOR + @Idempotent on the method, controller-level\n * @UseInterceptors + method decorator, controller inheritance). Without the\n * marker the inner pass would see its own 'processing' record and wait for\n * itself until the lock TTL expires — a self-deadlock. Mirrors\n * RATE_LIMIT_CONSUMED in the rate-limit guard.\n */\nconst IDEMPOTENCY_HANDLED = Symbol('redisx.idempotencyHandled');\n\n@Injectable()\nexport class IdempotencyInterceptor implements NestInterceptor {\n private readonly logger = new Logger(IdempotencyInterceptor.name);\n\n constructor(\n @Inject(IDEMPOTENCY_SERVICE) private readonly idempotencyService: IIdempotencyService,\n @Inject(IDEMPOTENCY_PLUGIN_OPTIONS) private readonly config: IIdempotencyPluginOptions,\n @Inject(Reflector) private readonly reflector: Reflector,\n @Inject(HttpAdapterHost) private readonly adapterHost: HttpAdapterHost,\n ) {}\n\n async intercept(context: ExecutionContext, next: CallHandler): Promise<Observable<unknown>> {\n // Idempotency must engage at most once per request even when the\n // interceptor is bound multiple times; a second pass is a plain\n // passthrough (see IDEMPOTENCY_HANDLED).\n const request = context.switchToHttp().getRequest<Record<symbol, unknown>>();\n if (request[IDEMPOTENCY_HANDLED]) {\n return next.handle();\n }\n\n const options = this.getOptions(context);\n const response = context.switchToHttp().getResponse();\n\n const key = await this.extractKey(context, options);\n\n if (!key) {\n return next.handle();\n }\n\n if (options.skip && (await options.skip(context))) {\n return next.handle();\n }\n\n request[IDEMPOTENCY_HANDLED] = true;\n\n const fingerprint = await this.generateFingerprint(context, options);\n\n let checkResult: Awaited<ReturnType<typeof this.idempotencyService.checkAndLock>>;\n try {\n checkResult = await this.idempotencyService.checkAndLock(key, fingerprint, {\n ttl: options.ttl,\n validateFingerprint: options.validateFingerprint,\n });\n } catch (error) {\n // The idempotency store is unavailable (e.g. Redis is down). Honor the\n // configured errorPolicy: 'fail-open' proceeds without idempotency\n // protection, 'fail-closed' (default) rejects by propagating the error.\n if ((this.config.errorPolicy ?? 'fail-closed') === 'fail-open') {\n this.logger.warn(`Idempotency store unavailable for key \"${key}\"; proceeding without idempotency (fail-open): ${(error as Error).message}`);\n return next.handle();\n }\n throw error;\n }\n\n if (!checkResult.isNew) {\n if (checkResult.fingerprintMismatch) {\n throw new IdempotencyFingerprintMismatchError(key);\n }\n\n const record = checkResult.record!;\n\n if (record.status === 'failed') {\n throw new IdempotencyFailedError(key, record.error);\n }\n\n return this.replayResponse(response, record);\n }\n\n return next.handle().pipe(\n tap({\n next: (data) => {\n Promise.resolve(\n this.idempotencyService.complete(\n key,\n {\n statusCode: response.statusCode,\n body: data,\n headers: this.extractHeaders(response, options),\n },\n { ttl: options.ttl, fingerprint },\n ),\n ).catch((err: Error) => {\n // The client already got its response; never let a store failure\n // become an unhandled rejection. The processing record will\n // expire after lockTimeout and a waiter/retry takes over.\n this.logger.error(`Failed to persist idempotency completion for key \"${key}\": ${err.message}`);\n });\n },\n error: (error) => {\n Promise.resolve(this.idempotencyService.fail(key, error.message, { fingerprint })).catch((err: Error) => {\n this.logger.error(`Failed to persist idempotency failure for key \"${key}\": ${err.message}`);\n });\n },\n }),\n );\n }\n\n private getOptions(context: ExecutionContext): IIdempotentOptions {\n return this.reflector.get<IIdempotentOptions>(IDEMPOTENT_OPTIONS, context.getHandler()) ?? {};\n }\n\n private async extractKey(context: ExecutionContext, options: IIdempotentOptions): Promise<string | null> {\n if (options.keyExtractor) {\n return options.keyExtractor(context);\n }\n\n const request = context.switchToHttp().getRequest();\n const headerName = this.config.headerName ?? 'Idempotency-Key';\n const value = request.headers[headerName.toLowerCase()];\n\n // A duplicated header arrives as an array; use the first occurrence.\n if (Array.isArray(value)) {\n return value[0] ?? null;\n }\n return value ?? null;\n }\n\n private async generateFingerprint(context: ExecutionContext, options: IIdempotentOptions): Promise<string> {\n if (this.config.fingerprintGenerator) {\n return this.config.fingerprintGenerator(context);\n }\n\n const request = context.switchToHttp().getRequest();\n const fields = options.fingerprintFields ?? this.config.fingerprintFields ?? ['method', 'path', 'body'];\n\n const parts: string[] = [];\n\n if (fields.includes('method')) parts.push(request.method);\n if (fields.includes('path')) parts.push(this.getRequestPath(request));\n if (fields.includes('body')) parts.push(JSON.stringify(request.body ?? {}));\n if (fields.includes('query')) parts.push(JSON.stringify(request.query ?? {}));\n\n const data = parts.join('|');\n return this.hash(data);\n }\n\n private getRequestPath(request: unknown): string {\n const httpAdapter = this.adapterHost.httpAdapter;\n const req = request as { path?: string; url?: string };\n return httpAdapter?.getRequestUrl?.(request) ?? req.url ?? req.path ?? '';\n }\n\n private hash(data: string): string {\n return createHash('sha256').update(data).digest('hex');\n }\n\n private replayResponse(response: unknown, record: IIdempotencyRecord): Observable<unknown> {\n const httpAdapter = this.adapterHost.httpAdapter;\n if (!httpAdapter) {\n throw new Error('IdempotencyInterceptor: HttpAdapterHost is not initialized. Ensure the NestJS application has bootstrapped with an HTTP adapter before handling requests.');\n }\n\n httpAdapter.status(response, record.statusCode ?? 200);\n\n if (record.headers) {\n const headers = JSON.parse(record.headers);\n for (const [key, value] of Object.entries(headers)) {\n httpAdapter.setHeader(response, key, value as string);\n }\n }\n\n const body = record.response ? JSON.parse(record.response) : null;\n return of(body);\n }\n\n private extractHeaders(response: { getHeader?: (name: string) => string | number | string[] | undefined }, options: IIdempotentOptions): Record<string, string> | undefined {\n if (!options.cacheHeaders?.length) return undefined;\n\n const headers: Record<string, string> = {};\n for (const name of options.cacheHeaders) {\n const value = response.getHeader?.(name);\n if (value) headers[name] = String(value);\n }\n\n return Object.keys(headers).length > 0 ? headers : undefined;\n }\n}\n","import { Injectable, Inject, Optional } from '@nestjs/common';\n\nimport { IDEMPOTENCY_PLUGIN_OPTIONS, IDEMPOTENCY_STORE } from '../../../shared/constants';\n\n/** Polling interval when waiting for an in-flight idempotent request to complete. */\nconst POLL_INTERVAL_MS = 100;\nimport { IdempotencyFingerprintMismatchError, IdempotencyTimeoutError } from '../../../shared/errors';\nimport { IIdempotencyPluginOptions, IIdempotencyRecord, IIdempotencyCheckResult, IIdempotencyResponse, IIdempotencyOptions } from '../../../shared/types';\nimport { IIdempotencyService } from '../ports/idempotency-service.port';\nimport { IIdempotencyStore } from '../ports/idempotency-store.port';\n\n// Optional metrics integration\nconst METRICS_SERVICE = Symbol.for('METRICS_SERVICE');\n\ninterface IMetricsService {\n incrementCounter(name: string, labels?: Record<string, string>, value?: number): void;\n observeHistogram(name: string, value: number, labels?: Record<string, string>): void;\n}\n\n/**\n * Idempotency service implementation\n */\n@Injectable()\nexport class IdempotencyService implements IIdempotencyService {\n constructor(\n @Inject(IDEMPOTENCY_PLUGIN_OPTIONS)\n private readonly config: IIdempotencyPluginOptions,\n @Inject(IDEMPOTENCY_STORE)\n private readonly store: IIdempotencyStore,\n @Optional() @Inject(METRICS_SERVICE) private readonly metrics?: IMetricsService,\n ) {}\n\n async checkAndLock(key: string, fingerprint: string, options: IIdempotencyOptions = {}): Promise<IIdempotencyCheckResult> {\n const startTime = Date.now();\n const fullKey = this.buildKey(key);\n const lockTimeout = options.lockTimeout ?? this.config.lockTimeout ?? 30000;\n const validateFingerprint = options.validateFingerprint ?? this.config.validateFingerprint ?? true;\n\n const result = await this.store.checkAndLock(fullKey, fingerprint, lockTimeout, validateFingerprint);\n\n if (result.status === 'new') {\n this.metrics?.incrementCounter('redisx_idempotency_requests_total', { status: 'new' });\n this.recordDuration(startTime);\n return { isNew: true };\n }\n\n if (result.status === 'fingerprint_mismatch') {\n this.metrics?.incrementCounter('redisx_idempotency_requests_total', { status: 'mismatch' });\n this.recordDuration(startTime);\n return { isNew: false, fingerprintMismatch: true };\n }\n\n if (result.status === 'processing') {\n // Wait for the in-flight attempt; if its record vanishes (the process\n // died between checkAndLock and complete, so the lock TTL expired),\n // atomically take over instead of failing the caller.\n const outcome = await this.waitForCompletion(fullKey, fingerprint, lockTimeout, validateFingerprint);\n\n if (outcome === 'takeover') {\n this.metrics?.incrementCounter('redisx_idempotency_requests_total', { status: 'takeover' });\n this.recordDuration(startTime);\n return { isNew: true };\n }\n\n this.metrics?.incrementCounter('redisx_idempotency_requests_total', { status: 'replay' });\n this.recordDuration(startTime);\n return { isNew: false, record: outcome };\n }\n\n // completed or failed - replay from cache\n this.metrics?.incrementCounter('redisx_idempotency_requests_total', { status: 'replay' });\n this.recordDuration(startTime);\n return { isNew: false, record: result.record };\n }\n\n private recordDuration(startTime: number): void {\n const duration = (Date.now() - startTime) / 1000;\n this.metrics?.observeHistogram('redisx_idempotency_duration_seconds', duration);\n }\n\n async complete(key: string, response: IIdempotencyResponse, options: IIdempotencyOptions = {}): Promise<void> {\n const fullKey = this.buildKey(key);\n const ttl = options.ttl ?? this.config.defaultTtl ?? 86400;\n\n await this.store.complete(\n fullKey,\n {\n fingerprint: options.fingerprint,\n statusCode: response.statusCode,\n response: JSON.stringify(response.body),\n headers: response.headers ? JSON.stringify(response.headers) : undefined,\n completedAt: Date.now(),\n },\n ttl,\n );\n }\n\n async fail(key: string, error: string, options: IIdempotencyOptions = {}): Promise<void> {\n const fullKey = this.buildKey(key);\n // Failed records are short-lived: keep them only for the lock window so a\n // retry returns the failure briefly, then a fresh attempt is allowed.\n const ttlSeconds = Math.ceil((this.config.lockTimeout ?? 30000) / 1000);\n await this.store.fail(fullKey, error, ttlSeconds, options.fingerprint);\n }\n\n async get(key: string): Promise<IIdempotencyRecord | null> {\n const fullKey = this.buildKey(key);\n return this.store.get(fullKey);\n }\n\n async delete(key: string): Promise<boolean> {\n const fullKey = this.buildKey(key);\n return this.store.delete(fullKey);\n }\n\n /**\n * Polls until the in-flight attempt completes or fails, bounded by\n * waitTimeout.\n *\n * If the processing record VANISHES (the first attempt died between\n * checkAndLock and complete, so its lock TTL expired), the waiter attempts\n * an atomic takeover via checkAndLock: the winner returns 'takeover' (the\n * caller executes the handler itself), losers keep waiting on the new\n * owner's record. This turns a dead first attempt into a self-healing\n * retry instead of surfacing an unexplained error to every waiter.\n *\n * @returns the completed/failed record to replay, or 'takeover' when this\n * caller now owns the key and must execute the request\n */\n private async waitForCompletion(key: string, fingerprint: string, lockTimeoutMs: number, validateFingerprint: boolean): Promise<IIdempotencyRecord | 'takeover'> {\n const waitTimeout = this.config.waitTimeout ?? 60000;\n const startTime = Date.now();\n while (Date.now() - startTime < waitTimeout) {\n const record = await this.store.get(key);\n\n if (!record) {\n // First attempt died — race for the lock (atomic Lua SET-if-absent).\n const retry = await this.store.checkAndLock(key, fingerprint, lockTimeoutMs, validateFingerprint);\n\n if (retry.status === 'new') {\n return 'takeover';\n }\n if (retry.status === 'completed' || retry.status === 'failed') {\n return retry.record!;\n }\n if (retry.status === 'fingerprint_mismatch') {\n // A different request re-claimed the key while we waited.\n throw new IdempotencyFingerprintMismatchError(key);\n }\n // 'processing' — another waiter won the takeover; keep waiting.\n } else if (record.status === 'completed' || record.status === 'failed') {\n return record;\n }\n\n await this.sleep(POLL_INTERVAL_MS);\n }\n\n throw new IdempotencyTimeoutError(key);\n }\n\n private buildKey(key: string): string {\n const prefix = this.config.keyPrefix ?? 'idempotency:';\n return `${prefix}${key}`;\n }\n\n private sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n }\n}\n","/**\n * Inline Lua scripts for idempotency operations.\n *\n * Scripts are stored as inline strings to avoid issues with file reading\n * after build (dist directory doesn't contain .lua files).\n */\n\n/**\n * Check and Lock Lua script for idempotency\n *\n * This script atomically checks if an idempotency key exists and locks it if new.\n *\n * KEYS[1] = idempotency key\n * ARGV[1] = fingerprint\n * ARGV[2] = lock timeout (ms)\n * ARGV[3] = current timestamp (ms)\n * ARGV[4] = validate fingerprint flag ('1' = compare fingerprints, '0' = skip)\n *\n * Returns:\n * - ['new'] - new request, lock acquired\n * - ['fingerprint_mismatch'] - same key, different fingerprint\n * - ['processing'] - another request is processing\n * - [status, statusCode, response, headers, error] - completed/failed record\n */\nexport const CHECK_AND_LOCK_SCRIPT = `\nlocal key = KEYS[1]\nlocal fingerprint = ARGV[1]\nlocal lock_timeout = tonumber(ARGV[2])\nlocal now = tonumber(ARGV[3])\nlocal validate = ARGV[4] ~= '0'\n\n-- Check if key exists\nlocal existing = redis.call('HGETALL', key)\n\nif #existing == 0 then\n -- New request - create lock\n redis.call('HMSET', key,\n 'fingerprint', fingerprint,\n 'status', 'processing',\n 'startedAt', now\n )\n redis.call('PEXPIRE', key, lock_timeout)\n return {'new'}\nend\n\n-- Convert to table\nlocal record = {}\nfor i = 1, #existing, 2 do\n record[existing[i]] = existing[i + 1]\nend\n\n-- Check fingerprint (skipped when validateFingerprint is disabled)\nif validate and record.fingerprint ~= fingerprint then\n return {'fingerprint_mismatch'}\nend\n\n-- Check status\nif record.status == 'processing' then\n -- Check if lock expired (stale)\n local started = tonumber(record.startedAt)\n if now - started > lock_timeout then\n -- Stale lock - take over\n redis.call('HMSET', key,\n 'status', 'processing',\n 'startedAt', now\n )\n redis.call('PEXPIRE', key, lock_timeout)\n return {'new'}\n end\n return {'processing'}\nend\n\n-- Completed or failed - return record\nreturn {\n record.status,\n record.statusCode or '',\n record.response or '',\n record.headers or '',\n record.error or ''\n}\n`.trim();\n","import { Injectable, Inject, OnModuleInit } from '@nestjs/common';\nimport { IRedisDriver } from '@nestjs-redisx/core';\n\nimport { IDEMPOTENCY_REDIS_DRIVER } from '../../../shared/constants';\nimport { IIdempotencyRecord } from '../../../shared/types';\nimport { IIdempotencyStore, ICheckAndLockResult, ICompleteData } from '../../application/ports/idempotency-store.port';\nimport { CHECK_AND_LOCK_SCRIPT } from '../scripts/lua-scripts';\n\n/**\n * Redis-based idempotency store implementation\n */\n@Injectable()\nexport class RedisIdempotencyStoreAdapter implements IIdempotencyStore, OnModuleInit {\n private checkAndLockSha: string | null = null;\n\n constructor(@Inject(IDEMPOTENCY_REDIS_DRIVER) private readonly driver: IRedisDriver) {}\n\n /**\n * Pre-load Lua script on module initialization\n */\n async onModuleInit(): Promise<void> {\n this.checkAndLockSha = await this.driver.scriptLoad(CHECK_AND_LOCK_SCRIPT);\n }\n\n async checkAndLock(key: string, fingerprint: string, lockTimeoutMs: number, validateFingerprint = true): Promise<ICheckAndLockResult> {\n const now = Date.now();\n const rawResult = await this.driver.evalsha(this.checkAndLockSha!, [key], [fingerprint, lockTimeoutMs, now, validateFingerprint ? '1' : '0']);\n\n // Normalize result: node-redis may return Buffer/null elements\n const result = (rawResult as unknown[]).map((v) => (v === null || v === undefined ? '' : String(v)));\n\n const status = result[0];\n\n if (status === 'new') {\n return { status: 'new' };\n }\n\n if (status === 'fingerprint_mismatch') {\n return { status: 'fingerprint_mismatch' };\n }\n\n if (status === 'processing') {\n return { status: 'processing' };\n }\n\n // completed or failed\n return {\n status: status as 'completed' | 'failed',\n record: {\n key,\n fingerprint,\n status: status as 'completed' | 'failed',\n statusCode: result[1] ? parseInt(result[1], 10) : undefined,\n response: result[2] || undefined,\n headers: result[3] || undefined,\n error: result[4] || undefined,\n startedAt: 0, // Not returned from Lua\n },\n };\n }\n\n async complete(key: string, data: ICompleteData, ttlSeconds: number): Promise<void> {\n const fields: Record<string, string> = {\n status: 'completed',\n statusCode: String(data.statusCode),\n response: data.response,\n headers: data.headers || '',\n completedAt: String(data.completedAt),\n };\n // Persist the fingerprint too: if the record expired mid-handler (handler\n // ran longer than lockTimeout), HMSET re-creates the key — without the\n // fingerprint every later replay would be misread as a mismatch (422).\n if (data.fingerprint) {\n fields.fingerprint = data.fingerprint;\n }\n await this.driver.hmset(key, fields);\n await this.driver.expire(key, ttlSeconds);\n }\n\n async fail(key: string, error: string, ttlSeconds: number, fingerprint?: string): Promise<void> {\n const fields: Record<string, string> = {\n status: 'failed',\n error,\n completedAt: String(Date.now()),\n };\n if (fingerprint) {\n fields.fingerprint = fingerprint;\n }\n await this.driver.hmset(key, fields);\n // Set an explicit expiry so the failed record does not rely on the leftover\n // lock TTL; once it expires a fresh attempt with the same key is allowed.\n await this.driver.expire(key, ttlSeconds);\n }\n\n async get(key: string): Promise<IIdempotencyRecord | null> {\n const data = await this.driver.hgetall(key);\n if (!data || Object.keys(data).length === 0) {\n return null;\n }\n\n return {\n key,\n fingerprint: data.fingerprint ?? '',\n status: data.status! as 'processing' | 'completed' | 'failed',\n statusCode: data.statusCode ? parseInt(data.statusCode, 10) : undefined,\n response: data.response || undefined,\n headers: data.headers || undefined,\n startedAt: data.startedAt ? parseInt(data.startedAt, 10) : 0,\n completedAt: data.completedAt ? parseInt(data.completedAt, 10) : undefined,\n error: data.error || undefined,\n };\n }\n\n async delete(key: string): Promise<boolean> {\n const result = await this.driver.del(key);\n return result > 0;\n }\n}\n","/**\n * Idempotency plugin for NestJS RedisX.\n * Provides request deduplication with response replay for idempotent operations.\n */\n\nimport { DynamicModule, ForwardReference, Provider, Type } from '@nestjs/common';\nimport { APP_FILTER, Reflector } from '@nestjs/core';\nimport { IRedisXPlugin, IPluginAsyncOptions, CLIENT_MANAGER, REDIS_CLIENTS_INITIALIZATION, RedisClientManager } from '@nestjs-redisx/core';\n\nimport { version } from '../package.json';\nimport { IdempotencyExceptionFilter } from './idempotency/api/filters/idempotency-exception.filter';\nimport { IdempotencyInterceptor } from './idempotency/api/interceptors/idempotency.interceptor';\nimport { IdempotencyService } from './idempotency/application/services/idempotency.service';\nimport { RedisIdempotencyStoreAdapter } from './idempotency/infrastructure/adapters/redis-idempotency-store.adapter';\nimport { IDEMPOTENCY_PLUGIN_OPTIONS, IDEMPOTENCY_REDIS_DRIVER, IDEMPOTENCY_SERVICE, IDEMPOTENCY_STORE } from './shared/constants';\nimport { IdempotencyConfigError } from './shared/errors';\nimport { IIdempotencyPluginOptions } from './shared/types';\n\nconst DEFAULT_IDEMPOTENCY_CONFIG: Required<Omit<IIdempotencyPluginOptions, 'isGlobal' | 'client' | 'fingerprintGenerator'>> = {\n defaultTtl: 86400,\n keyPrefix: 'idempotency:',\n headerName: 'Idempotency-Key',\n lockTimeout: 30000,\n waitTimeout: 60000,\n validateFingerprint: true,\n fingerprintFields: ['method', 'path', 'body'],\n errorPolicy: 'fail-closed',\n};\n\n/**\n * Idempotency plugin for NestJS RedisX.\n *\n * Provides request deduplication with response replay:\n * - Prevents duplicate processing of same request\n * - Replays successful responses\n * - Handles concurrent requests\n * - Validates request fingerprints\n *\n * @example\n * ```typescript\n * @Module({\n * imports: [\n * RedisModule.forRoot({\n * clients: { host: 'localhost', port: 6379 },\n * plugins: [\n * new IdempotencyPlugin({\n * defaultTtl: 86400,\n * headerName: 'Idempotency-Key',\n * validateFingerprint: true,\n * }),\n * ],\n * }),\n * ],\n * })\n * export class AppModule {}\n * ```\n */\nexport class IdempotencyPlugin implements IRedisXPlugin {\n readonly name = 'idempotency';\n readonly version: string = version;\n readonly description = 'Request deduplication with response replay for idempotent operations';\n\n private asyncOptions?: IPluginAsyncOptions<IIdempotencyPluginOptions>;\n\n constructor(private readonly options: IIdempotencyPluginOptions = {}) {}\n\n static registerAsync(asyncOptions: IPluginAsyncOptions<IIdempotencyPluginOptions>): IdempotencyPlugin {\n const plugin = new IdempotencyPlugin();\n plugin.asyncOptions = asyncOptions;\n return plugin;\n }\n\n private static mergeDefaults(options: IIdempotencyPluginOptions): IIdempotencyPluginOptions {\n const merged: IIdempotencyPluginOptions = {\n client: options.client,\n defaultTtl: options.defaultTtl ?? DEFAULT_IDEMPOTENCY_CONFIG.defaultTtl,\n keyPrefix: options.keyPrefix ?? DEFAULT_IDEMPOTENCY_CONFIG.keyPrefix,\n headerName: options.headerName ?? DEFAULT_IDEMPOTENCY_CONFIG.headerName,\n lockTimeout: options.lockTimeout ?? DEFAULT_IDEMPOTENCY_CONFIG.lockTimeout,\n waitTimeout: options.waitTimeout ?? DEFAULT_IDEMPOTENCY_CONFIG.waitTimeout,\n validateFingerprint: options.validateFingerprint ?? DEFAULT_IDEMPOTENCY_CONFIG.validateFingerprint,\n fingerprintFields: options.fingerprintFields ?? DEFAULT_IDEMPOTENCY_CONFIG.fingerprintFields,\n errorPolicy: options.errorPolicy ?? DEFAULT_IDEMPOTENCY_CONFIG.errorPolicy,\n fingerprintGenerator: options.fingerprintGenerator,\n };\n\n // Fail fast: non-positive timing values silently break TTL semantics\n // (e.g. lockTimeout 0 -> the processing record is deleted immediately).\n for (const knob of ['defaultTtl', 'lockTimeout', 'waitTimeout'] as const) {\n const value = merged[knob]!;\n if (typeof value !== 'number' || !Number.isFinite(value) || value <= 0) {\n throw new IdempotencyConfigError(`${knob} must be a positive number (got ${String(value)})`);\n }\n }\n\n return merged;\n }\n\n getImports(): Array<Type<unknown> | DynamicModule | ForwardReference> {\n return this.asyncOptions?.imports ?? [];\n }\n\n getProviders(): Provider[] {\n const optionsProvider: Provider = this.asyncOptions\n ? {\n provide: IDEMPOTENCY_PLUGIN_OPTIONS,\n useFactory: async (...args: unknown[]) => {\n const userOptions = await this.asyncOptions!.useFactory(...args);\n return IdempotencyPlugin.mergeDefaults(userOptions);\n },\n inject: this.asyncOptions.inject || [],\n }\n : {\n provide: IDEMPOTENCY_PLUGIN_OPTIONS,\n useValue: IdempotencyPlugin.mergeDefaults(this.options),\n };\n\n return [\n optionsProvider,\n // Plugin-specific Redis driver (resolves named client)\n {\n provide: IDEMPOTENCY_REDIS_DRIVER,\n useFactory: async (manager: RedisClientManager, _init: void, options: IIdempotencyPluginOptions) => {\n const clientName = options.client ?? 'default';\n try {\n return await manager.getClient(clientName);\n } catch {\n throw new Error(`IdempotencyPlugin: Redis client \"${clientName}\" not found. ` + `Available clients are configured in RedisModule.forRoot({ clients: { ... } }). ` + `Either add a \"${clientName}\" client or remove the \"client\" option to use the default connection.`);\n }\n },\n inject: [CLIENT_MANAGER, REDIS_CLIENTS_INITIALIZATION, IDEMPOTENCY_PLUGIN_OPTIONS],\n },\n { provide: IDEMPOTENCY_STORE, useClass: RedisIdempotencyStoreAdapter },\n { provide: IDEMPOTENCY_SERVICE, useClass: IdempotencyService },\n // Reflector is needed for @Idempotent decorator metadata\n Reflector,\n IdempotencyInterceptor,\n // Global exception filter to map idempotency errors to proper HTTP codes\n // (otherwise they surface as 500 since they are not HttpExceptions)\n { provide: APP_FILTER, useClass: IdempotencyExceptionFilter },\n ];\n }\n\n getExports(): Array<string | symbol | Provider> {\n return [IDEMPOTENCY_PLUGIN_OPTIONS, IDEMPOTENCY_SERVICE, IdempotencyInterceptor];\n }\n}\n"]}
@@ -36,4 +36,11 @@ export declare class IdempotencyFailedError extends IdempotencyError {
36
36
  export declare class IdempotencyRecordNotFoundError extends IdempotencyError {
37
37
  constructor(key: string);
38
38
  }
39
+ /**
40
+ * Thrown when the plugin is configured with invalid values (e.g. non-positive
41
+ * timeouts, which would silently break record TTL semantics).
42
+ */
43
+ export declare class IdempotencyConfigError extends IdempotencyError {
44
+ constructor(message: string);
45
+ }
39
46
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/shared/errors/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAE7D;;GAEG;AACH,qBAAa,gBAAiB,SAAQ,WAAW;aAI7B,cAAc,EAAE,MAAM;gBAFtC,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,SAAS,EACC,cAAc,EAAE,MAAM,EACtC,KAAK,CAAC,EAAE,KAAK;CAIhB;AAED;;GAEG;AACH,qBAAa,2BAA4B,SAAQ,gBAAgB;;CAIhE;AAED;;GAEG;AACH,qBAAa,mCAAoC,SAAQ,gBAAgB;gBAC3D,GAAG,EAAE,MAAM;CAGxB;AAED;;GAEG;AACH,qBAAa,uBAAwB,SAAQ,gBAAgB;gBAC/C,GAAG,EAAE,MAAM;CAGxB;AAED;;GAEG;AACH,qBAAa,sBAAuB,SAAQ,gBAAgB;gBAC9C,GAAG,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM;CAGxC;AAED;;GAEG;AACH,qBAAa,8BAA+B,SAAQ,gBAAgB;gBACtD,GAAG,EAAE,MAAM;CAGxB"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/shared/errors/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAE7D;;GAEG;AACH,qBAAa,gBAAiB,SAAQ,WAAW;aAI7B,cAAc,EAAE,MAAM;gBAFtC,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,SAAS,EACC,cAAc,EAAE,MAAM,EACtC,KAAK,CAAC,EAAE,KAAK;CAIhB;AAED;;GAEG;AACH,qBAAa,2BAA4B,SAAQ,gBAAgB;;CAIhE;AAED;;GAEG;AACH,qBAAa,mCAAoC,SAAQ,gBAAgB;gBAC3D,GAAG,EAAE,MAAM;CAGxB;AAED;;GAEG;AACH,qBAAa,uBAAwB,SAAQ,gBAAgB;gBAC/C,GAAG,EAAE,MAAM;CAGxB;AAED;;GAEG;AACH,qBAAa,sBAAuB,SAAQ,gBAAgB;gBAC9C,GAAG,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM;CAGxC;AAED;;GAEG;AACH,qBAAa,8BAA+B,SAAQ,gBAAgB;gBACtD,GAAG,EAAE,MAAM;CAGxB;AAED;;;GAGG;AACH,qBAAa,sBAAuB,SAAQ,gBAAgB;gBAC9C,OAAO,EAAE,MAAM;CAG5B"}
@@ -23,7 +23,7 @@ export interface IIdempotencyPluginOptions {
23
23
  /** Whether to validate request fingerprint. @default true */
24
24
  validateFingerprint?: boolean;
25
25
  /** Fields to include in fingerprint. @default ['method', 'path', 'body'] */
26
- fingerprintFields?: ('method' | 'path' | 'body' | 'query' | 'headers')[];
26
+ fingerprintFields?: ('method' | 'path' | 'body' | 'query')[];
27
27
  /** Custom fingerprint generator */
28
28
  fingerprintGenerator?: (context: ExecutionContext) => string | Promise<string>;
29
29
  /** Error handling strategy. @default 'fail-closed' */
@@ -77,5 +77,17 @@ export interface IIdempotencyResponse {
77
77
  export interface IIdempotencyOptions {
78
78
  ttl?: number;
79
79
  lockTimeout?: number;
80
+ /**
81
+ * Request fingerprint to persist alongside the record. Keeps replay working
82
+ * even when the record is (re)written after the original lock expired
83
+ * (e.g. a handler that ran longer than lockTimeout).
84
+ */
85
+ fingerprint?: string;
86
+ /**
87
+ * Compare the request fingerprint against the stored record.
88
+ * When false, a reused key replays regardless of the request contents.
89
+ * @default plugin validateFingerprint (true)
90
+ */
91
+ validateFingerprint?: boolean;
80
92
  }
81
93
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/shared/types/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAElD;;GAEG;AACH,MAAM,WAAW,yBAAyB;IACxC;;;OAGG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;IAEnB,oDAAoD;IACpD,MAAM,CAAC,EAAE,MAAM,CAAC;IAEhB,gFAAgF;IAChF,UAAU,CAAC,EAAE,MAAM,CAAC;IAEpB,mDAAmD;IACnD,SAAS,CAAC,EAAE,MAAM,CAAC;IAEnB,kEAAkE;IAClE,UAAU,CAAC,EAAE,MAAM,CAAC;IAEpB,uEAAuE;IACvE,WAAW,CAAC,EAAE,MAAM,CAAC;IAErB,gFAAgF;IAChF,WAAW,CAAC,EAAE,MAAM,CAAC;IAErB,6DAA6D;IAC7D,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAE9B,4EAA4E;IAC5E,iBAAiB,CAAC,EAAE,CAAC,QAAQ,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,SAAS,CAAC,EAAE,CAAC;IAEzE,mCAAmC;IACnC,oBAAoB,CAAC,EAAE,CAAC,OAAO,EAAE,gBAAgB,KAAK,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IAE/E,sDAAsD;IACtD,WAAW,CAAC,EAAE,WAAW,GAAG,aAAa,CAAC;CAC3C;AAED;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC,sBAAsB;IACtB,GAAG,EAAE,MAAM,CAAC;IAEZ,+BAA+B;IAC/B,WAAW,EAAE,MAAM,CAAC;IAEpB,qBAAqB;IACrB,MAAM,EAAE,YAAY,GAAG,WAAW,GAAG,QAAQ,CAAC;IAE9C,uBAAuB;IACvB,UAAU,CAAC,EAAE,MAAM,CAAC;IAEpB,kCAAkC;IAClC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAElB,+CAA+C;IAC/C,OAAO,CAAC,EAAE,MAAM,CAAC;IAEjB,6CAA6C;IAC7C,SAAS,EAAE,MAAM,CAAC;IAElB,oCAAoC;IACpC,WAAW,CAAC,EAAE,MAAM,CAAC;IAErB,8BAA8B;IAC9B,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;GAEG;AACH,MAAM,WAAW,uBAAuB;IACtC,oCAAoC;IACpC,KAAK,EAAE,OAAO,CAAC;IAEf,sCAAsC;IACtC,MAAM,CAAC,EAAE,kBAAkB,CAAC;IAE5B,8BAA8B;IAC9B,mBAAmB,CAAC,EAAE,OAAO,CAAC;CAC/B;AAED;;GAEG;AACH,MAAM,WAAW,oBAAoB;IACnC,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,OAAO,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAClC;AAED;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAClC,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/shared/types/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAElD;;GAEG;AACH,MAAM,WAAW,yBAAyB;IACxC;;;OAGG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;IAEnB,oDAAoD;IACpD,MAAM,CAAC,EAAE,MAAM,CAAC;IAEhB,gFAAgF;IAChF,UAAU,CAAC,EAAE,MAAM,CAAC;IAEpB,mDAAmD;IACnD,SAAS,CAAC,EAAE,MAAM,CAAC;IAEnB,kEAAkE;IAClE,UAAU,CAAC,EAAE,MAAM,CAAC;IAEpB,uEAAuE;IACvE,WAAW,CAAC,EAAE,MAAM,CAAC;IAErB,gFAAgF;IAChF,WAAW,CAAC,EAAE,MAAM,CAAC;IAErB,6DAA6D;IAC7D,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAE9B,4EAA4E;IAC5E,iBAAiB,CAAC,EAAE,CAAC,QAAQ,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC,EAAE,CAAC;IAE7D,mCAAmC;IACnC,oBAAoB,CAAC,EAAE,CAAC,OAAO,EAAE,gBAAgB,KAAK,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IAE/E,sDAAsD;IACtD,WAAW,CAAC,EAAE,WAAW,GAAG,aAAa,CAAC;CAC3C;AAED;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC,sBAAsB;IACtB,GAAG,EAAE,MAAM,CAAC;IAEZ,+BAA+B;IAC/B,WAAW,EAAE,MAAM,CAAC;IAEpB,qBAAqB;IACrB,MAAM,EAAE,YAAY,GAAG,WAAW,GAAG,QAAQ,CAAC;IAE9C,uBAAuB;IACvB,UAAU,CAAC,EAAE,MAAM,CAAC;IAEpB,kCAAkC;IAClC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAElB,+CAA+C;IAC/C,OAAO,CAAC,EAAE,MAAM,CAAC;IAEjB,6CAA6C;IAC7C,SAAS,EAAE,MAAM,CAAC;IAElB,oCAAoC;IACpC,WAAW,CAAC,EAAE,MAAM,CAAC;IAErB,8BAA8B;IAC9B,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;GAEG;AACH,MAAM,WAAW,uBAAuB;IACtC,oCAAoC;IACpC,KAAK,EAAE,OAAO,CAAC;IAEf,sCAAsC;IACtC,MAAM,CAAC,EAAE,kBAAkB,CAAC;IAE5B,8BAA8B;IAC9B,mBAAmB,CAAC,EAAE,OAAO,CAAC;CAC/B;AAED;;GAEG;AACH,MAAM,WAAW,oBAAoB;IACnC,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,OAAO,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAClC;AAED;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAClC,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;;OAIG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IAErB;;;;OAIG;IACH,mBAAmB,CAAC,EAAE,OAAO,CAAC;CAC/B"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nestjs-redisx/idempotency",
3
- "version": "1.6.0",
3
+ "version": "1.6.1",
4
4
  "description": "Idempotency plugin for NestJS RedisX with request deduplication and response replay",
5
5
  "author": "NestJS RedisX Team",
6
6
  "license": "MIT",
@@ -24,7 +24,7 @@
24
24
  "test": "SKIP_INTEGRATION=true vitest run",
25
25
  "test:watch": "SKIP_INTEGRATION=true vitest",
26
26
  "test:cov": "SKIP_INTEGRATION=true vitest run --coverage",
27
- "test:integration": "SKIP_INTEGRATION=false vitest run test/integration/idempotency.integration.spec.ts",
27
+ "test:integration": "SKIP_INTEGRATION=false vitest run test/integration --pool=forks --poolOptions.forks.singleFork=true",
28
28
  "test:integration:watch": "SKIP_INTEGRATION=false vitest watch test/integration/idempotency.integration.spec.ts",
29
29
  "docker:up": "cd ../.. && docker-compose up -d redis",
30
30
  "docker:down": "cd ../.. && docker-compose down",