@relaybus/core 0.0.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.cts CHANGED
@@ -14,71 +14,6 @@ declare function toMilliseconds(value: Duration, name: string): number;
14
14
  /** Largest delay Node.js timers support. Longer values fire after 1ms instead. */
15
15
  declare const MAX_TIMER_DELAY_MS = 2147483647;
16
16
 
17
- /**
18
- * Base class of every error thrown by relaybus.
19
- */
20
- declare class RelayBusError extends Error {
21
- name: string;
22
- }
23
- /** Invalid options passed to a relaybus component. */
24
- declare class ConfigurationError extends RelayBusError {
25
- name: string;
26
- }
27
- /**
28
- * Thrown by `publish` when no transaction can be found and the bus runs with
29
- * `transactionMode: 'required'` (the default). Publishing outside a transaction
30
- * would reintroduce the dual-write problem the outbox exists to prevent.
31
- */
32
- declare class TransactionRequiredError extends RelayBusError {
33
- name: string;
34
- }
35
- /**
36
- * Throw from a handler to dead-letter the delivery immediately, skipping the
37
- * remaining retry attempts.
38
- */
39
- declare class NonRetryableError extends RelayBusError {
40
- name: string;
41
- }
42
- /** A payload failed its event schema. Never retried: the payload will not change. */
43
- declare class EventValidationError extends NonRetryableError {
44
- readonly issues: readonly ValidationIssue[];
45
- name: string;
46
- constructor(message: string, issues: readonly ValidationIssue[]);
47
- }
48
- /**
49
- * A message received from a broker is not a valid event. Never retried:
50
- * transports should dead-letter it instead of redelivering it forever.
51
- */
52
- declare class InvalidEnvelopeError extends NonRetryableError {
53
- name: string;
54
- }
55
- /** The worker no longer owns the delivery it was processing. */
56
- declare class LeaseLostError extends RelayBusError {
57
- name: string;
58
- }
59
- /** A handler did not settle within its configured timeout. Retried like any failure. */
60
- declare class HandlerTimeoutError extends RelayBusError {
61
- name: string;
62
- }
63
- /**
64
- * Reason of a handler's abort signal when the bus stops before the handler
65
- * finished. The delivery is released and runs again without losing an attempt.
66
- */
67
- declare class DispatcherStoppedError extends RelayBusError {
68
- name: string;
69
- }
70
- interface ValidationIssue {
71
- readonly message: string;
72
- readonly path?: readonly PropertyKey[] | undefined;
73
- }
74
- /** Serializable snapshot of an error, stored with failed deliveries. */
75
- interface ErrorInfo {
76
- readonly name: string;
77
- readonly message: string;
78
- readonly stack?: string | undefined;
79
- }
80
- declare function toErrorInfo(error: unknown): ErrorInfo;
81
-
82
17
  /**
83
18
  * The Standard Schema interface (https://standardschema.dev), copied as the
84
19
  * specification recommends so relaybus needs no runtime dependency. Zod,
@@ -151,35 +86,459 @@ interface EventClass<TPayload = unknown> {
151
86
  readonly type: string;
152
87
  readonly schema: StandardSchemaV1<unknown, TPayload> | undefined;
153
88
  }
154
- /** Payload type of an event class: `PayloadOf<typeof OrderPlaced>`. */
155
- type PayloadOf<TEvent> = TEvent extends EventClass<infer TPayload> ? TPayload : never;
156
- /** Anything that names an event type: an event class or the type string itself. */
157
- type EventReference = {
158
- readonly type: string;
159
- } | string;
160
- interface DefineEventOptions<TSchema extends StandardSchemaV1> {
161
- /** Validates the payload on publish and before every handler call. */
162
- readonly schema: TSchema;
89
+ /** Payload type of an event class: `PayloadOf<typeof OrderPlaced>`. */
90
+ type PayloadOf<TEvent> = TEvent extends EventClass<infer TPayload> ? TPayload : never;
91
+ /** Anything that names an event type: an event class or the type string itself. */
92
+ type EventReference = {
93
+ readonly type: string;
94
+ } | string;
95
+ interface DefineEventOptions<TSchema extends StandardSchemaV1> {
96
+ /** Validates the payload on publish and before every handler call. */
97
+ readonly schema: TSchema;
98
+ }
99
+ declare function assertEventType(type: string): void;
100
+ /**
101
+ * Defines an event type. Extend the returned class to name it:
102
+ *
103
+ * ```ts
104
+ * export class OrderPlaced extends defineEvent<{ orderId: string }>('order.placed.v1') {}
105
+ * export class OrderShipped extends defineEvent('order.shipped.v1', { schema: orderShippedSchema }) {}
106
+ * ```
107
+ */
108
+ declare function defineEvent<TSchema extends StandardSchemaV1>(type: string, options: DefineEventOptions<TSchema>): EventClass<StandardSchemaV1.InferOutput<TSchema>>;
109
+ declare function defineEvent<TPayload>(type: string): EventClass<TPayload>;
110
+ /** Runs a Standard Schema and returns its output, or throws {@link EventValidationError}. */
111
+ declare function validatePayload<TPayload>(type: string, schema: StandardSchemaV1<unknown, TPayload> | undefined, payload: unknown): Promise<TPayload>;
112
+
113
+ /**
114
+ * How the delay between attempts is randomized.
115
+ * - `full`: uniform in [0, delay]. Spreads retries the most (AWS recommendation).
116
+ * - `equal`: uniform in [delay / 2, delay].
117
+ * - `none`: exactly `delay`. Synchronized retries can stampede a recovering dependency.
118
+ */
119
+ type JitterStrategy = 'full' | 'equal' | 'none';
120
+ interface RetryPolicyOptions {
121
+ /**
122
+ * Total attempts including the first one. Default 10 for handlers, which
123
+ * gives up after roughly 17 minutes of failures with the default delays.
124
+ * `Infinity` retries until the delivery succeeds.
125
+ */
126
+ readonly attempts?: number;
127
+ /** Delay before the second attempt. Default `'1s'`. */
128
+ readonly initialDelay?: Duration;
129
+ /** Growth factor of the delay per attempt. Default 2. */
130
+ readonly factor?: number;
131
+ /** Upper bound of a single delay. Default `'10m'`. */
132
+ readonly maxDelay?: Duration;
133
+ /** Default `'full'`. */
134
+ readonly jitter?: JitterStrategy;
135
+ /** Return false to dead-letter an error without retrying it. */
136
+ readonly retryIf?: (error: unknown) => boolean;
137
+ }
138
+ interface RetryPolicy {
139
+ readonly attempts: number;
140
+ readonly initialDelayMs: number;
141
+ readonly factor: number;
142
+ readonly maxDelayMs: number;
143
+ readonly jitter: JitterStrategy;
144
+ readonly retryIf: ((error: unknown) => boolean) | undefined;
145
+ }
146
+ /** Default policy of handlers. */
147
+ declare const DEFAULT_RETRY_POLICY: RetryPolicy;
148
+ /**
149
+ * Default policy of routes to transports: a broker outage must only delay
150
+ * events, so routes retry until the broker accepts them, at most every 5 minutes.
151
+ */
152
+ declare const DEFAULT_ROUTE_RETRY_POLICY: RetryPolicy;
153
+ declare function createRetryPolicy(options: RetryPolicyOptions | undefined, base?: RetryPolicy): RetryPolicy;
154
+ /**
155
+ * Delay before the next attempt after `failedAttempts` failures (1-based):
156
+ * capped exponential backoff with the policy's jitter.
157
+ */
158
+ declare function computeRetryDelay(policy: RetryPolicy, failedAttempts: number, random?: () => number): number;
159
+ declare function isRetryableError(policy: RetryPolicy, error: unknown): boolean;
160
+
161
+ /** Passed to every handler call. */
162
+ interface HandlerContext<TTransaction = unknown> {
163
+ readonly deliveryId: string;
164
+ readonly handler: string;
165
+ /** Attempt number, starting at 1. */
166
+ readonly attempt: number;
167
+ readonly maxAttempts: number;
168
+ /** Aborted when the handler times out, the lease is lost or the bus stops. */
169
+ readonly signal: AbortSignal;
170
+ /**
171
+ * Transaction the delivery completes in, for handlers subscribed with
172
+ * `transactional: true`. Write through it to make the handler's effects and
173
+ * the completion atomic. Undefined otherwise.
174
+ */
175
+ readonly transaction: TTransaction | undefined;
176
+ }
177
+ type EventHandler<TPayload = unknown, TTransaction = unknown> = (event: EventEnvelope<TPayload>, context: HandlerContext<TTransaction>) => unknown;
178
+ interface SubscribeOptions {
179
+ /**
180
+ * Stable id of the handler. Deliveries are recorded under it, so renaming it
181
+ * orphans pending deliveries. Letters, digits and `._:/-`.
182
+ */
183
+ readonly id: string;
184
+ /** Overrides the bus retry policy field by field. */
185
+ readonly retry?: RetryPolicyOptions;
186
+ /** Abort and retry when a call takes longer. `false` disables the bus default. */
187
+ readonly timeout?: Duration | false;
188
+ /** Maximum concurrent deliveries of this handler in this process. */
189
+ readonly concurrency?: number;
190
+ /**
191
+ * Run the handler inside a store transaction that also completes the
192
+ * delivery: its writes and the completion commit or roll back together,
193
+ * so a handler that writes only through `context.transaction` (or through
194
+ * events it publishes) takes effect exactly once.
195
+ *
196
+ * The lease is not renewed while the transaction is open. A transactional
197
+ * handler must finish within the lease duration (default 30s), or within its
198
+ * `timeout`, which extends the lease once to cover it; otherwise it is aborted.
199
+ */
200
+ readonly transactional?: boolean;
201
+ }
202
+ /** Public description of a registered handler or route. */
203
+ interface HandlerDescriptor {
204
+ readonly id: string;
205
+ readonly eventType: string;
206
+ readonly retry: RetryPolicy;
207
+ readonly timeoutMs: number | null;
208
+ readonly concurrency: number | null;
209
+ readonly transactional: boolean;
210
+ /** True for routes that forward events to a transport. */
211
+ readonly forwardsToTransport: boolean;
212
+ }
213
+
214
+ /**
215
+ * An event class as a catalog stores it: any class created with `defineEvent`,
216
+ * whatever its payload type. Use {@link PayloadOf} to recover the payload.
217
+ */
218
+ interface CatalogEventClass {
219
+ new (payload: never): DomainEvent;
220
+ readonly type: string;
221
+ readonly schema: StandardSchemaV1 | undefined;
222
+ }
223
+ /**
224
+ * The shape of a catalog: event classes by name. Create one with
225
+ * {@link defineCatalog}; the name of each entry becomes part of the handler
226
+ * method that consumes it (`OrderPlaced` is consumed by `onOrderPlaced`).
227
+ */
228
+ interface Catalog {
229
+ readonly [eventName: string]: CatalogEventClass;
230
+ }
231
+ /** Names of the events of a catalog, as string literals. */
232
+ type CatalogEventName<TCatalog extends Catalog> = Extract<keyof TCatalog, string>;
233
+ /**
234
+ * Union of the event instances a catalog can publish, for example
235
+ * `OrderPlaced | OrderShipped`; with `TName`, only those of the named events.
236
+ */
237
+ type CatalogEvents<TCatalog extends Catalog, TName extends keyof TCatalog = keyof TCatalog> = InstanceType<TCatalog[TName & CatalogEventName<TCatalog>]>;
238
+ /**
239
+ * Envelope a handler receives for an event class:
240
+ * `EventOf<typeof OrderPlaced>` is `EventEnvelope<{ orderId: string }>`.
241
+ */
242
+ type EventOf<TEvent> = EventEnvelope<PayloadOf<TEvent>>;
243
+ /**
244
+ * Same as {@link HandlersOf} with a required list of events:
245
+ * `PartialHandlersOf<typeof OrderEvents, 'OrderPlaced'>` equals
246
+ * `HandlersOf<typeof OrderEvents, 'OrderPlaced'>`.
247
+ *
248
+ * ```ts
249
+ * class AuditHandlers implements PartialHandlersOf<typeof OrderEvents, 'OrderPlaced'> {
250
+ * onOrderPlaced(event: EventOf<typeof OrderPlaced>) {}
251
+ * }
252
+ * ```
253
+ */
254
+ type PartialHandlersOf<TCatalog extends Catalog, TName extends keyof TCatalog, TTransaction = unknown> = {
255
+ readonly [TKey in TName & string as `on${TKey}`]: (event: EventOf<TCatalog[TKey]>, context: HandlerContext<TTransaction>) => unknown;
256
+ };
257
+ /**
258
+ * Handler methods for the events of a catalog: one `on${EventName}` method per
259
+ * event, taking that event's envelope. A class that
260
+ * `implements HandlersOf<typeof OrderEvents>` fails to compile until it has a
261
+ * method with the right payload type for each event. `TName` narrows it to the
262
+ * events a service consumes, which is how a service handles part of a catalog
263
+ * shared between services:
264
+ *
265
+ * ```ts
266
+ * class BillingHandlers implements HandlersOf<typeof OrderEvents, 'OrderPlaced'> {
267
+ * onOrderPlaced(event: EventOf<typeof OrderPlaced>) {}
268
+ * }
269
+ * ```
270
+ *
271
+ * `TTransaction` types `context.transaction` for transactional handlers.
272
+ */
273
+ type HandlersOf<TCatalog extends Catalog, TName extends keyof TCatalog = keyof TCatalog, TTransaction = unknown> = PartialHandlersOf<TCatalog, TName, TTransaction>;
274
+ /**
275
+ * Names of catalog events that an API call is limited to. Every name must be a
276
+ * key of the catalog, which the compiler checks; duplicates and an empty list
277
+ * are rejected at runtime with a {@link ConfigurationError}. Omitting the list
278
+ * means every event of the catalog.
279
+ */
280
+ interface CatalogEventSelection<TName> {
281
+ readonly events?: readonly TName[];
282
+ }
283
+ /** Options of {@link defineCatalog}. */
284
+ interface DefineCatalogOptions {
285
+ /**
286
+ * Name of the catalog, used in boot-check messages and as the default prefix
287
+ * of the handler ids that `subscribeCatalog` registers.
288
+ */
289
+ readonly name?: string;
290
+ }
291
+ /**
292
+ * Options of `RelayBus.subscribeCatalog`: {@link SubscribeOptions} for every
293
+ * handler of the catalog, and optionally the events to subscribe to. The plain
294
+ * `SubscribeCatalogOptions`, without a catalog, names no events, so it fits
295
+ * every catalog.
296
+ */
297
+ type SubscribeCatalogOptions<TCatalog extends Catalog = Catalog, TName extends keyof TCatalog = string extends keyof TCatalog ? never : keyof TCatalog> = Omit<SubscribeOptions, 'id'> & CatalogEventSelection<TName> & {
298
+ /**
299
+ * Prefix of the handler ids, which become `${idPrefix}.on${EventName}`.
300
+ * Default: the catalog name, then the class name of the handlers object.
301
+ * Deliveries are recorded under these ids, so renaming them orphans pending
302
+ * deliveries.
303
+ */
304
+ readonly idPrefix?: string;
305
+ };
306
+ /** One event of a catalog together with the handler method that consumes it. */
307
+ interface CatalogEntry {
308
+ /** Name under which the class was registered, for example `OrderPlaced`. */
309
+ readonly name: string;
310
+ readonly eventClass: CatalogEventClass;
311
+ /** Event type string, for example `order.placed.v1`. */
312
+ readonly type: string;
313
+ /** Expected handler method, for example `onOrderPlaced`. */
314
+ readonly handlerName: string;
315
+ }
316
+ /** What {@link describeCatalog} knows about a catalog. */
317
+ interface CatalogDescription {
318
+ readonly name: string | undefined;
319
+ readonly entries: readonly CatalogEntry[];
320
+ }
321
+ /**
322
+ * Groups event classes into a catalog: the complete set of events one service
323
+ * or domain owns. The catalog is frozen and keeps its keys and classes in its
324
+ * type, which enables compile-time checks:
325
+ *
326
+ * ```ts
327
+ * export const OrderEvents = defineCatalog({ OrderPlaced, OrderShipped }, { name: 'OrderEvents' });
328
+ *
329
+ * class ShippingHandlers implements HandlersOf<typeof OrderEvents> {
330
+ * onOrderPlaced(event: EventOf<typeof OrderPlaced>) {}
331
+ * // Compile error until onOrderShipped is added.
332
+ * }
333
+ * ```
334
+ *
335
+ * Throws {@link ConfigurationError} for an empty catalog, an entry that is not
336
+ * an event class, a name that is not made of letters, digits and underscores,
337
+ * or two entries with the same event type.
338
+ */
339
+ declare function defineCatalog<const TCatalog extends Catalog>(events: TCatalog, options?: DefineCatalogOptions): Readonly<TCatalog>;
340
+ /**
341
+ * Describes a catalog created by {@link defineCatalog}. The description and its
342
+ * entries are frozen. Throws {@link ConfigurationError} for any other object,
343
+ * so a hand-written record cannot slip past the checks that rely on it.
344
+ */
345
+ declare function describeCatalog(catalog: Catalog): CatalogDescription;
346
+ /**
347
+ * The handler signature of one catalog event, written as a method so that its
348
+ * parameters are compared bivariantly: a handler may annotate its context as
349
+ * `HandlerContext<PoolClient>` and still satisfy it. {@link MatchingHandlers}
350
+ * restores the strict check of the event parameter.
351
+ */
352
+ type BivariantHandler<TEvent> = {
353
+ handle(event: EventOf<TEvent>, context: HandlerContext): unknown;
354
+ }['handle'];
355
+ /**
356
+ * The shape {@link defineHandlers} constrains its handlers to: one method per
357
+ * selected event, with the context typed loosely enough for any transaction type.
358
+ */
359
+ type HandlerShapeOf<TCatalog extends Catalog, TName extends keyof TCatalog> = {
360
+ readonly [TKey in TName & string as `on${TKey}`]: BivariantHandler<TCatalog[TKey]>;
361
+ };
362
+ /**
363
+ * Checks every key of `THandlers` against the selected events of a catalog:
364
+ * keys that are no handler of a selected event become `never`, and a handler
365
+ * whose event parameter does not accept the event's envelope becomes the
366
+ * expected signature, so the compiler reports the mismatch at that key.
367
+ */
368
+ type MatchingHandlers<TCatalog extends Catalog, TName extends keyof TCatalog, THandlers> = {
369
+ readonly [TKey in keyof THandlers]: TKey extends `on${infer TEventName extends TName & string}` ? THandlers[TKey] extends (event: infer TEvent, ...rest: never[]) => unknown ? EventOf<TCatalog[TEventName]> extends TEvent ? THandlers[TKey] : (event: EventOf<TCatalog[TEventName]>, context: HandlerContext) => unknown : (event: EventOf<TCatalog[TEventName]>, context: HandlerContext) => unknown : never;
370
+ };
371
+ /**
372
+ * Declares the handlers of a catalog as an object: every event, or the events
373
+ * named in `options.events`. Compilation fails when a handler is missing,
374
+ * misnamed, extra (including a handler of an event that is not selected) or
375
+ * takes the wrong payload; the same checks run at runtime for callers without
376
+ * type checking. A handler may type its context with a transaction type, for
377
+ * example `HandlerContext<PoolClient>`.
378
+ *
379
+ * ```ts
380
+ * const handlers = defineHandlers(OrderEvents, {
381
+ * onOrderPlaced: (event) => reserveStock(event.payload.orderId),
382
+ * onOrderShipped: (event) => notify(event.payload.orderId),
383
+ * });
384
+ * bus.subscribeCatalog(OrderEvents, handlers, { idPrefix: 'fulfilment' });
385
+ *
386
+ * const billing = defineHandlers(
387
+ * OrderEvents,
388
+ * { onOrderPlaced: (event) => charge(event.payload.orderId) },
389
+ * { events: ['OrderPlaced'] },
390
+ * );
391
+ * bus.subscribeCatalog(OrderEvents, billing, { events: ['OrderPlaced'] });
392
+ * ```
393
+ */
394
+ declare function defineHandlers<TCatalog extends Catalog, THandlers extends HandlerShapeOf<TCatalog, TName>, const TName extends keyof TCatalog>(catalog: TCatalog, handlers: THandlers & MatchingHandlers<TCatalog, TName, THandlers>, options: {
395
+ readonly events: readonly TName[];
396
+ }): THandlers;
397
+ /** Without `events`, every handler of the catalog is required. */
398
+ declare function defineHandlers<TCatalog extends Catalog, THandlers extends HandlerShapeOf<TCatalog, keyof TCatalog>>(catalog: TCatalog, handlers: THandlers & MatchingHandlers<TCatalog, keyof TCatalog, THandlers>, options?: {
399
+ readonly events?: undefined;
400
+ }): THandlers;
401
+ /**
402
+ * Maps every key of `THandlers` that is not a handler of the selected events
403
+ * of the catalog (default: all of them) to `never`.
404
+ */
405
+ type NoExtraHandlers<TCatalog extends Catalog, THandlers, TName extends keyof TCatalog = keyof TCatalog> = {
406
+ readonly [TKey in Exclude<keyof THandlers, keyof HandlersOf<TCatalog, TName>>]: never;
407
+ };
408
+
409
+ /**
410
+ * What a service does with the events of a catalog:
411
+ * - `consume`: it handles them, so each needs a local handler.
412
+ * - `produce`: it publishes them for other services, so each needs a route to a transport.
413
+ */
414
+ type CatalogRole = 'consume' | 'produce';
415
+ /**
416
+ * The part of a catalog that one service consumes or produces, created with
417
+ * {@link consume} or {@link produce} and passed to the `catalogs` option of the
418
+ * bus (or `bus.registerCatalog`). `start()` then verifies the role for each
419
+ * declared event.
420
+ */
421
+ interface CatalogDeclaration<TCatalog extends Catalog = Catalog, TName extends keyof TCatalog = keyof TCatalog> {
422
+ readonly catalog: TCatalog;
423
+ readonly role: CatalogRole;
424
+ /** Names of the declared events, in catalog order. */
425
+ readonly events: readonly Extract<TName, string>[];
426
+ /** The declared events with their types and handler methods, in catalog order. */
427
+ readonly entries: readonly CatalogEntry[];
428
+ }
429
+ /**
430
+ * Declares that this service consumes events of a catalog, typically one
431
+ * shared between services through a contracts package. `start()` fails unless
432
+ * every declared event has a local handler (`subscribeCatalog`, `subscribe`,
433
+ * `@EventConsumer`, `@Subscribe`); a route to a transport does not count.
434
+ * Without `events`, every event of the catalog is declared. Unknown names do
435
+ * not compile and are rejected at runtime with a {@link ConfigurationError}.
436
+ *
437
+ * ```ts
438
+ * new RelayBus({ store, catalogs: [consume(OrderEvents, { events: ['OrderPlaced'] })] });
439
+ * ```
440
+ */
441
+ declare function consume<TCatalog extends Catalog, const TName extends keyof TCatalog = keyof TCatalog>(catalog: TCatalog, options?: CatalogEventSelection<TName>): CatalogDeclaration<TCatalog, TName>;
442
+ /**
443
+ * Declares that this service publishes events of a catalog for other services.
444
+ * `start()` fails unless every declared event has a route to a transport, so
445
+ * nothing is published without a destination; a local handler does not count.
446
+ * Combine it with {@link consume} for events the service also handles itself.
447
+ * Without `events`, every event of the catalog is declared.
448
+ *
449
+ * ```ts
450
+ * new RelayBus({
451
+ * store,
452
+ * transports: [kafka],
453
+ * routes: [{ event: OrderPlaced, transport: 'kafka', destination: 'orders' }],
454
+ * catalogs: [produce(OrderEvents, { events: ['OrderPlaced'] })],
455
+ * });
456
+ * ```
457
+ */
458
+ declare function produce<TCatalog extends Catalog, const TName extends keyof TCatalog = keyof TCatalog>(catalog: TCatalog, options?: CatalogEventSelection<TName>): CatalogDeclaration<TCatalog, TName>;
459
+
460
+ /**
461
+ * Base class of every error thrown by relaybus.
462
+ */
463
+ declare class RelayBusError extends Error {
464
+ name: string;
465
+ }
466
+ /** Invalid options passed to a relaybus component. */
467
+ declare class ConfigurationError extends RelayBusError {
468
+ name: string;
469
+ }
470
+ /**
471
+ * Thrown by `publish` when no transaction can be found and the bus runs with
472
+ * `transactionMode: 'required'` (the default). Publishing outside a transaction
473
+ * would reintroduce the dual-write problem the outbox exists to prevent.
474
+ */
475
+ declare class TransactionRequiredError extends RelayBusError {
476
+ name: string;
477
+ }
478
+ /**
479
+ * Throw from a handler to dead-letter the delivery immediately, skipping the
480
+ * remaining retry attempts.
481
+ */
482
+ declare class NonRetryableError extends RelayBusError {
483
+ name: string;
484
+ }
485
+ /** A payload failed its event schema. Never retried: the payload will not change. */
486
+ declare class EventValidationError extends NonRetryableError {
487
+ readonly issues: readonly ValidationIssue[];
488
+ name: string;
489
+ constructor(message: string, issues: readonly ValidationIssue[]);
490
+ }
491
+ /**
492
+ * A message received from a broker is not a valid event. Never retried:
493
+ * transports should dead-letter it instead of redelivering it forever.
494
+ */
495
+ declare class InvalidEnvelopeError extends NonRetryableError {
496
+ name: string;
497
+ }
498
+ /** The worker no longer owns the delivery it was processing. */
499
+ declare class LeaseLostError extends RelayBusError {
500
+ name: string;
501
+ /**
502
+ * Whether the store said so (a fenced write returned false). A worker that only noticed
503
+ * locally that its lease ran out may still own the delivery in the store and has to give it back.
504
+ */
505
+ readonly storeConfirmed: boolean;
506
+ constructor(message: string, options?: {
507
+ readonly storeConfirmed?: boolean;
508
+ });
509
+ }
510
+ /** A handler did not settle within its configured timeout. Retried like any failure. */
511
+ declare class HandlerTimeoutError extends RelayBusError {
512
+ name: string;
163
513
  }
164
- declare function assertEventType(type: string): void;
165
514
  /**
166
- * Defines an event type. Extend the returned class to name it:
167
- *
168
- * ```ts
169
- * export class OrderPlaced extends defineEvent<{ orderId: string }>('order.placed.v1') {}
170
- * export class OrderShipped extends defineEvent('order.shipped.v1', { schema: orderShippedSchema }) {}
171
- * ```
515
+ * Reason of a handler's abort signal when the bus stops before the handler
516
+ * finished. The delivery is released and runs again without losing an attempt.
172
517
  */
173
- declare function defineEvent<TSchema extends StandardSchemaV1>(type: string, options: DefineEventOptions<TSchema>): EventClass<StandardSchemaV1.InferOutput<TSchema>>;
174
- declare function defineEvent<TPayload>(type: string): EventClass<TPayload>;
175
- /** Runs a Standard Schema and returns its output, or throws {@link EventValidationError}. */
176
- declare function validatePayload<TPayload>(type: string, schema: StandardSchemaV1<unknown, TPayload> | undefined, payload: unknown): Promise<TPayload>;
518
+ declare class DispatcherStoppedError extends RelayBusError {
519
+ name: string;
520
+ }
521
+ interface ValidationIssue {
522
+ readonly message: string;
523
+ readonly path?: readonly PropertyKey[] | undefined;
524
+ }
525
+ /** Serializable snapshot of an error, stored with failed deliveries. */
526
+ interface ErrorInfo {
527
+ readonly name: string;
528
+ readonly message: string;
529
+ readonly stack?: string | undefined;
530
+ }
531
+ declare function toErrorInfo(error: unknown): ErrorInfo;
177
532
 
178
533
  /**
179
534
  * Lifecycle of one handler's delivery of one event.
180
535
  * `pending` (waiting or scheduled for retry) -> `processing` (leased by a worker)
181
536
  * -> `completed` | `pending` (retry) | `dead` (exhausted or non-retryable).
182
537
  * `dead` -> `pending` only through an explicit requeue.
538
+ *
539
+ * A completed delivery is deleted. `completed` records exist only in stores
540
+ * configured to keep a completion history (`retainCompleted` /
541
+ * `completionHistory`), where they are listed, counted and purged like before.
183
542
  */
184
543
  type DeliveryStatus = 'pending' | 'processing' | 'completed' | 'dead';
185
544
  declare const DELIVERY_STATUSES: readonly DeliveryStatus[];
@@ -205,6 +564,11 @@ interface AppendResult {
205
564
  * Lease expiry is compared with the `now` the caller passes, taken from the
206
565
  * bus clock. Keep worker clocks synchronized (NTP): a skew larger than a third
207
566
  * of the lease duration lets another worker reclaim a delivery that is still running.
567
+ *
568
+ * Stores may grant the lease on a group of deliveries (a lane) instead of on
569
+ * each delivery. The group is exclusive to `leaseOwner` while the lease is
570
+ * live, so other owners do not see its pending deliveries until it expires or
571
+ * is relinquished; every delivery of the group carries the group's fencing token.
208
572
  */
209
573
  interface ClaimRequest {
210
574
  /** Only deliveries of these handlers are claimed. */
@@ -292,14 +656,18 @@ interface EventStore<TTransaction = unknown> {
292
656
  * Leases up to `limit` deliveries that are due: pending with `availableAt <= now`,
293
657
  * or processing with an expired lease. A keyed delivery is only claimable when
294
658
  * no earlier delivery of the same handler and key is pending or processing.
295
- * Increments the attempt count. Safe to call from many processes at once.
659
+ * Counts the attempt (`attempt` of the result; a store may persist the count
660
+ * only when the attempt fails or its lease is lost). Safe to call from many
661
+ * processes at once.
296
662
  */
297
663
  claim(request: ClaimRequest): Promise<ClaimedDelivery[]>;
298
664
  /** Extends a lease the caller still holds. Returns false when the lease was lost. */
299
665
  extendLease(lease: LeaseReference, leaseExpiresAt: Date, now: Date): Promise<boolean>;
300
666
  /**
301
- * Marks a leased delivery completed. With `transaction` the change commits
302
- * together with the handler's own writes. Returns false when the lease was lost.
667
+ * Marks a leased delivery completed by deleting it (or moving it to the
668
+ * completion history, when the store keeps one). With `transaction` the change
669
+ * commits together with the handler's own writes. Returns false when the lease
670
+ * was lost.
303
671
  */
304
672
  complete(lease: LeaseReference, now: Date, transaction?: TTransaction): Promise<boolean>;
305
673
  /** Returns a leased delivery to pending, due at `availableAt`. */
@@ -309,6 +677,14 @@ interface EventStore<TTransaction = unknown> {
309
677
  * now, attempts decremented. Used when a handler is aborted at shutdown.
310
678
  */
311
679
  release(lease: LeaseReference, now: Date): Promise<boolean>;
680
+ /**
681
+ * Marks many leased deliveries completed with one round trip. Returns, for
682
+ * every lease in the same order, whether it was completed (false: lease lost
683
+ * or already finished). Each lease is fenced individually, exactly as in
684
+ * {@link EventStore.complete}. Optional: the dispatcher falls back to
685
+ * `complete` when a store does not implement it.
686
+ */
687
+ completeMany?(leases: readonly LeaseReference[], now: Date): Promise<boolean[]>;
312
688
  /** Moves a leased delivery to dead. */
313
689
  bury(lease: LeaseReference, error: ErrorInfo, now: Date): Promise<boolean>;
314
690
  listDeliveries(query: DeliveryQuery): Promise<DeliveryRecord[]>;
@@ -316,109 +692,23 @@ interface EventStore<TTransaction = unknown> {
316
692
  requeue(deliveryIds: readonly string[], now: Date): Promise<number>;
317
693
  purge(request: PurgeRequest): Promise<PurgeResult>;
318
694
  statistics(now: Date): Promise<EventStoreStatistics>;
319
- /** Releases connections the store opened itself. */
320
- close?(): Promise<void>;
321
- }
322
-
323
- /**
324
- * How the delay between attempts is randomized.
325
- * - `full`: uniform in [0, delay]. Spreads retries the most (AWS recommendation).
326
- * - `equal`: uniform in [delay / 2, delay].
327
- * - `none`: exactly `delay`. Synchronized retries can stampede a recovering dependency.
328
- */
329
- type JitterStrategy = 'full' | 'equal' | 'none';
330
- interface RetryPolicyOptions {
331
- /**
332
- * Total attempts including the first one. Default 10 for handlers, which
333
- * gives up after roughly 17 minutes of failures with the default delays.
334
- * `Infinity` retries until the delivery succeeds.
335
- */
336
- readonly attempts?: number;
337
- /** Delay before the second attempt. Default `'1s'`. */
338
- readonly initialDelay?: Duration;
339
- /** Growth factor of the delay per attempt. Default 2. */
340
- readonly factor?: number;
341
- /** Upper bound of a single delay. Default `'10m'`. */
342
- readonly maxDelay?: Duration;
343
- /** Default `'full'`. */
344
- readonly jitter?: JitterStrategy;
345
- /** Return false to dead-letter an error without retrying it. */
346
- readonly retryIf?: (error: unknown) => boolean;
347
- }
348
- interface RetryPolicy {
349
- readonly attempts: number;
350
- readonly initialDelayMs: number;
351
- readonly factor: number;
352
- readonly maxDelayMs: number;
353
- readonly jitter: JitterStrategy;
354
- readonly retryIf: ((error: unknown) => boolean) | undefined;
355
- }
356
- /** Default policy of handlers. */
357
- declare const DEFAULT_RETRY_POLICY: RetryPolicy;
358
- /**
359
- * Default policy of routes to transports: a broker outage must only delay
360
- * events, so routes retry until the broker accepts them, at most every 5 minutes.
361
- */
362
- declare const DEFAULT_ROUTE_RETRY_POLICY: RetryPolicy;
363
- declare function createRetryPolicy(options: RetryPolicyOptions | undefined, base?: RetryPolicy): RetryPolicy;
364
- /**
365
- * Delay before the next attempt after `failedAttempts` failures (1-based):
366
- * capped exponential backoff with the policy's jitter.
367
- */
368
- declare function computeRetryDelay(policy: RetryPolicy, failedAttempts: number, random?: () => number): number;
369
- declare function isRetryableError(policy: RetryPolicy, error: unknown): boolean;
370
-
371
- /** Passed to every handler call. */
372
- interface HandlerContext<TTransaction = unknown> {
373
- readonly deliveryId: string;
374
- readonly handler: string;
375
- /** Attempt number, starting at 1. */
376
- readonly attempt: number;
377
- readonly maxAttempts: number;
378
- /** Aborted when the handler times out, the lease is lost or the bus stops. */
379
- readonly signal: AbortSignal;
380
695
  /**
381
- * Transaction the delivery completes in, for handlers subscribed with
382
- * `transactional: true`. Write through it to make the handler's effects and
383
- * the completion atomic. Undefined otherwise.
384
- */
385
- readonly transaction: TTransaction | undefined;
386
- }
387
- type EventHandler<TPayload = unknown, TTransaction = unknown> = (event: EventEnvelope<TPayload>, context: HandlerContext<TTransaction>) => unknown;
388
- interface SubscribeOptions {
389
- /**
390
- * Stable id of the handler. Deliveries are recorded under it, so renaming it
391
- * orphans pending deliveries. Letters, digits and `._:/-`.
696
+ * Hands back every lease group that `leaseOwner` holds without running work,
697
+ * so other workers can take them at once instead of after lease expiry.
698
+ * Called at graceful shutdown after the owner's deliveries settled. Groups
699
+ * with running deliveries stay leased. Optional.
392
700
  */
393
- readonly id: string;
394
- /** Overrides the bus retry policy field by field. */
395
- readonly retry?: RetryPolicyOptions;
396
- /** Abort and retry when a call takes longer. `false` disables the bus default. */
397
- readonly timeout?: Duration | false;
398
- /** Maximum concurrent deliveries of this handler in this process. */
399
- readonly concurrency?: number;
701
+ relinquish?(leaseOwner: string, now: Date): Promise<void>;
400
702
  /**
401
- * Run the handler inside a store transaction that also completes the
402
- * delivery: its writes and the completion commit or roll back together,
403
- * so a handler that writes only through `context.transaction` (or through
404
- * events it publishes) takes effect exactly once.
405
- *
406
- * The lease is not renewed while the transaction is open. A transactional
407
- * handler must finish within the lease duration (default 30s), or within its
408
- * `timeout`, which extends the lease once to cover it; otherwise it is aborted.
703
+ * Calls `listener` soon after a transaction that added deliveries due at once has committed, in this
704
+ * process or in any other one, so an idle dispatcher claims them without waiting for its next poll.
705
+ * A hint, never a guarantee: a report may be late, merged with others, or lost (while a connection
706
+ * is re-established, for instance), and polling still finds every delivery. Resolves with a function
707
+ * that stops listening, or with `undefined` when this store cannot listen. Optional.
409
708
  */
410
- readonly transactional?: boolean;
411
- }
412
- /** Public description of a registered handler or route. */
413
- interface HandlerDescriptor {
414
- readonly id: string;
415
- readonly eventType: string;
416
- readonly retry: RetryPolicy;
417
- readonly timeoutMs: number | null;
418
- readonly concurrency: number | null;
419
- readonly transactional: boolean;
420
- /** True for routes that forward events to a transport. */
421
- readonly forwardsToTransport: boolean;
709
+ watchNewDeliveries?(listener: () => void): Promise<(() => Promise<void>) | undefined>;
710
+ /** Releases connections the store opened itself. */
711
+ close?(): Promise<void>;
422
712
  }
423
713
 
424
714
  /**
@@ -430,45 +720,7 @@ declare function createUuidV7(timestamp?: number): string;
430
720
  /** Produces unique identifiers for events, deliveries and lease tokens. */
431
721
  type IdentifierGenerator = () => string;
432
722
 
433
- interface InMemoryEventStoreOptions {
434
- /** Allow use when `NODE_ENV` is `production`. Data is lost on restart. Default false. */
435
- readonly allowInProduction?: boolean;
436
- readonly generateIdentifier?: IdentifierGenerator;
437
- }
438
- /** Transaction handle of {@link InMemoryEventStore}. Writes apply on commit only. */
439
- declare class InMemoryTransaction {
440
- #private;
441
- get state(): 'open' | 'committed' | 'rolled-back';
442
- enqueue(operation: () => void): void;
443
- /** Runs `finisher` once the transaction commits or rolls back, like a lock release. */
444
- onFinish(finisher: () => void): void;
445
- commit(): void;
446
- rollback(): void;
447
- }
448
- /**
449
- * Event store kept in process memory, with the same semantics as the database
450
- * stores: transactional appends, leases, fencing and per-key ordering. Payloads
451
- * are round-tripped through JSON exactly like a database would store them.
452
- * Meant for tests and local development.
453
- */
454
- declare class InMemoryEventStore implements EventStore<InMemoryTransaction> {
455
- #private;
456
- constructor(options?: InMemoryEventStoreOptions);
457
- transaction<T>(work: (transaction: InMemoryTransaction) => Promise<T>): Promise<T>;
458
- append(transaction: InMemoryTransaction, entries: readonly AppendEntry[], now: Date): Promise<AppendResult>;
459
- claim(request: ClaimRequest): Promise<ClaimedDelivery[]>;
460
- extendLease(lease: LeaseReference, leaseExpiresAt: Date, now: Date): Promise<boolean>;
461
- complete(lease: LeaseReference, now: Date, transaction?: InMemoryTransaction): Promise<boolean>;
462
- reschedule(lease: LeaseReference, availableAt: Date, error: ErrorInfo, now: Date): Promise<boolean>;
463
- release(lease: LeaseReference, now: Date): Promise<boolean>;
464
- bury(lease: LeaseReference, error: ErrorInfo, now: Date): Promise<boolean>;
465
- listDeliveries(query: DeliveryQuery): Promise<DeliveryRecord[]>;
466
- requeue(deliveryIds: readonly string[], now: Date): Promise<number>;
467
- purge(request: PurgeRequest): Promise<PurgeResult>;
468
- statistics(now: Date): Promise<EventStoreStatistics>;
469
- }
470
-
471
- type FailureStage = 'claim' | 'dispatch' | 'complete' | 'reschedule' | 'bury' | 'release' | 'extend-lease' | 'purge' | 'observer';
723
+ type FailureStage = 'claim' | 'dispatch' | 'complete' | 'reschedule' | 'bury' | 'release' | 'relinquish' | 'extend-lease' | 'purge' | 'watch' | 'observer';
472
724
  /**
473
725
  * Hooks for logging, metrics and tracing. Every method is optional, runs
474
726
  * synchronously on the hot path and must not throw; errors it throws are
@@ -580,6 +832,15 @@ interface DispatcherOptions {
580
832
  readonly leaseDuration?: Duration;
581
833
  /** How long `stop` waits for running handlers before aborting them. Default `'30s'`. */
582
834
  readonly shutdownTimeout?: Duration;
835
+ /**
836
+ * Longest a finished delivery waits so that deliveries finishing around the
837
+ * same time are completed with one store call (when the store supports it).
838
+ * A delivery that finishes while nothing else runs is completed at once.
839
+ * `0` completes every delivery on its own. Default `'2ms'`.
840
+ */
841
+ readonly completionBatchWindow?: Duration;
842
+ /** Most deliveries completed in one store call. Default 200. */
843
+ readonly completionBatchSize?: number;
583
844
  }
584
845
  interface RetentionOptions {
585
846
  /**
@@ -643,6 +904,16 @@ interface RelayBusOptions<TTransaction = unknown> {
643
904
  readonly clock?: Clock;
644
905
  readonly generateIdentifier?: IdentifierGenerator;
645
906
  readonly random?: () => number;
907
+ /**
908
+ * Catalogs this process is checked against when it starts. `start()` throws a
909
+ * {@link ConfigurationError} listing every event that is not covered:
910
+ * - a catalog: every event needs a handler or a route;
911
+ * - `consume(catalog, { events })`: each declared event needs a local handler;
912
+ * - `produce(catalog, { events })`: each declared event needs a route to a transport.
913
+ *
914
+ * See `registerCatalog`.
915
+ */
916
+ readonly catalogs?: readonly (Catalog | CatalogDeclaration)[];
646
917
  }
647
918
  interface PublishOptions<TTransaction = unknown> {
648
919
  /** Transaction to write into. Default: the current scope, then `transactionResolver`. */
@@ -690,6 +961,38 @@ declare class RelayBus<TTransaction = unknown> {
690
961
  */
691
962
  subscribe<TPayload>(event: EventClass<TPayload>, handler: EventHandler<TPayload, TTransaction>, options: SubscribeOptions): void;
692
963
  subscribe(eventType: string, handler: EventHandler<unknown, TTransaction>, options: SubscribeOptions): void;
964
+ /**
965
+ * Subscribes the `on${EventName}` methods of `handlers` to the events of
966
+ * `catalog`, or to the events named in `options.events`, with the stable
967
+ * handler ids `${prefix}.on${EventName}`. The prefix is `options.idPrefix`,
968
+ * else the catalog name, else the class name of `handlers`. Methods run with
969
+ * `this` bound to `handlers`; methods of events that are not selected are
970
+ * ignored. A missing handler of a selected event fails to compile, and
971
+ * throws {@link ConfigurationError} at runtime, as does an invalid or taken
972
+ * id; handlers registered before the failure stay registered.
973
+ *
974
+ * ```ts
975
+ * bus.subscribeCatalog(OrderEvents, new BillingHandlers(), { events: ['OrderPlaced'] });
976
+ * ```
977
+ */
978
+ subscribeCatalog<TCatalog extends Catalog, const TName extends keyof TCatalog>(catalog: TCatalog, handlers: NoInfer<HandlersOf<TCatalog, TName, TTransaction>>, options: SubscribeCatalogOptions<NoInfer<TCatalog>, TName> & {
979
+ readonly events: readonly TName[];
980
+ }): void;
981
+ /** Without a statically known `events` list, every handler of the catalog is required. */
982
+ subscribeCatalog<TCatalog extends Catalog>(catalog: TCatalog, handlers: HandlersOf<TCatalog, keyof TCatalog, TTransaction>, options?: SubscribeCatalogOptions<NoInfer<TCatalog>>): void;
983
+ /**
984
+ * Adds a catalog, or a `consume` / `produce` declaration, to the checks
985
+ * `start()` runs; `start()` throws a {@link ConfigurationError} naming each
986
+ * event that is not covered and what it lacks. Equivalent to the `catalogs`
987
+ * option.
988
+ */
989
+ registerCatalog(catalog: Catalog | CatalogDeclaration): void;
990
+ /**
991
+ * A view of this bus that publishes only the events of `catalog`, or only
992
+ * the events named in `options.events` (for example the events this service
993
+ * produces). Other events fail to compile and are rejected at runtime.
994
+ */
995
+ forCatalog<TCatalog extends Catalog, const TName extends keyof TCatalog = keyof TCatalog>(catalog: TCatalog, options?: CatalogEventSelection<TName>): CatalogPublisher<TCatalog, TTransaction, TName>;
693
996
  /** Registered handlers and routes, for diagnostics. */
694
997
  handlers(): readonly HandlerDescriptor[];
695
998
  /**
@@ -741,4 +1044,82 @@ declare class RelayBus<TTransaction = unknown> {
741
1044
  purgeExpired(): Promise<PurgeResult>;
742
1045
  }
743
1046
 
744
- export { type AppendEntry, type AppendResult, type ClaimRequest, type ClaimedDelivery, type Clock, ConfigurationError, DEFAULT_RETRY_POLICY, DEFAULT_ROUTE_RETRY_POLICY, DELIVERY_STATUSES, type DefineEventOptions, type DeliveryQuery, type DeliveryRecord, type DeliveryStatus, type DispatcherOptions, DispatcherStoppedError, type DomainEvent, type Duration, type ErrorInfo, type EventClass, type EventEnvelope, type EventHandler, type EventHeaders, type EventReference, type EventStore, type EventStoreStatistics, EventValidationError, type FailureStage, type HandlerContext, type HandlerDescriptor, HandlerTimeoutError, type IdentifierGenerator, InMemoryEventStore, type InMemoryEventStoreOptions, InMemoryTransaction, type InboundSubscriptionOptions, InvalidEnvelopeError, type JitterStrategy, LeaseLostError, type LeaseReference, MAX_TIMER_DELAY_MS, type NewDelivery, NonRetryableError, type PayloadOf, type PublishOneOptions, type PublishOptions, type PurgeRequest, type PurgeResult, type ReceiveStatus, RelayBus, RelayBusError, type RelayBusLogger, type RelayBusObserver, type RelayBusOptions, type RetentionOptions, type RetryPolicy, type RetryPolicyOptions, type RouteOptions, StandardSchemaV1, type SubscribeOptions, type TransactionMode, TransactionRequiredError, type Transport, type TransportMessageHandler, type UnroutedEventPolicy, type ValidationIssue, assertEventType, computeRetryDelay, createRetryPolicy, createUuidV7, defineEvent, isRetryableError, systemClock, toErrorInfo, toMilliseconds, validatePayload };
1047
+ /** The publishing half of a bus, which a {@link CatalogPublisher} delegates to. */
1048
+ interface PublishTarget<TTransaction> {
1049
+ publish<TPayload>(event: DomainEvent<TPayload>, options?: PublishOneOptions<TTransaction>): Promise<EventEnvelope<TPayload>>;
1050
+ publishAll(events: readonly DomainEvent[], options?: PublishOptions<TTransaction>): Promise<EventEnvelope[]>;
1051
+ }
1052
+ /**
1053
+ * A view of a bus that publishes only the events of one catalog, or only the
1054
+ * events a service produces from it. Obtain it with `bus.forCatalog(catalog)`
1055
+ * or `bus.forCatalog(catalog, { events: ['OrderPlaced'] })`. Publishing an
1056
+ * event outside the view fails to compile; a mistyped event that gets past the
1057
+ * compiler (a cast, plain JavaScript, or a class with a structurally identical
1058
+ * payload) is rejected with a {@link ConfigurationError} before anything is stored.
1059
+ *
1060
+ * ```ts
1061
+ * const orders = bus.forCatalog(OrderEvents);
1062
+ * await orders.publish(new OrderPlaced({ orderId }));
1063
+ * await orders.publish(new UserRegistered({ userId })); // compile error
1064
+ * ```
1065
+ */
1066
+ declare class CatalogPublisher<TCatalog extends Catalog, TTransaction = unknown, TName extends keyof TCatalog = keyof TCatalog> {
1067
+ #private;
1068
+ /**
1069
+ * Throws {@link ConfigurationError} when `catalog` was not created with
1070
+ * `defineCatalog` or `options.events` names no event, an unknown event or
1071
+ * the same event twice.
1072
+ */
1073
+ constructor(catalog: TCatalog, target: PublishTarget<TTransaction>, options?: CatalogEventSelection<TName>);
1074
+ /** Same as `bus.publish`, limited to the events of the view. */
1075
+ publish<TEvent extends CatalogEvents<TCatalog, TName>>(event: TEvent, options?: PublishOneOptions<TTransaction>): Promise<EventEnvelope<TEvent['payload']>>;
1076
+ /** Same as `bus.publishAll`, limited to the events of the view. Nothing is stored unless every event belongs to it. */
1077
+ publishAll(events: readonly CatalogEvents<TCatalog, TName>[], options?: PublishOptions<TTransaction>): Promise<EventEnvelope[]>;
1078
+ }
1079
+
1080
+ interface InMemoryEventStoreOptions {
1081
+ /** Allow use when `NODE_ENV` is `production`. Data is lost on restart. Default false. */
1082
+ readonly allowInProduction?: boolean;
1083
+ readonly generateIdentifier?: IdentifierGenerator;
1084
+ /**
1085
+ * Keep a record of completed deliveries (status `completed`, listed, counted
1086
+ * and purged by `retention.completed`). Default false: a completed delivery
1087
+ * is deleted, like in the database stores.
1088
+ */
1089
+ readonly retainCompleted?: boolean;
1090
+ }
1091
+ /** Transaction handle of {@link InMemoryEventStore}. Writes apply on commit only. */
1092
+ declare class InMemoryTransaction {
1093
+ #private;
1094
+ get state(): 'open' | 'committed' | 'rolled-back';
1095
+ enqueue(operation: () => void): void;
1096
+ /** Runs `finisher` once the transaction commits or rolls back, like a lock release. */
1097
+ onFinish(finisher: () => void): void;
1098
+ commit(): void;
1099
+ rollback(): void;
1100
+ }
1101
+ /**
1102
+ * Event store kept in process memory, with the same semantics as the database
1103
+ * stores: transactional appends, leases, fencing and per-key ordering. Payloads
1104
+ * are round-tripped through JSON exactly like a database would store them.
1105
+ * Meant for tests and local development.
1106
+ */
1107
+ declare class InMemoryEventStore implements EventStore<InMemoryTransaction> {
1108
+ #private;
1109
+ constructor(options?: InMemoryEventStoreOptions);
1110
+ transaction<T>(work: (transaction: InMemoryTransaction) => Promise<T>): Promise<T>;
1111
+ append(transaction: InMemoryTransaction, entries: readonly AppendEntry[], now: Date): Promise<AppendResult>;
1112
+ claim(request: ClaimRequest): Promise<ClaimedDelivery[]>;
1113
+ extendLease(lease: LeaseReference, leaseExpiresAt: Date, now: Date): Promise<boolean>;
1114
+ complete(lease: LeaseReference, now: Date, transaction?: InMemoryTransaction): Promise<boolean>;
1115
+ completeMany(leases: readonly LeaseReference[], now: Date): Promise<boolean[]>;
1116
+ reschedule(lease: LeaseReference, availableAt: Date, error: ErrorInfo, now: Date): Promise<boolean>;
1117
+ release(lease: LeaseReference, now: Date): Promise<boolean>;
1118
+ bury(lease: LeaseReference, error: ErrorInfo, now: Date): Promise<boolean>;
1119
+ listDeliveries(query: DeliveryQuery): Promise<DeliveryRecord[]>;
1120
+ requeue(deliveryIds: readonly string[], now: Date): Promise<number>;
1121
+ purge(request: PurgeRequest): Promise<PurgeResult>;
1122
+ statistics(now: Date): Promise<EventStoreStatistics>;
1123
+ }
1124
+
1125
+ export { type AppendEntry, type AppendResult, type Catalog, type CatalogDeclaration, type CatalogDescription, type CatalogEntry, type CatalogEventClass, type CatalogEventName, type CatalogEventSelection, type CatalogEvents, CatalogPublisher, type CatalogRole, type ClaimRequest, type ClaimedDelivery, type Clock, ConfigurationError, DEFAULT_RETRY_POLICY, DEFAULT_ROUTE_RETRY_POLICY, DELIVERY_STATUSES, type DefineCatalogOptions, type DefineEventOptions, type DeliveryQuery, type DeliveryRecord, type DeliveryStatus, type DispatcherOptions, DispatcherStoppedError, type DomainEvent, type Duration, type ErrorInfo, type EventClass, type EventEnvelope, type EventHandler, type EventHeaders, type EventOf, type EventReference, type EventStore, type EventStoreStatistics, EventValidationError, type FailureStage, type HandlerContext, type HandlerDescriptor, type HandlerShapeOf, HandlerTimeoutError, type HandlersOf, type IdentifierGenerator, InMemoryEventStore, type InMemoryEventStoreOptions, InMemoryTransaction, type InboundSubscriptionOptions, InvalidEnvelopeError, type JitterStrategy, LeaseLostError, type LeaseReference, MAX_TIMER_DELAY_MS, type MatchingHandlers, type NewDelivery, type NoExtraHandlers, NonRetryableError, type PartialHandlersOf, type PayloadOf, type PublishOneOptions, type PublishOptions, type PublishTarget, type PurgeRequest, type PurgeResult, type ReceiveStatus, RelayBus, RelayBusError, type RelayBusLogger, type RelayBusObserver, type RelayBusOptions, type RetentionOptions, type RetryPolicy, type RetryPolicyOptions, type RouteOptions, StandardSchemaV1, type SubscribeCatalogOptions, type SubscribeOptions, type TransactionMode, TransactionRequiredError, type Transport, type TransportMessageHandler, type UnroutedEventPolicy, type ValidationIssue, assertEventType, computeRetryDelay, consume, createRetryPolicy, createUuidV7, defineCatalog, defineEvent, defineHandlers, describeCatalog, isRetryableError, produce, systemClock, toErrorInfo, toMilliseconds, validatePayload };