@relaybus/core 0.4.1 → 0.5.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.cjs CHANGED
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- var chunkJ76M7XTS_cjs = require('./chunk-J76M7XTS.cjs');
3
+ var chunk2E6NSHGE_cjs = require('./chunk-2E6NSHGE.cjs');
4
4
  var crypto = require('crypto');
5
5
  var async_hooks = require('async_hooks');
6
6
  var os = require('os');
@@ -10,32 +10,50 @@ var systemClock = {
10
10
  now: () => /* @__PURE__ */ new Date()
11
11
  };
12
12
 
13
+ // src/catalog-wire-names.ts
14
+ function checkWireNames(wireNames, { events, label }) {
15
+ const checked = /* @__PURE__ */ new Map();
16
+ const owners = /* @__PURE__ */ new Map();
17
+ for (const [event, wire] of Object.entries(wireNames ?? {})) {
18
+ if (!events.includes(event)) {
19
+ throw new chunk2E6NSHGE_cjs.UnknownCatalogEventError(
20
+ `${label} has no event named '${event}' to give a wire name; its events are ${events.join(", ")}`
21
+ );
22
+ }
23
+ if (typeof wire !== "string" || wire.length === 0) {
24
+ throw new chunk2E6NSHGE_cjs.ConfigurationError(`${label} needs a non-empty wire name for '${event}'`);
25
+ }
26
+ const owner = owners.get(wire);
27
+ if (owner !== void 0) {
28
+ throw new chunk2E6NSHGE_cjs.WireTypeCollisionError(
29
+ `${label} gives events '${owner}' and '${event}' the same wire name '${wire}'`
30
+ );
31
+ }
32
+ owners.set(wire, event);
33
+ checked.set(event, wire);
34
+ }
35
+ return checked;
36
+ }
37
+
13
38
  // src/catalog.ts
14
39
  var EVENT_NAME_PATTERN = /^[A-Za-z_][A-Za-z0-9_]*$/;
15
40
  var descriptions = /* @__PURE__ */ new WeakMap();
41
+ var catalogWireNames = /* @__PURE__ */ new WeakMap();
16
42
  function defineCatalog(events, options = {}) {
17
43
  const label = options.name === void 0 ? "Catalog" : `Catalog '${options.name}'`;
18
44
  const names = Object.keys(events);
19
45
  if (names.length === 0) {
20
- throw new chunkJ76M7XTS_cjs.ConfigurationError(`${label} must contain at least one event`);
46
+ throw new chunk2E6NSHGE_cjs.ConfigurationError(`${label} must contain at least one event`);
21
47
  }
48
+ const wireTypes = checkWireNames(options.wireTypes, { events: names, label });
22
49
  const owners = /* @__PURE__ */ new Map();
23
50
  const entries = names.map((name) => {
24
51
  const eventClass = events[name];
25
- if (!EVENT_NAME_PATTERN.test(name)) {
26
- throw new chunkJ76M7XTS_cjs.ConfigurationError(
27
- `${label} entry '${name}' must be a valid identifier made of letters, digits and underscores because it names the handler method on${name} and its handler id`
28
- );
29
- }
30
- if (typeof eventClass !== "function" || typeof eventClass.type !== "string") {
31
- throw new chunkJ76M7XTS_cjs.ConfigurationError(
32
- `${label} entry '${name}' must be an event class created with defineEvent`
33
- );
34
- }
35
- chunkJ76M7XTS_cjs.assertEventType(eventClass.type);
52
+ assertCatalogEntry(name, { eventClass, label });
53
+ chunk2E6NSHGE_cjs.assertEventType(eventClass.type);
36
54
  const owner = owners.get(eventClass.type);
37
55
  if (owner !== void 0) {
38
- throw new chunkJ76M7XTS_cjs.ConfigurationError(
56
+ throw new chunk2E6NSHGE_cjs.ConfigurationError(
39
57
  `${label} entries '${owner}' and '${name}' share the event type '${eventClass.type}'`
40
58
  );
41
59
  }
@@ -44,12 +62,29 @@ function defineCatalog(events, options = {}) {
44
62
  });
45
63
  const catalog = Object.freeze({ ...events });
46
64
  descriptions.set(catalog, Object.freeze({ name: options.name, entries: Object.freeze(entries) }));
65
+ catalogWireNames.set(catalog, wireTypes);
47
66
  return catalog;
48
67
  }
68
+ function wireNamesOf(catalog) {
69
+ describeCatalog(catalog);
70
+ return catalogWireNames.get(catalog);
71
+ }
72
+ function assertCatalogEntry(name, { eventClass, label }) {
73
+ if (!EVENT_NAME_PATTERN.test(name)) {
74
+ throw new chunk2E6NSHGE_cjs.ConfigurationError(
75
+ `${label} entry '${name}' must be a valid identifier made of letters, digits and underscores because it names the handler method on${name} and its handler id`
76
+ );
77
+ }
78
+ if (typeof eventClass !== "function" || typeof eventClass.type !== "string") {
79
+ throw new chunk2E6NSHGE_cjs.ConfigurationError(
80
+ `${label} entry '${name}' must be an event class created with defineEvent`
81
+ );
82
+ }
83
+ }
49
84
  function describeCatalog(catalog) {
50
85
  const description = descriptions.get(catalog);
51
86
  if (description === void 0) {
52
- throw new chunkJ76M7XTS_cjs.ConfigurationError("Expected a catalog created with defineCatalog");
87
+ throw new chunk2E6NSHGE_cjs.ConfigurationError("Expected a catalog created with defineCatalog");
53
88
  }
54
89
  return description;
55
90
  }
@@ -64,19 +99,19 @@ function selectCatalogEntries(catalog, events) {
64
99
  }
65
100
  const label = catalogLabel(catalog);
66
101
  if (events.length === 0) {
67
- throw new chunkJ76M7XTS_cjs.ConfigurationError(
102
+ throw new chunk2E6NSHGE_cjs.ConfigurationError(
68
103
  `The events selected from ${label} must name at least one event; omit events to select all of them`
69
104
  );
70
105
  }
71
106
  const selected = /* @__PURE__ */ new Set();
72
107
  for (const event of events) {
73
108
  if (!entries.some((entry) => entry.name === event)) {
74
- throw new chunkJ76M7XTS_cjs.ConfigurationError(
109
+ throw new chunk2E6NSHGE_cjs.ConfigurationError(
75
110
  `${capitalize(label)} has no event named '${String(event)}'; its events are ${entries.map((entry) => entry.name).join(", ")}`
76
111
  );
77
112
  }
78
113
  if (selected.has(event)) {
79
- throw new chunkJ76M7XTS_cjs.ConfigurationError(`Event '${String(event)}' of ${label} is selected twice`);
114
+ throw new chunk2E6NSHGE_cjs.ConfigurationError(`Event '${String(event)}' of ${label} is selected twice`);
80
115
  }
81
116
  selected.add(event);
82
117
  }
@@ -88,7 +123,7 @@ function capitalize(text) {
88
123
  function requireCatalogEntry(catalog, event) {
89
124
  const entry = describeCatalog(catalog).entries.find((candidate) => candidate.type === event.type);
90
125
  if (entry === void 0) {
91
- throw new chunkJ76M7XTS_cjs.ConfigurationError(
126
+ throw new chunk2E6NSHGE_cjs.ConfigurationError(
92
127
  `Event type '${String(event.type)}' is not part of ${catalogLabel(catalog)}`
93
128
  );
94
129
  }
@@ -104,13 +139,13 @@ function resolveCatalogHandlers(catalog, {
104
139
  const lookup = handlers;
105
140
  const missing = entries.filter((entry) => typeof lookup[entry.handlerName] !== "function");
106
141
  if (missing.length > 0) {
107
- throw new chunkJ76M7XTS_cjs.ConfigurationError(
142
+ throw new chunk2E6NSHGE_cjs.ConfigurationError(
108
143
  `Handlers for catalog ${name === void 0 ? "events" : `'${name}'`} lack methods: ${missing.map((entry) => entry.handlerName).join(", ")}`
109
144
  );
110
145
  }
111
146
  const prefix = idPrefix ?? name ?? classNameOf(handlers);
112
147
  if (prefix === void 0) {
113
- throw new chunkJ76M7XTS_cjs.ConfigurationError(
148
+ throw new chunk2E6NSHGE_cjs.ConfigurationError(
114
149
  "Cannot derive handler ids: name the catalog, pass an idPrefix or use a class instance as handlers"
115
150
  );
116
151
  }
@@ -141,7 +176,7 @@ function assertExactHandlers(catalog, { entries, handlers }) {
141
176
  ...extra.map((key) => `unexpected ${key}`),
142
177
  ...notFunctions.map((key) => `${key} is not a function`)
143
178
  ];
144
- throw new chunkJ76M7XTS_cjs.ConfigurationError(
179
+ throw new chunk2E6NSHGE_cjs.ConfigurationError(
145
180
  `Handlers do not match ${catalogLabel(catalog)}: ${problems.join(", ")}`
146
181
  );
147
182
  }
@@ -192,7 +227,7 @@ var CatalogPublisher = class {
192
227
  #assertPublishable(event) {
193
228
  const { name } = requireCatalogEntry(this.#catalog, event);
194
229
  if (!this.#publishes(name)) {
195
- throw new chunkJ76M7XTS_cjs.ConfigurationError(
230
+ throw new chunk2E6NSHGE_cjs.ConfigurationError(
196
231
  `Event type '${event.type}' is not one of the events this view of ${catalogLabel(
197
232
  this.#catalog
198
233
  )} publishes (${this.#entries.map((entry) => entry.name).join(", ")})`
@@ -209,6 +244,9 @@ function consume(catalog, options = {}) {
209
244
  function produce(catalog, options = {}) {
210
245
  return declare(catalog, { role: "produce", options });
211
246
  }
247
+ function isCatalogDeclaration(value) {
248
+ return typeof value === "object" && value !== null && declarations.has(value);
249
+ }
212
250
  function declare(catalog, { role, options }) {
213
251
  const entries = selectCatalogEntries(catalog, options.events);
214
252
  const declaration = Object.freeze({
@@ -246,7 +284,7 @@ function assertCatalogsCovered(requirements, registrationsOf) {
246
284
  }
247
285
  if (problems.size > 0) {
248
286
  const heading = onlyBareCatalogs ? "Catalog events without a handler or route:" : "Catalog events this service does not cover:";
249
- throw new chunkJ76M7XTS_cjs.ConfigurationError(
287
+ throw new chunk2E6NSHGE_cjs.ConfigurationError(
250
288
  `${heading}
251
289
  ${[...problems].map((line) => ` - ${line}`).join("\n")}`
252
290
  );
@@ -274,6 +312,144 @@ function describeGap(entry, {
274
312
  return `${subject}: produced here, needs a route to a transport${handlerNote}`;
275
313
  }
276
314
 
315
+ // src/route-wire-types.ts
316
+ function mergeRouteWireTypes(declared, routed) {
317
+ const merged = /* @__PURE__ */ new Map();
318
+ for (const { transport, type, wire } of declared) {
319
+ const types = merged.get(transport) ?? /* @__PURE__ */ new Map();
320
+ merged.set(transport, types);
321
+ addWireType(types, { type, wire, transport });
322
+ }
323
+ for (const [transport, types] of merged) {
324
+ assertDistinctOnTheWire(types, {
325
+ transport,
326
+ routedTypes: routed.filter((item) => item.transport === transport).map((item) => item.type)
327
+ });
328
+ }
329
+ return new Map([...merged].map(([transport, types]) => [transport, Object.fromEntries(types)]));
330
+ }
331
+ function assertDistinctOnTheWire(types, {
332
+ transport,
333
+ routedTypes
334
+ }) {
335
+ const owners = /* @__PURE__ */ new Map();
336
+ for (const type of /* @__PURE__ */ new Set([...types.keys(), ...routedTypes])) {
337
+ const name = types.get(type) ?? type;
338
+ const owner = owners.get(name);
339
+ if (owner !== void 0) {
340
+ throw new chunk2E6NSHGE_cjs.WireTypeCollisionError(
341
+ `Event types '${owner}' and '${type}' both travel as '${name}' on transport '${transport}'`
342
+ );
343
+ }
344
+ owners.set(name, type);
345
+ }
346
+ }
347
+ function addWireType(types, {
348
+ type,
349
+ wire,
350
+ transport
351
+ }) {
352
+ const current = types.get(type);
353
+ if (current !== void 0 && current !== wire) {
354
+ throw new chunk2E6NSHGE_cjs.WireTypeCollisionError(
355
+ `Event type '${type}' has the wire names '${current}' and '${wire}' on transport '${transport}'; a transport writes one`
356
+ );
357
+ }
358
+ const owner = [...types].find(
359
+ ([otherType, otherWire]) => otherWire === wire && otherType !== type
360
+ );
361
+ if (owner !== void 0) {
362
+ throw new chunk2E6NSHGE_cjs.WireTypeCollisionError(
363
+ `Event types '${owner[0]}' and '${type}' share the wire name '${wire}' on transport '${transport}'`
364
+ );
365
+ }
366
+ types.set(type, wire);
367
+ }
368
+
369
+ // src/catalog-routes.ts
370
+ var catalogRoutes = /* @__PURE__ */ new WeakSet();
371
+ function route(declaration, options) {
372
+ if (!isCatalogDeclaration(declaration) || declaration.role !== "produce") {
373
+ throw new chunk2E6NSHGE_cjs.ConfigurationError("route() takes a produce(catalog, { events }) declaration");
374
+ }
375
+ const wireNames = checkWireNames(options.wireTypes, {
376
+ events: declaration.events,
377
+ label: `The route of ${catalogLabel(declaration.catalog)}`
378
+ });
379
+ const created = Object.freeze({
380
+ declaration,
381
+ options: Object.freeze({ ...options }),
382
+ wireNames
383
+ });
384
+ catalogRoutes.add(created);
385
+ return created;
386
+ }
387
+ function catalogWireTypes(catalog) {
388
+ return Object.fromEntries(
389
+ describeCatalog(catalog).entries.flatMap((entry) => {
390
+ const wire = wireNamesOf(catalog).get(entry.name);
391
+ return wire === void 0 ? [] : [[entry.type, wire]];
392
+ })
393
+ );
394
+ }
395
+ function isCatalogRoute(definition) {
396
+ return catalogRoutes.has(definition);
397
+ }
398
+ function expandRoutes(definitions) {
399
+ if (!definitions.some(isCatalogRoute)) {
400
+ return { routes: definitions, wireTypes: /* @__PURE__ */ new Map() };
401
+ }
402
+ const expanded = definitions.flatMap(
403
+ (definition) => isCatalogRoute(definition) ? expandCatalogRoute(definition) : [plainRoute(definition)]
404
+ );
405
+ assertNotRoutedTwice(expanded);
406
+ return {
407
+ routes: expanded.map((item) => item.route),
408
+ wireTypes: mergeRouteWireTypes(
409
+ expanded.flatMap(declaredWireType),
410
+ expanded.map((item) => ({ transport: item.route.transport, type: item.type }))
411
+ )
412
+ };
413
+ }
414
+ function declaredWireType({
415
+ route: { transport },
416
+ type,
417
+ wireType
418
+ }) {
419
+ return wireType === void 0 ? [] : [{ transport, type, wire: wireType }];
420
+ }
421
+ function plainRoute(definition) {
422
+ const { event } = definition;
423
+ const type = typeof event === "string" ? event : event.type;
424
+ return { route: definition, type, fromCatalog: false, wireType: void 0 };
425
+ }
426
+ function expandCatalogRoute({ declaration, options, wireNames }) {
427
+ const catalogNames = wireNamesOf(declaration.catalog);
428
+ const shared = Object.fromEntries(
429
+ Object.entries(options).filter(([name]) => name !== "wireTypes")
430
+ );
431
+ return declaration.entries.map((entry) => ({
432
+ route: { ...shared, event: entry.eventClass },
433
+ type: entry.type,
434
+ fromCatalog: true,
435
+ wireType: wireNames.get(entry.name) ?? catalogNames.get(entry.name)
436
+ }));
437
+ }
438
+ function assertNotRoutedTwice(expanded) {
439
+ const seen = /* @__PURE__ */ new Map();
440
+ for (const item of expanded) {
441
+ const destination = item.route.destination ?? item.type;
442
+ const key = JSON.stringify([item.route.transport, destination, item.type]);
443
+ const earlier = seen.get(key);
444
+ if (earlier !== void 0 && (earlier.fromCatalog || item.fromCatalog)) {
445
+ throw new chunk2E6NSHGE_cjs.DuplicateRouteEventError(
446
+ `Event type ${item.type} is routed to '${destination}' of transport '${item.route.transport}' twice`
447
+ );
448
+ }
449
+ seen.set(key, item);
450
+ }
451
+ }
452
+
277
453
  // src/event-store.ts
278
454
  var DELIVERY_STATUSES = [
279
455
  "pending",
@@ -281,6 +457,11 @@ var DELIVERY_STATUSES = [
281
457
  "completed",
282
458
  "dead"
283
459
  ];
460
+ var TRANSACTION_ISOLATION_LEVELS = [
461
+ "read committed",
462
+ "repeatable read",
463
+ "serializable"
464
+ ];
284
465
  var UUID_BYTES = 16;
285
466
  var TIMESTAMP_BYTES = 6;
286
467
  var BYTE_VALUES = 256;
@@ -512,7 +693,7 @@ var InMemoryRowLocks = class {
512
693
  };
513
694
 
514
695
  // src/in-memory/in-memory-settlement.ts
515
- var InMemorySettlement = class extends chunkJ76M7XTS_cjs.LeaseSettlement {
696
+ var InMemorySettlement = class extends chunk2E6NSHGE_cjs.LeaseSettlement {
516
697
  #tables;
517
698
  #retainCompleted;
518
699
  constructor(tables, retainCompleted) {
@@ -606,7 +787,7 @@ var InMemoryTransaction = class {
606
787
  }
607
788
  enqueue(operation) {
608
789
  if (this.#state !== "open") {
609
- throw new chunkJ76M7XTS_cjs.ConfigurationError(`Cannot write through a ${this.#state} in-memory transaction`);
790
+ throw new chunk2E6NSHGE_cjs.ConfigurationError(`Cannot write through a ${this.#state} in-memory transaction`);
610
791
  }
611
792
  this.#operations.push(operation);
612
793
  }
@@ -641,7 +822,7 @@ var InMemoryEventStore = class {
641
822
  #generateIdentifier;
642
823
  constructor(options = {}) {
643
824
  if (process.env["NODE_ENV"] === "production" && options.allowInProduction !== true) {
644
- throw new chunkJ76M7XTS_cjs.ConfigurationError(
825
+ throw new chunk2E6NSHGE_cjs.ConfigurationError(
645
826
  "InMemoryEventStore loses every event on restart. Use a database store in production, or pass allowInProduction: true"
646
827
  );
647
828
  }
@@ -699,7 +880,7 @@ var InMemoryEventStore = class {
699
880
  #newDeliveries(entry, pendingDeliveryKeys) {
700
881
  const eventId = entry.event.id;
701
882
  return entry.deliveries.filter((delivery) => {
702
- const deliveryKey = chunkJ76M7XTS_cjs.CompositeKey.of(eventId, delivery.handler);
883
+ const deliveryKey = chunk2E6NSHGE_cjs.CompositeKey.of(eventId, delivery.handler);
703
884
  if (pendingDeliveryKeys.has(deliveryKey) || this.#tables.events.get(eventId)?.handlers.has(delivery.handler) === true) {
704
885
  return false;
705
886
  }
@@ -846,8 +1027,8 @@ var ShutdownDeadline = class {
846
1027
  signal.addEventListener("abort", () => resolve("timeout"), { once: true });
847
1028
  });
848
1029
  this.#cancelTimers = [
849
- chunkJ76M7XTS_cjs.schedule(clock, { run: drain, delayMs: Math.floor(timeoutMs * DRAIN_SHARE) }),
850
- chunkJ76M7XTS_cjs.schedule(clock, { run: () => this.#controller.abort(), delayMs: timeoutMs })
1030
+ chunk2E6NSHGE_cjs.schedule(clock, { run: drain, delayMs: Math.floor(timeoutMs * DRAIN_SHARE) }),
1031
+ chunk2E6NSHGE_cjs.schedule(clock, { run: () => this.#controller.abort(), delayMs: timeoutMs })
851
1032
  ];
852
1033
  }
853
1034
  /** Aborted when the deadline passes. */
@@ -902,7 +1083,7 @@ var BusLifecycle = class {
902
1083
  /** Throws unless the bus is idle: a bus starts once. */
903
1084
  assertStartable() {
904
1085
  if (this.#state !== "idle") {
905
- throw new chunkJ76M7XTS_cjs.LifecycleError(
1086
+ throw new chunk2E6NSHGE_cjs.LifecycleError(
906
1087
  this.#state === "stopped" ? "A stopped relaybus cannot be started again; create a new instance" : `Cannot start a relaybus that is ${this.#state}`
907
1088
  );
908
1089
  }
@@ -923,6 +1104,13 @@ var BusLifecycle = class {
923
1104
  markStopped() {
924
1105
  this.#state = "stopped";
925
1106
  }
1107
+ /** Closes the transports a failed start may have connected, within `timeoutMs`. */
1108
+ closeAfterFailedStart(timeoutMs) {
1109
+ return withinDeadline({ clock: this.#parts.clock, timeoutMs }, async (deadline) => {
1110
+ await this.#parts.closeTransports(deadline);
1111
+ return 0;
1112
+ });
1113
+ }
926
1114
  /** The stop of the bus within `timeoutMs`; a later call shares the first one's. */
927
1115
  stop(timeoutMs) {
928
1116
  if (this.#stopping !== void 0) {
@@ -997,10 +1185,10 @@ function toDescriptor(registration, eventType) {
997
1185
  }
998
1186
  function describeEvent(event) {
999
1187
  if (typeof event === "string") {
1000
- chunkJ76M7XTS_cjs.assertEventType(event);
1188
+ chunk2E6NSHGE_cjs.assertEventType(event);
1001
1189
  return { type: event, schema: void 0 };
1002
1190
  }
1003
- chunkJ76M7XTS_cjs.assertEventType(event.type);
1191
+ chunk2E6NSHGE_cjs.assertEventType(event.type);
1004
1192
  return { type: event.type, schema: event.schema };
1005
1193
  }
1006
1194
 
@@ -1059,7 +1247,7 @@ var BusObservation = class _BusObservation {
1059
1247
  eventTypes: input.eventTypes,
1060
1248
  durationMs: this.#now() - startedAt,
1061
1249
  outcome,
1062
- ...error === void 0 ? {} : { error: chunkJ76M7XTS_cjs.toErrorInfo(error) }
1250
+ ...error === void 0 ? {} : { error: chunk2E6NSHGE_cjs.toErrorInfo(error) }
1063
1251
  });
1064
1252
  };
1065
1253
  try {
@@ -1082,7 +1270,7 @@ var BusObservation = class _BusObservation {
1082
1270
  this.#reportReceive(input, { outcome, startedAt, failure: void 0 });
1083
1271
  return outcome;
1084
1272
  } catch (error) {
1085
- const outcome = error instanceof chunkJ76M7XTS_cjs.InvalidEnvelopeError ? "invalid" : "error";
1273
+ const outcome = error instanceof chunk2E6NSHGE_cjs.InvalidEnvelopeError ? "invalid" : "error";
1086
1274
  this.#reportReceive(input, { outcome, startedAt, failure: error });
1087
1275
  throw error;
1088
1276
  }
@@ -1098,7 +1286,7 @@ var BusObservation = class _BusObservation {
1098
1286
  traceContext: isObject(headers) ? traceContextOf(headers) : void 0,
1099
1287
  outcome: report.outcome,
1100
1288
  durationMs: this.#now() - report.startedAt,
1101
- ...report.failure === void 0 ? {} : { error: chunkJ76M7XTS_cjs.toErrorInfo(report.failure) }
1289
+ ...report.failure === void 0 ? {} : { error: chunk2E6NSHGE_cjs.toErrorInfo(report.failure) }
1102
1290
  });
1103
1291
  }
1104
1292
  /** The handler of a route: sends inside the tracer's send span and reports each send. */
@@ -1128,7 +1316,7 @@ var BusObservation = class _BusObservation {
1128
1316
  destination,
1129
1317
  durationMs: this.#now() - startedAt,
1130
1318
  outcome,
1131
- ...error === void 0 ? {} : { error: chunkJ76M7XTS_cjs.toErrorInfo(error) }
1319
+ ...error === void 0 ? {} : { error: chunk2E6NSHGE_cjs.toErrorInfo(error) }
1132
1320
  });
1133
1321
  };
1134
1322
  try {
@@ -1148,7 +1336,7 @@ var BusObservation = class _BusObservation {
1148
1336
  return void 0;
1149
1337
  }
1150
1338
  let reading = false;
1151
- return chunkJ76M7XTS_cjs.schedule(this.#clock, {
1339
+ return chunk2E6NSHGE_cjs.schedule(this.#clock, {
1152
1340
  run: () => {
1153
1341
  if (reading) {
1154
1342
  return;
@@ -1228,7 +1416,7 @@ var DispatchObservation = class _DispatchObservation {
1228
1416
  lanes: 0,
1229
1417
  durationMs: this.#clock.now().getTime() - startedAt,
1230
1418
  outcome: "error",
1231
- error: chunkJ76M7XTS_cjs.toErrorInfo(error)
1419
+ error: chunk2E6NSHGE_cjs.toErrorInfo(error)
1232
1420
  });
1233
1421
  throw error;
1234
1422
  }
@@ -1259,7 +1447,7 @@ var DispatchObservation = class _DispatchObservation {
1259
1447
  await settled;
1260
1448
  } catch (error) {
1261
1449
  const durationMs2 = this.#clock.now().getTime() - startedAt;
1262
- const errorInfo = chunkJ76M7XTS_cjs.toErrorInfo(error);
1450
+ const errorInfo = chunk2E6NSHGE_cjs.toErrorInfo(error);
1263
1451
  this.#notifier.notify("onHandlerFinished", {
1264
1452
  delivery,
1265
1453
  kind,
@@ -1330,7 +1518,7 @@ var ClaimLoop = class {
1330
1518
  this.#requestClaim();
1331
1519
  return;
1332
1520
  }
1333
- this.#cooldownTimer ??= chunkJ76M7XTS_cjs.schedule(this.#parts.dependencies.clock, {
1521
+ this.#cooldownTimer ??= chunk2E6NSHGE_cjs.schedule(this.#parts.dependencies.clock, {
1334
1522
  run: () => {
1335
1523
  this.#cooldownTimer = void 0;
1336
1524
  this.#requestClaim();
@@ -1424,7 +1612,7 @@ var ClaimLoop = class {
1424
1612
  async #waitForWakeUp() {
1425
1613
  if (this.#signalledMinimum === void 0) {
1426
1614
  await new Promise((resolve) => {
1427
- const cancel = chunkJ76M7XTS_cjs.schedule(this.#parts.dependencies.clock, {
1615
+ const cancel = chunk2E6NSHGE_cjs.schedule(this.#parts.dependencies.clock, {
1428
1616
  run: () => {
1429
1617
  this.#signalledMinimum = 1;
1430
1618
  resolve();
@@ -1458,7 +1646,7 @@ var ClaimLoop = class {
1458
1646
  });
1459
1647
  }
1460
1648
  } else if (free > 0) {
1461
- this.#quietRefillTimer ??= chunkJ76M7XTS_cjs.schedule(this.#parts.dependencies.clock, {
1649
+ this.#quietRefillTimer ??= chunk2E6NSHGE_cjs.schedule(this.#parts.dependencies.clock, {
1462
1650
  run: () => {
1463
1651
  this.#quietRefillTimer = void 0;
1464
1652
  this.#signal(1);
@@ -1555,14 +1743,14 @@ var LeaseRenewal = class {
1555
1743
  #abortLeaseLost(storeConfirmed = false) {
1556
1744
  const { delivery, controller } = this.#target;
1557
1745
  controller.abort(
1558
- new chunkJ76M7XTS_cjs.LeaseLostError(`Lease on delivery ${delivery.deliveryId} was lost`, { storeConfirmed })
1746
+ new chunk2E6NSHGE_cjs.LeaseLostError(`Lease on delivery ${delivery.deliveryId} was lost`, { storeConfirmed })
1559
1747
  );
1560
1748
  }
1561
1749
  };
1562
1750
  function startLeaseHeartbeat(dependencies, target) {
1563
1751
  const renewal = new LeaseRenewal(dependencies, target);
1564
1752
  const { clock, settings } = dependencies;
1565
- return chunkJ76M7XTS_cjs.schedule(clock, {
1753
+ return chunk2E6NSHGE_cjs.schedule(clock, {
1566
1754
  run: () => renewal.beat(),
1567
1755
  delayMs: Math.max(1, Math.floor(settings.leaseDurationMs / RENEWALS_PER_LEASE)),
1568
1756
  repeat: true,
@@ -1614,7 +1802,7 @@ var DeliveryExecutor = class {
1614
1802
  if (delivery.attempt > registration.retry.attempts) {
1615
1803
  await outcomes.bury(
1616
1804
  delivery,
1617
- new chunkJ76M7XTS_cjs.AttemptsExhaustedError(
1805
+ new chunk2E6NSHGE_cjs.AttemptsExhaustedError(
1618
1806
  `Delivery reached attempt ${delivery.attempt} of ${registration.retry.attempts} after its lease expired`
1619
1807
  )
1620
1808
  );
@@ -1658,11 +1846,11 @@ var DeliveryExecutor = class {
1658
1846
  /** Records a failed run; undefined when there is nothing to record (the store confirmed the lost lease). */
1659
1847
  #recordFailure(registration, { delivery, error }) {
1660
1848
  const { dependencies, outcomes } = this.#parts;
1661
- if (error instanceof chunkJ76M7XTS_cjs.LeaseLostError) {
1849
+ if (error instanceof chunk2E6NSHGE_cjs.LeaseLostError) {
1662
1850
  dependencies.notifier.notify("onLeaseLost", { delivery });
1663
1851
  return error.storeConfirmed ? void 0 : outcomes.giveBack(registration, { delivery, error });
1664
1852
  }
1665
- if (error instanceof chunkJ76M7XTS_cjs.DispatcherStoppedError) {
1853
+ if (error instanceof chunk2E6NSHGE_cjs.DispatcherStoppedError) {
1666
1854
  return outcomes.release(delivery);
1667
1855
  }
1668
1856
  return outcomes.fail(registration, { delivery, error });
@@ -1670,7 +1858,7 @@ var DeliveryExecutor = class {
1670
1858
  async #runHandler(registration, { delivery, controller }) {
1671
1859
  const { dependencies, invocation, outcomes } = this.#parts;
1672
1860
  const { store, clock } = dependencies;
1673
- const payload = await chunkJ76M7XTS_cjs.validatePayload(
1861
+ const payload = await chunk2E6NSHGE_cjs.validatePayload(
1674
1862
  delivery.event.type,
1675
1863
  registration.schema,
1676
1864
  delivery.event.payload
@@ -1680,7 +1868,7 @@ var DeliveryExecutor = class {
1680
1868
  await store.transaction(async (transaction) => {
1681
1869
  await invocation.invoke({ registration, event, delivery, controller, transaction });
1682
1870
  if (!await store.complete(delivery, clock.now(), transaction)) {
1683
- throw new chunkJ76M7XTS_cjs.LeaseLostError(`Lease on delivery ${delivery.deliveryId} was lost`, {
1871
+ throw new chunk2E6NSHGE_cjs.LeaseLostError(`Lease on delivery ${delivery.deliveryId} was lost`, {
1684
1872
  storeConfirmed: true
1685
1873
  });
1686
1874
  }
@@ -1704,14 +1892,14 @@ var DeliveryExecutor = class {
1704
1892
  if (registration.timeoutMs !== null && now.getTime() + registration.timeoutMs >= expiry) {
1705
1893
  expiry = now.getTime() + registration.timeoutMs + settings.leaseDurationMs;
1706
1894
  if (!await store.extendLease(delivery, new Date(expiry), now)) {
1707
- throw new chunkJ76M7XTS_cjs.LeaseLostError(`Lease on delivery ${delivery.deliveryId} was lost`, {
1895
+ throw new chunk2E6NSHGE_cjs.LeaseLostError(`Lease on delivery ${delivery.deliveryId} was lost`, {
1708
1896
  storeConfirmed: true
1709
1897
  });
1710
1898
  }
1711
1899
  }
1712
- return chunkJ76M7XTS_cjs.schedule(clock, {
1900
+ return chunk2E6NSHGE_cjs.schedule(clock, {
1713
1901
  run: () => {
1714
- controller.abort(new chunkJ76M7XTS_cjs.LeaseLostError(`Lease on delivery ${delivery.deliveryId} expired`));
1902
+ controller.abort(new chunk2E6NSHGE_cjs.LeaseLostError(`Lease on delivery ${delivery.deliveryId} expired`));
1715
1903
  },
1716
1904
  delayMs: Math.max(0, expiry - clock.now().getTime()),
1717
1905
  unref: true
@@ -1744,7 +1932,7 @@ function createRetryPolicy(options, base = DEFAULT_RETRY_POLICY) {
1744
1932
  }
1745
1933
  function mergeRetryPolicy(options, base) {
1746
1934
  const overrides = options ?? {};
1747
- const initialDelayMs = overrides.initialDelay === void 0 ? base.initialDelayMs : chunkJ76M7XTS_cjs.Milliseconds.of(overrides.initialDelay, "retry.initialDelay");
1935
+ const initialDelayMs = overrides.initialDelay === void 0 ? base.initialDelayMs : chunk2E6NSHGE_cjs.Milliseconds.of(overrides.initialDelay, "retry.initialDelay");
1748
1936
  return {
1749
1937
  attempts: overrides.attempts ?? base.attempts,
1750
1938
  initialDelayMs,
@@ -1752,25 +1940,25 @@ function mergeRetryPolicy(options, base) {
1752
1940
  maxDelayMs: overrides.maxDelay === void 0 ? (
1753
1941
  // Raising only the initial delay must not trip the inherited cap.
1754
1942
  Math.max(base.maxDelayMs, initialDelayMs)
1755
- ) : chunkJ76M7XTS_cjs.Milliseconds.of(overrides.maxDelay, "retry.maxDelay"),
1943
+ ) : chunk2E6NSHGE_cjs.Milliseconds.of(overrides.maxDelay, "retry.maxDelay"),
1756
1944
  jitter: overrides.jitter ?? base.jitter,
1757
1945
  retryIf: overrides.retryIf ?? base.retryIf
1758
1946
  };
1759
1947
  }
1760
1948
  function assertValidRetryPolicy(policy) {
1761
1949
  if (policy.attempts !== Number.POSITIVE_INFINITY && (!Number.isInteger(policy.attempts) || policy.attempts < 1)) {
1762
- throw new chunkJ76M7XTS_cjs.ConfigurationError(
1950
+ throw new chunk2E6NSHGE_cjs.ConfigurationError(
1763
1951
  `retry.attempts must be a positive integer or Infinity, received ${policy.attempts}`
1764
1952
  );
1765
1953
  }
1766
1954
  if (!Number.isFinite(policy.factor) || policy.factor < 1) {
1767
- throw new chunkJ76M7XTS_cjs.ConfigurationError(`retry.factor must be at least 1, received ${policy.factor}`);
1955
+ throw new chunk2E6NSHGE_cjs.ConfigurationError(`retry.factor must be at least 1, received ${policy.factor}`);
1768
1956
  }
1769
1957
  if (policy.maxDelayMs < policy.initialDelayMs) {
1770
- throw new chunkJ76M7XTS_cjs.ConfigurationError("retry.maxDelay must not be smaller than retry.initialDelay");
1958
+ throw new chunk2E6NSHGE_cjs.ConfigurationError("retry.maxDelay must not be smaller than retry.initialDelay");
1771
1959
  }
1772
1960
  if (!JITTER_STRATEGIES.includes(policy.jitter)) {
1773
- throw new chunkJ76M7XTS_cjs.ConfigurationError(
1961
+ throw new chunk2E6NSHGE_cjs.ConfigurationError(
1774
1962
  `retry.jitter must be one of ${JITTER_STRATEGIES.join(", ")}, received '${String(policy.jitter)}'`
1775
1963
  );
1776
1964
  }
@@ -1788,7 +1976,7 @@ function computeRetryDelay(policy, failedAttempts, random = Math.random) {
1788
1976
  }
1789
1977
  }
1790
1978
  function isRetryableError(policy, error) {
1791
- if (error instanceof chunkJ76M7XTS_cjs.NonRetryableError) {
1979
+ if (error instanceof chunk2E6NSHGE_cjs.NonRetryableError) {
1792
1980
  return false;
1793
1981
  }
1794
1982
  if (policy.retryIf === void 0) {
@@ -1838,7 +2026,7 @@ var DeliveryOutcomes = class {
1838
2026
  continue;
1839
2027
  }
1840
2028
  if (!completed) {
1841
- throw new chunkJ76M7XTS_cjs.LeaseLostError(`Lease on delivery ${delivery.deliveryId} was lost`, {
2029
+ throw new chunk2E6NSHGE_cjs.LeaseLostError(`Lease on delivery ${delivery.deliveryId} was lost`, {
1842
2030
  storeConfirmed: true
1843
2031
  });
1844
2032
  }
@@ -1876,7 +2064,7 @@ var DeliveryOutcomes = class {
1876
2064
  }
1877
2065
  bury(delivery, error) {
1878
2066
  const { store, clock, notifier } = this.#parts.dependencies;
1879
- const errorInfo = chunkJ76M7XTS_cjs.toErrorInfo(error);
2067
+ const errorInfo = chunk2E6NSHGE_cjs.toErrorInfo(error);
1880
2068
  return this.#change({
1881
2069
  operation: "bury",
1882
2070
  delivery,
@@ -1899,7 +2087,7 @@ var DeliveryOutcomes = class {
1899
2087
  #reschedule(registration, { delivery, error }) {
1900
2088
  const { store, clock, random, notifier } = this.#parts.dependencies;
1901
2089
  const policy = registration.retry;
1902
- const errorInfo = chunkJ76M7XTS_cjs.toErrorInfo(error);
2090
+ const errorInfo = chunk2E6NSHGE_cjs.toErrorInfo(error);
1903
2091
  const now = clock.now();
1904
2092
  const availableAt = new Date(
1905
2093
  now.getTime() + computeRetryDelay(policy, delivery.attempt, random)
@@ -1942,6 +2130,10 @@ function runInIsolatedScope(scope, work) {
1942
2130
  function getCurrentScope() {
1943
2131
  return storage.getStore();
1944
2132
  }
2133
+ function getCurrentIsolationLevel() {
2134
+ const scope = storage.getStore();
2135
+ return scope?.isolation !== void 0 && scope.isolation.transaction === scope.transaction ? scope.isolation.level : void 0;
2136
+ }
1945
2137
 
1946
2138
  // src/dispatch/handler-invocation.ts
1947
2139
  function rejectOnAbort(signal) {
@@ -1992,7 +2184,7 @@ var HandlerInvocation = class {
1992
2184
  return this.#timers.start({
1993
2185
  run: () => {
1994
2186
  controller.abort(
1995
- new chunkJ76M7XTS_cjs.HandlerTimeoutError(
2187
+ new chunk2E6NSHGE_cjs.HandlerTimeoutError(
1996
2188
  `Handler '${registration.id}' did not settle within ${timeoutMs}ms`
1997
2189
  )
1998
2190
  );
@@ -2016,7 +2208,7 @@ var HandlerInvocation = class {
2016
2208
  await settledWork;
2017
2209
  return;
2018
2210
  }
2019
- if (reason instanceof chunkJ76M7XTS_cjs.DispatcherStoppedError) {
2211
+ if (reason instanceof chunk2E6NSHGE_cjs.DispatcherStoppedError) {
2020
2212
  return;
2021
2213
  }
2022
2214
  const deadline = Date.now() + this.#dependencies.settings.leaseDurationMs;
@@ -2037,7 +2229,7 @@ function contextOf(input) {
2037
2229
  async function waitUntil(clock, { work, deadline }) {
2038
2230
  let cancel;
2039
2231
  const timedOut = new Promise((resolve) => {
2040
- cancel = chunkJ76M7XTS_cjs.schedule(clock, {
2232
+ cancel = chunk2E6NSHGE_cjs.schedule(clock, {
2041
2233
  run: () => resolve("timeout"),
2042
2234
  delayMs: Math.max(0, deadline - Date.now())
2043
2235
  });
@@ -2082,7 +2274,7 @@ var InFlightDeliveries = class {
2082
2274
 
2083
2275
  // src/dispatch/settlement-recovery.ts
2084
2276
  function pendingKeyOf(delivery) {
2085
- return chunkJ76M7XTS_cjs.DeliveryLeaseIdentity.of(delivery.deliveryId, delivery.leaseToken);
2277
+ return chunk2E6NSHGE_cjs.DeliveryLeaseIdentity.of(delivery.deliveryId, delivery.leaseToken);
2086
2278
  }
2087
2279
  var SettlementRecovery = class {
2088
2280
  #dependencies;
@@ -2099,7 +2291,7 @@ var SettlementRecovery = class {
2099
2291
  return;
2100
2292
  }
2101
2293
  this.#pending.set(pendingKeyOf(entry.delivery), entry);
2102
- this.#recoveryTimer ??= chunkJ76M7XTS_cjs.schedule(this.#dependencies.clock, {
2294
+ this.#recoveryTimer ??= chunk2E6NSHGE_cjs.schedule(this.#dependencies.clock, {
2103
2295
  run: () => void this.#recoverSettlements(),
2104
2296
  delayMs: this.#dependencies.settings.pollIntervalMs,
2105
2297
  repeat: true,
@@ -2109,7 +2301,7 @@ var SettlementRecovery = class {
2109
2301
  /** A new claim of this delivery makes whatever was pending for its earlier claims moot. */
2110
2302
  forgetEarlierClaims(delivery) {
2111
2303
  for (const key of [...this.#pending.keys()].filter(
2112
- (pending) => pending.startsWith(chunkJ76M7XTS_cjs.DeliveryLeaseIdentity.prefixOf(delivery.deliveryId))
2304
+ (pending) => pending.startsWith(chunk2E6NSHGE_cjs.DeliveryLeaseIdentity.prefixOf(delivery.deliveryId))
2113
2305
  )) {
2114
2306
  this.#pending.delete(key);
2115
2307
  }
@@ -2195,7 +2387,7 @@ var CompletionBatcher = class {
2195
2387
  if (this.#closing || this.#pending.length >= this.#settings.maxBatchSize || this.#pending.length >= this.#runningCount()) {
2196
2388
  this.#flush();
2197
2389
  } else {
2198
- this.#timer ??= chunkJ76M7XTS_cjs.schedule(this.#clock, {
2390
+ this.#timer ??= chunk2E6NSHGE_cjs.schedule(this.#clock, {
2199
2391
  run: () => this.#flush(),
2200
2392
  delayMs: this.#settings.windowMs
2201
2393
  });
@@ -2258,7 +2450,7 @@ var DeliveryTimers = class {
2258
2450
  cancelTimer();
2259
2451
  this.#waiting.delete(cancel);
2260
2452
  };
2261
- const cancelTimer = chunkJ76M7XTS_cjs.schedule(this.#clock, {
2453
+ const cancelTimer = chunk2E6NSHGE_cjs.schedule(this.#clock, {
2262
2454
  run: () => {
2263
2455
  this.#waiting.delete(cancel);
2264
2456
  task.run();
@@ -2419,7 +2611,7 @@ var Dispatcher = class {
2419
2611
  await deadline.race(this.#looping);
2420
2612
  const settled = this.#inFlight.allSettled();
2421
2613
  if (await deadline.raceDrain(settled) === "timeout") {
2422
- this.#inFlight.abortAll(new chunkJ76M7XTS_cjs.DispatcherStoppedError("relaybus dispatcher stopped"));
2614
+ this.#inFlight.abortAll(new chunk2E6NSHGE_cjs.DispatcherStoppedError("relaybus dispatcher stopped"));
2423
2615
  if (await deadline.race(settled) === "timeout") {
2424
2616
  return this.#abandon();
2425
2617
  }
@@ -2467,14 +2659,14 @@ var HandlerRegistry = class {
2467
2659
  register(input, defaults) {
2468
2660
  const { options } = input;
2469
2661
  if (!HANDLER_ID_PATTERN.test(options.id)) {
2470
- throw new chunkJ76M7XTS_cjs.ConfigurationError(
2662
+ throw new chunk2E6NSHGE_cjs.ConfigurationError(
2471
2663
  `Handler id '${options.id}' must be 1-255 characters of letters, digits, '.', '_', ':', '/' or '-'`
2472
2664
  );
2473
2665
  }
2474
2666
  const claimIds = [options.id, ...input.legacyIds ?? []];
2475
2667
  this.#assertUnregistered(claimIds);
2476
2668
  if (options.concurrency !== void 0) {
2477
- chunkJ76M7XTS_cjs.PositiveInteger.of(options.concurrency, `Concurrency of handler '${options.id}'`);
2669
+ chunk2E6NSHGE_cjs.PositiveInteger.of(options.concurrency, `Concurrency of handler '${options.id}'`);
2478
2670
  }
2479
2671
  const registration = {
2480
2672
  id: options.id,
@@ -2497,7 +2689,7 @@ var HandlerRegistry = class {
2497
2689
  #assertUnregistered(claimIds) {
2498
2690
  for (const id of claimIds) {
2499
2691
  if (this.#byClaimId.has(id)) {
2500
- throw new chunkJ76M7XTS_cjs.ConfigurationError(`Handler id '${id}' is already registered`);
2692
+ throw new chunk2E6NSHGE_cjs.ConfigurationError(`Handler id '${id}' is already registered`);
2501
2693
  }
2502
2694
  }
2503
2695
  }
@@ -2530,7 +2722,7 @@ function resolveTimeout(timeout, { fallback, handlerId }) {
2530
2722
  if (timeout === false) {
2531
2723
  return null;
2532
2724
  }
2533
- return chunkJ76M7XTS_cjs.Milliseconds.forTimer(timeout, `Timeout of handler '${handlerId}'`);
2725
+ return chunk2E6NSHGE_cjs.Milliseconds.forTimer(timeout, `Timeout of handler '${handlerId}'`);
2534
2726
  }
2535
2727
 
2536
2728
  // src/notifier.ts
@@ -2563,7 +2755,7 @@ var Notifier = class {
2563
2755
  }
2564
2756
  }
2565
2757
  failure(stage, error) {
2566
- const errorInfo = chunkJ76M7XTS_cjs.toErrorInfo(error);
2758
+ const errorInfo = chunk2E6NSHGE_cjs.toErrorInfo(error);
2567
2759
  this.#logger.error(
2568
2760
  `[Notifier](failure) relaybus ${stage} failed | ${errorInfo.name}: ${errorInfo.message}`
2569
2761
  );
@@ -2577,7 +2769,7 @@ var Notifier = class {
2577
2769
  }
2578
2770
  #reportObserverFailure(failedObserver, error) {
2579
2771
  this.#logObserverFailure(error);
2580
- const errorInfo = chunkJ76M7XTS_cjs.toErrorInfo(error);
2772
+ const errorInfo = chunk2E6NSHGE_cjs.toErrorInfo(error);
2581
2773
  for (const observer of this.#observers) {
2582
2774
  if (observer === failedObserver) {
2583
2775
  continue;
@@ -2590,7 +2782,7 @@ var Notifier = class {
2590
2782
  }
2591
2783
  }
2592
2784
  #logObserverFailure(error) {
2593
- const errorInfo = chunkJ76M7XTS_cjs.toErrorInfo(error);
2785
+ const errorInfo = chunk2E6NSHGE_cjs.toErrorInfo(error);
2594
2786
  this.#logger.warn(
2595
2787
  `[Notifier](logObserverFailure) relaybus observer threw | ${errorInfo.name}: ${errorInfo.message}`
2596
2788
  );
@@ -2621,7 +2813,7 @@ var DEFAULTS = {
2621
2813
  };
2622
2814
  function oneOf(value, { allowed, name }) {
2623
2815
  if (!allowed.includes(value)) {
2624
- throw new chunkJ76M7XTS_cjs.ConfigurationError(
2816
+ throw new chunk2E6NSHGE_cjs.ConfigurationError(
2625
2817
  `${name} must be one of ${allowed.map((option) => `'${option}'`).join(", ")}, received '${String(value)}'`
2626
2818
  );
2627
2819
  }
@@ -2631,10 +2823,10 @@ function isDispatcherEnabled(options) {
2631
2823
  return options.dispatcher?.enabled ?? true;
2632
2824
  }
2633
2825
  function resolveBacklogInterval(options) {
2634
- return options.backlogInterval === void 0 ? void 0 : chunkJ76M7XTS_cjs.Milliseconds.forTimer(options.backlogInterval, "backlogInterval");
2826
+ return options.backlogInterval === void 0 ? void 0 : chunk2E6NSHGE_cjs.Milliseconds.forTimer(options.backlogInterval, "backlogInterval");
2635
2827
  }
2636
2828
  function resolveTransactionTimeout(options) {
2637
- return chunkJ76M7XTS_cjs.Milliseconds.forTimer(
2829
+ return chunk2E6NSHGE_cjs.Milliseconds.forTimer(
2638
2830
  options.transactionTimeout ?? DEFAULTS.transactionTimeout,
2639
2831
  "transactionTimeout"
2640
2832
  );
@@ -2642,41 +2834,41 @@ function resolveTransactionTimeout(options) {
2642
2834
  function createHandlerDefaults(options) {
2643
2835
  return {
2644
2836
  retry: createRetryPolicy(options.retry),
2645
- timeoutMs: options.handlerTimeout === void 0 || options.handlerTimeout === false ? null : chunkJ76M7XTS_cjs.Milliseconds.forTimer(options.handlerTimeout, "handlerTimeout")
2837
+ timeoutMs: options.handlerTimeout === void 0 || options.handlerTimeout === false ? null : chunk2E6NSHGE_cjs.Milliseconds.forTimer(options.handlerTimeout, "handlerTimeout")
2646
2838
  };
2647
2839
  }
2648
2840
  function createDispatcherSettings(options, generateIdentifier) {
2649
2841
  const dispatcher = options ?? {};
2650
- const concurrency = chunkJ76M7XTS_cjs.PositiveInteger.of(
2842
+ const concurrency = chunk2E6NSHGE_cjs.PositiveInteger.of(
2651
2843
  dispatcher.concurrency ?? DEFAULTS.concurrency,
2652
2844
  "dispatcher.concurrency"
2653
2845
  );
2654
2846
  return {
2655
2847
  owner: `${os.hostname()}:${process.pid}:${generateIdentifier()}`,
2656
2848
  concurrency,
2657
- batchSize: chunkJ76M7XTS_cjs.PositiveInteger.of(dispatcher.batchSize ?? concurrency, "dispatcher.batchSize"),
2849
+ batchSize: chunk2E6NSHGE_cjs.PositiveInteger.of(dispatcher.batchSize ?? concurrency, "dispatcher.batchSize"),
2658
2850
  ...createDispatcherTimings(dispatcher)
2659
2851
  };
2660
2852
  }
2661
2853
  function createDispatcherTimings(dispatcher) {
2662
2854
  return {
2663
- pollIntervalMs: chunkJ76M7XTS_cjs.Milliseconds.forTimer(
2855
+ pollIntervalMs: chunk2E6NSHGE_cjs.Milliseconds.forTimer(
2664
2856
  dispatcher.pollInterval ?? DEFAULTS.pollInterval,
2665
2857
  "dispatcher.pollInterval"
2666
2858
  ),
2667
- leaseDurationMs: chunkJ76M7XTS_cjs.Milliseconds.forTimer(
2859
+ leaseDurationMs: chunk2E6NSHGE_cjs.Milliseconds.forTimer(
2668
2860
  dispatcher.leaseDuration ?? DEFAULTS.leaseDuration,
2669
2861
  "dispatcher.leaseDuration"
2670
2862
  ),
2671
- shutdownTimeoutMs: chunkJ76M7XTS_cjs.Milliseconds.forTimer(
2863
+ shutdownTimeoutMs: chunk2E6NSHGE_cjs.Milliseconds.forTimer(
2672
2864
  dispatcher.shutdownTimeout ?? DEFAULTS.shutdownTimeout,
2673
2865
  "dispatcher.shutdownTimeout"
2674
2866
  ),
2675
- completionBatchWindowMs: chunkJ76M7XTS_cjs.Milliseconds.of(
2867
+ completionBatchWindowMs: chunk2E6NSHGE_cjs.Milliseconds.of(
2676
2868
  dispatcher.completionBatchWindow ?? DEFAULTS.completionBatchWindow,
2677
2869
  "dispatcher.completionBatchWindow"
2678
2870
  ),
2679
- completionBatchSize: chunkJ76M7XTS_cjs.PositiveInteger.of(
2871
+ completionBatchSize: chunk2E6NSHGE_cjs.PositiveInteger.of(
2680
2872
  dispatcher.completionBatchSize ?? DEFAULTS.completionBatchSize,
2681
2873
  "dispatcher.completionBatchSize"
2682
2874
  )
@@ -2686,9 +2878,9 @@ function createRetentionSettings(options) {
2686
2878
  const completed = options?.completed ?? DEFAULTS.completedRetention;
2687
2879
  const dead = options?.dead ?? false;
2688
2880
  return {
2689
- completedMs: completed === false ? null : chunkJ76M7XTS_cjs.Milliseconds.of(completed, "retention.completed"),
2690
- deadMs: dead === false ? null : chunkJ76M7XTS_cjs.Milliseconds.of(dead, "retention.dead"),
2691
- intervalMs: chunkJ76M7XTS_cjs.Milliseconds.forTimer(
2881
+ completedMs: completed === false ? null : chunk2E6NSHGE_cjs.Milliseconds.of(completed, "retention.completed"),
2882
+ deadMs: dead === false ? null : chunk2E6NSHGE_cjs.Milliseconds.of(dead, "retention.dead"),
2883
+ intervalMs: chunk2E6NSHGE_cjs.Milliseconds.forTimer(
2692
2884
  options?.interval ?? DEFAULTS.retentionInterval,
2693
2885
  "retention.interval"
2694
2886
  )
@@ -2699,61 +2891,61 @@ function createRetentionSettings(options) {
2699
2891
  var TRANSPORT_HANDLER_PREFIX = "relaybus.transport";
2700
2892
  function groupRoutes(routes) {
2701
2893
  const groups = /* @__PURE__ */ new Map();
2702
- for (const route of routes) {
2703
- const type = routedType(route.event);
2704
- const destination = route.destination ?? type;
2705
- const id = `${TRANSPORT_HANDLER_PREFIX}:${route.transport}:${destination}`;
2894
+ for (const route2 of routes) {
2895
+ const type = routedType(route2.event);
2896
+ const destination = route2.destination ?? type;
2897
+ const id = `${TRANSPORT_HANDLER_PREFIX}:${route2.transport}:${destination}`;
2706
2898
  const group = groups.get(id);
2707
2899
  if (group === void 0) {
2708
- groups.set(id, createGroup(route, { id, destination, type }));
2900
+ groups.set(id, createGroup(route2, { id, destination, type }));
2709
2901
  } else {
2710
- addRoute(group, { route, type, destination });
2902
+ addRoute(group, { route: route2, type, destination });
2711
2903
  }
2712
2904
  }
2713
2905
  return [...groups.values()];
2714
2906
  }
2715
- function createGroup(route, {
2907
+ function createGroup(route2, {
2716
2908
  id,
2717
2909
  destination,
2718
2910
  type
2719
2911
  }) {
2720
2912
  return {
2721
- transport: route.transport,
2913
+ transport: route2.transport,
2722
2914
  destination,
2723
2915
  id,
2724
2916
  eventTypes: [type],
2725
2917
  legacyIds: [`${id}:${type}`],
2726
- route,
2727
- ...resolveSettings(route, id)
2918
+ route: route2,
2919
+ ...resolveSettings(route2, id)
2728
2920
  };
2729
2921
  }
2730
2922
  function addRoute(group, {
2731
- route,
2923
+ route: route2,
2732
2924
  type,
2733
2925
  destination
2734
2926
  }) {
2735
2927
  const target = `'${group.destination}' of transport '${group.transport}'`;
2736
- if (route.transport !== group.transport) {
2737
- throw new chunkJ76M7XTS_cjs.ConfigurationError(
2738
- `Routes to ${target} and to '${destination}' of transport '${route.transport}' would share the handler id '${group.id}'; rename the transport or the destination`
2928
+ if (route2.transport !== group.transport) {
2929
+ throw new chunk2E6NSHGE_cjs.ConfigurationError(
2930
+ `Routes to ${target} and to '${destination}' of transport '${route2.transport}' would share the handler id '${group.id}'; rename the transport or the destination`
2739
2931
  );
2740
2932
  }
2741
2933
  if (group.eventTypes.includes(type)) {
2742
- throw new chunkJ76M7XTS_cjs.ConfigurationError(`Event type ${type} is routed to ${target} twice`);
2934
+ throw new chunk2E6NSHGE_cjs.ConfigurationError(`Event type ${type} is routed to ${target} twice`);
2743
2935
  }
2744
- const { retry, timeoutMs } = resolveSettings(route, group.id);
2936
+ const { retry, timeoutMs } = resolveSettings(route2, group.id);
2745
2937
  if (timeoutMs !== group.timeoutMs || !sameRetryPolicy(retry, group.retry)) {
2746
- throw new chunkJ76M7XTS_cjs.ConfigurationError(
2938
+ throw new chunk2E6NSHGE_cjs.ConfigurationError(
2747
2939
  `Routes to ${target} must share retry and timeout, because their events are sent in one queue per key | types=${group.eventTypes[0]},${type}`
2748
2940
  );
2749
2941
  }
2750
2942
  group.eventTypes.push(type);
2751
2943
  group.legacyIds.push(`${group.id}:${type}`);
2752
2944
  }
2753
- function resolveSettings(route, id) {
2945
+ function resolveSettings(route2, id) {
2754
2946
  return {
2755
- retry: createRetryPolicy(route.retry, DEFAULT_ROUTE_RETRY_POLICY),
2756
- timeoutMs: route.timeout === void 0 ? DEFAULTS.routeTimeoutMs : chunkJ76M7XTS_cjs.Milliseconds.forTimer(route.timeout, `Timeout of handler '${id}'`)
2947
+ retry: createRetryPolicy(route2.retry, DEFAULT_ROUTE_RETRY_POLICY),
2948
+ timeoutMs: route2.timeout === void 0 ? DEFAULTS.routeTimeoutMs : chunk2E6NSHGE_cjs.Milliseconds.forTimer(route2.timeout, `Timeout of handler '${id}'`)
2757
2949
  };
2758
2950
  }
2759
2951
  function sameRetryPolicy(left, right) {
@@ -2763,15 +2955,39 @@ function sameRetryPolicy(left, right) {
2763
2955
  }
2764
2956
  function routedType(event) {
2765
2957
  const type = typeof event === "string" ? event : event.type;
2766
- chunkJ76M7XTS_cjs.assertEventType(type);
2958
+ chunk2E6NSHGE_cjs.assertEventType(type);
2767
2959
  return type;
2768
2960
  }
2769
2961
 
2962
+ // src/bus/transport-claims.ts
2963
+ var claims = /* @__PURE__ */ new WeakMap();
2964
+ function claimTransports(transports, declaring) {
2965
+ const next = transports.map((transport) => {
2966
+ const current = claims.get(transport) ?? { buses: 0, declared: false };
2967
+ const claim = {
2968
+ buses: current.buses + 1,
2969
+ declared: current.declared || declaring.has(transport.name)
2970
+ };
2971
+ if (claim.buses > 1 && claim.declared) {
2972
+ throw new chunk2E6NSHGE_cjs.WireTypesUnsupportedError(
2973
+ `Transport '${transport.name}' is used by more than one bus, and wire names are declared for it; they would change what the other bus writes. Give each bus its own transport`
2974
+ );
2975
+ }
2976
+ return [transport, claim];
2977
+ });
2978
+ return () => {
2979
+ for (const [transport, claim] of next) {
2980
+ claims.set(transport, claim);
2981
+ }
2982
+ };
2983
+ }
2984
+
2770
2985
  // src/bus/bus-connections.ts
2771
2986
  var BusConnections = class {
2772
2987
  #parts;
2773
2988
  #transports = /* @__PURE__ */ new Map();
2774
2989
  #subscriptions;
2990
+ #pendingWireTypes;
2775
2991
  constructor(parts, subscriptions) {
2776
2992
  this.#parts = parts;
2777
2993
  this.#subscriptions = subscriptions;
@@ -2784,17 +3000,26 @@ var BusConnections = class {
2784
3000
  register({ transports, routes }) {
2785
3001
  for (const transport of transports) {
2786
3002
  if (this.#transports.has(transport.name)) {
2787
- throw new chunkJ76M7XTS_cjs.ConfigurationError(`Transport name '${transport.name}' is used twice`);
3003
+ throw new chunk2E6NSHGE_cjs.ConfigurationError(`Transport name '${transport.name}' is used twice`);
2788
3004
  }
2789
3005
  this.#transports.set(transport.name, transport);
2790
3006
  }
2791
- this.#registerRoutes(routes);
3007
+ const { wireTypes } = this.#registerRoutes(expandRoutes(routes));
2792
3008
  for (const subscription of this.#subscriptions) {
2793
3009
  const transport = this.#requireTransport(subscription.transport);
2794
3010
  if (transport.subscribe === void 0) {
2795
- throw new chunkJ76M7XTS_cjs.ConfigurationError(`Transport '${transport.name}' cannot subscribe`);
3011
+ throw new chunk2E6NSHGE_cjs.ConfigurationError(`Transport '${transport.name}' cannot subscribe`);
2796
3012
  }
2797
3013
  }
3014
+ this.#pendingWireTypes = this.#prepareWireTypes(wireTypes);
3015
+ }
3016
+ /**
3017
+ * Applies the wire names the routes declared. The bus calls it last, after every check of its
3018
+ * options, so a refused configuration leaves the transports as they were.
3019
+ */
3020
+ commitWireTypes() {
3021
+ this.#pendingWireTypes?.();
3022
+ this.#pendingWireTypes = void 0;
2798
3023
  }
2799
3024
  /** True while every transport reports itself ready. */
2800
3025
  allReady() {
@@ -2828,7 +3053,7 @@ var BusConnections = class {
2828
3053
  }
2829
3054
  return {
2830
3055
  onInvalidMessage: ({ destination, error }) => {
2831
- const errorInfo = chunkJ76M7XTS_cjs.toErrorInfo(error);
3056
+ const errorInfo = chunk2E6NSHGE_cjs.toErrorInfo(error);
2832
3057
  notifier.notify("onInvalidMessage", {
2833
3058
  transport: transport.name,
2834
3059
  system: transport.system ?? transport.name,
@@ -2839,18 +3064,42 @@ var BusConnections = class {
2839
3064
  }
2840
3065
  };
2841
3066
  }
2842
- #registerRoutes(routes) {
2843
- for (const route of routes) {
2844
- this.#requireTransport(route.transport);
3067
+ #registerRoutes({ routes, wireTypes }) {
3068
+ for (const route2 of routes) {
3069
+ this.#requireTransport(route2.transport);
2845
3070
  }
2846
3071
  for (const group of groupRoutes(routes)) {
2847
3072
  this.#registerRoute(group);
2848
3073
  }
3074
+ return { wireTypes };
3075
+ }
3076
+ /**
3077
+ * Hands the wire names that catalog routes declare to the transports whose codecs write them:
3078
+ * every transport checks its names first, then all apply them. A transport carries one wire
3079
+ * format, so a transport that two buses use cannot take names declared by either of them.
3080
+ */
3081
+ #prepareWireTypes(wireTypes) {
3082
+ const applies = [...wireTypes].map(([name, types]) => {
3083
+ const transport = this.#transports.get(name);
3084
+ if (transport.declareWireTypes === void 0) {
3085
+ throw new chunk2E6NSHGE_cjs.WireTypesUnsupportedError(
3086
+ `Transport '${name}' cannot map event types to wire names, but routes or catalogs declare them for: ${Object.keys(types).join(", ")}`
3087
+ );
3088
+ }
3089
+ return transport.declareWireTypes(types);
3090
+ });
3091
+ const claim = claimTransports([...this.#transports.values()], new Set(wireTypes.keys()));
3092
+ return () => {
3093
+ for (const apply of applies) {
3094
+ apply();
3095
+ }
3096
+ claim();
3097
+ };
2849
3098
  }
2850
3099
  /** Registers one handler for every route to a destination, so a key's events reach it in commit order. */
2851
3100
  #registerRoute(group) {
2852
3101
  const transport = this.#transports.get(group.transport);
2853
- const { route, destination } = group;
3102
+ const { route: route2, destination } = group;
2854
3103
  this.#parts.registry.register(
2855
3104
  {
2856
3105
  eventTypes: group.eventTypes,
@@ -2859,8 +3108,8 @@ var BusConnections = class {
2859
3108
  handle: this.#parts.observation?.sender(transport, destination) ?? ((envelope) => transport.publish(envelope, destination)),
2860
3109
  options: {
2861
3110
  id: group.id,
2862
- ...route.retry === void 0 ? {} : { retry: route.retry },
2863
- ...route.timeout === void 0 ? {} : { timeout: route.timeout }
3111
+ ...route2.retry === void 0 ? {} : { retry: route2.retry },
3112
+ ...route2.timeout === void 0 ? {} : { timeout: route2.timeout }
2864
3113
  },
2865
3114
  forwardsToTransport: true
2866
3115
  },
@@ -2870,7 +3119,7 @@ var BusConnections = class {
2870
3119
  #requireTransport(name) {
2871
3120
  const transport = this.#transports.get(name);
2872
3121
  if (transport === void 0) {
2873
- throw new chunkJ76M7XTS_cjs.ConfigurationError(`No transport named '${name}' is configured`);
3122
+ throw new chunk2E6NSHGE_cjs.ConfigurationError(`No transport named '${name}' is configured`);
2874
3123
  }
2875
3124
  return transport;
2876
3125
  }
@@ -2914,7 +3163,7 @@ var BusMaintenance = class {
2914
3163
  #scheduleRetention() {
2915
3164
  const { clock, retention } = this.#parts;
2916
3165
  if (retention.completedMs !== null || retention.deadMs !== null) {
2917
- this.#retentionTimer = chunkJ76M7XTS_cjs.schedule(clock, {
3166
+ this.#retentionTimer = chunk2E6NSHGE_cjs.schedule(clock, {
2918
3167
  run: () => {
2919
3168
  this.purgeExpired().catch(() => void 0);
2920
3169
  },
@@ -2955,7 +3204,7 @@ var EventPublisher = class {
2955
3204
  return [];
2956
3205
  }
2957
3206
  const now = this.#parts.clock.now();
2958
- const delayMs = options.delay === void 0 ? 0 : chunkJ76M7XTS_cjs.Milliseconds.of(options.delay, "delay");
3207
+ const delayMs = options.delay === void 0 ? 0 : chunk2E6NSHGE_cjs.Milliseconds.of(options.delay, "delay");
2959
3208
  const availableAt = new Date(now.getTime() + delayMs);
2960
3209
  const context = { options, id, scope: getCurrentScope(), now };
2961
3210
  const entries = await this.#createEntries(events, { context, availableAt });
@@ -2993,55 +3242,67 @@ var EventPublisher = class {
2993
3242
  context,
2994
3243
  delayMs
2995
3244
  }) {
2996
- const { result, committed } = await this.#appendInTransaction(entries, context);
3245
+ const { result, committed, transaction } = await this.#appendInTransaction(entries, context);
2997
3246
  const appended = new Set(result.appendedEventIds);
2998
3247
  const published = entries.map((entry) => entry.event).filter((envelope) => appended.has(envelope.id));
2999
3248
  if (published.length > 0) {
3000
- this.#parts.notifier.notify("onPublished", {
3001
- envelopes: published,
3002
- deliveries: result.appendedDeliveryCount
3003
- });
3004
- if (delayMs === 0) {
3005
- this.#parts.scheduleWake(committed);
3249
+ const report = { envelopes: published, deliveries: result.appendedDeliveryCount };
3250
+ const wake = delayMs === 0 ? committed : void 0;
3251
+ const isolation = context.scope?.isolation;
3252
+ if (!committed && isolation !== void 0 && isolation.transaction === transaction) {
3253
+ isolation.afterCommit.push(() => this.#announce(report, wake));
3254
+ } else {
3255
+ this.#announce(report, wake);
3006
3256
  }
3007
3257
  }
3008
3258
  }
3259
+ /** Tells the observers about a publish, and wakes the dispatcher (`wake` is `committed`) when the events are due now. */
3260
+ #announce(report, wake) {
3261
+ this.#parts.notifier.notify("onPublished", report);
3262
+ if (wake !== void 0) {
3263
+ this.#parts.scheduleWake(wake);
3264
+ }
3265
+ }
3009
3266
  /** Appends in the caller's transaction, else in one of the store's; `committed` tells which. */
3010
3267
  async #appendInTransaction(entries, { options, scope, now }) {
3011
3268
  const { store, transactionMode, transactionResolver } = this.#parts;
3012
3269
  const transaction = options.transaction ?? scope?.transaction ?? transactionResolver?.();
3013
3270
  if (transaction !== void 0) {
3014
- return { result: await store.append(transaction, entries, now), committed: false };
3271
+ return {
3272
+ result: await store.append(transaction, entries, now),
3273
+ committed: false,
3274
+ transaction
3275
+ };
3015
3276
  }
3016
3277
  if (transactionMode === "required") {
3017
- throw new chunkJ76M7XTS_cjs.TransactionRequiredError(
3278
+ throw new chunk2E6NSHGE_cjs.TransactionRequiredError(
3018
3279
  "publish found no transaction. Pass { transaction }, publish inside bus.transaction(), configure transactionResolver, or set transactionMode to optional"
3019
3280
  );
3020
3281
  }
3021
3282
  const result = await store.transaction(
3022
3283
  (ownTransaction) => store.append(ownTransaction, entries, now)
3023
3284
  );
3024
- return { result, committed: true };
3285
+ return { result, committed: true, transaction: void 0 };
3025
3286
  }
3026
3287
  async #createEnvelope(input) {
3027
3288
  const { event, options, id, scope, now, correlationId } = input;
3028
3289
  if (typeof event.type !== "string") {
3029
- throw new chunkJ76M7XTS_cjs.ConfigurationError(
3290
+ throw new chunk2E6NSHGE_cjs.ConfigurationError(
3030
3291
  "publish expects an instance of a class created with defineEvent"
3031
3292
  );
3032
3293
  }
3033
- chunkJ76M7XTS_cjs.assertEventType(event.type);
3294
+ chunk2E6NSHGE_cjs.assertEventType(event.type);
3034
3295
  const schema = event.constructor.schema;
3035
- const payload = await chunkJ76M7XTS_cjs.validatePayload(event.type, schema, event.payload);
3296
+ const payload = await chunk2E6NSHGE_cjs.validatePayload(event.type, schema, event.payload);
3036
3297
  if (options.key !== void 0 && options.key.length === 0) {
3037
- throw new chunkJ76M7XTS_cjs.ConfigurationError("key must not be empty");
3298
+ throw new chunk2E6NSHGE_cjs.ConfigurationError("key must not be empty");
3038
3299
  }
3039
3300
  const eventId = id ?? this.#parts.generateIdentifier();
3040
3301
  return {
3041
3302
  id: eventId,
3042
3303
  type: event.type,
3043
3304
  source: this.#parts.source,
3044
- key: options.key ?? chunkJ76M7XTS_cjs.keyFromDefinition(event, payload),
3305
+ key: options.key ?? chunk2E6NSHGE_cjs.keyFromDefinition(event, payload),
3045
3306
  payload,
3046
3307
  headers: { ...options.headers },
3047
3308
  correlationId: correlationId ?? eventId,
@@ -3054,19 +3315,19 @@ var EventPublisher = class {
3054
3315
  // src/received-envelope.ts
3055
3316
  function normalizeEnvelope(envelope) {
3056
3317
  if (typeof envelope.id !== "string" || envelope.id.length === 0) {
3057
- throw new chunkJ76M7XTS_cjs.InvalidEnvelopeError("Received event has no id");
3318
+ throw new chunk2E6NSHGE_cjs.InvalidEnvelopeError("Received event has no id");
3058
3319
  }
3059
3320
  if (typeof envelope.type !== "string") {
3060
- throw new chunkJ76M7XTS_cjs.InvalidEnvelopeError(`Received event ${envelope.id} has no type`);
3321
+ throw new chunk2E6NSHGE_cjs.InvalidEnvelopeError(`Received event ${envelope.id} has no type`);
3061
3322
  }
3062
3323
  try {
3063
- chunkJ76M7XTS_cjs.assertEventType(envelope.type);
3324
+ chunk2E6NSHGE_cjs.assertEventType(envelope.type);
3064
3325
  } catch (error) {
3065
- throw new chunkJ76M7XTS_cjs.InvalidEnvelopeError(chunkJ76M7XTS_cjs.toErrorInfo(error).message);
3326
+ throw new chunk2E6NSHGE_cjs.InvalidEnvelopeError(chunk2E6NSHGE_cjs.toErrorInfo(error).message);
3066
3327
  }
3067
3328
  const occurredAt = toDate(envelope.occurredAt);
3068
3329
  if (occurredAt === void 0) {
3069
- throw new chunkJ76M7XTS_cjs.InvalidEnvelopeError(`Received event ${envelope.id} has an invalid occurredAt`);
3330
+ throw new chunk2E6NSHGE_cjs.InvalidEnvelopeError(`Received event ${envelope.id} has an invalid occurredAt`);
3070
3331
  }
3071
3332
  return {
3072
3333
  id: envelope.id,
@@ -3148,6 +3409,8 @@ function assembleRelayBus(options, hooks) {
3148
3409
  settings,
3149
3410
  observation: DispatchObservation.create({ ...core, tracer: options.tracer })
3150
3411
  });
3412
+ const inboxAndOutbox = createInboxAndOutbox(core, hooks);
3413
+ connections.commitWireTypes();
3151
3414
  return {
3152
3415
  ...core,
3153
3416
  maintenance,
@@ -3156,7 +3419,7 @@ function assembleRelayBus(options, hooks) {
3156
3419
  dispatcherEnabled,
3157
3420
  shutdownTimeoutMs: settings.shutdownTimeoutMs,
3158
3421
  dispatcher,
3159
- ...createInboxAndOutbox(core, hooks)
3422
+ ...inboxAndOutbox
3160
3423
  };
3161
3424
  }
3162
3425
  function resolveCore(options) {
@@ -3208,6 +3471,95 @@ function createInboxAndOutbox(core, hooks) {
3208
3471
  };
3209
3472
  }
3210
3473
 
3474
+ // src/transaction-budget.ts
3475
+ async function runBudgetedTransaction(store, { clock, timeoutMs, work, isolationLevel }) {
3476
+ const startedAt = clock.now().getTime();
3477
+ const overrun = () => clock.now().getTime() - startedAt >= timeoutMs;
3478
+ let committing = false;
3479
+ try {
3480
+ return await store.transaction(
3481
+ async (transaction) => {
3482
+ committing = false;
3483
+ const result = await work(transaction);
3484
+ if (overrun()) {
3485
+ throw createTimeoutError(timeoutMs, void 0);
3486
+ }
3487
+ committing = true;
3488
+ return result;
3489
+ },
3490
+ isolationLevel === void 0 ? { timeoutMs } : { timeoutMs, isolationLevel }
3491
+ );
3492
+ } catch (error) {
3493
+ if (error instanceof chunk2E6NSHGE_cjs.TransactionTimeoutError || committing || !overrun()) {
3494
+ throw error;
3495
+ }
3496
+ throw createTimeoutError(timeoutMs, error);
3497
+ }
3498
+ }
3499
+ function createTimeoutError(timeoutMs, cause) {
3500
+ const message = `The transaction ran past its ${timeoutMs} ms budget (transactionTimeout) and was rolled back. The usual cause is a slow call to another service (HTTP, gRPC, a broker, a lock) inside the transaction: make external calls before the transaction starts or after it commits, or raise transactionTimeout`;
3501
+ return cause === void 0 ? new chunk2E6NSHGE_cjs.TransactionTimeoutError(message) : new chunk2E6NSHGE_cjs.TransactionTimeoutError(`${message} | cause=${chunk2E6NSHGE_cjs.toErrorInfo(cause).message}`, { cause });
3502
+ }
3503
+
3504
+ // src/isolated-transaction.ts
3505
+ var DEFAULT_ISOLATED_ATTEMPTS = 3;
3506
+ var RERUN_LEVELS = /* @__PURE__ */ new Set([
3507
+ "repeatable read",
3508
+ "serializable"
3509
+ ]);
3510
+ async function runIsolatedTransaction({ store, clock, transactionTimeoutMs }, { options, work }) {
3511
+ const { isolationLevel } = options;
3512
+ assertSupported(store, options);
3513
+ const maxAttempts = attemptsOf(options);
3514
+ for (let attempt = 1; ; attempt += 1) {
3515
+ const afterCommit = [];
3516
+ let committed;
3517
+ try {
3518
+ committed = {
3519
+ value: await runBudgetedTransaction(store, {
3520
+ clock,
3521
+ timeoutMs: transactionTimeoutMs,
3522
+ isolationLevel,
3523
+ work: (transaction) => runInScope(
3524
+ { transaction, isolation: { transaction, level: isolationLevel, afterCommit } },
3525
+ () => work(transaction)
3526
+ )
3527
+ })
3528
+ };
3529
+ } catch (error) {
3530
+ if (attempt >= maxAttempts || error instanceof chunk2E6NSHGE_cjs.TransactionTimeoutError || store.isSerializationFailure?.(error) !== true) {
3531
+ throw error;
3532
+ }
3533
+ continue;
3534
+ }
3535
+ for (const announce of afterCommit) {
3536
+ announce();
3537
+ }
3538
+ return committed.value;
3539
+ }
3540
+ }
3541
+ function attemptsOf({ isolationLevel, maxAttempts }) {
3542
+ return RERUN_LEVELS.has(isolationLevel) ? maxAttempts ?? DEFAULT_ISOLATED_ATTEMPTS : 1;
3543
+ }
3544
+ function assertSupported(store, { isolationLevel, maxAttempts }) {
3545
+ if (!TRANSACTION_ISOLATION_LEVELS.includes(isolationLevel)) {
3546
+ throw new chunk2E6NSHGE_cjs.ConfigurationError(
3547
+ `Unknown isolationLevel ${JSON.stringify(isolationLevel)}: use ${TRANSACTION_ISOLATION_LEVELS.map((level) => `'${level}'`).join(", ")}`
3548
+ );
3549
+ }
3550
+ if (maxAttempts !== void 0 && !(Number.isInteger(maxAttempts) && maxAttempts >= 1)) {
3551
+ throw new chunk2E6NSHGE_cjs.ConfigurationError(
3552
+ `maxAttempts must be a positive integer, got ${String(maxAttempts)}`
3553
+ );
3554
+ }
3555
+ if (store.isolationLevels?.includes(isolationLevel) !== true) {
3556
+ const supported = store.isolationLevels ?? [];
3557
+ throw new chunk2E6NSHGE_cjs.ConfigurationError(
3558
+ `The event store does not support isolationLevel '${isolationLevel}' (supported: ${supported.length === 0 ? "none" : supported.join(", ")}). PostgreSQL stores support all levels; MongoDB and the in-memory store run transactions at their own level only`
3559
+ );
3560
+ }
3561
+ }
3562
+
3211
3563
  // src/bus/publish-wake-ups.ts
3212
3564
  var FIRST_WAKE_MS = 25;
3213
3565
  var SECOND_WAKE_MS = 100;
@@ -3252,7 +3604,7 @@ var PublishWakeUps = class {
3252
3604
  }
3253
3605
  #arm(delayMs) {
3254
3606
  this.#dueAt = this.#dependencies.clock.now().getTime() + delayMs;
3255
- return chunkJ76M7XTS_cjs.schedule(this.#dependencies.clock, {
3607
+ return chunk2E6NSHGE_cjs.schedule(this.#dependencies.clock, {
3256
3608
  run: () => this.#fire(),
3257
3609
  delayMs,
3258
3610
  unref: true
@@ -3260,36 +3612,6 @@ var PublishWakeUps = class {
3260
3612
  }
3261
3613
  };
3262
3614
 
3263
- // src/transaction-budget.ts
3264
- async function runBudgetedTransaction(store, { clock, timeoutMs, work }) {
3265
- const startedAt = clock.now().getTime();
3266
- const overrun = () => clock.now().getTime() - startedAt >= timeoutMs;
3267
- let committing = false;
3268
- try {
3269
- return await store.transaction(
3270
- async (transaction) => {
3271
- committing = false;
3272
- const result = await work(transaction);
3273
- if (overrun()) {
3274
- throw createTimeoutError(timeoutMs, void 0);
3275
- }
3276
- committing = true;
3277
- return result;
3278
- },
3279
- { timeoutMs }
3280
- );
3281
- } catch (error) {
3282
- if (error instanceof chunkJ76M7XTS_cjs.TransactionTimeoutError || committing || !overrun()) {
3283
- throw error;
3284
- }
3285
- throw createTimeoutError(timeoutMs, error);
3286
- }
3287
- }
3288
- function createTimeoutError(timeoutMs, cause) {
3289
- const message = `The transaction ran past its ${timeoutMs} ms budget (transactionTimeout) and was rolled back. The usual cause is a slow call to another service (HTTP, gRPC, a broker, a lock) inside the transaction: make external calls before the transaction starts or after it commits, or raise transactionTimeout`;
3290
- return cause === void 0 ? new chunkJ76M7XTS_cjs.TransactionTimeoutError(message) : new chunkJ76M7XTS_cjs.TransactionTimeoutError(`${message} | cause=${chunkJ76M7XTS_cjs.toErrorInfo(cause).message}`, { cause });
3291
- }
3292
-
3293
3615
  // src/relay-bus.ts
3294
3616
  var DEFAULT_DRAIN_BATCHES = 1e3;
3295
3617
  var RelayBus = class {
@@ -3381,8 +3703,16 @@ var RelayBus = class {
3381
3703
  * Runs `work` in a new store transaction. `publish` calls inside it join the
3382
3704
  * transaction without passing it explicitly. The transaction is held to `transactionTimeout`
3383
3705
  * and rolled back with a {@link TransactionTimeoutError} when it runs past it.
3706
+ *
3707
+ * With `options.isolationLevel` it runs at that level, and at `'repeatable read'` or
3708
+ * `'serializable'` a serialization failure reruns `work` in a new transaction (see
3709
+ * {@link TransactionOptions}), so `work` may run more than once. Without options nothing
3710
+ * changes. The call always opens a transaction of its own, also inside another one.
3384
3711
  */
3385
- async transaction(work) {
3712
+ async transaction(work, options) {
3713
+ if (options?.isolationLevel !== void 0) {
3714
+ return runIsolatedTransaction(this.#parts, { options, work });
3715
+ }
3386
3716
  return runBudgetedTransaction(this.#parts.store, {
3387
3717
  clock: this.#parts.clock,
3388
3718
  timeoutMs: this.#parts.transactionTimeoutMs,
@@ -3397,6 +3727,15 @@ var RelayBus = class {
3397
3727
  currentTransaction() {
3398
3728
  return getCurrentScope()?.transaction ?? this.#parts.transactionResolver?.();
3399
3729
  }
3730
+ /**
3731
+ * The isolation level the transaction `publish` would join was opened at by
3732
+ * `bus.transaction({ isolationLevel })` (or `@RelayBusTransaction({ isolationLevel })`);
3733
+ * undefined for every other transaction, including those without a level and the one of
3734
+ * `transactionResolver`.
3735
+ */
3736
+ currentIsolationLevel() {
3737
+ return getCurrentIsolationLevel();
3738
+ }
3400
3739
  /**
3401
3740
  * Stores an event received from a broker and schedules it for local handlers.
3402
3741
  * Idempotent on the event id. Transports call it and acknowledge the message
@@ -3431,7 +3770,7 @@ var RelayBus = class {
3431
3770
  * get the result of the first.
3432
3771
  */
3433
3772
  async stop(options = {}) {
3434
- const timeoutMs = options.timeout === void 0 ? this.#parts.shutdownTimeoutMs : chunkJ76M7XTS_cjs.Milliseconds.forTimer(options.timeout, "timeout");
3773
+ const timeoutMs = options.timeout === void 0 ? this.#parts.shutdownTimeoutMs : chunk2E6NSHGE_cjs.Milliseconds.forTimer(options.timeout, "timeout");
3435
3774
  const { stopping, first } = this.#lifecycle.stop(timeoutMs);
3436
3775
  if (!first) {
3437
3776
  return stopping;
@@ -3465,7 +3804,7 @@ var RelayBus = class {
3465
3804
  }
3466
3805
  total += processed;
3467
3806
  }
3468
- throw new chunkJ76M7XTS_cjs.DrainIncompleteError(`drain did not finish within ${maxBatches} batches`);
3807
+ throw new chunk2E6NSHGE_cjs.DrainIncompleteError(`drain did not finish within ${maxBatches} batches`);
3469
3808
  }
3470
3809
  listDeliveries(query = {}) {
3471
3810
  return this.#parts.store.listDeliveries(query);
@@ -3510,12 +3849,12 @@ var RelayBus = class {
3510
3849
  );
3511
3850
  }
3512
3851
  } catch (error) {
3513
- await this.#closeAfterFailedStart();
3852
+ await this.#lifecycle.closeAfterFailedStart(this.#parts.shutdownTimeoutMs);
3514
3853
  this.#lifecycle.markStopped();
3515
3854
  throw error;
3516
3855
  }
3517
3856
  if (this.#lifecycle.state !== "starting") {
3518
- throw new chunkJ76M7XTS_cjs.LifecycleError("relaybus was stopped before it finished starting");
3857
+ throw new chunk2E6NSHGE_cjs.LifecycleError("relaybus was stopped before it finished starting");
3519
3858
  }
3520
3859
  if (this.#parts.dispatcherEnabled) {
3521
3860
  this.#parts.dispatcher.start();
@@ -3526,20 +3865,10 @@ var RelayBus = class {
3526
3865
  `[RelayBus](startComponents) relaybus started | handlers=${this.#parts.registry.all().length} dispatcher=${this.#parts.dispatcherEnabled}`
3527
3866
  );
3528
3867
  }
3529
- /** Closes the transports a failed start may have connected, within the shutdown timeout. */
3530
- #closeAfterFailedStart() {
3531
- return withinDeadline(
3532
- { clock: this.#parts.clock, timeoutMs: this.#parts.shutdownTimeoutMs },
3533
- async (deadline) => {
3534
- await this.#closeTransports(deadline);
3535
- return 0;
3536
- }
3537
- );
3538
- }
3539
3868
  #reportUnrouted(envelope) {
3540
3869
  const message = `No handler or route for event type, event not stored | type=${envelope.type} id=${envelope.id}`;
3541
3870
  if (this.#parts.unroutedEvents === "throw") {
3542
- throw new chunkJ76M7XTS_cjs.ConfigurationError(message);
3871
+ throw new chunk2E6NSHGE_cjs.ConfigurationError(message);
3543
3872
  }
3544
3873
  this.#parts.logger.warn(`[RelayBus](reportUnrouted) ${message}`);
3545
3874
  }
@@ -3562,7 +3891,7 @@ var RelayBus = class {
3562
3891
  await deadline.race(transport.close(options));
3563
3892
  } catch (error) {
3564
3893
  this.#parts.logger.error(
3565
- `[RelayBus](closeTransports) Failed to close transport | transport=${transport.name} error=${chunkJ76M7XTS_cjs.toErrorInfo(error).message}`
3894
+ `[RelayBus](closeTransports) Failed to close transport | transport=${transport.name} error=${chunk2E6NSHGE_cjs.toErrorInfo(error).message}`
3566
3895
  );
3567
3896
  }
3568
3897
  }
@@ -3571,79 +3900,99 @@ var RelayBus = class {
3571
3900
 
3572
3901
  Object.defineProperty(exports, "AttemptsExhaustedError", {
3573
3902
  enumerable: true,
3574
- get: function () { return chunkJ76M7XTS_cjs.AttemptsExhaustedError; }
3903
+ get: function () { return chunk2E6NSHGE_cjs.AttemptsExhaustedError; }
3575
3904
  });
3576
3905
  Object.defineProperty(exports, "ConfigurationError", {
3577
3906
  enumerable: true,
3578
- get: function () { return chunkJ76M7XTS_cjs.ConfigurationError; }
3907
+ get: function () { return chunk2E6NSHGE_cjs.ConfigurationError; }
3579
3908
  });
3580
3909
  Object.defineProperty(exports, "DispatcherStoppedError", {
3581
3910
  enumerable: true,
3582
- get: function () { return chunkJ76M7XTS_cjs.DispatcherStoppedError; }
3911
+ get: function () { return chunk2E6NSHGE_cjs.DispatcherStoppedError; }
3583
3912
  });
3584
3913
  Object.defineProperty(exports, "DrainIncompleteError", {
3585
3914
  enumerable: true,
3586
- get: function () { return chunkJ76M7XTS_cjs.DrainIncompleteError; }
3915
+ get: function () { return chunk2E6NSHGE_cjs.DrainIncompleteError; }
3916
+ });
3917
+ Object.defineProperty(exports, "DuplicateRouteEventError", {
3918
+ enumerable: true,
3919
+ get: function () { return chunk2E6NSHGE_cjs.DuplicateRouteEventError; }
3587
3920
  });
3588
3921
  Object.defineProperty(exports, "EventValidationError", {
3589
3922
  enumerable: true,
3590
- get: function () { return chunkJ76M7XTS_cjs.EventValidationError; }
3923
+ get: function () { return chunk2E6NSHGE_cjs.EventValidationError; }
3591
3924
  });
3592
3925
  Object.defineProperty(exports, "HandlerTimeoutError", {
3593
3926
  enumerable: true,
3594
- get: function () { return chunkJ76M7XTS_cjs.HandlerTimeoutError; }
3927
+ get: function () { return chunk2E6NSHGE_cjs.HandlerTimeoutError; }
3595
3928
  });
3596
3929
  Object.defineProperty(exports, "InvalidEnvelopeError", {
3597
3930
  enumerable: true,
3598
- get: function () { return chunkJ76M7XTS_cjs.InvalidEnvelopeError; }
3931
+ get: function () { return chunk2E6NSHGE_cjs.InvalidEnvelopeError; }
3599
3932
  });
3600
3933
  Object.defineProperty(exports, "LeaseLostError", {
3601
3934
  enumerable: true,
3602
- get: function () { return chunkJ76M7XTS_cjs.LeaseLostError; }
3935
+ get: function () { return chunk2E6NSHGE_cjs.LeaseLostError; }
3603
3936
  });
3604
3937
  Object.defineProperty(exports, "LifecycleError", {
3605
3938
  enumerable: true,
3606
- get: function () { return chunkJ76M7XTS_cjs.LifecycleError; }
3939
+ get: function () { return chunk2E6NSHGE_cjs.LifecycleError; }
3607
3940
  });
3608
3941
  Object.defineProperty(exports, "MAX_TIMER_DELAY_MS", {
3609
3942
  enumerable: true,
3610
- get: function () { return chunkJ76M7XTS_cjs.MAX_TIMER_DELAY_MS; }
3943
+ get: function () { return chunk2E6NSHGE_cjs.MAX_TIMER_DELAY_MS; }
3611
3944
  });
3612
3945
  Object.defineProperty(exports, "NonRetryableError", {
3613
3946
  enumerable: true,
3614
- get: function () { return chunkJ76M7XTS_cjs.NonRetryableError; }
3947
+ get: function () { return chunk2E6NSHGE_cjs.NonRetryableError; }
3615
3948
  });
3616
3949
  Object.defineProperty(exports, "RelayBusError", {
3617
3950
  enumerable: true,
3618
- get: function () { return chunkJ76M7XTS_cjs.RelayBusError; }
3951
+ get: function () { return chunk2E6NSHGE_cjs.RelayBusError; }
3952
+ });
3953
+ Object.defineProperty(exports, "TransactionIsolationConflictError", {
3954
+ enumerable: true,
3955
+ get: function () { return chunk2E6NSHGE_cjs.TransactionIsolationConflictError; }
3619
3956
  });
3620
3957
  Object.defineProperty(exports, "TransactionRequiredError", {
3621
3958
  enumerable: true,
3622
- get: function () { return chunkJ76M7XTS_cjs.TransactionRequiredError; }
3959
+ get: function () { return chunk2E6NSHGE_cjs.TransactionRequiredError; }
3623
3960
  });
3624
3961
  Object.defineProperty(exports, "TransactionTimeoutError", {
3625
3962
  enumerable: true,
3626
- get: function () { return chunkJ76M7XTS_cjs.TransactionTimeoutError; }
3963
+ get: function () { return chunk2E6NSHGE_cjs.TransactionTimeoutError; }
3964
+ });
3965
+ Object.defineProperty(exports, "UnknownCatalogEventError", {
3966
+ enumerable: true,
3967
+ get: function () { return chunk2E6NSHGE_cjs.UnknownCatalogEventError; }
3968
+ });
3969
+ Object.defineProperty(exports, "WireTypeCollisionError", {
3970
+ enumerable: true,
3971
+ get: function () { return chunk2E6NSHGE_cjs.WireTypeCollisionError; }
3972
+ });
3973
+ Object.defineProperty(exports, "WireTypesUnsupportedError", {
3974
+ enumerable: true,
3975
+ get: function () { return chunk2E6NSHGE_cjs.WireTypesUnsupportedError; }
3627
3976
  });
3628
3977
  Object.defineProperty(exports, "assertEventType", {
3629
3978
  enumerable: true,
3630
- get: function () { return chunkJ76M7XTS_cjs.assertEventType; }
3979
+ get: function () { return chunk2E6NSHGE_cjs.assertEventType; }
3631
3980
  });
3632
3981
  Object.defineProperty(exports, "defineEvent", {
3633
3982
  enumerable: true,
3634
- get: function () { return chunkJ76M7XTS_cjs.defineEvent; }
3983
+ get: function () { return chunk2E6NSHGE_cjs.defineEvent; }
3635
3984
  });
3636
3985
  Object.defineProperty(exports, "toErrorInfo", {
3637
3986
  enumerable: true,
3638
- get: function () { return chunkJ76M7XTS_cjs.toErrorInfo; }
3987
+ get: function () { return chunk2E6NSHGE_cjs.toErrorInfo; }
3639
3988
  });
3640
3989
  Object.defineProperty(exports, "toMilliseconds", {
3641
3990
  enumerable: true,
3642
- get: function () { return chunkJ76M7XTS_cjs.toMilliseconds; }
3991
+ get: function () { return chunk2E6NSHGE_cjs.toMilliseconds; }
3643
3992
  });
3644
3993
  Object.defineProperty(exports, "validatePayload", {
3645
3994
  enumerable: true,
3646
- get: function () { return chunkJ76M7XTS_cjs.validatePayload; }
3995
+ get: function () { return chunk2E6NSHGE_cjs.validatePayload; }
3647
3996
  });
3648
3997
  exports.CatalogPublisher = CatalogPublisher;
3649
3998
  exports.DEFAULT_RETRY_POLICY = DEFAULT_RETRY_POLICY;
@@ -3654,6 +4003,8 @@ exports.InMemoryTransaction = InMemoryTransaction;
3654
4003
  exports.RelayBus = RelayBus;
3655
4004
  exports.TRACEPARENT_HEADER = TRACEPARENT_HEADER;
3656
4005
  exports.TRACESTATE_HEADER = TRACESTATE_HEADER;
4006
+ exports.TRANSACTION_ISOLATION_LEVELS = TRANSACTION_ISOLATION_LEVELS;
4007
+ exports.catalogWireTypes = catalogWireTypes;
3657
4008
  exports.computeRetryDelay = computeRetryDelay;
3658
4009
  exports.consume = consume;
3659
4010
  exports.createRetryPolicy = createRetryPolicy;
@@ -3663,6 +4014,7 @@ exports.defineHandlers = defineHandlers;
3663
4014
  exports.describeCatalog = describeCatalog;
3664
4015
  exports.isRetryableError = isRetryableError;
3665
4016
  exports.produce = produce;
4017
+ exports.route = route;
3666
4018
  exports.systemClock = systemClock;
3667
4019
  exports.traceContextOf = traceContextOf;
3668
4020
  //# sourceMappingURL=index.cjs.map