import * as express from 'express'; import { IdempotencyOptions } from '../models/models'; /** * This class represent the idempotency service. * It contains all the logic. */ export declare class IdempotencyService { private _options; /** * Server-side set of requests already marked as an idempotency hit. Keyed on * the request object itself so a client cannot spoof a hit by sending a * header. Entries are garbage-collected once the request is released. */ private _hits; /** * Constructor, used to initialize default values if options are not provided. * @param options Options provided */ constructor(options?: IdempotencyOptions); /** * Provide middleware function to enable idempotency. * @param req Express request * @param res Express response * @param next Express next function */ provideMiddlewareFunction(req: express.Request, res: express.Response, next: express.NextFunction): Promise; /** * Verify if the request is idempotent and so, nothing should be done * in term of processing. * @param req Request to validate hit */ isHit(req: express.Request): boolean; /** * Indicate that an error occurs during targeted process and idempotency must not occurs. * @param req Request to report in error */ reportError(req: express.Request): Promise; /** * Convert a request into a idempotency request which keeps only minimal representation. * @param req */ private convertToIdempotencyRequest; /** * Keep only the whitelisted request headers (case-insensitive) so secrets * (Authorization, Cookie, API keys) are never persisted at rest by the data * adapter. Node lower-cases incoming header names and the whitelist is * lower-cased at construction time, so the comparison is case-insensitive. * Mirrors the response-header whitelist applied in `buildIdempotencyResponse`. * @param headers Raw request headers */ private filterRequestHeaders; /** * Extract idempotency key from request. * @param req */ extractIdempotencyKeyFromReq(req: express.Request): string; /** * Override function, which is the correct way. But Typescript won't allow it because there is multiple overloads. * @param res * @param resource */ private setupHooks; /** * Hook into send function of the response to receive the body. * The optional `onSend` callback is invoked synchronously as the very first * thing inside the patched send, before the promise resolves — this ensures * any flag (e.g. `settled`) is set before the `finish` event fires. * @param res * @param onSend Optional synchronous callback invoked when send is called */ private sendHook; /** * Build idempotency response from hook responses and the response itself. * @param res * @param statusCode * @param body */ private buildIdempotencyResponse; /** * Initiate processing for a new (or taken-over) resource, translating a failed * `create` into the correct outcome. * * `findByIdempotencyKey` then `create` is not atomic, so two strictly * concurrent requests with the same key can both reach `create`; the loser * gets a duplicate-key rejection from the data adapter. Rather than letting * that bubble up as a `500`, we re-check the store: if a resource now exists, * a concurrent request won the race and we surface the standard `409` * Conflict; otherwise the failure is a genuine adapter outage and is * propagated unchanged. This makes the adapter's unique constraint — not a * lucky read — the real concurrency guarantee. * @param res Express response * @param resource Freshly built resource (with createdAt stamped) * @param next Guarded next function from the caller (safeNext) */ private startProcessingOrConflict; /** * Parse a `createdAt` value into a Unix timestamp (ms). * Accepts a Date object, a numeric epoch, or a string that can be parsed by * the Date constructor (e.g. ISO 8601 produced by JSON serialisation of a Date). * Returns null when the value is absent, not parseable, or results in NaN. * @param value Raw createdAt value from the resource */ private parseCreatedAt; /** * Determine whether the in-progress lease for the given resource has expired. * Returns false (not expired) when processingTimeout is disabled, when createdAt * is absent or non-parseable, or when the resource is still within the timeout window. * @param resource Resource to evaluate */ private isLeaseExpired; /** * Determine whether the current hook is still the legitimate owner of the resource * and is allowed to persist its response. Guards against zombie writes after a * takeover has occurred. * On the nominal path (age within timeout or timeout disabled) returns true with no * extra adapter read. Only performs a refetch when our own lease has already expired. * @param resource The resource snapshot captured at hook setup time */ private canStillPersist; /** * Emit an internal warning. Centralised so it can be replaced by a * pluggable logger in a future release without touching call sites. * @param message Warning message */ private logWarning; }