@beignet/core
Version:
Core framework primitives for Beignet
81 lines • 3.02 kB
TypeScript
/**
* Idempotency hooks for @beignet/core/server
*/
import { type IdempotencyMeta, type IdempotencyPort, type IdempotencyScope } from "../../idempotency/index.js";
import { type ActivityActor, type ActivityTenant } from "../../ports/index.js";
import type { HttpRequestLike, ServerHook } from "../types.js";
/**
* Ports required by idempotency hooks.
*/
export type IdempotencyPorts = {
idempotency: IdempotencyPort;
};
/**
* Minimal context shape required for actor- and tenant-scoped idempotency.
*/
export type CtxWithIdempotency = {
ports: IdempotencyPorts;
actor?: ActivityActor;
tenant?: ActivityTenant;
};
/**
* Options for `createIdempotencyHooks(...)`.
*/
export interface IdempotencyHooksOptions<Ctx> {
/**
* Build the idempotency namespace for a contract.
*
* Defaults to `http.<contract name>` so HTTP reservations never collide with
* use-case `runIdempotently(...)` namespaces.
*/
namespace?: (args: {
contract: {
name: string;
};
}) => string;
/**
* Build the idempotency scope after context exists.
*
* Defaults to a scope derived from `meta.scope`: omitted metadata scopes by
* `ctx.actor?.id` and also `ctx.tenant?.id` when present, `"actor"` scopes by
* the actor only, `"global"` stays global, `"tenant"` scopes by the tenant,
* and `"actor-tenant"` scopes by both.
*/
scope?: (args: {
ctx: Ctx;
req: HttpRequestLike;
meta: IdempotencyMeta;
}) => IdempotencyScope;
/**
* Build the fingerprint input from the parsed request.
*
* Defaults to `{ path, query, body }`.
*/
fingerprintInput?: (args: {
path: unknown;
query: unknown;
body: unknown;
}) => unknown;
}
/**
* Create metadata-driven idempotency hooks.
*
* The hook reads `contract.metadata.idempotency` and enforces it with
* `ctx.ports.idempotency`. In `beforeHandle` it reserves the client key after
* request parsing and route hook identity resolution, replays completed matching
* responses with an `idempotency-replayed: true` header, and rejects in-progress
* or conflicting keys with the framework
* `IdempotencyInProgress`/`IdempotencyConflict` catalog errors. After the
* response-validation phase, it stores final route-owned 2xx framework-neutral
* responses for replay and releases the reservation for framework-owned
* responses, errors, non-2xx responses, and native `Response` results, which
* are not replayable.
*
* Use `runIdempotently(...)` from `@beignet/core/idempotency` for non-HTTP
* workflows such as jobs, listeners, webhooks, and schedules.
*
* @param options - Optional namespace, scope, and fingerprint-input builders.
* @returns A server hook backed by `ctx.ports.idempotency`.
*/
export declare function createIdempotencyHooks<Ctx extends CtxWithIdempotency>(options?: IdempotencyHooksOptions<Ctx>): ServerHook<Ctx, IdempotencyPorts>;
//# sourceMappingURL=idempotency.d.ts.map