import type { FastifyRequest } from "fastify"; type Awaitable = T | Promise; export interface IdempotencyAcquireInput { storageKey: string; fingerprint: string; leaseToken: string; now: number; leaseExpiresAt: number; recordExpiresAt: number; } export type IdempotencyAcquireResult = { kind: "acquired"; } | { kind: "replay"; response: TResponse; } | { kind: "conflict"; } | { kind: "in_progress"; retryAt: number; }; export interface IdempotencyCompleteInput { storageKey: string; leaseToken: string; response: TResponse; now: number; recordExpiresAt: number; } /** * Durable adapters must linearize operations per storageKey. The lease token is a * fencing token: complete and release may mutate only the record owned by that * token. Responses must be serializable by the selected adapter. */ export interface IdempotencyStore { acquire(input: IdempotencyAcquireInput): Promise>; complete(input: IdempotencyCompleteInput): Promise; release(storageKey: string, leaseToken: string): Promise; purgeExpired(now: number, limit?: number): Promise; close?(): Awaitable; } export interface LocalIdempotencyStoreOptions { maxEntries?: number; cleanupIntervalMs?: number; cloneResponse?: (response: TResponse) => TResponse; } export declare class IdempotencyStoreCapacityError extends Error { readonly retryAt?: number; constructor(retryAt?: number); } /** * A bounded single-process adapter. It is useful for local deployments and tests; * multi-instance deployments should provide a database or Redis implementation of * IdempotencyStore so acquisition and fencing remain shared and atomic. */ export declare class LocalIdempotencyStore implements IdempotencyStore { private readonly entries; private readonly maxEntries; private readonly cleanupIntervalMs; private readonly cloneResponse; private lastCleanupAt; constructor(options?: LocalIdempotencyStoreOptions); get size(): number; acquire(input: IdempotencyAcquireInput): Promise>; complete(input: IdempotencyCompleteInput): Promise; release(storageKey: string, leaseToken: string): Promise; purgeExpired(now: number, limit?: number): Promise; close(): void; private earliestExpiry; } export interface IdempotencyCoordinatorOptions { leaseMs?: number; retentionMs?: number; maxKeyLength?: number; now?: () => number; leaseToken?: () => string; } export interface BeginIdempotencyInput { actorId: string; scope: string; key: string; fingerprint: string; } export interface IdempotencyLease { storageKey: string; leaseToken: string; } export type BeginIdempotencyResult = { kind: "acquired"; lease: IdempotencyLease; } | { kind: "replay"; response: TResponse; } | { kind: "conflict"; } | { kind: "in_progress"; retryAfterMs: number; }; export type ExecuteIdempotencyResult = { kind: "executed"; response: TResponse; } | { kind: "replay"; response: TResponse; } | { kind: "conflict"; } | { kind: "in_progress"; retryAfterMs: number; }; export declare class IdempotencyLeaseLostError extends Error { constructor(); } export declare class IdempotencyCoordinator { private readonly store; private readonly leaseMs; private readonly retentionMs; private readonly maxKeyLength; private readonly now; private readonly leaseToken; constructor(store: IdempotencyStore, options?: IdempotencyCoordinatorOptions); begin(input: BeginIdempotencyInput): Promise>; complete(lease: IdempotencyLease, response: TResponse): Promise; release(lease: IdempotencyLease): Promise; execute(input: BeginIdempotencyInput, operation: () => Promise): Promise>; close(): Promise; } export interface RequestFingerprintInput { action: string; method?: string; contentType?: string; query?: unknown; payload?: unknown; } /** Hashes canonical JSON, so object key order does not create false conflicts. */ export declare function createRequestFingerprint(input: RequestFingerprintInput): string; /** * Uses Fastify's route template rather than the raw URL. Callers may override the * payload when the body is not JSON (for example, with a precomputed file digest). */ export declare function fingerprintFastifyRequest(request: Pick, payload?: unknown): string; export declare function idempotencyRequestAction(request: Pick): string; export declare function idempotencyKeyFromRequest(request: Pick): string | undefined; export declare function createIdempotencyStorageKey(input: Pick): string; export {}; //# sourceMappingURL=idempotency.d.ts.map