@relaybus/core 0.1.0 → 0.3.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
@@ -1,8 +1,5 @@
1
- /** Source of the current time. Replace it in tests to control retries and leases. */
2
- interface Clock {
3
- now(): Date;
4
- }
5
- declare const systemClock: Clock;
1
+ import { E as EventEnvelope, D as DomainEvent, S as StandardSchemaV1, P as PayloadOf, a as EventHeaders, b as EventReference, C as Clock, c as EventClass } from './event-DCRTuYKI.cjs';
2
+ export { d as DefineEventKeyOptions, e as DefineEventOptions, f as EventKeyFunction, g as assertEventType, h as defineEvent, s as systemClock, v as validatePayload } from './event-DCRTuYKI.cjs';
6
3
 
7
4
  /**
8
5
  * A duration in milliseconds, or a string such as `'250ms'`, `'30s'`, `'5m'`,
@@ -14,15 +11,369 @@ declare function toMilliseconds(value: Duration, name: string): number;
14
11
  /** Largest delay Node.js timers support. Longer values fire after 1ms instead. */
15
12
  declare const MAX_TIMER_DELAY_MS = 2147483647;
16
13
 
14
+ /**
15
+ * How the delay between attempts is randomized.
16
+ * - `full`: uniform in [0, delay]. Spreads retries the most (AWS recommendation).
17
+ * - `equal`: uniform in [delay / 2, delay].
18
+ * - `none`: exactly `delay`. Synchronized retries can stampede a recovering dependency.
19
+ */
20
+ type JitterStrategy = 'full' | 'equal' | 'none';
21
+ interface RetryPolicyOptions {
22
+ /**
23
+ * Total attempts including the first one. Default 10 for handlers, which
24
+ * gives up after roughly 17 minutes of failures with the default delays.
25
+ * `Infinity` retries until the delivery succeeds.
26
+ */
27
+ readonly attempts?: number;
28
+ /** Delay before the second attempt. Default `'1s'`. */
29
+ readonly initialDelay?: Duration;
30
+ /** Growth factor of the delay per attempt. Default 2. */
31
+ readonly factor?: number;
32
+ /** Upper bound of a single delay. Default `'10m'`. */
33
+ readonly maxDelay?: Duration;
34
+ /** Default `'full'`. */
35
+ readonly jitter?: JitterStrategy;
36
+ /** Return false to dead-letter an error without retrying it. */
37
+ readonly retryIf?: (error: unknown) => boolean;
38
+ }
39
+ interface RetryPolicy {
40
+ readonly attempts: number;
41
+ readonly initialDelayMs: number;
42
+ readonly factor: number;
43
+ readonly maxDelayMs: number;
44
+ readonly jitter: JitterStrategy;
45
+ readonly retryIf: ((error: unknown) => boolean) | undefined;
46
+ }
47
+ /** Default policy of handlers. */
48
+ declare const DEFAULT_RETRY_POLICY: RetryPolicy;
49
+ /**
50
+ * Default policy of routes to transports: a broker outage must only delay
51
+ * events, so routes retry until the broker accepts them, at most every 5 minutes.
52
+ */
53
+ declare const DEFAULT_ROUTE_RETRY_POLICY: RetryPolicy;
54
+ declare function createRetryPolicy(options: RetryPolicyOptions | undefined, base?: RetryPolicy): RetryPolicy;
55
+ /**
56
+ * Delay before the next attempt after `failedAttempts` failures (1-based):
57
+ * capped exponential backoff with the policy's jitter.
58
+ */
59
+ declare function computeRetryDelay(policy: RetryPolicy, failedAttempts: number, random?: () => number): number;
60
+ declare function isRetryableError(policy: RetryPolicy, error: unknown): boolean;
61
+
62
+ /** Passed to every handler call. */
63
+ interface HandlerContext<TTransaction = unknown> {
64
+ readonly deliveryId: string;
65
+ readonly handler: string;
66
+ /** Attempt number, starting at 1. */
67
+ readonly attempt: number;
68
+ readonly maxAttempts: number;
69
+ /** Aborted when the handler times out, the lease is lost or the bus stops. */
70
+ readonly signal: AbortSignal;
71
+ /**
72
+ * Transaction the delivery completes in, for handlers subscribed with
73
+ * `transactional: true`. Write through it to make the handler's effects and
74
+ * the completion atomic. Undefined otherwise.
75
+ */
76
+ readonly transaction: TTransaction | undefined;
77
+ }
78
+ type EventHandler<TPayload = unknown, TTransaction = unknown> = (event: EventEnvelope<TPayload>, context: HandlerContext<TTransaction>) => unknown;
79
+ interface SubscribeOptions {
80
+ /**
81
+ * Stable id of the handler. Deliveries are recorded under it, so renaming it
82
+ * orphans pending deliveries. Letters, digits and `._:/-`.
83
+ */
84
+ readonly id: string;
85
+ /** Overrides the bus retry policy field by field. */
86
+ readonly retry?: RetryPolicyOptions;
87
+ /** Abort and retry when a call takes longer. `false` disables the bus default. */
88
+ readonly timeout?: Duration | false;
89
+ /** Maximum concurrent deliveries of this handler in this process. */
90
+ readonly concurrency?: number;
91
+ /**
92
+ * Run the handler inside a store transaction that also completes the
93
+ * delivery: its writes and the completion commit or roll back together,
94
+ * so a handler that writes only through `context.transaction` (or through
95
+ * events it publishes) takes effect exactly once.
96
+ *
97
+ * The lease is not renewed while the transaction is open. A transactional
98
+ * handler must finish within the lease duration (default 30s), or within its
99
+ * `timeout`, which extends the lease once to cover it; otherwise it is aborted.
100
+ */
101
+ readonly transactional?: boolean;
102
+ }
103
+ /** Public description of a registered handler or route. */
104
+ interface HandlerDescriptor {
105
+ readonly id: string;
106
+ readonly eventType: string;
107
+ readonly retry: RetryPolicy;
108
+ readonly timeoutMs: number | null;
109
+ readonly concurrency: number | null;
110
+ readonly transactional: boolean;
111
+ /** True for routes that forward events to a transport. */
112
+ readonly forwardsToTransport: boolean;
113
+ }
114
+
115
+ /**
116
+ * An event class as a catalog stores it: any class created with `defineEvent`,
117
+ * whatever its payload type. Use {@link PayloadOf} to recover the payload.
118
+ */
119
+ interface CatalogEventClass {
120
+ new (payload: never): DomainEvent;
121
+ readonly type: string;
122
+ readonly schema: StandardSchemaV1 | undefined;
123
+ }
124
+ /**
125
+ * The shape of a catalog: event classes by name. Create one with
126
+ * {@link defineCatalog}; the name of each entry becomes part of the handler
127
+ * method that consumes it (`OrderPlaced` is consumed by `onOrderPlaced`).
128
+ */
129
+ interface Catalog {
130
+ readonly [eventName: string]: CatalogEventClass;
131
+ }
132
+ /** Names of the events of a catalog, as string literals. */
133
+ type CatalogEventName<TCatalog extends Catalog> = Extract<keyof TCatalog, string>;
134
+ /**
135
+ * Union of the event instances a catalog can publish, for example
136
+ * `OrderPlaced | OrderShipped`; with `TName`, only those of the named events.
137
+ */
138
+ type CatalogEvents<TCatalog extends Catalog, TName extends keyof TCatalog = keyof TCatalog> = InstanceType<TCatalog[TName & CatalogEventName<TCatalog>]>;
139
+ /**
140
+ * Envelope a handler receives for an event class:
141
+ * `EventOf<typeof OrderPlaced>` is `EventEnvelope<{ orderId: string }>`.
142
+ */
143
+ type EventOf<TEvent> = EventEnvelope<PayloadOf<TEvent>>;
144
+ /**
145
+ * Same as {@link HandlersOf} with a required list of events:
146
+ * `PartialHandlersOf<typeof OrderEvents, 'OrderPlaced'>` equals
147
+ * `HandlersOf<typeof OrderEvents, 'OrderPlaced'>`.
148
+ *
149
+ * ```ts
150
+ * class AuditHandlers implements PartialHandlersOf<typeof OrderEvents, 'OrderPlaced'> {
151
+ * onOrderPlaced(event: EventOf<typeof OrderPlaced>) {}
152
+ * }
153
+ * ```
154
+ */
155
+ type PartialHandlersOf<TCatalog extends Catalog, TName extends keyof TCatalog, TTransaction = unknown> = {
156
+ readonly [TKey in TName & string as `on${TKey}`]: (event: EventOf<TCatalog[TKey]>, context: HandlerContext<TTransaction>) => unknown;
157
+ };
158
+ /**
159
+ * Handler methods for the events of a catalog: one `on${EventName}` method per
160
+ * event, taking that event's envelope. A class that
161
+ * `implements HandlersOf<typeof OrderEvents>` fails to compile until it has a
162
+ * method with the right payload type for each event. `TName` narrows it to the
163
+ * events a service consumes, which is how a service handles part of a catalog
164
+ * shared between services:
165
+ *
166
+ * ```ts
167
+ * class BillingHandlers implements HandlersOf<typeof OrderEvents, 'OrderPlaced'> {
168
+ * onOrderPlaced(event: EventOf<typeof OrderPlaced>) {}
169
+ * }
170
+ * ```
171
+ *
172
+ * `TTransaction` types `context.transaction` for transactional handlers.
173
+ */
174
+ type HandlersOf<TCatalog extends Catalog, TName extends keyof TCatalog = keyof TCatalog, TTransaction = unknown> = PartialHandlersOf<TCatalog, TName, TTransaction>;
175
+ /**
176
+ * Names of catalog events that an API call is limited to. Every name must be a
177
+ * key of the catalog, which the compiler checks; duplicates and an empty list
178
+ * are rejected at runtime with a {@link ConfigurationError}. Omitting the list
179
+ * means every event of the catalog.
180
+ */
181
+ interface CatalogEventSelection<TName> {
182
+ readonly events?: readonly TName[];
183
+ }
184
+ /** Options of {@link defineCatalog}. */
185
+ interface DefineCatalogOptions {
186
+ /**
187
+ * Name of the catalog, used in boot-check messages and as the default prefix
188
+ * of the handler ids that `subscribeCatalog` registers.
189
+ */
190
+ readonly name?: string;
191
+ }
192
+ /**
193
+ * Options of `RelayBus.subscribeCatalog`: {@link SubscribeOptions} for every
194
+ * handler of the catalog, and optionally the events to subscribe to. The plain
195
+ * `SubscribeCatalogOptions`, without a catalog, names no events, so it fits
196
+ * every catalog.
197
+ */
198
+ type SubscribeCatalogOptions<TCatalog extends Catalog = Catalog, TName extends keyof TCatalog = string extends keyof TCatalog ? never : keyof TCatalog> = Omit<SubscribeOptions, 'id'> & CatalogEventSelection<TName> & {
199
+ /**
200
+ * Prefix of the handler ids, which become `${idPrefix}.on${EventName}`.
201
+ * Default: the catalog name, then the class name of the handlers object.
202
+ * Deliveries are recorded under these ids, so renaming them orphans pending
203
+ * deliveries.
204
+ */
205
+ readonly idPrefix?: string;
206
+ };
207
+ /** One event of a catalog together with the handler method that consumes it. */
208
+ interface CatalogEntry {
209
+ /** Name under which the class was registered, for example `OrderPlaced`. */
210
+ readonly name: string;
211
+ readonly eventClass: CatalogEventClass;
212
+ /** Event type string, for example `order.placed.v1`. */
213
+ readonly type: string;
214
+ /** Expected handler method, for example `onOrderPlaced`. */
215
+ readonly handlerName: string;
216
+ }
217
+ /** What {@link describeCatalog} knows about a catalog. */
218
+ interface CatalogDescription {
219
+ readonly name: string | undefined;
220
+ readonly entries: readonly CatalogEntry[];
221
+ }
222
+ /**
223
+ * Groups event classes into a catalog: the complete set of events one service
224
+ * or domain owns. The catalog is frozen and keeps its keys and classes in its
225
+ * type, which enables compile-time checks:
226
+ *
227
+ * ```ts
228
+ * export const OrderEvents = defineCatalog({ OrderPlaced, OrderShipped }, { name: 'OrderEvents' });
229
+ *
230
+ * class ShippingHandlers implements HandlersOf<typeof OrderEvents> {
231
+ * onOrderPlaced(event: EventOf<typeof OrderPlaced>) {}
232
+ * // Compile error until onOrderShipped is added.
233
+ * }
234
+ * ```
235
+ *
236
+ * Throws {@link ConfigurationError} for an empty catalog, an entry that is not
237
+ * an event class, a name that is not made of letters, digits and underscores,
238
+ * or two entries with the same event type.
239
+ */
240
+ declare function defineCatalog<const TCatalog extends Catalog>(events: TCatalog, options?: DefineCatalogOptions): Readonly<TCatalog>;
241
+ /**
242
+ * Describes a catalog created by {@link defineCatalog}. The description and its
243
+ * entries are frozen. Throws {@link ConfigurationError} for any other object,
244
+ * so a hand-written record cannot slip past the checks that rely on it.
245
+ */
246
+ declare function describeCatalog(catalog: Catalog): CatalogDescription;
247
+ /**
248
+ * The handler signature of one catalog event, written as a method so that its
249
+ * parameters are compared bivariantly: a handler may annotate its context as
250
+ * `HandlerContext<PoolClient>` and still satisfy it. {@link MatchingHandlers}
251
+ * restores the strict check of the event parameter.
252
+ */
253
+ type BivariantHandler<TEvent> = {
254
+ handle(event: EventOf<TEvent>, context: HandlerContext): unknown;
255
+ }['handle'];
256
+ /**
257
+ * The shape {@link defineHandlers} constrains its handlers to: one method per
258
+ * selected event, with the context typed loosely enough for any transaction type.
259
+ */
260
+ type HandlerShapeOf<TCatalog extends Catalog, TName extends keyof TCatalog> = {
261
+ readonly [TKey in TName & string as `on${TKey}`]: BivariantHandler<TCatalog[TKey]>;
262
+ };
263
+ /**
264
+ * Checks every key of `THandlers` against the selected events of a catalog:
265
+ * keys that are no handler of a selected event become `never`, and a handler
266
+ * whose event parameter does not accept the event's envelope becomes the
267
+ * expected signature, so the compiler reports the mismatch at that key.
268
+ */
269
+ type MatchingHandlers<TCatalog extends Catalog, TName extends keyof TCatalog, THandlers> = {
270
+ 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;
271
+ };
272
+ /**
273
+ * Declares the handlers of a catalog as an object: every event, or the events
274
+ * named in `options.events`. Compilation fails when a handler is missing,
275
+ * misnamed, extra (including a handler of an event that is not selected) or
276
+ * takes the wrong payload; the same checks run at runtime for callers without
277
+ * type checking. A handler may type its context with a transaction type, for
278
+ * example `HandlerContext<PoolClient>`.
279
+ *
280
+ * ```ts
281
+ * const handlers = defineHandlers(OrderEvents, {
282
+ * onOrderPlaced: (event) => reserveStock(event.payload.orderId),
283
+ * onOrderShipped: (event) => notify(event.payload.orderId),
284
+ * });
285
+ * bus.subscribeCatalog(OrderEvents, handlers, { idPrefix: 'fulfilment' });
286
+ *
287
+ * const billing = defineHandlers(
288
+ * OrderEvents,
289
+ * { onOrderPlaced: (event) => charge(event.payload.orderId) },
290
+ * { events: ['OrderPlaced'] },
291
+ * );
292
+ * bus.subscribeCatalog(OrderEvents, billing, { events: ['OrderPlaced'] });
293
+ * ```
294
+ */
295
+ 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: {
296
+ readonly events: readonly TName[];
297
+ }): THandlers;
298
+ /** Without `events`, every handler of the catalog is required. */
299
+ declare function defineHandlers<TCatalog extends Catalog, THandlers extends HandlerShapeOf<TCatalog, keyof TCatalog>>(catalog: TCatalog, handlers: THandlers & MatchingHandlers<TCatalog, keyof TCatalog, THandlers>, options?: {
300
+ readonly events?: undefined;
301
+ }): THandlers;
302
+ /**
303
+ * Maps every key of `THandlers` that is not a handler of the selected events
304
+ * of the catalog (default: all of them) to `never`.
305
+ */
306
+ type NoExtraHandlers<TCatalog extends Catalog, THandlers, TName extends keyof TCatalog = keyof TCatalog> = {
307
+ readonly [TKey in Exclude<keyof THandlers, keyof HandlersOf<TCatalog, TName>>]: never;
308
+ };
309
+
310
+ /**
311
+ * What a service does with the events of a catalog:
312
+ * - `consume`: it handles them, so each needs a local handler.
313
+ * - `produce`: it publishes them for other services, so each needs a route to a transport.
314
+ */
315
+ type CatalogRole = 'consume' | 'produce';
316
+ /**
317
+ * The part of a catalog that one service consumes or produces, created with
318
+ * {@link consume} or {@link produce} and passed to the `catalogs` option of the
319
+ * bus (or `bus.registerCatalog`). `start()` then verifies the role for each
320
+ * declared event.
321
+ */
322
+ interface CatalogDeclaration<TCatalog extends Catalog = Catalog, TName extends keyof TCatalog = keyof TCatalog> {
323
+ readonly catalog: TCatalog;
324
+ readonly role: CatalogRole;
325
+ /** Names of the declared events, in catalog order. */
326
+ readonly events: readonly Extract<TName, string>[];
327
+ /** The declared events with their types and handler methods, in catalog order. */
328
+ readonly entries: readonly CatalogEntry[];
329
+ }
330
+ /**
331
+ * Declares that this service consumes events of a catalog, typically one
332
+ * shared between services through a contracts package. `start()` fails unless
333
+ * every declared event has a local handler (`subscribeCatalog`, `subscribe`,
334
+ * `@RelayBusCatalogConsumer`, `@RelayBusConsumer`); a route to a transport does not count.
335
+ * Without `events`, every event of the catalog is declared. Unknown names do
336
+ * not compile and are rejected at runtime with a {@link ConfigurationError}.
337
+ *
338
+ * ```ts
339
+ * new RelayBus({ store, catalogs: [consume(OrderEvents, { events: ['OrderPlaced'] })] });
340
+ * ```
341
+ */
342
+ declare function consume<TCatalog extends Catalog, const TName extends keyof TCatalog = keyof TCatalog>(catalog: TCatalog, options?: CatalogEventSelection<TName>): CatalogDeclaration<TCatalog, TName>;
343
+ /**
344
+ * Declares that this service publishes events of a catalog for other services.
345
+ * `start()` fails unless every declared event has a route to a transport, so
346
+ * nothing is published without a destination; a local handler does not count.
347
+ * Combine it with {@link consume} for events the service also handles itself.
348
+ * Without `events`, every event of the catalog is declared.
349
+ *
350
+ * ```ts
351
+ * new RelayBus({
352
+ * store,
353
+ * transports: [kafka],
354
+ * routes: [{ event: OrderPlaced, transport: 'kafka', destination: 'orders' }],
355
+ * catalogs: [produce(OrderEvents, { events: ['OrderPlaced'] })],
356
+ * });
357
+ * ```
358
+ */
359
+ declare function produce<TCatalog extends Catalog, const TName extends keyof TCatalog = keyof TCatalog>(catalog: TCatalog, options?: CatalogEventSelection<TName>): CatalogDeclaration<TCatalog, TName>;
360
+
17
361
  /**
18
362
  * Base class of every error thrown by relaybus.
19
363
  */
20
364
  declare class RelayBusError extends Error {
21
365
  name: string;
366
+ /**
367
+ * Stable, machine-readable identifier of the error class, such as `RELAYBUS_LEASE_LOST`.
368
+ * Unlike `name` and `message`, a code is never renamed once released; branch on it in
369
+ * alerts and dashboards. Subclasses declared outside relaybus inherit `RELAYBUS_ERROR`.
370
+ */
371
+ readonly code: string;
22
372
  }
23
373
  /** Invalid options passed to a relaybus component. */
24
374
  declare class ConfigurationError extends RelayBusError {
25
375
  name: string;
376
+ readonly code: string;
26
377
  }
27
378
  /**
28
379
  * Thrown by `publish` when no transaction can be found and the bus runs with
@@ -31,6 +382,16 @@ declare class ConfigurationError extends RelayBusError {
31
382
  */
32
383
  declare class TransactionRequiredError extends RelayBusError {
33
384
  name: string;
385
+ readonly code: string;
386
+ }
387
+ /**
388
+ * A `bus.transaction` (or `@RelayBusTransaction()` method) ran past its `transactionTimeout` and
389
+ * was rolled back. Its `cause` is the error the database raised when it ended the transaction,
390
+ * when there was one. Usually a slow call to another service made inside the transaction.
391
+ */
392
+ declare class TransactionTimeoutError extends RelayBusError {
393
+ name: string;
394
+ readonly code: string;
34
395
  }
35
396
  /**
36
397
  * Throw from a handler to dead-letter the delivery immediately, skipping the
@@ -38,11 +399,13 @@ declare class TransactionRequiredError extends RelayBusError {
38
399
  */
39
400
  declare class NonRetryableError extends RelayBusError {
40
401
  name: string;
402
+ readonly code: string;
41
403
  }
42
404
  /** A payload failed its event schema. Never retried: the payload will not change. */
43
405
  declare class EventValidationError extends NonRetryableError {
44
406
  readonly issues: readonly ValidationIssue[];
45
407
  name: string;
408
+ readonly code: string;
46
409
  constructor(message: string, issues: readonly ValidationIssue[]);
47
410
  }
48
411
  /**
@@ -51,14 +414,25 @@ declare class EventValidationError extends NonRetryableError {
51
414
  */
52
415
  declare class InvalidEnvelopeError extends NonRetryableError {
53
416
  name: string;
417
+ readonly code: string;
54
418
  }
55
419
  /** The worker no longer owns the delivery it was processing. */
56
420
  declare class LeaseLostError extends RelayBusError {
57
421
  name: string;
422
+ readonly code: string;
423
+ /**
424
+ * Whether the store said so (a fenced write returned false). A worker that only noticed
425
+ * locally that its lease ran out may still own the delivery in the store and has to give it back.
426
+ */
427
+ readonly storeConfirmed: boolean;
428
+ constructor(message: string, options?: {
429
+ readonly storeConfirmed?: boolean;
430
+ });
58
431
  }
59
432
  /** A handler did not settle within its configured timeout. Retried like any failure. */
60
433
  declare class HandlerTimeoutError extends RelayBusError {
61
434
  name: string;
435
+ readonly code: string;
62
436
  }
63
437
  /**
64
438
  * Reason of a handler's abort signal when the bus stops before the handler
@@ -66,6 +440,25 @@ declare class HandlerTimeoutError extends RelayBusError {
66
440
  */
67
441
  declare class DispatcherStoppedError extends RelayBusError {
68
442
  name: string;
443
+ readonly code: string;
444
+ }
445
+ /**
446
+ * A delivery came back after its lease expired on its last attempt, so it is
447
+ * dead-lettered without running its handler again.
448
+ */
449
+ declare class AttemptsExhaustedError extends RelayBusError {
450
+ name: string;
451
+ readonly code: string;
452
+ }
453
+ /** A lifecycle method was called in a state that does not allow it, such as starting a bus twice. */
454
+ declare class LifecycleError extends RelayBusError {
455
+ name: string;
456
+ readonly code: string;
457
+ }
458
+ /** `drain` still found due deliveries after its maximum number of batches. */
459
+ declare class DrainIncompleteError extends RelayBusError {
460
+ name: string;
461
+ readonly code: string;
69
462
  }
70
463
  interface ValidationIssue {
71
464
  readonly message: string;
@@ -76,110 +469,20 @@ interface ErrorInfo {
76
469
  readonly name: string;
77
470
  readonly message: string;
78
471
  readonly stack?: string | undefined;
472
+ /** The {@link RelayBusError.code} of a relaybus error; absent for any other error. */
473
+ readonly code?: string | undefined;
79
474
  }
80
475
  declare function toErrorInfo(error: unknown): ErrorInfo;
81
476
 
82
- /**
83
- * The Standard Schema interface (https://standardschema.dev), copied as the
84
- * specification recommends so relaybus needs no runtime dependency. Zod,
85
- * Valibot, ArkType and other libraries implement it.
86
- */
87
- interface StandardSchemaV1<Input = unknown, Output = Input> {
88
- readonly '~standard': StandardSchemaV1.Props<Input, Output>;
89
- }
90
- declare namespace StandardSchemaV1 {
91
- interface Props<Input = unknown, Output = Input> {
92
- readonly version: 1;
93
- readonly vendor: string;
94
- readonly validate: (value: unknown) => Result<Output> | Promise<Result<Output>>;
95
- readonly types?: Types<Input, Output> | undefined;
96
- }
97
- type Result<Output> = SuccessResult<Output> | FailureResult;
98
- interface SuccessResult<Output> {
99
- readonly value: Output;
100
- readonly issues?: undefined;
101
- }
102
- interface FailureResult {
103
- readonly issues: ReadonlyArray<Issue>;
104
- }
105
- interface Issue {
106
- readonly message: string;
107
- readonly path?: ReadonlyArray<PropertyKey | PathSegment> | undefined;
108
- }
109
- interface PathSegment {
110
- readonly key: PropertyKey;
111
- }
112
- interface Types<Input = unknown, Output = Input> {
113
- readonly input: Input;
114
- readonly output: Output;
115
- }
116
- type InferInput<Schema extends StandardSchemaV1> = NonNullable<Schema['~standard']['types']>['input'];
117
- type InferOutput<Schema extends StandardSchemaV1> = NonNullable<Schema['~standard']['types']>['output'];
118
- }
119
-
120
- /** String metadata carried with an event, such as `traceparent`. */
121
- type EventHeaders = Readonly<Record<string, string>>;
122
- /**
123
- * An event as stored and delivered. Field names follow CloudEvents where they
124
- * overlap (`id`, `type`, `source`, `time` as `occurredAt`, `subject` as `key`).
125
- */
126
- interface EventEnvelope<TPayload = unknown> {
127
- /** Globally unique id. Handlers and transports deduplicate on it. */
128
- readonly id: string;
129
- /** Event type, for example `order.created.v1`. */
130
- readonly type: string;
131
- /** Logical name of the publishing service. */
132
- readonly source: string;
133
- /** Ordering and partition key. Events sharing a key are handled in publish order. */
134
- readonly key: string | null;
135
- readonly payload: TPayload;
136
- readonly headers: EventHeaders;
137
- /** Id shared by every event of one business flow. */
138
- readonly correlationId: string | null;
139
- /** Id of the event whose handler published this one. */
140
- readonly causationId: string | null;
141
- readonly occurredAt: Date;
142
- }
143
- /** An instance of a class created with {@link defineEvent}. */
144
- interface DomainEvent<TPayload = unknown> {
145
- readonly type: string;
146
- readonly payload: TPayload;
147
- }
148
- /** A class created with {@link defineEvent}. */
149
- interface EventClass<TPayload = unknown> {
150
- new (payload: TPayload): DomainEvent<TPayload>;
151
- readonly type: string;
152
- readonly schema: StandardSchemaV1<unknown, TPayload> | undefined;
153
- }
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;
163
- }
164
- declare function assertEventType(type: string): void;
165
- /**
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
- * ```
172
- */
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>;
177
-
178
477
  /**
179
478
  * Lifecycle of one handler's delivery of one event.
180
479
  * `pending` (waiting or scheduled for retry) -> `processing` (leased by a worker)
181
480
  * -> `completed` | `pending` (retry) | `dead` (exhausted or non-retryable).
182
481
  * `dead` -> `pending` only through an explicit requeue.
482
+ *
483
+ * A completed delivery is deleted. `completed` records exist only in stores
484
+ * configured to keep a completion history (`retainCompleted` /
485
+ * `completionHistory`), where they are listed, counted and purged like before.
183
486
  */
184
487
  type DeliveryStatus = 'pending' | 'processing' | 'completed' | 'dead';
185
488
  declare const DELIVERY_STATUSES: readonly DeliveryStatus[];
@@ -205,6 +508,11 @@ interface AppendResult {
205
508
  * Lease expiry is compared with the `now` the caller passes, taken from the
206
509
  * bus clock. Keep worker clocks synchronized (NTP): a skew larger than a third
207
510
  * of the lease duration lets another worker reclaim a delivery that is still running.
511
+ *
512
+ * Stores may grant the lease on a group of deliveries (a lane) instead of on
513
+ * each delivery. The group is exclusive to `leaseOwner` while the lease is
514
+ * live, so other owners do not see its pending deliveries until it expires or
515
+ * is relinquished; every delivery of the group carries the group's fencing token.
208
516
  */
209
517
  interface ClaimRequest {
210
518
  /** Only deliveries of these handlers are claimed. */
@@ -271,6 +579,26 @@ interface EventStoreStatistics {
271
579
  /** `availableAt` of the oldest delivery that is due but not yet processed. */
272
580
  readonly oldestDueAt: Date | null;
273
581
  }
582
+ /** How `stop` closes a transport or a store. */
583
+ interface CloseOptions {
584
+ /**
585
+ * Aborted when the shutdown deadline passes (`bus.stop({ timeout })`), possibly before `close` is
586
+ * called. From then on, close what you opened yourself at once (destroy connections, cancel
587
+ * running queries) instead of waiting for it to finish; the bus no longer waits for `close`.
588
+ * Leave alone what the application passed in.
589
+ */
590
+ readonly signal?: AbortSignal;
591
+ }
592
+ /** How a store runs one transaction of `bus.transaction`. */
593
+ interface EventStoreTransactionOptions {
594
+ /**
595
+ * Time budget of the whole transaction, in milliseconds. A store makes the database enforce it
596
+ * where it can (for example a statement or idle-in-transaction timeout), so a transaction that
597
+ * waits on something slow is ended by the server and its locks are released. The bus rolls back
598
+ * a transaction that still ran past the budget, so a store may also ignore it.
599
+ */
600
+ readonly timeoutMs: number;
601
+ }
274
602
  /**
275
603
  * Storage port. Implementations persist events and deliveries in the same
276
604
  * database as the application's business data so that both commit atomically.
@@ -280,8 +608,11 @@ interface EventStoreStatistics {
280
608
  interface EventStore<TTransaction = unknown> {
281
609
  /** Creates tables, collections and indexes when they do not exist. */
282
610
  setup?(): Promise<void>;
283
- /** Runs `work` inside a new database transaction and commits when it resolves. */
284
- transaction<T>(work: (transaction: TTransaction) => Promise<T>): Promise<T>;
611
+ /**
612
+ * Runs `work` inside a new database transaction and commits when it resolves. `options` come
613
+ * from `bus.transaction`; the store's own transactions and the dispatcher's pass none.
614
+ */
615
+ transaction<T>(work: (transaction: TTransaction) => Promise<T>, options?: EventStoreTransactionOptions): Promise<T>;
285
616
  /**
286
617
  * Writes events and their deliveries inside `transaction`. Idempotent on
287
618
  * event id and on (event id, handler). Assigns a per-key sequence to keyed
@@ -292,14 +623,18 @@ interface EventStore<TTransaction = unknown> {
292
623
  * Leases up to `limit` deliveries that are due: pending with `availableAt <= now`,
293
624
  * or processing with an expired lease. A keyed delivery is only claimable when
294
625
  * 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.
626
+ * Counts the attempt (`attempt` of the result; a store may persist the count
627
+ * only when the attempt fails or its lease is lost). Safe to call from many
628
+ * processes at once.
296
629
  */
297
630
  claim(request: ClaimRequest): Promise<ClaimedDelivery[]>;
298
631
  /** Extends a lease the caller still holds. Returns false when the lease was lost. */
299
632
  extendLease(lease: LeaseReference, leaseExpiresAt: Date, now: Date): Promise<boolean>;
300
633
  /**
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.
634
+ * Marks a leased delivery completed by deleting it (or moving it to the
635
+ * completion history, when the store keeps one). With `transaction` the change
636
+ * commits together with the handler's own writes. Returns false when the lease
637
+ * was lost.
303
638
  */
304
639
  complete(lease: LeaseReference, now: Date, transaction?: TTransaction): Promise<boolean>;
305
640
  /** Returns a leased delivery to pending, due at `availableAt`. */
@@ -309,6 +644,14 @@ interface EventStore<TTransaction = unknown> {
309
644
  * now, attempts decremented. Used when a handler is aborted at shutdown.
310
645
  */
311
646
  release(lease: LeaseReference, now: Date): Promise<boolean>;
647
+ /**
648
+ * Marks many leased deliveries completed with one round trip. Returns, for
649
+ * every lease in the same order, whether it was completed (false: lease lost
650
+ * or already finished). Each lease is fenced individually, exactly as in
651
+ * {@link EventStore.complete}. Optional: the dispatcher falls back to
652
+ * `complete` when a store does not implement it.
653
+ */
654
+ completeMany?(leases: readonly LeaseReference[], now: Date): Promise<boolean[]>;
312
655
  /** Moves a leased delivery to dead. */
313
656
  bury(lease: LeaseReference, error: ErrorInfo, now: Date): Promise<boolean>;
314
657
  listDeliveries(query: DeliveryQuery): Promise<DeliveryRecord[]>;
@@ -316,159 +659,195 @@ interface EventStore<TTransaction = unknown> {
316
659
  requeue(deliveryIds: readonly string[], now: Date): Promise<number>;
317
660
  purge(request: PurgeRequest): Promise<PurgeResult>;
318
661
  statistics(now: Date): Promise<EventStoreStatistics>;
319
- /** Releases connections the store opened itself. */
320
- close?(): Promise<void>;
662
+ /**
663
+ * Hands back every lease group that `leaseOwner` holds without running work,
664
+ * so other workers can take them at once instead of after lease expiry.
665
+ * Called at graceful shutdown after the owner's deliveries settled. Groups
666
+ * with running deliveries stay leased. Optional.
667
+ */
668
+ relinquish?(leaseOwner: string, now: Date): Promise<void>;
669
+ /**
670
+ * Calls `listener` soon after a transaction that added deliveries due at once has committed, in this
671
+ * process or in any other one, so an idle dispatcher claims them without waiting for its next poll.
672
+ * A hint, never a guarantee: a report may be late, merged with others, or lost (while a connection
673
+ * is re-established, for instance), and polling still finds every delivery. Resolves with a function
674
+ * that stops listening, or with `undefined` when this store cannot listen. Optional.
675
+ */
676
+ watchNewDeliveries?(listener: () => void): Promise<(() => Promise<void>) | undefined>;
677
+ /** Releases connections the store opened itself; forced once `options.signal` aborts. */
678
+ close?(options?: CloseOptions): Promise<void>;
321
679
  }
322
680
 
323
681
  /**
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.
682
+ * RFC 9562 UUID version 7: 48-bit Unix millisecond timestamp followed by random
683
+ * bits. Time-ordered, so it keeps B-tree indexes compact, and globally unique,
684
+ * so it is safe as an idempotency key across services.
328
685
  */
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;
686
+ declare function createUuidV7(timestamp?: number): string;
687
+ /** Produces unique identifiers for events, deliveries and lease tokens. */
688
+ type IdentifierGenerator = () => string;
689
+
690
+ /** Event header that carries the W3C trace context (`version-traceid-parentid-flags`). */
691
+ declare const TRACEPARENT_HEADER = "traceparent";
692
+ /** Event header that carries the vendor part of the W3C trace context. */
693
+ declare const TRACESTATE_HEADER = "tracestate";
694
+ /** A W3C Trace Context as it travels in event headers. */
695
+ interface TraceContext {
696
+ readonly traceparent: string;
697
+ readonly tracestate?: string;
355
698
  }
356
- /** Default policy of handlers. */
357
- declare const DEFAULT_RETRY_POLICY: RetryPolicy;
358
699
  /**
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.
700
+ * The W3C trace context an event carries in its headers, or undefined when it has none or a
701
+ * malformed one. A handler or a consumer span continues the publisher's trace from it.
702
+ *
703
+ * ```ts
704
+ * bus.subscribe(OrderPlaced, (event) => startSpan({ parent: traceContextOf(event.headers) }), ...)
705
+ * ```
361
706
  */
362
- declare const DEFAULT_ROUTE_RETRY_POLICY: RetryPolicy;
363
- declare function createRetryPolicy(options: RetryPolicyOptions | undefined, base?: RetryPolicy): RetryPolicy;
707
+ declare function traceContextOf(headers: EventHeaders): TraceContext | undefined;
708
+ /** A `publish` or `publishAll` call. */
709
+ interface PublishTraceInput {
710
+ /** The type of every event of the call, in order. */
711
+ readonly eventTypes: readonly string[];
712
+ }
713
+ /** A route sending one event to its transport. */
714
+ interface SendTraceInput {
715
+ readonly envelope: EventEnvelope;
716
+ readonly transport: string;
717
+ /** The messaging system of the transport (`kafka`, `rabbitmq`), else its name. */
718
+ readonly system: string;
719
+ readonly destination: string;
720
+ }
721
+ /** A local handler running for one claimed delivery. */
722
+ interface ProcessTraceInput {
723
+ readonly delivery: ClaimedDelivery;
724
+ }
364
725
  /**
365
- * Delay before the next attempt after `failedAttempts` failures (1-based):
366
- * capped exponential backoff with the policy's jitter.
726
+ * Tracing hooks, for example from `@relaybus/otel`. Each one wraps an operation: it starts a span,
727
+ * calls `run` inside it and ends the span when the returned promise settles, passing its result or
728
+ * error through unchanged. relaybus itself depends on no tracing library.
367
729
  */
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;
730
+ interface RelayBusTracer {
380
731
  /**
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.
732
+ * Wraps one publish call. `run` takes the trace headers (`traceparent`, `tracestate`) to write
733
+ * into every event of the call, so its consumers continue the trace; headers passed to
734
+ * `publish` explicitly win.
384
735
  */
385
- readonly transaction: TTransaction | undefined;
386
- }
387
- type EventHandler<TPayload = unknown, TTransaction = unknown> = (event: EventEnvelope<TPayload>, context: HandlerContext<TTransaction>) => unknown;
388
- interface SubscribeOptions {
736
+ publish?<T>(input: PublishTraceInput, run: (headers: EventHeaders) => Promise<T>): Promise<T>;
389
737
  /**
390
- * Stable id of the handler. Deliveries are recorded under it, so renaming it
391
- * orphans pending deliveries. Letters, digits and `._:/-`.
738
+ * Wraps one send of a route. `run` takes the trace headers to send the event with, in place of
739
+ * the ones it was stored with, so the consumer's trace continues from this send.
392
740
  */
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;
741
+ send?<T>(input: SendTraceInput, run: (headers: EventHeaders) => Promise<T>): Promise<T>;
400
742
  /**
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.
743
+ * Wraps one run of a local handler; the handler runs inside `run`, so what it publishes or calls
744
+ * joins the span. The event's own trace context is in `traceContextOf(delivery.event.headers)`.
409
745
  */
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;
746
+ process?<T>(input: ProcessTraceInput, run: () => Promise<T>): Promise<T>;
422
747
  }
423
748
 
749
+ type FailureStage = 'claim' | 'dispatch' | 'complete' | 'reschedule' | 'bury' | 'release' | 'relinquish' | 'extend-lease' | 'purge' | 'watch' | 'backlog' | 'observer';
750
+ /** How a timed operation ended. On `error` the event carries the error. */
751
+ type OperationOutcome = 'success' | 'error';
752
+ /** Whether a delivery runs a local handler or forwards its event to a transport (a route). */
753
+ type DeliveryKind = 'handler' | 'route';
754
+ /** A `publish` or `publishAll` call returned or threw. */
755
+ interface PublishFinishedEvent {
756
+ /** The type of every event of the call, in order. */
757
+ readonly eventTypes: readonly string[];
758
+ readonly durationMs: number;
759
+ readonly outcome: OperationOutcome;
760
+ readonly error?: ErrorInfo;
761
+ }
762
+ /** One claim query of the dispatcher answered or failed. */
763
+ interface ClaimFinishedEvent {
764
+ /** Handler ids the claim asked for. */
765
+ readonly handlers: readonly string[];
766
+ /** Most deliveries the claim asked for (the batch size of this query). */
767
+ readonly requested: number;
768
+ readonly claimed: number;
769
+ /** Distinct leases among the claimed deliveries: a store that leases lanes counts each lane once. */
770
+ readonly lanes: number;
771
+ readonly durationMs: number;
772
+ readonly outcome: OperationOutcome;
773
+ readonly error?: ErrorInfo;
774
+ }
775
+ /** A handler (or a route's send) is about to run for a claimed delivery. */
776
+ interface HandlerStartedEvent {
777
+ readonly delivery: ClaimedDelivery;
778
+ readonly kind: DeliveryKind;
779
+ /** From the event's `occurredAt` to this start, across services for a received event. */
780
+ readonly endToEndLatencyMs: number;
781
+ }
424
782
  /**
425
- * RFC 9562 UUID version 7: 48-bit Unix millisecond timestamp followed by random
426
- * bits. Time-ordered, so it keeps B-tree indexes compact, and globally unique,
427
- * so it is safe as an idempotency key across services.
783
+ * A handler run ended: it resolved (`success`) or threw, timed out, lost its lease or was
784
+ * stopped (`error`). What happens to the delivery next is reported by the delivery hooks.
428
785
  */
429
- declare function createUuidV7(timestamp?: number): string;
430
- /** Produces unique identifiers for events, deliveries and lease tokens. */
431
- type IdentifierGenerator = () => string;
432
-
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;
786
+ interface HandlerFinishedEvent {
787
+ readonly delivery: ClaimedDelivery;
788
+ readonly kind: DeliveryKind;
789
+ readonly durationMs: number;
790
+ readonly outcome: OperationOutcome;
791
+ readonly error?: ErrorInfo;
437
792
  }
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;
793
+ /** A route handed an event to its transport, which acknowledged it or failed. */
794
+ interface TransportSentEvent {
795
+ readonly envelope: EventEnvelope;
796
+ /** Name of the transport. */
797
+ readonly transport: string;
798
+ /** The messaging system of the transport (`kafka`, `rabbitmq`), else its name. */
799
+ readonly system: string;
800
+ readonly destination: string;
801
+ readonly durationMs: number;
802
+ readonly outcome: OperationOutcome;
803
+ readonly error?: ErrorInfo;
447
804
  }
448
805
  /**
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.
806
+ * What `receive` did with an event from a broker: stored it (`accepted`), found it stored already
807
+ * (`duplicate`), had no handler for it (`ignored`), rejected it as malformed (`invalid`) or failed
808
+ * (`error`, the broker redelivers it).
453
809
  */
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>;
810
+ type ReceiveOutcome = 'accepted' | 'duplicate' | 'ignored' | 'invalid' | 'error';
811
+ /** One event from a broker went through `receive`. */
812
+ interface ReceiveFinishedEvent {
813
+ /** Name of the transport that delivered it; undefined when `bus.receive` was called directly. */
814
+ readonly transport: string | undefined;
815
+ /** Messaging system of that transport (`kafka`, `rabbitmq`), else its name. */
816
+ readonly system: string | undefined;
817
+ /** Topic, routing key or queue it arrived on, when the transport tells. */
818
+ readonly destination: string | undefined;
819
+ /** The event's type and source as received; undefined when missing or not text. */
820
+ readonly eventType: string | undefined;
821
+ readonly source: string | undefined;
822
+ /** W3C trace context the event carries, to continue the trace of its publisher. */
823
+ readonly traceContext: TraceContext | undefined;
824
+ readonly outcome: ReceiveOutcome;
825
+ readonly durationMs: number;
826
+ readonly error?: ErrorInfo;
827
+ }
828
+ /**
829
+ * A transport could not decode an inbound message (codec failure, malformed record) and
830
+ * dead-lettered or skipped it. It never reached the bus, so `onReceiveFinished` does not see it.
831
+ */
832
+ interface InvalidMessageEvent {
833
+ /** Name of the transport. */
834
+ readonly transport: string;
835
+ /** The messaging system of the transport (`kafka`, `rabbitmq`), else its name. */
836
+ readonly system: string;
837
+ /** Topic, routing key or queue the message arrived on. */
838
+ readonly destination: string;
839
+ /** Why the message was rejected. */
840
+ readonly reason: string;
841
+ readonly error: ErrorInfo;
842
+ }
843
+ /** A periodic reading of the store's backlog (`RelayBusOptions.backlogInterval`). */
844
+ interface BacklogEvent {
845
+ /** Deliveries waiting to run, due or not. */
846
+ readonly pending: number;
847
+ /** How long the oldest due delivery has waited; 0 when none is due. */
848
+ readonly oldestPendingAgeMs: number;
849
+ readonly statistics: EventStoreStatistics;
469
850
  }
470
-
471
- type FailureStage = 'claim' | 'dispatch' | 'complete' | 'reschedule' | 'bury' | 'release' | 'extend-lease' | 'purge' | 'observer';
472
851
  /**
473
852
  * Hooks for logging, metrics and tracing. Every method is optional, runs
474
853
  * synchronously on the hot path and must not throw; errors it throws are
@@ -517,6 +896,17 @@ interface RelayBusObserver {
517
896
  readonly stage: FailureStage;
518
897
  readonly error: ErrorInfo;
519
898
  }): void;
899
+ /** A publish call ended; called before the publishing transaction commits. */
900
+ onPublishFinished?(event: PublishFinishedEvent): void;
901
+ onClaimFinished?(event: ClaimFinishedEvent): void;
902
+ onHandlerStarted?(event: HandlerStartedEvent): void;
903
+ onHandlerFinished?(event: HandlerFinishedEvent): void;
904
+ onTransportSent?(event: TransportSentEvent): void;
905
+ onReceiveFinished?(event: ReceiveFinishedEvent): void;
906
+ /** A transport rejected an inbound message it could not decode. */
907
+ onInvalidMessage?(event: InvalidMessageEvent): void;
908
+ /** Called every `backlogInterval` while the bus runs, when that option is set. */
909
+ onBacklog?(event: BacklogEvent): void;
520
910
  }
521
911
  /** Minimal logger. Nest's `Logger` and `console` both satisfy it. */
522
912
  interface RelayBusLogger {
@@ -525,8 +915,35 @@ interface RelayBusLogger {
525
915
  error(message: string): void;
526
916
  }
527
917
 
528
- /** Receives an event from a broker. Resolve only after it is durably stored. */
529
- type TransportMessageHandler = (envelope: EventEnvelope) => Promise<void>;
918
+ /** Where an incoming message came from, for observers and traces. */
919
+ interface TransportMessage {
920
+ /** Topic, routing key or queue the message arrived on. */
921
+ readonly destination: string;
922
+ }
923
+ /** A message the transport could not turn into an event, so it never reaches the bus. */
924
+ interface InvalidTransportMessage {
925
+ /** Topic, routing key or queue the message arrived on. */
926
+ readonly destination: string;
927
+ /** What the codec or decoder threw. */
928
+ readonly error: unknown;
929
+ }
930
+ /**
931
+ * What the bus offers a transport at `subscribe`, beyond the message handler. Present only when
932
+ * an observer implements the matching hook, so a transport can skip the work otherwise.
933
+ */
934
+ interface TransportSubscriptionHooks {
935
+ /**
936
+ * Call once for each inbound message the transport rejected as undecodable (dead-lettered or
937
+ * skipped) before it reached `onMessage`. Messages the bus rejected itself are reported by
938
+ * `onReceiveFinished` and must not be reported here. Never throws.
939
+ */
940
+ readonly onInvalidMessage: (message: InvalidTransportMessage) => void;
941
+ }
942
+ /**
943
+ * Receives an event from a broker. Resolve only after it is durably stored. Transports pass
944
+ * `message` to tell where the event arrived; it is optional for transports written before it.
945
+ */
946
+ type TransportMessageHandler = (envelope: EventEnvelope, message?: TransportMessage) => Promise<void>;
530
947
  /**
531
948
  * Broker port used to exchange events between services.
532
949
  *
@@ -540,17 +957,25 @@ type TransportMessageHandler = (envelope: EventEnvelope) => Promise<void>;
540
957
  interface Transport {
541
958
  /** Unique name referenced by routes and subscriptions. */
542
959
  readonly name: string;
960
+ /**
961
+ * The messaging system, as OpenTelemetry names it (`kafka`, `rabbitmq`), reported to observers
962
+ * and traces. Default: the transport's name.
963
+ */
964
+ readonly system?: string;
543
965
  connect?(): Promise<void>;
544
966
  publish(envelope: EventEnvelope, destination: string): Promise<void>;
545
- subscribe?(destinations: readonly string[], onMessage: TransportMessageHandler): Promise<void>;
967
+ subscribe?(destinations: readonly string[], onMessage: TransportMessageHandler, hooks?: TransportSubscriptionHooks): Promise<void>;
546
968
  /**
547
969
  * Whether the transport can currently publish and, when subscribed, is
548
970
  * consuming. Health checks use it to report a dead consumer or a lost
549
971
  * connection. Transports without it count as ready.
550
972
  */
551
973
  isReady?(): boolean;
552
- /** Releases connections. A closed transport is not reopened. */
553
- close(): Promise<void>;
974
+ /**
975
+ * Releases connections. A closed transport is not reopened. Once `options.signal` aborts (the
976
+ * shutdown deadline passed), stop waiting and drop the connections it opened itself.
977
+ */
978
+ close(options?: CloseOptions): Promise<void>;
554
979
  }
555
980
 
556
981
  /**
@@ -578,8 +1003,21 @@ interface DispatcherOptions {
578
1003
  readonly pollInterval?: Duration;
579
1004
  /** Lease length; renewed every third of it while the handler runs. Default `'30s'`. */
580
1005
  readonly leaseDuration?: Duration;
581
- /** How long `stop` waits for running handlers before aborting them. Default `'30s'`. */
1006
+ /**
1007
+ * Longest `stop` takes in all, and its default `timeout`: waiting for running handlers, handing
1008
+ * back leases, closing transports and the store. Past it, `stop` aborts the handlers still
1009
+ * running, force-closes what relaybus opened itself and resolves. Default `'30s'`.
1010
+ */
582
1011
  readonly shutdownTimeout?: Duration;
1012
+ /**
1013
+ * Longest a finished delivery waits so that deliveries finishing around the
1014
+ * same time are completed with one store call (when the store supports it).
1015
+ * A delivery that finishes while nothing else runs is completed at once.
1016
+ * `0` completes every delivery on its own. Default `'2ms'`.
1017
+ */
1018
+ readonly completionBatchWindow?: Duration;
1019
+ /** Most deliveries completed in one store call. Default 200. */
1020
+ readonly completionBatchSize?: number;
583
1021
  }
584
1022
  interface RetentionOptions {
585
1023
  /**
@@ -594,6 +1032,11 @@ interface RetentionOptions {
594
1032
  /** How often the cleanup runs. Default `'1h'`. */
595
1033
  readonly interval?: Duration;
596
1034
  }
1035
+ /**
1036
+ * Every route to one destination of one transport is one handler,
1037
+ * `relaybus.transport:<transport>:<destination>`: its events of one key are sent in commit
1038
+ * order whatever their types. Such routes must resolve to the same `retry` and `timeout`.
1039
+ */
597
1040
  interface RouteOptions {
598
1041
  /** Event class or type forwarded to the transport. */
599
1042
  readonly event: EventReference;
@@ -616,6 +1059,15 @@ interface RelayBusOptions<TTransaction = unknown> {
616
1059
  readonly source?: string;
617
1060
  /** Default `'required'`. */
618
1061
  readonly transactionMode?: TransactionMode;
1062
+ /**
1063
+ * Time budget of every `bus.transaction` (and `@RelayBusTransaction()` method). The store makes
1064
+ * the database enforce it where it can (PostgreSQL: `statement_timeout`, plus
1065
+ * `idle_in_transaction_session_timeout` and on PostgreSQL 17+ `transaction_timeout`; MongoDB:
1066
+ * `maxCommitTimeMS`), and a transaction that still runs past
1067
+ * it is rolled back with a {@link TransactionTimeoutError}. Keep calls to other services out of
1068
+ * transactions instead of raising it. Default `'5s'`.
1069
+ */
1070
+ readonly transactionTimeout?: Duration;
619
1071
  /**
620
1072
  * Finds the caller's active transaction when none is passed to `publish`
621
1073
  * and none was opened with `bus.transaction`, for example from
@@ -637,12 +1089,38 @@ interface RelayBusOptions<TTransaction = unknown> {
637
1089
  readonly subscriptions?: readonly InboundSubscriptionOptions[];
638
1090
  /** Create tables and indexes on start. Default true. */
639
1091
  readonly setupStore?: boolean;
1092
+ /**
1093
+ * Hooks for logging and metrics. The hooks an observer implements are read when the bus is
1094
+ * created; the newer ones (`onPublishFinished` and the other timed hooks) are skipped entirely,
1095
+ * clock reads included, when no observer implements them.
1096
+ */
640
1097
  readonly observers?: readonly RelayBusObserver[];
1098
+ /**
1099
+ * Spans around publish, send and handler runs, and the W3C trace context written into published
1100
+ * events (`traceparent`, `tracestate` headers). `@relaybus/otel` provides one. Default: none.
1101
+ */
1102
+ readonly tracer?: RelayBusTracer;
1103
+ /**
1104
+ * Reads `statistics()` this often while the bus runs and reports the backlog (pending deliveries,
1105
+ * age of the oldest due one) to the observers' `onBacklog`. Each reading is a store query, so it
1106
+ * is off unless set; `'15s'` to `'1m'` suits metrics scraped every minute. Default: off.
1107
+ */
1108
+ readonly backlogInterval?: Duration;
641
1109
  /** Default: `console`. */
642
1110
  readonly logger?: RelayBusLogger;
643
1111
  readonly clock?: Clock;
644
1112
  readonly generateIdentifier?: IdentifierGenerator;
645
1113
  readonly random?: () => number;
1114
+ /**
1115
+ * Catalogs this process is checked against when it starts. `start()` throws a
1116
+ * {@link ConfigurationError} listing every event that is not covered:
1117
+ * - a catalog: every event needs a handler or a route;
1118
+ * - `consume(catalog, { events })`: each declared event needs a local handler;
1119
+ * - `produce(catalog, { events })`: each declared event needs a route to a transport.
1120
+ *
1121
+ * See `registerCatalog`.
1122
+ */
1123
+ readonly catalogs?: readonly (Catalog | CatalogDeclaration)[];
646
1124
  }
647
1125
  interface PublishOptions<TTransaction = unknown> {
648
1126
  /** Transaction to write into. Default: the current scope, then `transactionResolver`. */
@@ -650,7 +1128,9 @@ interface PublishOptions<TTransaction = unknown> {
650
1128
  /**
651
1129
  * Ordering and partition key. Events with the same key are handled one at a
652
1130
  * time per handler, in commit order. A delayed or retrying event holds back
653
- * later events of its key.
1131
+ * later events of its key. Default: the `key` of the event's definition
1132
+ * (`defineEvent(type, { key })`), else none. An explicit key wins over the definition's, and in
1133
+ * `publishAll` applies to every event of the call.
654
1134
  */
655
1135
  readonly key?: string;
656
1136
  /** Deliver no earlier than this delay from now. */
@@ -671,6 +1151,27 @@ interface PublishOneOptions<TTransaction = unknown> extends PublishOptions<TTran
671
1151
  readonly id?: string;
672
1152
  }
673
1153
  type ReceiveStatus = 'accepted' | 'duplicate' | 'ignored';
1154
+
1155
+ interface StopOptions {
1156
+ /**
1157
+ * Longest the whole `stop` may take. Default: `dispatcher.shutdownTimeout`. Keep it below the time
1158
+ * the platform waits before killing the process (Kubernetes `terminationGracePeriodSeconds`).
1159
+ */
1160
+ readonly timeout?: Duration;
1161
+ }
1162
+ /** How a `stop` went. */
1163
+ interface StopResult {
1164
+ /** False when the deadline passed and `stop` aborted handlers or force-closed connections. */
1165
+ readonly graceful: boolean;
1166
+ /**
1167
+ * Deliveries whose handlers still ran at the deadline. Their leases expire after
1168
+ * `dispatcher.leaseDuration` and another process runs them again (at-least-once).
1169
+ */
1170
+ readonly abandonedDeliveries: number;
1171
+ /** Time `stop` took, on the bus clock. */
1172
+ readonly durationMs: number;
1173
+ }
1174
+
674
1175
  /**
675
1176
  * Reliable event bus: events are stored with business data in one transaction,
676
1177
  * then dispatched to every subscribed handler and route with independent
@@ -690,6 +1191,38 @@ declare class RelayBus<TTransaction = unknown> {
690
1191
  */
691
1192
  subscribe<TPayload>(event: EventClass<TPayload>, handler: EventHandler<TPayload, TTransaction>, options: SubscribeOptions): void;
692
1193
  subscribe(eventType: string, handler: EventHandler<unknown, TTransaction>, options: SubscribeOptions): void;
1194
+ /**
1195
+ * Subscribes the `on${EventName}` methods of `handlers` to the events of
1196
+ * `catalog`, or to the events named in `options.events`, with the stable
1197
+ * handler ids `${prefix}.on${EventName}`. The prefix is `options.idPrefix`,
1198
+ * else the catalog name, else the class name of `handlers`. Methods run with
1199
+ * `this` bound to `handlers`; methods of events that are not selected are
1200
+ * ignored. A missing handler of a selected event fails to compile, and
1201
+ * throws {@link ConfigurationError} at runtime, as does an invalid or taken
1202
+ * id; handlers registered before the failure stay registered.
1203
+ *
1204
+ * ```ts
1205
+ * bus.subscribeCatalog(OrderEvents, new BillingHandlers(), { events: ['OrderPlaced'] });
1206
+ * ```
1207
+ */
1208
+ subscribeCatalog<TCatalog extends Catalog, const TName extends keyof TCatalog>(catalog: TCatalog, handlers: NoInfer<HandlersOf<TCatalog, TName, TTransaction>>, options: SubscribeCatalogOptions<NoInfer<TCatalog>, TName> & {
1209
+ readonly events: readonly TName[];
1210
+ }): void;
1211
+ /** Without a statically known `events` list, every handler of the catalog is required. */
1212
+ subscribeCatalog<TCatalog extends Catalog>(catalog: TCatalog, handlers: HandlersOf<TCatalog, keyof TCatalog, TTransaction>, options?: SubscribeCatalogOptions<NoInfer<TCatalog>>): void;
1213
+ /**
1214
+ * Adds a catalog, or a `consume` / `produce` declaration, to the checks
1215
+ * `start()` runs; `start()` throws a {@link ConfigurationError} naming each
1216
+ * event that is not covered and what it lacks. Equivalent to the `catalogs`
1217
+ * option.
1218
+ */
1219
+ registerCatalog(catalog: Catalog | CatalogDeclaration): void;
1220
+ /**
1221
+ * A view of this bus that publishes only the events of `catalog`, or only
1222
+ * the events named in `options.events` (for example the events this service
1223
+ * produces). Other events fail to compile and are rejected at runtime.
1224
+ */
1225
+ forCatalog<TCatalog extends Catalog, const TName extends keyof TCatalog = keyof TCatalog>(catalog: TCatalog, options?: CatalogEventSelection<TName>): CatalogPublisher<TCatalog, TTransaction, TName>;
693
1226
  /** Registered handlers and routes, for diagnostics. */
694
1227
  handlers(): readonly HandlerDescriptor[];
695
1228
  /**
@@ -702,9 +1235,16 @@ declare class RelayBus<TTransaction = unknown> {
702
1235
  publishAll(events: readonly DomainEvent[], options?: PublishOptions<TTransaction>): Promise<EventEnvelope[]>;
703
1236
  /**
704
1237
  * Runs `work` in a new store transaction. `publish` calls inside it join the
705
- * transaction without passing it explicitly.
1238
+ * transaction without passing it explicitly. The transaction is held to `transactionTimeout`
1239
+ * and rolled back with a {@link TransactionTimeoutError} when it runs past it.
706
1240
  */
707
1241
  transaction<T>(work: (transaction: TTransaction) => Promise<T>): Promise<T>;
1242
+ /**
1243
+ * The transaction `publish` would join right now: the one of the enclosing `bus.transaction`
1244
+ * (or `@RelayBusTransaction()` method, or transactional handler), else the one
1245
+ * `transactionResolver` finds. Repositories use it to write in the same transaction.
1246
+ */
1247
+ currentTransaction(): TTransaction | undefined;
708
1248
  /**
709
1249
  * Stores an event received from a broker and schedules it for local handlers.
710
1250
  * Idempotent on the event id. Transports call it and acknowledge the message
@@ -719,10 +1259,15 @@ declare class RelayBus<TTransaction = unknown> {
719
1259
  */
720
1260
  start(): Promise<void>;
721
1261
  /**
722
- * Stops dispatching, waits for running handlers, closes transports and the
723
- * store. Waits for a start in progress first. Safe to call more than once.
1262
+ * Stops claiming, waits for running handlers, hands back leases, closes transports and the store,
1263
+ * all within `options.timeout` (default `dispatcher.shutdownTimeout`). When the deadline passes it
1264
+ * aborts the handlers still running, force-closes what relaybus opened itself (never pools or
1265
+ * clients the application passed in) and resolves anyway: it never rejects because of the
1266
+ * deadline. Abandoned deliveries stay leased until their lease expires and then run again
1267
+ * (at-least-once). Waits for a start in progress first. Safe to call more than once: later calls
1268
+ * get the result of the first.
724
1269
  */
725
- stop(): Promise<void>;
1270
+ stop(options?: StopOptions): Promise<StopResult>;
726
1271
  /**
727
1272
  * Claims and runs one batch of due deliveries, resolving when they settle.
728
1273
  * Returns how many ran. Intended for tests and scripts.
@@ -741,4 +1286,83 @@ declare class RelayBus<TTransaction = unknown> {
741
1286
  purgeExpired(): Promise<PurgeResult>;
742
1287
  }
743
1288
 
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 };
1289
+ /** The publishing half of a bus, which a {@link CatalogPublisher} delegates to. */
1290
+ interface PublishTarget<TTransaction> {
1291
+ publish<TPayload>(event: DomainEvent<TPayload>, options?: PublishOneOptions<TTransaction>): Promise<EventEnvelope<TPayload>>;
1292
+ publishAll(events: readonly DomainEvent[], options?: PublishOptions<TTransaction>): Promise<EventEnvelope[]>;
1293
+ }
1294
+ /**
1295
+ * A view of a bus that publishes only the events of one catalog, or only the
1296
+ * events a service produces from it. Obtain it with `bus.forCatalog(catalog)`
1297
+ * or `bus.forCatalog(catalog, { events: ['OrderPlaced'] })`. Publishing an
1298
+ * event outside the view fails to compile; a mistyped event that gets past the
1299
+ * compiler (a cast, plain JavaScript, or a class with a structurally identical
1300
+ * payload) is rejected with a {@link ConfigurationError} before anything is stored.
1301
+ *
1302
+ * ```ts
1303
+ * const orders = bus.forCatalog(OrderEvents);
1304
+ * await orders.publish(new OrderPlaced({ orderId }));
1305
+ * await orders.publish(new UserRegistered({ userId })); // compile error
1306
+ * ```
1307
+ */
1308
+ declare class CatalogPublisher<TCatalog extends Catalog, TTransaction = unknown, TName extends keyof TCatalog = keyof TCatalog> {
1309
+ #private;
1310
+ /**
1311
+ * Throws {@link ConfigurationError} when `catalog` was not created with
1312
+ * `defineCatalog` or `options.events` names no event, an unknown event or
1313
+ * the same event twice.
1314
+ */
1315
+ constructor(catalog: TCatalog, target: PublishTarget<TTransaction>, options?: CatalogEventSelection<TName>);
1316
+ /** Same as `bus.publish`, limited to the events of the view. */
1317
+ publish<TEvent extends CatalogEvents<TCatalog, TName>>(event: TEvent, options?: PublishOneOptions<TTransaction>): Promise<EventEnvelope<TEvent['payload']>>;
1318
+ /** Same as `bus.publishAll`, limited to the events of the view. Nothing is stored unless every event belongs to it. */
1319
+ publishAll(events: readonly CatalogEvents<TCatalog, TName>[], options?: PublishOptions<TTransaction>): Promise<EventEnvelope[]>;
1320
+ }
1321
+
1322
+ /** Transaction handle of {@link InMemoryEventStore}. Writes apply on commit only. */
1323
+ declare class InMemoryTransaction {
1324
+ #private;
1325
+ get state(): 'open' | 'committed' | 'rolled-back';
1326
+ enqueue(operation: () => void): void;
1327
+ /** Runs `finisher` once the transaction commits or rolls back, like a lock release. */
1328
+ onFinish(finisher: () => void): void;
1329
+ commit(): void;
1330
+ rollback(): void;
1331
+ }
1332
+
1333
+ interface InMemoryEventStoreOptions {
1334
+ /** Allow use when `NODE_ENV` is `production`. Data is lost on restart. Default false. */
1335
+ readonly allowInProduction?: boolean;
1336
+ readonly generateIdentifier?: IdentifierGenerator;
1337
+ /**
1338
+ * Keep a record of completed deliveries (status `completed`, listed, counted
1339
+ * and purged by `retention.completed`). Default false: a completed delivery
1340
+ * is deleted, like in the database stores.
1341
+ */
1342
+ readonly retainCompleted?: boolean;
1343
+ }
1344
+ /**
1345
+ * Event store kept in process memory, with the same semantics as the database
1346
+ * stores: transactional appends, leases, fencing and per-key ordering. Payloads
1347
+ * are round-tripped through JSON exactly like a database would store them.
1348
+ * Meant for tests and local development.
1349
+ */
1350
+ declare class InMemoryEventStore implements EventStore<InMemoryTransaction> {
1351
+ #private;
1352
+ constructor(options?: InMemoryEventStoreOptions);
1353
+ transaction<T>(work: (transaction: InMemoryTransaction) => Promise<T>): Promise<T>;
1354
+ append(transaction: InMemoryTransaction, entries: readonly AppendEntry[], now: Date): Promise<AppendResult>;
1355
+ claim(request: ClaimRequest): Promise<ClaimedDelivery[]>;
1356
+ extendLease(lease: LeaseReference, leaseExpiresAt: Date, now: Date): Promise<boolean>;
1357
+ complete(lease: LeaseReference, now: Date, transaction?: InMemoryTransaction): Promise<boolean>;
1358
+ completeMany(leases: readonly LeaseReference[], now: Date): Promise<boolean[]>;
1359
+ reschedule(lease: LeaseReference, availableAt: Date, error: ErrorInfo, now: Date): Promise<boolean>;
1360
+ release(lease: LeaseReference, now: Date): Promise<boolean>;
1361
+ bury(lease: LeaseReference, error: ErrorInfo, now: Date): Promise<boolean>;
1362
+ listDeliveries(query: DeliveryQuery): Promise<DeliveryRecord[]>;
1363
+ requeue(deliveryIds: readonly string[], now: Date): Promise<number>;
1364
+ purge(request: PurgeRequest): Promise<PurgeResult>;
1365
+ statistics(now: Date): Promise<EventStoreStatistics>;
1366
+ }
1367
+
1368
+ export { type AppendEntry, type AppendResult, AttemptsExhaustedError, type BacklogEvent, type Catalog, type CatalogDeclaration, type CatalogDescription, type CatalogEntry, type CatalogEventClass, type CatalogEventName, type CatalogEventSelection, type CatalogEvents, CatalogPublisher, type CatalogRole, type ClaimFinishedEvent, type ClaimRequest, type ClaimedDelivery, Clock, type CloseOptions, ConfigurationError, DEFAULT_RETRY_POLICY, DEFAULT_ROUTE_RETRY_POLICY, DELIVERY_STATUSES, type DefineCatalogOptions, type DeliveryKind, type DeliveryQuery, type DeliveryRecord, type DeliveryStatus, type DispatcherOptions, DispatcherStoppedError, DomainEvent, DrainIncompleteError, type Duration, type ErrorInfo, EventClass, EventEnvelope, type EventHandler, EventHeaders, type EventOf, EventReference, type EventStore, type EventStoreStatistics, type EventStoreTransactionOptions, EventValidationError, type FailureStage, type HandlerContext, type HandlerDescriptor, type HandlerFinishedEvent, type HandlerShapeOf, type HandlerStartedEvent, HandlerTimeoutError, type HandlersOf, type IdentifierGenerator, InMemoryEventStore, type InMemoryEventStoreOptions, InMemoryTransaction, type InboundSubscriptionOptions, InvalidEnvelopeError, type InvalidMessageEvent, type InvalidTransportMessage, type JitterStrategy, LeaseLostError, type LeaseReference, LifecycleError, MAX_TIMER_DELAY_MS, type MatchingHandlers, type NewDelivery, type NoExtraHandlers, NonRetryableError, type OperationOutcome, type PartialHandlersOf, PayloadOf, type ProcessTraceInput, type PublishFinishedEvent, type PublishOneOptions, type PublishOptions, type PublishTarget, type PublishTraceInput, type PurgeRequest, type PurgeResult, type ReceiveFinishedEvent, type ReceiveOutcome, type ReceiveStatus, RelayBus, RelayBusError, type RelayBusLogger, type RelayBusObserver, type RelayBusOptions, type RelayBusTracer, type RetentionOptions, type RetryPolicy, type RetryPolicyOptions, type RouteOptions, type SendTraceInput, StandardSchemaV1, type StopOptions, type StopResult, type SubscribeCatalogOptions, type SubscribeOptions, TRACEPARENT_HEADER, TRACESTATE_HEADER, type TraceContext, type TransactionMode, TransactionRequiredError, TransactionTimeoutError, type Transport, type TransportMessage, type TransportMessageHandler, type TransportSentEvent, type TransportSubscriptionHooks, type UnroutedEventPolicy, type ValidationIssue, computeRetryDelay, consume, createRetryPolicy, createUuidV7, defineCatalog, defineHandlers, describeCatalog, isRetryableError, produce, toErrorInfo, toMilliseconds, traceContextOf };