96 lines
14 KiB
Markdown
96 lines
14 KiB
Markdown
# @suaveplan/idempotency - Idempotency key guard and dedup: store adapters (memory, database, redis), distributed locking, middleware, key generation, metrics, OTel instrumentation.
|
|
|
|
## Summary
|
|
|
|
Idempotency key guard and deduplication for async operations. The package provides a complete `pending → completed | failed` state machine, three store backends (memory, database, Redis), distributed locking across multiple processes, request middleware, key generation with payload fingerprinting, version history management, and OTel metrics instrumentation. It is designed to protect any operation that must not execute more than once per request — payment charges, email sends, record creation — regardless of retries or race conditions.
|
|
|
|
<!-- begin:auto-docs -->
|
|
## Documentation tree
|
|
|
|
- [distributed-guard](./src/distributed/distributed-guard.md) — Provides distributed idempotency protection via a pluggable `LockProvider` that serializes concurrent access across mul…
|
|
- [redis-lock-provider](./src/distributed/redis-lock-provider.md) — Provides a Redis-backed implementation of the `LockProvider` interface using atomic `SET NX` (set-if-not-exists) operat…
|
|
- [error-codes](./src/error-codes/error-codes.md) — Defines and registers all stable error codes and their HTTP/retry metadata for the `@suaveplan/idempotency` package. Ev…
|
|
- [error-codes](./src/error-codes/error-codes.md) — Defines and registers all stable error codes and their HTTP/retry metadata for the `@suaveplan/idempotency` package. Ev…
|
|
- [errors](./src/errors/idempotency-error.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
|
|
- [idempotency-error](./src/errors/idempotency-error.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
|
|
- [guard](./src/guard/idempotency-guard.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
|
|
- [idempotency-guard](./src/guard/idempotency-guard.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
|
|
- [key](./src/key/key-generator.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
|
|
- [key-generator](./src/key/key-generator.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
|
|
- [metrics](./src/metrics/idempotency-metrics.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
|
|
- [idempotency-metrics](./src/metrics/idempotency-metrics.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
|
|
- [middleware](./src/middleware/idempotency-middleware.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
|
|
- [idempotency-middleware](./src/middleware/idempotency-middleware.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
|
|
- [database-store](./src/store/database-store.md) — Provides a persistent `IdempotencyStoreAdapter<T>` backed by any relational database via the `IdempotencyDatabaseAdapte…
|
|
- [memory-store](./src/store/memory-store.md) — Provides an in-memory implementation of `IdempotencyStoreAdapter<T>` backed by a `Map`. It is the default store for uni…
|
|
- [redis-store](./src/store/redis-store.md) — Provides a Redis-backed `IdempotencyStoreAdapter<T>` with automatic key expiration using Redis native TTL. By accepting…
|
|
- [versioning](./src/versioning/key-versioning.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
|
|
- [key-versioning](./src/versioning/key-versioning.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
|
|
<!-- end:auto-docs --> - [idempotency-error](./src/errors/idempotency-error.md) — <!-- begin:auto-docs --> _(no submodules)_- [guard](./src/guard/idempotency-guard.md) — <!-- begin:auto-docs --> _(no submodules)_- [idempotency-guard](./src/guard/idempotency-guard.md) — <!-- begin:auto-docs --> _(no submodules)_- [key](./src/key/key-generator.md) — <!-- begin:auto-docs --> _(no submodules)_- [key-generator](./src/key/key-generator.md) — <!-- begin:auto-docs --> _(no submodules)_- [metrics](./src/metrics/idempotency-metrics.md) — <!-- begin:auto-docs --> _(no submodules)_- [idempotency-metrics](./src/metrics/idempotency-metrics.md) — <!-- begin:auto-docs --> _(no submodules)_- [middleware](./src/middleware/idempotency-middleware.md) — <!-- begin:auto-docs --> _(no submodules)_- [idempotency-middleware](./src/middleware/idempotency-middleware.md) — <!-- begin:auto-docs --> _(no submodules)_- [database-store](./src/store/database-store.md) — Provides a persistent `IdempotencyStoreAdapter<T>` backed by any relational database via the `IdempotencyDatabaseAdapte…
|
|
- [memory-store](./src/store/memory-store.md) — Provides an in-memory implementation of `IdempotencyStoreAdapter<T>` backed by a `Map`. It is the default store for uni…
|
|
- [redis-store](./src/store/redis-store.md) — Provides a Redis-backed `IdempotencyStoreAdapter<T>` with automatic key expiration using Redis native TTL. By accepting…
|
|
- [versioning](./src/versioning/key-versioning.md) — <!-- begin:auto-docs --> _(no submodules)_- [key-versioning](./src/versioning/key-versioning.md) — <!-- begin:auto-docs --> _(no submodules)_- [error-codes](./src/error-codes.md) — Defines and registers all stable error codes and their HTTP/retry metadata for the `@suaveplan/idempotency` package. Ev…
|
|
- [idempotency-error](./src/errors/idempotency-error.md) — <!-- begin:auto-docs --> _(no submodules)_- [guard](./src/guard/idempotency-guard.md) — <!-- begin:auto-docs --> _(no submodules)_- [idempotency-guard](./src/guard/idempotency-guard.md) — <!-- begin:auto-docs --> _(no submodules)_- [key](./src/key/key-generator.md) — <!-- begin:auto-docs --> _(no submodules)_- [key-generator](./src/key/key-generator.md) — <!-- begin:auto-docs --> _(no submodules)_- [metrics](./src/metrics/idempotency-metrics.md) — <!-- begin:auto-docs --> _(no submodules)_- [idempotency-metrics](./src/metrics/idempotency-metrics.md) — <!-- begin:auto-docs --> _(no submodules)_- [middleware](./src/middleware/idempotency-middleware.md) — <!-- begin:auto-docs --> _(no submodules)_- [idempotency-middleware](./src/middleware/idempotency-middleware.md) — <!-- begin:auto-docs --> _(no submodules)_- [database-store](./src/store/database-store.md) — Provides a persistent `IdempotencyStoreAdapter<T>` backed by any relational database via the `IdempotencyDatabaseAdapte…
|
|
- [memory-store](./src/store/memory-store.md) — Provides an in-memory implementation of `IdempotencyStoreAdapter<T>` backed by a `Map`. It is the default store for uni…
|
|
- [redis-store](./src/store/redis-store.md) — Provides a Redis-backed `IdempotencyStoreAdapter<T>` with automatic key expiration using Redis native TTL. By accepting…
|
|
- [versioning](./src/versioning/key-versioning.md) — <!-- begin:auto-docs --> _(no submodules)_- [key-versioning](./src/versioning/key-versioning.md) — <!-- begin:auto-docs --> _(no submodules)_- [error-codes](./src/error-codes.md) — Defines and registers all stable error codes and their HTTP/retry metadata for the `@suaveplan/idempotency` package. Ev…
|
|
- [idempotency-error](./src/errors/idempotency-error.md) — <!-- begin:auto-docs --> _(no submodules)_- [guard](./src/guard/idempotency-guard.md) — <!-- begin:auto-docs --> _(no submodules)_- [idempotency-guard](./src/guard/idempotency-guard.md) — <!-- begin:auto-docs --> _(no submodules)_- [key](./src/key/key-generator.md) — <!-- begin:auto-docs --> _(no submodules)_- [key-generator](./src/key/key-generator.md) — <!-- begin:auto-docs --> _(no submodules)_- [metrics](./src/metrics/idempotency-metrics.md) — <!-- begin:auto-docs --> _(no submodules)_- [idempotency-metrics](./src/metrics/idempotency-metrics.md) — <!-- begin:auto-docs --> _(no submodules)_- [middleware](./src/middleware/idempotency-middleware.md) — <!-- begin:auto-docs --> _(no submodules)_- [idempotency-middleware](./src/middleware/idempotency-middleware.md) — <!-- begin:auto-docs --> _(no submodules)_- [database-store](./src/store/database-store.md) — Provides a persistent `IdempotencyStoreAdapter<T>` backed by any relational database via the `IdempotencyDatabaseAdapte…
|
|
- [memory-store](./src/store/memory-store.md) — Provides an in-memory implementation of `IdempotencyStoreAdapter<T>` backed by a `Map`. It is the default store for uni…
|
|
- [redis-store](./src/store/redis-store.md) — Provides a Redis-backed `IdempotencyStoreAdapter<T>` with automatic key expiration using Redis native TTL. By accepting…
|
|
- [versioning](./src/versioning/key-versioning.md) — <!-- begin:auto-docs --> _(no submodules)_- [key-versioning](./src/versioning/key-versioning.md) — <!-- begin:auto-docs --> _(no submodules)_- [error-codes](./src/error-codes.md) — Defines and registers all stable error codes and their HTTP/retry metadata for the `@suaveplan/idempotency` package. Ev…
|
|
- [idempotency-error](./src/errors/idempotency-error.md) — <!-- begin:auto-docs --> _(no submodules)_- [guard](./src/guard/idempotency-guard.md) — <!-- begin:auto-docs --> _(no submodules)_ - [idempotency-guard](./src/guard/idempotency-guard.md) — <!-- begin:auto-docs --> _(no submodules)_- [key](./src/key/key-generator.md) — <!-- begin:auto-docs --> _(no submodules)_ - [key-generator](./src/key/key-generator.md) — <!-- begin:auto-docs --> _(no submodules)_- [metrics](./src/metrics/idempotency-metrics.md) — <!-- begin:auto-docs --> _(no submodules)_ - [idempotency-metrics](./src/metrics/idempotency-metrics.md) — <!-- begin:auto-docs --> _(no submodules)_- [middleware](./src/middleware/idempotency-middleware.md) — <!-- begin:auto-docs --> _(no submodules)_ - [idempotency-middleware](./src/middleware/idempotency-middleware.md) — <!-- begin:auto-docs --> _(no submodules)_- [database-store](./src/store/database-store.md) — Provides a persistent `IdempotencyStoreAdapter<T>` backed by any relational database via the `IdempotencyDatabaseAdapte…
|
|
- [memory-store](./src/store/memory-store.md) — Provides an in-memory implementation of `IdempotencyStoreAdapter<T>` backed by a `Map`. It is the default store for uni…
|
|
- [redis-store](./src/store/redis-store.md) — Provides a Redis-backed `IdempotencyStoreAdapter<T>` with automatic key expiration using Redis native TTL. By accepting…
|
|
- [versioning](./src/versioning/key-versioning.md) — <!-- begin:auto-docs --> _(no submodules)_ - [key-versioning](./src/versioning/key-versioning.md) — <!-- begin:auto-docs --> _(no submodules)_- [error-codes](./src/error-codes.md) — Defines and registers all stable error codes and their HTTP/retry metadata for the `@suaveplan/idempotency` package. Ev…
|
|
|
|
## Install
|
|
|
|
```bash
|
|
bun add @suaveplan/idempotency
|
|
```
|
|
|
|
## Quick start
|
|
|
|
```ts
|
|
import { IdempotencyGuard } from "@suaveplan/idempotency/guard";
|
|
import { MemoryIdempotencyStore } from "@suaveplan/idempotency/store";
|
|
|
|
const guard = new IdempotencyGuard({
|
|
store: new MemoryIdempotencyStore(),
|
|
ttlMs: 3_600_000, // 1 hour
|
|
});
|
|
|
|
// First call: executes the handler and caches the result
|
|
const result = await guard.execute("order-42", () => createOrder(payload));
|
|
|
|
// Subsequent calls with the same key: return the cached result immediately
|
|
const again = await guard.execute("order-42", () => createOrder(payload));
|
|
// result === again — handler was not called again
|
|
|
|
// With payload fingerprinting and AbortSignal
|
|
const controller = new AbortController();
|
|
const safe = await guard.execute(
|
|
"charge-99",
|
|
() => chargeCard(payload),
|
|
{ input: payload, signal: controller.signal }
|
|
);
|
|
```
|
|
|
|
## Modules
|
|
|
|
| Module | Description |
|
|
|--------|-------------|
|
|
| [src/guard/idempotency-guard.md](src/guard/idempotency-guard.md) | `IdempotencyGuard` — single-node idempotency guard with full state machine, injectable clock, AbortSignal support, and OTel instrumentation |
|
|
| [src/distributed/distributed-guard.md](src/distributed/distributed-guard.md) | `DistributedIdempotencyGuard` — multi-node guard backed by a pluggable `LockProvider`; `MemoryLockProvider` for tests; `RedisLockProvider` for production |
|
|
| [src/distributed/redis-lock-provider.md](src/distributed/redis-lock-provider.md) | `RedisLockProvider` — Redis-backed distributed lock provider with TTL and exponential back-off |
|
|
| [src/store/memory-store.md](src/store/memory-store.md) | `MemoryIdempotencyStore` — in-memory store for tests and single-process deployments; lazy TTL expiration |
|
|
| [src/store/database-store.md](src/store/database-store.md) | `DatabaseIdempotencyStore` — SQL-backed store via a generic `IdempotencyDatabaseAdapter`; auto-creates table; supports upsert |
|
|
| [src/store/redis-store.md](src/store/redis-store.md) | `RedisIdempotencyStore` — Redis-backed store with native key TTL and configurable namespace prefix |
|
|
| [src/key/key-generator.md](src/key/key-generator.md) | Key generation utilities: `generateIdempotencyKey`, `compositeKey`, `fingerprintPayload`, `fingerprintPayloadAsync` |
|
|
| [src/middleware/idempotency-middleware.md](src/middleware/idempotency-middleware.md) | `createIdempotencyMiddleware` — framework-agnostic middleware factory for Express, Fastify, Hono, or custom request types |
|
|
| [src/metrics/idempotency-metrics.md](src/metrics/idempotency-metrics.md) | `IdempotencyMetrics` — aggregate and rolling-window counters, OTel counter/histogram instruments, periodic reporter |
|
|
| [src/versioning/key-versioning.md](src/versioning/key-versioning.md) | `KeyVersioning` — multi-version record management with `reject`, `overwrite`, and `version` conflict strategies |
|
|
| [src/errors/idempotency-error.md](src/errors/idempotency-error.md) | Structured error hierarchy: `IdempotencyConflictError`, `IdempotencyMismatchError`, `LockAcquisitionError`, `VersionConflictError` |
|
|
| [src/error-codes.md](src/error-codes.md) | Stable `IDEMPOTENCY.*` error code registry |
|
|
|