2.6 KiB
memory.ts — default in-memory DeadLetterSink
Purpose
Provides the default DeadLetterSink implementation shipped with the plugin: an unbounded Array<DeadLetterEntry> backed sink suitable for tests and development environments. The list is most-recent-first via unshift, so a caller invoking list({ limit: 10 }) always sees the ten most-recent exhausted deliveries. Production deployments typically configure a sibling sink (S3, Postgres, Mailpit) so dead letters survive process restart; this implementation exists so the plugin can boot with zero configuration and so tests can assert exhaustion behavior without standing up storage.
Features
recordis a singleunshift— O(1) on every write.listwalks the array applying the optionalendpointIdfilter andlimitcap; returns a frozen-by-array slice.- No bounded capacity — operators relying on the in-memory sink in long-running services should rotate the plugin or wire a durable sibling.
Usage: Basic
import { MemoryDeadLetterSink } from "@suaveplan/api-plugin-webhook/dead-letter/memory";
const sink = new MemoryDeadLetterSink();
const failed = await sink.list();
Usage: Advanced
import { MemoryDeadLetterSink } from "@suaveplan/api-plugin-webhook/dead-letter/memory";
const sink = new MemoryDeadLetterSink();
await sink.record({
id: "dl-1",
endpointId: "ep-1",
tenantId: null,
event: "order.created",
payload: { id: "w-1", event: "order.created", data: {}, timestamp: "..." },
attempts: [],
firstAttemptAt: "2026-05-18T00:00:00.000Z",
finalAttemptAt: "2026-05-18T00:00:00.000Z",
reason: "5x HTTP 500",
});
const onlyForEp1 = await sink.list({ endpointId: "ep-1", limit: 5 });
console.log(onlyForEp1.length); // 1
API Reference
new MemoryDeadLetterSink()— no constructor arguments.record(entry)— pushes the entry to the front of the internal array.list(filter?)— applies the optionalendpointIdandlimitfilter and returns a snapshot. Most-recent first.
Implementation Notes
Because the sink is an unbounded array in process memory, it must NOT be used as the durable record in production systems. The plugin docs flag this — operators wire a durable sibling sink or a composite sink that records to both memory (for fast inspection) and durable storage. The implementation chose unshift over push so consumers do not need to compute reverse() for the "most-recent first" view that operators want; the small cost (O(n) per insert) is acceptable for the in-memory implementation's intended use case.