@relaybus/core 0.4.1 → 1.0.0-next.1

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,5 +1,8 @@
1
- import { D as Duration, E as EventEnvelope, a as DomainEvent, S as StandardSchemaV1, P as PayloadOf, C as ClaimedDelivery, b as EventHeaders, c as EventStoreStatistics, d as ErrorInfo, e as PurgeResult, f as EventStore, T as Transport, g as EventReference, h as Clock, A as AppendEntry, i as AppendResult, j as ClaimRequest, L as LeaseReference, k as DeliveryQuery, l as DeliveryRecord, m as PurgeRequest, n as EventClass } from './transport-DIFdeXQY.cjs';
2
- export { o as AttemptsExhaustedError, p as CloseOptions, q as ConfigurationError, r as DELIVERY_STATUSES, s as DefineEventKeyOptions, t as DefineEventOptions, u as DeliveryStatus, v as DispatcherStoppedError, w as DrainIncompleteError, x as EventKeyFunction, y as EventStoreTransactionOptions, z as EventValidationError, H as HandlerTimeoutError, I as InvalidEnvelopeError, B as InvalidTransportMessage, F as LeaseLostError, G as LifecycleError, M as MAX_TIMER_DELAY_MS, N as NewDelivery, J as NonRetryableError, R as RelayBusError, K as TransactionRequiredError, O as TransactionTimeoutError, Q as TransportMessage, U as TransportMessageHandler, V as TransportSubscriptionHooks, W as ValidationIssue, X as assertEventType, Y as defineEvent, Z as systemClock, _ as toErrorInfo, $ as toMilliseconds, a0 as validatePayload } from './transport-DIFdeXQY.cjs';
1
+ import { D as Duration, E as EventEnvelope, a as DomainEvent, S as StandardSchemaV1, P as PayloadOf, C as ClaimedDelivery, b as EventHeaders, c as EventStoreStatistics, d as ErrorInfo, e as PurgeResult, f as EventReference, g as EventStore, T as Transport, h as Clock, i as TransactionIsolationLevel, A as AppendEntry, j as AppendResult, k as ClaimRequest, L as LeaseReference, l as DeliveryQuery, m as DeliveryRecord, n as PurgeRequest, o as EventClass } from './transport-CXoZgh5u.cjs';
2
+ export { p as AttemptsExhaustedError, q as CloseOptions, r as ConfigurationError, s as DELIVERY_STATUSES, t as DefineEventKeyOptions, u as DefineEventOptions, v as DeliveryStatus, w as DispatcherStoppedError, x as DrainIncompleteError, y as DuplicateRouteEventError, z as EventKeyFunction, B as EventStoreTransactionOptions, F as EventValidationError, H as HandlerTimeoutError, I as InvalidEnvelopeError, G as InvalidTransportMessage, J as LeaseLostError, K as LifecycleError, M as MAX_TIMER_DELAY_MS, N as NewDelivery, O as NonRetryableError, R as RelayBusError, Q as TRANSACTION_ISOLATION_LEVELS, U as TransactionIsolationConflictError, V as TransactionRequiredError, W as TransactionTimeoutError, X as TransportMessage, Y as TransportMessageHandler, Z as TransportSubscriptionHooks, _ as UnknownCatalogEventError, $ as ValidationIssue, a0 as WireTypeCollisionError, a1 as WireTypesUnsupportedError, a2 as assertEventType, a3 as defineEvent, a4 as systemClock, a5 as toErrorInfo, a6 as toMilliseconds, a7 as validatePayload } from './transport-CXoZgh5u.cjs';
3
+
4
+ /** Wire names by catalog event name, for the events that have one. */
5
+ type WireNames<TName extends string = string> = Readonly<Partial<Record<TName, string>>>;
3
6
 
4
7
  /**
5
8
  * How the delay between attempts is randomized.
@@ -172,12 +175,20 @@ interface CatalogEventSelection<TName> {
172
175
  readonly events?: readonly TName[];
173
176
  }
174
177
  /** Options of {@link defineCatalog}. */
175
- interface DefineCatalogOptions {
178
+ interface DefineCatalogOptions<TCatalog extends Catalog = Catalog> {
176
179
  /**
177
180
  * Name of the catalog, used in boot-check messages and as the default prefix
178
181
  * of the handler ids that `subscribeCatalog` registers.
179
182
  */
180
183
  readonly name?: string;
184
+ /**
185
+ * Names the wire format uses for events instead of their event types, by catalog event name:
186
+ * `{ OrderPlaced: 'Order Placed' }`. A route of the catalog (`route(produce(...))`) hands them
187
+ * to its transport's codec, which writes and reads the event under that name, exactly as the
188
+ * `wireTypes` of `jsonEnvelope` would; a `wireTypes` of the route wins for its events. Events
189
+ * without a name travel under their event type.
190
+ */
191
+ readonly wireTypes?: WireNames<Extract<keyof TCatalog, string>>;
181
192
  }
182
193
  /**
183
194
  * Options of `RelayBus.subscribeCatalog`: {@link SubscribeOptions} for every
@@ -225,9 +236,10 @@ interface CatalogDescription {
225
236
  *
226
237
  * Throws {@link ConfigurationError} for an empty catalog, an entry that is not
227
238
  * an event class, a name that is not made of letters, digits and underscores,
228
- * or two entries with the same event type.
239
+ * or two entries with the same event type; `UnknownCatalogEventError` for a wire name of an
240
+ * event the catalog does not have, `WireTypeCollisionError` for one wire name given twice.
229
241
  */
230
- declare function defineCatalog<const TCatalog extends Catalog>(events: TCatalog, options?: DefineCatalogOptions): Readonly<TCatalog>;
242
+ declare function defineCatalog<const TCatalog extends Catalog>(events: TCatalog, options?: DefineCatalogOptions<TCatalog>): Readonly<TCatalog>;
231
243
  /**
232
244
  * Describes a catalog created by {@link defineCatalog}. The description and its
233
245
  * entries are frozen. Throws {@link ConfigurationError} for any other object,
@@ -348,6 +360,55 @@ declare function consume<TCatalog extends Catalog, const TName extends keyof TCa
348
360
  */
349
361
  declare function produce<TCatalog extends Catalog, const TName extends keyof TCatalog = keyof TCatalog>(catalog: TCatalog, options?: CatalogEventSelection<TName>): CatalogDeclaration<TCatalog, TName>;
350
362
 
363
+ /**
364
+ * Options of {@link route}: a {@link RouteOptions} without `event`, because the route sends every
365
+ * event of its catalog declaration, each as a route of its own with these settings.
366
+ */
367
+ interface CatalogRouteOptions<TName extends string = string> extends Omit<RouteOptions, 'event'> {
368
+ /**
369
+ * Names the wire format uses for these events instead of their event types, by catalog event
370
+ * name. They override the `wireTypes` of the catalog and reach the codec of `transport`, which
371
+ * writes and reads the events under those names, as the `wireTypes` of `jsonEnvelope` would.
372
+ * Needs a codec that maps type names (`jsonEnvelope`) and a transport that accepts them.
373
+ */
374
+ readonly wireTypes?: WireNames<TName>;
375
+ }
376
+ /** The routes of a catalog declaration, created by {@link route}. */
377
+ interface CatalogRoute {
378
+ readonly declaration: CatalogDeclaration;
379
+ readonly options: CatalogRouteOptions;
380
+ /** The checked `wireTypes` of the route, by catalog event name. */
381
+ readonly wireNames: ReadonlyMap<string, string>;
382
+ }
383
+ /**
384
+ * Routes the events of a `produce(...)` declaration to one transport with shared settings. Each
385
+ * event becomes a route to `destination` (default: its own event type), so several events of a
386
+ * catalog reach one topic or queue without a route apiece, in commit order per key. Pass it in
387
+ * `routes` next to plain routes; a plain route per event keeps working.
388
+ *
389
+ * ```ts
390
+ * routes: [
391
+ * route(produce(OrderEvents, { events: ['OrderPlaced', 'OrderShipped'] }), {
392
+ * transport: 'kafka',
393
+ * destination: 'orders',
394
+ * wireTypes: { OrderPlaced: 'Order Placed' },
395
+ * }),
396
+ * ]
397
+ * ```
398
+ *
399
+ * @throws ConfigurationError for anything but a `produce(...)` declaration, and
400
+ * `UnknownCatalogEventError` or `WireTypeCollisionError` for bad `wireTypes`.
401
+ */
402
+ declare function route<TCatalog extends Catalog, TName extends keyof TCatalog>(declaration: CatalogDeclaration<TCatalog, TName>, options: CatalogRouteOptions<Extract<TName, string>>): CatalogRoute;
403
+ /**
404
+ * Event type to wire name of the events of `catalog` that have one (`wireTypes` of
405
+ * `defineCatalog`), for the `wireTypes` of a `jsonEnvelope` that reads them: a service that only
406
+ * consumes a catalog has no route to declare them, and gives its codec these instead.
407
+ */
408
+ declare function catalogWireTypes(catalog: Catalog): Readonly<Record<string, string>>;
409
+ /** Plain routes and catalog routes, as `RelayBusOptions.routes` accepts them. */
410
+ type RouteDefinition = RouteOptions | CatalogRoute;
411
+
351
412
  /**
352
413
  * RFC 9562 UUID version 7: 48-bit Unix millisecond timestamp followed by random
353
414
  * bits. Time-ordered, so it keeps B-tree indexes compact, and globally unique,
@@ -526,7 +587,9 @@ interface BacklogEvent {
526
587
  interface RelayBusObserver {
527
588
  /**
528
589
  * Events were written into the publishing transaction. Called before that
529
- * transaction commits, so it can still roll back. Duplicates are not reported.
590
+ * transaction commits, so it can still roll back, except in a `bus.transaction` with an
591
+ * `isolationLevel`, whose publishes are reported once the attempt that committed is over (an
592
+ * attempt may be rolled back and rerun). Duplicates are not reported.
530
593
  */
531
594
  onPublished?(event: {
532
595
  readonly envelopes: readonly EventEnvelope[];
@@ -591,6 +654,27 @@ interface RelayBusLogger {
591
654
  * but no longer atomic with business writes made outside that transaction.
592
655
  */
593
656
  type TransactionMode = 'required' | 'optional';
657
+ /** Options of one `bus.transaction` (and `@RelayBusTransaction()` method). */
658
+ interface TransactionOptions {
659
+ /**
660
+ * Runs the transaction at this isolation level; the store must support it, else the call
661
+ * throws a {@link ConfigurationError} (PostgreSQL stores do; MongoDB and the in-memory store
662
+ * do not). Without it the transaction runs exactly as before, at the database default.
663
+ *
664
+ * With `'repeatable read'` or `'serializable'`, a serialization failure (PostgreSQL SQLSTATE
665
+ * `40001`) rolls the transaction back and `work` runs again in a new one, up to
666
+ * {@link TransactionOptions.maxAttempts} times, so **`work` may run more than once**: keep
667
+ * everything but database writes through the transaction (HTTP calls, broker publishes,
668
+ * counters, emails) out of it. Events published by a failed attempt are rolled back with it.
669
+ */
670
+ readonly isolationLevel?: TransactionIsolationLevel;
671
+ /**
672
+ * How many times `work` may run when `isolationLevel` is `'repeatable read'` or `'serializable'`
673
+ * and the transaction fails with a serialization failure; the last failure is rethrown unchanged.
674
+ * Each attempt is held to its own `transactionTimeout`. A positive integer. Default 3.
675
+ */
676
+ readonly maxAttempts?: number;
677
+ }
594
678
  /**
595
679
  * What `publish` does with an event that has no handler or route in this
596
680
  * process. Deliveries are created from the publisher's registrations, so a
@@ -690,8 +774,12 @@ interface RelayBusOptions<TTransaction = unknown> {
690
774
  readonly dispatcher?: DispatcherOptions;
691
775
  readonly retention?: RetentionOptions;
692
776
  readonly transports?: readonly Transport[];
693
- /** Events forwarded to brokers after they commit. */
694
- readonly routes?: readonly RouteOptions[];
777
+ /**
778
+ * Events forwarded to brokers after they commit: a route per event class or type, or
779
+ * `route(produce(Catalog, { events }), { transport, destination, ... })` for several events of a
780
+ * catalog with shared settings.
781
+ */
782
+ readonly routes?: readonly RouteDefinition[];
695
783
  /** Broker destinations whose events are stored and dispatched to local handlers. */
696
784
  readonly subscriptions?: readonly InboundSubscriptionOptions[];
697
785
  /** Create tables and indexes on start. Default true. */
@@ -923,14 +1011,26 @@ declare class RelayBus<TTransaction = unknown> {
923
1011
  * Runs `work` in a new store transaction. `publish` calls inside it join the
924
1012
  * transaction without passing it explicitly. The transaction is held to `transactionTimeout`
925
1013
  * and rolled back with a {@link TransactionTimeoutError} when it runs past it.
1014
+ *
1015
+ * With `options.isolationLevel` it runs at that level, and at `'repeatable read'` or
1016
+ * `'serializable'` a serialization failure reruns `work` in a new transaction (see
1017
+ * {@link TransactionOptions}), so `work` may run more than once. Without options nothing
1018
+ * changes. The call always opens a transaction of its own, also inside another one.
926
1019
  */
927
- transaction<T>(work: (transaction: TTransaction) => Promise<T>): Promise<T>;
1020
+ transaction<T>(work: (transaction: TTransaction) => Promise<T>, options?: TransactionOptions): Promise<T>;
928
1021
  /**
929
1022
  * The transaction `publish` would join right now: the one of the enclosing `bus.transaction`
930
1023
  * (or `@RelayBusTransaction()` method, or transactional handler), else the one
931
1024
  * `transactionResolver` finds. Repositories use it to write in the same transaction.
932
1025
  */
933
1026
  currentTransaction(): TTransaction | undefined;
1027
+ /**
1028
+ * The isolation level the transaction `publish` would join was opened at by
1029
+ * `bus.transaction({ isolationLevel })` (or `@RelayBusTransaction({ isolationLevel })`);
1030
+ * undefined for every other transaction, including those without a level and the one of
1031
+ * `transactionResolver`.
1032
+ */
1033
+ currentIsolationLevel(): TransactionIsolationLevel | undefined;
934
1034
  /**
935
1035
  * Stores an event received from a broker and schedules it for local handlers.
936
1036
  * Idempotent on the event id. Transports call it and acknowledge the message
@@ -977,5 +1077,5 @@ declare class RelayBus<TTransaction = unknown> {
977
1077
  purgeExpired(): Promise<PurgeResult>;
978
1078
  }
979
1079
 
980
- export { AppendEntry, AppendResult, CatalogPublisher, ClaimRequest, ClaimedDelivery, Clock, DEFAULT_RETRY_POLICY, DEFAULT_ROUTE_RETRY_POLICY, DeliveryQuery, DeliveryRecord, DomainEvent, Duration, ErrorInfo, EventClass, EventEnvelope, EventHeaders, EventReference, EventStore, EventStoreStatistics, InMemoryEventStore, InMemoryTransaction, LeaseReference, PayloadOf, PurgeRequest, PurgeResult, RelayBus, StandardSchemaV1, TRACEPARENT_HEADER, TRACESTATE_HEADER, Transport, computeRetryDelay, consume, createRetryPolicy, createUuidV7, defineCatalog, defineHandlers, describeCatalog, isRetryableError, produce, traceContextOf };
981
- export type { BacklogEvent, Catalog, CatalogDeclaration, CatalogDescription, CatalogEntry, CatalogEventClass, CatalogEventName, CatalogEventSelection, CatalogEvents, CatalogRole, ClaimFinishedEvent, DefineCatalogOptions, DeliveryKind, DispatcherOptions, EventHandler, EventOf, FailureStage, HandlerContext, HandlerDescriptor, HandlerFinishedEvent, HandlerShapeOf, HandlerStartedEvent, HandlersOf, IdentifierGenerator, InMemoryEventStoreOptions, InboundSubscriptionOptions, InvalidMessageEvent, JitterStrategy, MatchingHandlers, NoExtraHandlers, OperationOutcome, PartialHandlersOf, ProcessTraceInput, PublishFinishedEvent, PublishOneOptions, PublishOptions, PublishTarget, PublishTraceInput, ReceiveFinishedEvent, ReceiveOutcome, ReceiveStatus, RelayBusLogger, RelayBusObserver, RelayBusOptions, RelayBusTracer, RetentionOptions, RetryPolicy, RetryPolicyOptions, RouteOptions, SendTraceInput, StopOptions, StopResult, SubscribeCatalogOptions, SubscribeOptions, TraceContext, TransactionMode, TransportSentEvent, UnroutedEventPolicy };
1080
+ export { AppendEntry, AppendResult, CatalogPublisher, ClaimRequest, ClaimedDelivery, Clock, DEFAULT_RETRY_POLICY, DEFAULT_ROUTE_RETRY_POLICY, DeliveryQuery, DeliveryRecord, DomainEvent, Duration, ErrorInfo, EventClass, EventEnvelope, EventHeaders, EventReference, EventStore, EventStoreStatistics, InMemoryEventStore, InMemoryTransaction, LeaseReference, PayloadOf, PurgeRequest, PurgeResult, RelayBus, StandardSchemaV1, TRACEPARENT_HEADER, TRACESTATE_HEADER, TransactionIsolationLevel, Transport, catalogWireTypes, computeRetryDelay, consume, createRetryPolicy, createUuidV7, defineCatalog, defineHandlers, describeCatalog, isRetryableError, produce, route, traceContextOf };
1081
+ export type { BacklogEvent, Catalog, CatalogDeclaration, CatalogDescription, CatalogEntry, CatalogEventClass, CatalogEventName, CatalogEventSelection, CatalogEvents, CatalogRole, CatalogRoute, CatalogRouteOptions, ClaimFinishedEvent, DefineCatalogOptions, DeliveryKind, DispatcherOptions, EventHandler, EventOf, FailureStage, HandlerContext, HandlerDescriptor, HandlerFinishedEvent, HandlerShapeOf, HandlerStartedEvent, HandlersOf, IdentifierGenerator, InMemoryEventStoreOptions, InboundSubscriptionOptions, InvalidMessageEvent, JitterStrategy, MatchingHandlers, NoExtraHandlers, OperationOutcome, PartialHandlersOf, ProcessTraceInput, PublishFinishedEvent, PublishOneOptions, PublishOptions, PublishTarget, PublishTraceInput, ReceiveFinishedEvent, ReceiveOutcome, ReceiveStatus, RelayBusLogger, RelayBusObserver, RelayBusOptions, RelayBusTracer, RetentionOptions, RetryPolicy, RetryPolicyOptions, RouteDefinition, RouteOptions, SendTraceInput, StopOptions, StopResult, SubscribeCatalogOptions, SubscribeOptions, TraceContext, TransactionMode, TransactionOptions, TransportSentEvent, UnroutedEventPolicy };
package/dist/index.d.ts CHANGED
@@ -1,5 +1,8 @@
1
- import { D as Duration, E as EventEnvelope, a as DomainEvent, S as StandardSchemaV1, P as PayloadOf, C as ClaimedDelivery, b as EventHeaders, c as EventStoreStatistics, d as ErrorInfo, e as PurgeResult, f as EventStore, T as Transport, g as EventReference, h as Clock, A as AppendEntry, i as AppendResult, j as ClaimRequest, L as LeaseReference, k as DeliveryQuery, l as DeliveryRecord, m as PurgeRequest, n as EventClass } from './transport-DIFdeXQY.js';
2
- export { o as AttemptsExhaustedError, p as CloseOptions, q as ConfigurationError, r as DELIVERY_STATUSES, s as DefineEventKeyOptions, t as DefineEventOptions, u as DeliveryStatus, v as DispatcherStoppedError, w as DrainIncompleteError, x as EventKeyFunction, y as EventStoreTransactionOptions, z as EventValidationError, H as HandlerTimeoutError, I as InvalidEnvelopeError, B as InvalidTransportMessage, F as LeaseLostError, G as LifecycleError, M as MAX_TIMER_DELAY_MS, N as NewDelivery, J as NonRetryableError, R as RelayBusError, K as TransactionRequiredError, O as TransactionTimeoutError, Q as TransportMessage, U as TransportMessageHandler, V as TransportSubscriptionHooks, W as ValidationIssue, X as assertEventType, Y as defineEvent, Z as systemClock, _ as toErrorInfo, $ as toMilliseconds, a0 as validatePayload } from './transport-DIFdeXQY.js';
1
+ import { D as Duration, E as EventEnvelope, a as DomainEvent, S as StandardSchemaV1, P as PayloadOf, C as ClaimedDelivery, b as EventHeaders, c as EventStoreStatistics, d as ErrorInfo, e as PurgeResult, f as EventReference, g as EventStore, T as Transport, h as Clock, i as TransactionIsolationLevel, A as AppendEntry, j as AppendResult, k as ClaimRequest, L as LeaseReference, l as DeliveryQuery, m as DeliveryRecord, n as PurgeRequest, o as EventClass } from './transport-CXoZgh5u.js';
2
+ export { p as AttemptsExhaustedError, q as CloseOptions, r as ConfigurationError, s as DELIVERY_STATUSES, t as DefineEventKeyOptions, u as DefineEventOptions, v as DeliveryStatus, w as DispatcherStoppedError, x as DrainIncompleteError, y as DuplicateRouteEventError, z as EventKeyFunction, B as EventStoreTransactionOptions, F as EventValidationError, H as HandlerTimeoutError, I as InvalidEnvelopeError, G as InvalidTransportMessage, J as LeaseLostError, K as LifecycleError, M as MAX_TIMER_DELAY_MS, N as NewDelivery, O as NonRetryableError, R as RelayBusError, Q as TRANSACTION_ISOLATION_LEVELS, U as TransactionIsolationConflictError, V as TransactionRequiredError, W as TransactionTimeoutError, X as TransportMessage, Y as TransportMessageHandler, Z as TransportSubscriptionHooks, _ as UnknownCatalogEventError, $ as ValidationIssue, a0 as WireTypeCollisionError, a1 as WireTypesUnsupportedError, a2 as assertEventType, a3 as defineEvent, a4 as systemClock, a5 as toErrorInfo, a6 as toMilliseconds, a7 as validatePayload } from './transport-CXoZgh5u.js';
3
+
4
+ /** Wire names by catalog event name, for the events that have one. */
5
+ type WireNames<TName extends string = string> = Readonly<Partial<Record<TName, string>>>;
3
6
 
4
7
  /**
5
8
  * How the delay between attempts is randomized.
@@ -172,12 +175,20 @@ interface CatalogEventSelection<TName> {
172
175
  readonly events?: readonly TName[];
173
176
  }
174
177
  /** Options of {@link defineCatalog}. */
175
- interface DefineCatalogOptions {
178
+ interface DefineCatalogOptions<TCatalog extends Catalog = Catalog> {
176
179
  /**
177
180
  * Name of the catalog, used in boot-check messages and as the default prefix
178
181
  * of the handler ids that `subscribeCatalog` registers.
179
182
  */
180
183
  readonly name?: string;
184
+ /**
185
+ * Names the wire format uses for events instead of their event types, by catalog event name:
186
+ * `{ OrderPlaced: 'Order Placed' }`. A route of the catalog (`route(produce(...))`) hands them
187
+ * to its transport's codec, which writes and reads the event under that name, exactly as the
188
+ * `wireTypes` of `jsonEnvelope` would; a `wireTypes` of the route wins for its events. Events
189
+ * without a name travel under their event type.
190
+ */
191
+ readonly wireTypes?: WireNames<Extract<keyof TCatalog, string>>;
181
192
  }
182
193
  /**
183
194
  * Options of `RelayBus.subscribeCatalog`: {@link SubscribeOptions} for every
@@ -225,9 +236,10 @@ interface CatalogDescription {
225
236
  *
226
237
  * Throws {@link ConfigurationError} for an empty catalog, an entry that is not
227
238
  * an event class, a name that is not made of letters, digits and underscores,
228
- * or two entries with the same event type.
239
+ * or two entries with the same event type; `UnknownCatalogEventError` for a wire name of an
240
+ * event the catalog does not have, `WireTypeCollisionError` for one wire name given twice.
229
241
  */
230
- declare function defineCatalog<const TCatalog extends Catalog>(events: TCatalog, options?: DefineCatalogOptions): Readonly<TCatalog>;
242
+ declare function defineCatalog<const TCatalog extends Catalog>(events: TCatalog, options?: DefineCatalogOptions<TCatalog>): Readonly<TCatalog>;
231
243
  /**
232
244
  * Describes a catalog created by {@link defineCatalog}. The description and its
233
245
  * entries are frozen. Throws {@link ConfigurationError} for any other object,
@@ -348,6 +360,55 @@ declare function consume<TCatalog extends Catalog, const TName extends keyof TCa
348
360
  */
349
361
  declare function produce<TCatalog extends Catalog, const TName extends keyof TCatalog = keyof TCatalog>(catalog: TCatalog, options?: CatalogEventSelection<TName>): CatalogDeclaration<TCatalog, TName>;
350
362
 
363
+ /**
364
+ * Options of {@link route}: a {@link RouteOptions} without `event`, because the route sends every
365
+ * event of its catalog declaration, each as a route of its own with these settings.
366
+ */
367
+ interface CatalogRouteOptions<TName extends string = string> extends Omit<RouteOptions, 'event'> {
368
+ /**
369
+ * Names the wire format uses for these events instead of their event types, by catalog event
370
+ * name. They override the `wireTypes` of the catalog and reach the codec of `transport`, which
371
+ * writes and reads the events under those names, as the `wireTypes` of `jsonEnvelope` would.
372
+ * Needs a codec that maps type names (`jsonEnvelope`) and a transport that accepts them.
373
+ */
374
+ readonly wireTypes?: WireNames<TName>;
375
+ }
376
+ /** The routes of a catalog declaration, created by {@link route}. */
377
+ interface CatalogRoute {
378
+ readonly declaration: CatalogDeclaration;
379
+ readonly options: CatalogRouteOptions;
380
+ /** The checked `wireTypes` of the route, by catalog event name. */
381
+ readonly wireNames: ReadonlyMap<string, string>;
382
+ }
383
+ /**
384
+ * Routes the events of a `produce(...)` declaration to one transport with shared settings. Each
385
+ * event becomes a route to `destination` (default: its own event type), so several events of a
386
+ * catalog reach one topic or queue without a route apiece, in commit order per key. Pass it in
387
+ * `routes` next to plain routes; a plain route per event keeps working.
388
+ *
389
+ * ```ts
390
+ * routes: [
391
+ * route(produce(OrderEvents, { events: ['OrderPlaced', 'OrderShipped'] }), {
392
+ * transport: 'kafka',
393
+ * destination: 'orders',
394
+ * wireTypes: { OrderPlaced: 'Order Placed' },
395
+ * }),
396
+ * ]
397
+ * ```
398
+ *
399
+ * @throws ConfigurationError for anything but a `produce(...)` declaration, and
400
+ * `UnknownCatalogEventError` or `WireTypeCollisionError` for bad `wireTypes`.
401
+ */
402
+ declare function route<TCatalog extends Catalog, TName extends keyof TCatalog>(declaration: CatalogDeclaration<TCatalog, TName>, options: CatalogRouteOptions<Extract<TName, string>>): CatalogRoute;
403
+ /**
404
+ * Event type to wire name of the events of `catalog` that have one (`wireTypes` of
405
+ * `defineCatalog`), for the `wireTypes` of a `jsonEnvelope` that reads them: a service that only
406
+ * consumes a catalog has no route to declare them, and gives its codec these instead.
407
+ */
408
+ declare function catalogWireTypes(catalog: Catalog): Readonly<Record<string, string>>;
409
+ /** Plain routes and catalog routes, as `RelayBusOptions.routes` accepts them. */
410
+ type RouteDefinition = RouteOptions | CatalogRoute;
411
+
351
412
  /**
352
413
  * RFC 9562 UUID version 7: 48-bit Unix millisecond timestamp followed by random
353
414
  * bits. Time-ordered, so it keeps B-tree indexes compact, and globally unique,
@@ -526,7 +587,9 @@ interface BacklogEvent {
526
587
  interface RelayBusObserver {
527
588
  /**
528
589
  * Events were written into the publishing transaction. Called before that
529
- * transaction commits, so it can still roll back. Duplicates are not reported.
590
+ * transaction commits, so it can still roll back, except in a `bus.transaction` with an
591
+ * `isolationLevel`, whose publishes are reported once the attempt that committed is over (an
592
+ * attempt may be rolled back and rerun). Duplicates are not reported.
530
593
  */
531
594
  onPublished?(event: {
532
595
  readonly envelopes: readonly EventEnvelope[];
@@ -591,6 +654,27 @@ interface RelayBusLogger {
591
654
  * but no longer atomic with business writes made outside that transaction.
592
655
  */
593
656
  type TransactionMode = 'required' | 'optional';
657
+ /** Options of one `bus.transaction` (and `@RelayBusTransaction()` method). */
658
+ interface TransactionOptions {
659
+ /**
660
+ * Runs the transaction at this isolation level; the store must support it, else the call
661
+ * throws a {@link ConfigurationError} (PostgreSQL stores do; MongoDB and the in-memory store
662
+ * do not). Without it the transaction runs exactly as before, at the database default.
663
+ *
664
+ * With `'repeatable read'` or `'serializable'`, a serialization failure (PostgreSQL SQLSTATE
665
+ * `40001`) rolls the transaction back and `work` runs again in a new one, up to
666
+ * {@link TransactionOptions.maxAttempts} times, so **`work` may run more than once**: keep
667
+ * everything but database writes through the transaction (HTTP calls, broker publishes,
668
+ * counters, emails) out of it. Events published by a failed attempt are rolled back with it.
669
+ */
670
+ readonly isolationLevel?: TransactionIsolationLevel;
671
+ /**
672
+ * How many times `work` may run when `isolationLevel` is `'repeatable read'` or `'serializable'`
673
+ * and the transaction fails with a serialization failure; the last failure is rethrown unchanged.
674
+ * Each attempt is held to its own `transactionTimeout`. A positive integer. Default 3.
675
+ */
676
+ readonly maxAttempts?: number;
677
+ }
594
678
  /**
595
679
  * What `publish` does with an event that has no handler or route in this
596
680
  * process. Deliveries are created from the publisher's registrations, so a
@@ -690,8 +774,12 @@ interface RelayBusOptions<TTransaction = unknown> {
690
774
  readonly dispatcher?: DispatcherOptions;
691
775
  readonly retention?: RetentionOptions;
692
776
  readonly transports?: readonly Transport[];
693
- /** Events forwarded to brokers after they commit. */
694
- readonly routes?: readonly RouteOptions[];
777
+ /**
778
+ * Events forwarded to brokers after they commit: a route per event class or type, or
779
+ * `route(produce(Catalog, { events }), { transport, destination, ... })` for several events of a
780
+ * catalog with shared settings.
781
+ */
782
+ readonly routes?: readonly RouteDefinition[];
695
783
  /** Broker destinations whose events are stored and dispatched to local handlers. */
696
784
  readonly subscriptions?: readonly InboundSubscriptionOptions[];
697
785
  /** Create tables and indexes on start. Default true. */
@@ -923,14 +1011,26 @@ declare class RelayBus<TTransaction = unknown> {
923
1011
  * Runs `work` in a new store transaction. `publish` calls inside it join the
924
1012
  * transaction without passing it explicitly. The transaction is held to `transactionTimeout`
925
1013
  * and rolled back with a {@link TransactionTimeoutError} when it runs past it.
1014
+ *
1015
+ * With `options.isolationLevel` it runs at that level, and at `'repeatable read'` or
1016
+ * `'serializable'` a serialization failure reruns `work` in a new transaction (see
1017
+ * {@link TransactionOptions}), so `work` may run more than once. Without options nothing
1018
+ * changes. The call always opens a transaction of its own, also inside another one.
926
1019
  */
927
- transaction<T>(work: (transaction: TTransaction) => Promise<T>): Promise<T>;
1020
+ transaction<T>(work: (transaction: TTransaction) => Promise<T>, options?: TransactionOptions): Promise<T>;
928
1021
  /**
929
1022
  * The transaction `publish` would join right now: the one of the enclosing `bus.transaction`
930
1023
  * (or `@RelayBusTransaction()` method, or transactional handler), else the one
931
1024
  * `transactionResolver` finds. Repositories use it to write in the same transaction.
932
1025
  */
933
1026
  currentTransaction(): TTransaction | undefined;
1027
+ /**
1028
+ * The isolation level the transaction `publish` would join was opened at by
1029
+ * `bus.transaction({ isolationLevel })` (or `@RelayBusTransaction({ isolationLevel })`);
1030
+ * undefined for every other transaction, including those without a level and the one of
1031
+ * `transactionResolver`.
1032
+ */
1033
+ currentIsolationLevel(): TransactionIsolationLevel | undefined;
934
1034
  /**
935
1035
  * Stores an event received from a broker and schedules it for local handlers.
936
1036
  * Idempotent on the event id. Transports call it and acknowledge the message
@@ -977,5 +1077,5 @@ declare class RelayBus<TTransaction = unknown> {
977
1077
  purgeExpired(): Promise<PurgeResult>;
978
1078
  }
979
1079
 
980
- export { AppendEntry, AppendResult, CatalogPublisher, ClaimRequest, ClaimedDelivery, Clock, DEFAULT_RETRY_POLICY, DEFAULT_ROUTE_RETRY_POLICY, DeliveryQuery, DeliveryRecord, DomainEvent, Duration, ErrorInfo, EventClass, EventEnvelope, EventHeaders, EventReference, EventStore, EventStoreStatistics, InMemoryEventStore, InMemoryTransaction, LeaseReference, PayloadOf, PurgeRequest, PurgeResult, RelayBus, StandardSchemaV1, TRACEPARENT_HEADER, TRACESTATE_HEADER, Transport, computeRetryDelay, consume, createRetryPolicy, createUuidV7, defineCatalog, defineHandlers, describeCatalog, isRetryableError, produce, traceContextOf };
981
- export type { BacklogEvent, Catalog, CatalogDeclaration, CatalogDescription, CatalogEntry, CatalogEventClass, CatalogEventName, CatalogEventSelection, CatalogEvents, CatalogRole, ClaimFinishedEvent, DefineCatalogOptions, DeliveryKind, DispatcherOptions, EventHandler, EventOf, FailureStage, HandlerContext, HandlerDescriptor, HandlerFinishedEvent, HandlerShapeOf, HandlerStartedEvent, HandlersOf, IdentifierGenerator, InMemoryEventStoreOptions, InboundSubscriptionOptions, InvalidMessageEvent, JitterStrategy, MatchingHandlers, NoExtraHandlers, OperationOutcome, PartialHandlersOf, ProcessTraceInput, PublishFinishedEvent, PublishOneOptions, PublishOptions, PublishTarget, PublishTraceInput, ReceiveFinishedEvent, ReceiveOutcome, ReceiveStatus, RelayBusLogger, RelayBusObserver, RelayBusOptions, RelayBusTracer, RetentionOptions, RetryPolicy, RetryPolicyOptions, RouteOptions, SendTraceInput, StopOptions, StopResult, SubscribeCatalogOptions, SubscribeOptions, TraceContext, TransactionMode, TransportSentEvent, UnroutedEventPolicy };
1080
+ export { AppendEntry, AppendResult, CatalogPublisher, ClaimRequest, ClaimedDelivery, Clock, DEFAULT_RETRY_POLICY, DEFAULT_ROUTE_RETRY_POLICY, DeliveryQuery, DeliveryRecord, DomainEvent, Duration, ErrorInfo, EventClass, EventEnvelope, EventHeaders, EventReference, EventStore, EventStoreStatistics, InMemoryEventStore, InMemoryTransaction, LeaseReference, PayloadOf, PurgeRequest, PurgeResult, RelayBus, StandardSchemaV1, TRACEPARENT_HEADER, TRACESTATE_HEADER, TransactionIsolationLevel, Transport, catalogWireTypes, computeRetryDelay, consume, createRetryPolicy, createUuidV7, defineCatalog, defineHandlers, describeCatalog, isRetryableError, produce, route, traceContextOf };
1081
+ export type { BacklogEvent, Catalog, CatalogDeclaration, CatalogDescription, CatalogEntry, CatalogEventClass, CatalogEventName, CatalogEventSelection, CatalogEvents, CatalogRole, CatalogRoute, CatalogRouteOptions, ClaimFinishedEvent, DefineCatalogOptions, DeliveryKind, DispatcherOptions, EventHandler, EventOf, FailureStage, HandlerContext, HandlerDescriptor, HandlerFinishedEvent, HandlerShapeOf, HandlerStartedEvent, HandlersOf, IdentifierGenerator, InMemoryEventStoreOptions, InboundSubscriptionOptions, InvalidMessageEvent, JitterStrategy, MatchingHandlers, NoExtraHandlers, OperationOutcome, PartialHandlersOf, ProcessTraceInput, PublishFinishedEvent, PublishOneOptions, PublishOptions, PublishTarget, PublishTraceInput, ReceiveFinishedEvent, ReceiveOutcome, ReceiveStatus, RelayBusLogger, RelayBusObserver, RelayBusOptions, RelayBusTracer, RetentionOptions, RetryPolicy, RetryPolicyOptions, RouteDefinition, RouteOptions, SendTraceInput, StopOptions, StopResult, SubscribeCatalogOptions, SubscribeOptions, TraceContext, TransactionMode, TransactionOptions, TransportSentEvent, UnroutedEventPolicy };