@relaybus/core 0.2.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +120 -8
- package/dist/chunk-3OHMEMZA.cjs +205 -0
- package/dist/chunk-3OHMEMZA.cjs.map +1 -0
- package/dist/chunk-WS274DL3.js +183 -0
- package/dist/chunk-WS274DL3.js.map +1 -0
- package/dist/event-DCRTuYKI.d.cts +142 -0
- package/dist/event-DCRTuYKI.d.ts +142 -0
- package/dist/index.cjs +1405 -589
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +372 -129
- package/dist/index.d.ts +372 -129
- package/dist/index.js +1283 -524
- package/dist/index.js.map +1 -1
- package/dist/internal/index.cjs +444 -0
- package/dist/internal/index.cjs.map +1 -0
- package/dist/internal/index.d.cts +171 -0
- package/dist/internal/index.d.ts +171 -0
- package/dist/internal/index.js +425 -0
- package/dist/internal/index.js.map +1 -0
- package/package.json +18 -1
package/dist/index.cjs
CHANGED
|
@@ -1,74 +1,15 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
+
var chunk3OHMEMZA_cjs = require('./chunk-3OHMEMZA.cjs');
|
|
3
4
|
var crypto = require('crypto');
|
|
4
|
-
var os = require('os');
|
|
5
5
|
var async_hooks = require('async_hooks');
|
|
6
|
+
var os = require('os');
|
|
6
7
|
|
|
7
8
|
// src/clock.ts
|
|
8
9
|
var systemClock = {
|
|
9
10
|
now: () => /* @__PURE__ */ new Date()
|
|
10
11
|
};
|
|
11
12
|
|
|
12
|
-
// src/errors.ts
|
|
13
|
-
var RelayBusError = class extends Error {
|
|
14
|
-
// Explicit names survive minification, unlike `new.target.name`.
|
|
15
|
-
name = "RelayBusError";
|
|
16
|
-
};
|
|
17
|
-
var ConfigurationError = class extends RelayBusError {
|
|
18
|
-
name = "ConfigurationError";
|
|
19
|
-
};
|
|
20
|
-
var TransactionRequiredError = class extends RelayBusError {
|
|
21
|
-
name = "TransactionRequiredError";
|
|
22
|
-
};
|
|
23
|
-
var NonRetryableError = class extends RelayBusError {
|
|
24
|
-
name = "NonRetryableError";
|
|
25
|
-
};
|
|
26
|
-
var EventValidationError = class extends NonRetryableError {
|
|
27
|
-
constructor(message, issues) {
|
|
28
|
-
super(message);
|
|
29
|
-
this.issues = issues;
|
|
30
|
-
}
|
|
31
|
-
issues;
|
|
32
|
-
name = "EventValidationError";
|
|
33
|
-
};
|
|
34
|
-
var InvalidEnvelopeError = class extends NonRetryableError {
|
|
35
|
-
name = "InvalidEnvelopeError";
|
|
36
|
-
};
|
|
37
|
-
var LeaseLostError = class extends RelayBusError {
|
|
38
|
-
name = "LeaseLostError";
|
|
39
|
-
/**
|
|
40
|
-
* Whether the store said so (a fenced write returned false). A worker that only noticed
|
|
41
|
-
* locally that its lease ran out may still own the delivery in the store and has to give it back.
|
|
42
|
-
*/
|
|
43
|
-
storeConfirmed;
|
|
44
|
-
constructor(message, options = {}) {
|
|
45
|
-
super(message);
|
|
46
|
-
this.storeConfirmed = options.storeConfirmed ?? false;
|
|
47
|
-
}
|
|
48
|
-
};
|
|
49
|
-
var HandlerTimeoutError = class extends RelayBusError {
|
|
50
|
-
name = "HandlerTimeoutError";
|
|
51
|
-
};
|
|
52
|
-
var DispatcherStoppedError = class extends RelayBusError {
|
|
53
|
-
name = "DispatcherStoppedError";
|
|
54
|
-
};
|
|
55
|
-
function toErrorInfo(error) {
|
|
56
|
-
if (error instanceof Error) {
|
|
57
|
-
return { name: error.name, message: error.message, stack: error.stack };
|
|
58
|
-
}
|
|
59
|
-
return { name: "NonError", message: safeStringify(error) };
|
|
60
|
-
}
|
|
61
|
-
function safeStringify(value) {
|
|
62
|
-
if (typeof value === "string") {
|
|
63
|
-
return value;
|
|
64
|
-
}
|
|
65
|
-
try {
|
|
66
|
-
return JSON.stringify(value) ?? String(value);
|
|
67
|
-
} catch {
|
|
68
|
-
return String(value);
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
|
|
72
13
|
// src/duration.ts
|
|
73
14
|
var MILLISECONDS_PER_UNIT = {
|
|
74
15
|
ms: 1,
|
|
@@ -81,7 +22,7 @@ var DURATION_PATTERN = /^(\d+(?:\.\d+)?)(ms|s|m|h|d)$/;
|
|
|
81
22
|
function toMilliseconds(value, name) {
|
|
82
23
|
if (typeof value === "number") {
|
|
83
24
|
if (!Number.isFinite(value) || value < 0) {
|
|
84
|
-
throw new ConfigurationError(
|
|
25
|
+
throw new chunk3OHMEMZA_cjs.ConfigurationError(
|
|
85
26
|
`${name} must be a non-negative finite number of milliseconds, received ${value}`
|
|
86
27
|
);
|
|
87
28
|
}
|
|
@@ -89,7 +30,7 @@ function toMilliseconds(value, name) {
|
|
|
89
30
|
}
|
|
90
31
|
const match = DURATION_PATTERN.exec(value);
|
|
91
32
|
if (match === null) {
|
|
92
|
-
throw new ConfigurationError(
|
|
33
|
+
throw new chunk3OHMEMZA_cjs.ConfigurationError(
|
|
93
34
|
`${name} must look like '500ms', '30s', '5m', '1h' or '7d', received '${value}'`
|
|
94
35
|
);
|
|
95
36
|
}
|
|
@@ -100,56 +41,16 @@ var MAX_TIMER_DELAY_MS = 2147483647;
|
|
|
100
41
|
function toTimerMilliseconds(value, name) {
|
|
101
42
|
const milliseconds = toMilliseconds(value, name);
|
|
102
43
|
if (milliseconds === 0) {
|
|
103
|
-
throw new ConfigurationError(`${name} must be greater than zero`);
|
|
44
|
+
throw new chunk3OHMEMZA_cjs.ConfigurationError(`${name} must be greater than zero`);
|
|
104
45
|
}
|
|
105
46
|
if (milliseconds > MAX_TIMER_DELAY_MS) {
|
|
106
|
-
throw new ConfigurationError(
|
|
47
|
+
throw new chunk3OHMEMZA_cjs.ConfigurationError(
|
|
107
48
|
`${name} must not exceed ${MAX_TIMER_DELAY_MS}ms (about 24.8 days), received ${milliseconds}ms`
|
|
108
49
|
);
|
|
109
50
|
}
|
|
110
51
|
return milliseconds;
|
|
111
52
|
}
|
|
112
53
|
|
|
113
|
-
// src/event.ts
|
|
114
|
-
var EVENT_TYPE_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._:/-]{0,254}$/;
|
|
115
|
-
function assertEventType(type) {
|
|
116
|
-
if (!EVENT_TYPE_PATTERN.test(type)) {
|
|
117
|
-
throw new ConfigurationError(
|
|
118
|
-
`Event type '${type}' must be 1-255 characters of letters, digits, '.', '_', ':', '/' or '-', starting with a letter or digit`
|
|
119
|
-
);
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
function defineEvent(type, options) {
|
|
123
|
-
assertEventType(type);
|
|
124
|
-
const schema = options?.schema;
|
|
125
|
-
return class DefinedEvent {
|
|
126
|
-
constructor(payload) {
|
|
127
|
-
this.payload = payload;
|
|
128
|
-
}
|
|
129
|
-
payload;
|
|
130
|
-
static type = type;
|
|
131
|
-
static schema = schema;
|
|
132
|
-
type = type;
|
|
133
|
-
};
|
|
134
|
-
}
|
|
135
|
-
async function validatePayload(type, schema, payload) {
|
|
136
|
-
if (schema === void 0) {
|
|
137
|
-
return payload;
|
|
138
|
-
}
|
|
139
|
-
const result = await schema["~standard"].validate(payload);
|
|
140
|
-
if (result.issues !== void 0) {
|
|
141
|
-
const issues = result.issues.map((issue) => ({
|
|
142
|
-
message: issue.message,
|
|
143
|
-
path: issue.path?.map((segment) => typeof segment === "object" ? segment.key : segment)
|
|
144
|
-
}));
|
|
145
|
-
throw new EventValidationError(
|
|
146
|
-
`Payload of '${type}' failed validation: ${issues.map((issue) => issue.message).join("; ")}`,
|
|
147
|
-
issues
|
|
148
|
-
);
|
|
149
|
-
}
|
|
150
|
-
return result.value;
|
|
151
|
-
}
|
|
152
|
-
|
|
153
54
|
// src/catalog.ts
|
|
154
55
|
var EVENT_NAME_PATTERN = /^[A-Za-z_][A-Za-z0-9_]*$/;
|
|
155
56
|
var descriptions = /* @__PURE__ */ new WeakMap();
|
|
@@ -157,25 +58,25 @@ function defineCatalog(events, options = {}) {
|
|
|
157
58
|
const label = options.name === void 0 ? "Catalog" : `Catalog '${options.name}'`;
|
|
158
59
|
const names = Object.keys(events);
|
|
159
60
|
if (names.length === 0) {
|
|
160
|
-
throw new ConfigurationError(`${label} must contain at least one event`);
|
|
61
|
+
throw new chunk3OHMEMZA_cjs.ConfigurationError(`${label} must contain at least one event`);
|
|
161
62
|
}
|
|
162
63
|
const owners = /* @__PURE__ */ new Map();
|
|
163
64
|
const entries = names.map((name) => {
|
|
164
65
|
const eventClass = events[name];
|
|
165
66
|
if (!EVENT_NAME_PATTERN.test(name)) {
|
|
166
|
-
throw new ConfigurationError(
|
|
67
|
+
throw new chunk3OHMEMZA_cjs.ConfigurationError(
|
|
167
68
|
`${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`
|
|
168
69
|
);
|
|
169
70
|
}
|
|
170
71
|
if (typeof eventClass !== "function" || typeof eventClass.type !== "string") {
|
|
171
|
-
throw new ConfigurationError(
|
|
72
|
+
throw new chunk3OHMEMZA_cjs.ConfigurationError(
|
|
172
73
|
`${label} entry '${name}' must be an event class created with defineEvent`
|
|
173
74
|
);
|
|
174
75
|
}
|
|
175
|
-
assertEventType(eventClass.type);
|
|
76
|
+
chunk3OHMEMZA_cjs.assertEventType(eventClass.type);
|
|
176
77
|
const owner = owners.get(eventClass.type);
|
|
177
78
|
if (owner !== void 0) {
|
|
178
|
-
throw new ConfigurationError(
|
|
79
|
+
throw new chunk3OHMEMZA_cjs.ConfigurationError(
|
|
179
80
|
`${label} entries '${owner}' and '${name}' share the event type '${eventClass.type}'`
|
|
180
81
|
);
|
|
181
82
|
}
|
|
@@ -189,7 +90,7 @@ function defineCatalog(events, options = {}) {
|
|
|
189
90
|
function describeCatalog(catalog) {
|
|
190
91
|
const description = descriptions.get(catalog);
|
|
191
92
|
if (description === void 0) {
|
|
192
|
-
throw new ConfigurationError("Expected a catalog created with defineCatalog");
|
|
93
|
+
throw new chunk3OHMEMZA_cjs.ConfigurationError("Expected a catalog created with defineCatalog");
|
|
193
94
|
}
|
|
194
95
|
return description;
|
|
195
96
|
}
|
|
@@ -204,19 +105,19 @@ function selectCatalogEntries(catalog, events) {
|
|
|
204
105
|
}
|
|
205
106
|
const label = catalogLabel(catalog);
|
|
206
107
|
if (events.length === 0) {
|
|
207
|
-
throw new ConfigurationError(
|
|
108
|
+
throw new chunk3OHMEMZA_cjs.ConfigurationError(
|
|
208
109
|
`The events selected from ${label} must name at least one event; omit events to select all of them`
|
|
209
110
|
);
|
|
210
111
|
}
|
|
211
112
|
const selected = /* @__PURE__ */ new Set();
|
|
212
113
|
for (const event of events) {
|
|
213
114
|
if (!entries.some((entry) => entry.name === event)) {
|
|
214
|
-
throw new ConfigurationError(
|
|
115
|
+
throw new chunk3OHMEMZA_cjs.ConfigurationError(
|
|
215
116
|
`${capitalize(label)} has no event named '${String(event)}'; its events are ${entries.map((entry) => entry.name).join(", ")}`
|
|
216
117
|
);
|
|
217
118
|
}
|
|
218
119
|
if (selected.has(event)) {
|
|
219
|
-
throw new ConfigurationError(`Event '${String(event)}' of ${label} is selected twice`);
|
|
120
|
+
throw new chunk3OHMEMZA_cjs.ConfigurationError(`Event '${String(event)}' of ${label} is selected twice`);
|
|
220
121
|
}
|
|
221
122
|
selected.add(event);
|
|
222
123
|
}
|
|
@@ -228,26 +129,29 @@ function capitalize(text) {
|
|
|
228
129
|
function requireCatalogEntry(catalog, event) {
|
|
229
130
|
const entry = describeCatalog(catalog).entries.find((candidate) => candidate.type === event.type);
|
|
230
131
|
if (entry === void 0) {
|
|
231
|
-
throw new ConfigurationError(
|
|
132
|
+
throw new chunk3OHMEMZA_cjs.ConfigurationError(
|
|
232
133
|
`Event type '${String(event.type)}' is not part of ${catalogLabel(catalog)}`
|
|
233
134
|
);
|
|
234
135
|
}
|
|
235
136
|
return entry;
|
|
236
137
|
}
|
|
237
|
-
function resolveCatalogHandlers(catalog,
|
|
138
|
+
function resolveCatalogHandlers(catalog, {
|
|
139
|
+
handlers,
|
|
140
|
+
options
|
|
141
|
+
}) {
|
|
238
142
|
const { name } = describeCatalog(catalog);
|
|
239
143
|
const { idPrefix, events, ...subscribeOptions } = options;
|
|
240
144
|
const entries = selectCatalogEntries(catalog, events);
|
|
241
145
|
const lookup = handlers;
|
|
242
146
|
const missing = entries.filter((entry) => typeof lookup[entry.handlerName] !== "function");
|
|
243
147
|
if (missing.length > 0) {
|
|
244
|
-
throw new ConfigurationError(
|
|
148
|
+
throw new chunk3OHMEMZA_cjs.ConfigurationError(
|
|
245
149
|
`Handlers for catalog ${name === void 0 ? "events" : `'${name}'`} lack methods: ${missing.map((entry) => entry.handlerName).join(", ")}`
|
|
246
150
|
);
|
|
247
151
|
}
|
|
248
152
|
const prefix = idPrefix ?? name ?? classNameOf(handlers);
|
|
249
153
|
if (prefix === void 0) {
|
|
250
|
-
throw new ConfigurationError(
|
|
154
|
+
throw new chunk3OHMEMZA_cjs.ConfigurationError(
|
|
251
155
|
"Cannot derive handler ids: name the catalog, pass an idPrefix or use a class instance as handlers"
|
|
252
156
|
);
|
|
253
157
|
}
|
|
@@ -264,7 +168,7 @@ function classNameOf(handlers) {
|
|
|
264
168
|
const className = handlers.constructor.name;
|
|
265
169
|
return className === "Object" || className === "" ? void 0 : className;
|
|
266
170
|
}
|
|
267
|
-
function assertExactHandlers(catalog, entries, handlers) {
|
|
171
|
+
function assertExactHandlers(catalog, { entries, handlers }) {
|
|
268
172
|
const expected = new Set(entries.map((entry) => entry.handlerName));
|
|
269
173
|
const present = Object.keys(handlers);
|
|
270
174
|
const missing = [...expected].filter((handlerName) => !present.includes(handlerName));
|
|
@@ -278,13 +182,16 @@ function assertExactHandlers(catalog, entries, handlers) {
|
|
|
278
182
|
...extra.map((key) => `unexpected ${key}`),
|
|
279
183
|
...notFunctions.map((key) => `${key} is not a function`)
|
|
280
184
|
];
|
|
281
|
-
throw new ConfigurationError(
|
|
185
|
+
throw new chunk3OHMEMZA_cjs.ConfigurationError(
|
|
282
186
|
`Handlers do not match ${catalogLabel(catalog)}: ${problems.join(", ")}`
|
|
283
187
|
);
|
|
284
188
|
}
|
|
285
189
|
}
|
|
286
190
|
function defineHandlers(catalog, handlers, options = {}) {
|
|
287
|
-
assertExactHandlers(catalog,
|
|
191
|
+
assertExactHandlers(catalog, {
|
|
192
|
+
entries: selectCatalogEntries(catalog, options.events),
|
|
193
|
+
handlers
|
|
194
|
+
});
|
|
288
195
|
return handlers;
|
|
289
196
|
}
|
|
290
197
|
|
|
@@ -326,7 +233,7 @@ var CatalogPublisher = class {
|
|
|
326
233
|
#assertPublishable(event) {
|
|
327
234
|
const { name } = requireCatalogEntry(this.#catalog, event);
|
|
328
235
|
if (!this.#publishes(name)) {
|
|
329
|
-
throw new ConfigurationError(
|
|
236
|
+
throw new chunk3OHMEMZA_cjs.ConfigurationError(
|
|
330
237
|
`Event type '${event.type}' is not one of the events this view of ${catalogLabel(
|
|
331
238
|
this.#catalog
|
|
332
239
|
)} publishes (${this.#entries.map((entry) => entry.name).join(", ")})`
|
|
@@ -338,12 +245,12 @@ var CatalogPublisher = class {
|
|
|
338
245
|
// src/catalog-coverage.ts
|
|
339
246
|
var declarations = /* @__PURE__ */ new WeakSet();
|
|
340
247
|
function consume(catalog, options = {}) {
|
|
341
|
-
return declare(catalog, "consume", options);
|
|
248
|
+
return declare(catalog, { role: "consume", options });
|
|
342
249
|
}
|
|
343
250
|
function produce(catalog, options = {}) {
|
|
344
|
-
return declare(catalog, "produce", options);
|
|
251
|
+
return declare(catalog, { role: "produce", options });
|
|
345
252
|
}
|
|
346
|
-
function declare(catalog, role, options) {
|
|
253
|
+
function declare(catalog, { role, options }) {
|
|
347
254
|
const entries = selectCatalogEntries(catalog, options.events);
|
|
348
255
|
const declaration = Object.freeze({
|
|
349
256
|
catalog,
|
|
@@ -371,7 +278,7 @@ function assertCatalogsCovered(requirements, registrationsOf) {
|
|
|
371
278
|
let onlyBareCatalogs = true;
|
|
372
279
|
for (const { catalogName, entries, need } of requirements) {
|
|
373
280
|
for (const entry of entries) {
|
|
374
|
-
const problem = describeGap(entry, need, registrationsOf(entry.type));
|
|
281
|
+
const problem = describeGap(entry, { need, registrations: registrationsOf(entry.type) });
|
|
375
282
|
if (problem !== void 0) {
|
|
376
283
|
problems.add(`${catalogName === void 0 ? "" : `${catalogName}.`}${problem}`);
|
|
377
284
|
onlyBareCatalogs &&= need === "handler-or-route";
|
|
@@ -380,13 +287,16 @@ function assertCatalogsCovered(requirements, registrationsOf) {
|
|
|
380
287
|
}
|
|
381
288
|
if (problems.size > 0) {
|
|
382
289
|
const heading = onlyBareCatalogs ? "Catalog events without a handler or route:" : "Catalog events this service does not cover:";
|
|
383
|
-
throw new ConfigurationError(
|
|
290
|
+
throw new chunk3OHMEMZA_cjs.ConfigurationError(
|
|
384
291
|
`${heading}
|
|
385
292
|
${[...problems].map((line) => ` - ${line}`).join("\n")}`
|
|
386
293
|
);
|
|
387
294
|
}
|
|
388
295
|
}
|
|
389
|
-
function describeGap(entry,
|
|
296
|
+
function describeGap(entry, {
|
|
297
|
+
need,
|
|
298
|
+
registrations: { handlers, routes }
|
|
299
|
+
}) {
|
|
390
300
|
const subject = `${entry.name} (${entry.type})`;
|
|
391
301
|
if (need === "handler-or-route") {
|
|
392
302
|
return handlers + routes > 0 ? void 0 : `${subject}: expected handler method ${entry.handlerName}`;
|
|
@@ -412,20 +322,26 @@ var DELIVERY_STATUSES = [
|
|
|
412
322
|
"completed",
|
|
413
323
|
"dead"
|
|
414
324
|
];
|
|
325
|
+
var UUID_BYTES = 16;
|
|
326
|
+
var TIMESTAMP_BYTES = 6;
|
|
327
|
+
var BYTE_VALUES = 256;
|
|
328
|
+
var LOW_NIBBLE = 15;
|
|
329
|
+
var VERSION_7 = 112;
|
|
330
|
+
var VARIANT_PAYLOAD = 63;
|
|
331
|
+
var RFC_9562_VARIANT = 128;
|
|
415
332
|
function createUuidV7(timestamp = Date.now()) {
|
|
416
|
-
const bytes = crypto.randomBytes(
|
|
333
|
+
const bytes = crypto.randomBytes(UUID_BYTES);
|
|
417
334
|
let remaining = timestamp;
|
|
418
|
-
for (let index =
|
|
419
|
-
bytes[index] = remaining %
|
|
420
|
-
remaining = Math.floor(remaining /
|
|
335
|
+
for (let index = TIMESTAMP_BYTES - 1; index >= 0; index -= 1) {
|
|
336
|
+
bytes[index] = remaining % BYTE_VALUES;
|
|
337
|
+
remaining = Math.floor(remaining / BYTE_VALUES);
|
|
421
338
|
}
|
|
422
|
-
bytes[6] = bytes[6] &
|
|
423
|
-
bytes[8] = bytes[8] &
|
|
424
|
-
|
|
425
|
-
return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;
|
|
339
|
+
bytes[6] = bytes[6] & LOW_NIBBLE | VERSION_7;
|
|
340
|
+
bytes[8] = bytes[8] & VARIANT_PAYLOAD | RFC_9562_VARIANT;
|
|
341
|
+
return bytes.toString("hex").replace(/^(.{8})(.{4})(.{4})(.{4})(.{12})$/, "$1-$2-$3-$4-$5");
|
|
426
342
|
}
|
|
427
343
|
|
|
428
|
-
// src/in-memory-
|
|
344
|
+
// src/in-memory-transaction.ts
|
|
429
345
|
var InMemoryTransaction = class {
|
|
430
346
|
#operations = [];
|
|
431
347
|
#finishers = [];
|
|
@@ -435,7 +351,7 @@ var InMemoryTransaction = class {
|
|
|
435
351
|
}
|
|
436
352
|
enqueue(operation) {
|
|
437
353
|
if (this.#state !== "open") {
|
|
438
|
-
throw new ConfigurationError(`Cannot write through a ${this.#state} in-memory transaction`);
|
|
354
|
+
throw new chunk3OHMEMZA_cjs.ConfigurationError(`Cannot write through a ${this.#state} in-memory transaction`);
|
|
439
355
|
}
|
|
440
356
|
this.#operations.push(operation);
|
|
441
357
|
}
|
|
@@ -460,6 +376,9 @@ var InMemoryTransaction = class {
|
|
|
460
376
|
}
|
|
461
377
|
}
|
|
462
378
|
};
|
|
379
|
+
|
|
380
|
+
// src/in-memory-event-store.ts
|
|
381
|
+
var DEFAULT_LIST_LIMIT = 100;
|
|
463
382
|
var InMemoryEventStore = class {
|
|
464
383
|
#events = /* @__PURE__ */ new Map();
|
|
465
384
|
#deliveries = /* @__PURE__ */ new Map();
|
|
@@ -471,7 +390,7 @@ var InMemoryEventStore = class {
|
|
|
471
390
|
#retainCompleted;
|
|
472
391
|
constructor(options = {}) {
|
|
473
392
|
if (process.env["NODE_ENV"] === "production" && options.allowInProduction !== true) {
|
|
474
|
-
throw new ConfigurationError(
|
|
393
|
+
throw new chunk3OHMEMZA_cjs.ConfigurationError(
|
|
475
394
|
"InMemoryEventStore loses every event on restart. Use a database store in production, or pass allowInProduction: true"
|
|
476
395
|
);
|
|
477
396
|
}
|
|
@@ -489,6 +408,7 @@ var InMemoryEventStore = class {
|
|
|
489
408
|
throw error;
|
|
490
409
|
}
|
|
491
410
|
}
|
|
411
|
+
// eslint-disable-next-line relaybus/max-two-params -- EventStore port signature
|
|
492
412
|
async append(transaction, entries, now) {
|
|
493
413
|
await this.#lock(
|
|
494
414
|
transaction,
|
|
@@ -513,50 +433,63 @@ var InMemoryEventStore = class {
|
|
|
513
433
|
appendedEventIds.push(eventId);
|
|
514
434
|
pendingEventIds.add(eventId);
|
|
515
435
|
}
|
|
516
|
-
const newDeliveries = entry
|
|
517
|
-
const deliveryKey = `${eventId}\0${delivery.handler}`;
|
|
518
|
-
if (pendingDeliveryKeys.has(deliveryKey) || this.#events.get(eventId)?.handlers.has(delivery.handler) === true) {
|
|
519
|
-
return false;
|
|
520
|
-
}
|
|
521
|
-
pendingDeliveryKeys.add(deliveryKey);
|
|
522
|
-
return true;
|
|
523
|
-
});
|
|
436
|
+
const newDeliveries = this.#newDeliveries(entry, pendingDeliveryKeys);
|
|
524
437
|
appendedDeliveryCount += newDeliveries.length;
|
|
525
438
|
transaction.enqueue(() => {
|
|
526
|
-
|
|
527
|
-
this.#events.set(eventId, {
|
|
528
|
-
envelope: serialized,
|
|
529
|
-
sequence: this.#nextSequence(serialized.key),
|
|
530
|
-
createdAt: now,
|
|
531
|
-
handlers: /* @__PURE__ */ new Set(),
|
|
532
|
-
completedAt: null
|
|
533
|
-
});
|
|
534
|
-
}
|
|
535
|
-
const stored = this.#events.get(eventId);
|
|
536
|
-
for (const delivery of newDeliveries) {
|
|
537
|
-
stored.handlers.add(delivery.handler);
|
|
538
|
-
this.#deliveries.set(delivery.id, {
|
|
539
|
-
id: delivery.id,
|
|
540
|
-
eventId,
|
|
541
|
-
handler: delivery.handler,
|
|
542
|
-
key: stored.envelope.key,
|
|
543
|
-
sequence: stored.sequence,
|
|
544
|
-
status: "pending",
|
|
545
|
-
attempts: 0,
|
|
546
|
-
availableAt: delivery.availableAt,
|
|
547
|
-
leaseOwner: null,
|
|
548
|
-
leaseToken: null,
|
|
549
|
-
leaseExpiresAt: null,
|
|
550
|
-
lastError: null,
|
|
551
|
-
createdAt: now,
|
|
552
|
-
updatedAt: now,
|
|
553
|
-
completedAt: null
|
|
554
|
-
});
|
|
555
|
-
}
|
|
439
|
+
this.#applyAppend({ eventId, serialized, isDuplicate, newDeliveries, now });
|
|
556
440
|
});
|
|
557
441
|
}
|
|
558
442
|
return { appendedEventIds, duplicateEventIds, appendedDeliveryCount };
|
|
559
443
|
}
|
|
444
|
+
/**
|
|
445
|
+
* The deliveries of `entry` whose handler the event neither has nor gets earlier in the
|
|
446
|
+
* transaction; remembers them in `pendingDeliveryKeys`.
|
|
447
|
+
*/
|
|
448
|
+
#newDeliveries(entry, pendingDeliveryKeys) {
|
|
449
|
+
const eventId = entry.event.id;
|
|
450
|
+
return entry.deliveries.filter((delivery) => {
|
|
451
|
+
const deliveryKey = `${eventId}\0${delivery.handler}`;
|
|
452
|
+
if (pendingDeliveryKeys.has(deliveryKey) || this.#events.get(eventId)?.handlers.has(delivery.handler) === true) {
|
|
453
|
+
return false;
|
|
454
|
+
}
|
|
455
|
+
pendingDeliveryKeys.add(deliveryKey);
|
|
456
|
+
return true;
|
|
457
|
+
});
|
|
458
|
+
}
|
|
459
|
+
/** Applies one entry of an append when its transaction commits. */
|
|
460
|
+
#applyAppend(input) {
|
|
461
|
+
const { eventId, serialized, isDuplicate, newDeliveries, now } = input;
|
|
462
|
+
if (!isDuplicate) {
|
|
463
|
+
this.#events.set(eventId, {
|
|
464
|
+
envelope: serialized,
|
|
465
|
+
sequence: this.#nextSequence(serialized.key),
|
|
466
|
+
createdAt: now,
|
|
467
|
+
handlers: /* @__PURE__ */ new Set(),
|
|
468
|
+
completedAt: null
|
|
469
|
+
});
|
|
470
|
+
}
|
|
471
|
+
const stored = this.#events.get(eventId);
|
|
472
|
+
for (const delivery of newDeliveries) {
|
|
473
|
+
stored.handlers.add(delivery.handler);
|
|
474
|
+
this.#deliveries.set(delivery.id, {
|
|
475
|
+
id: delivery.id,
|
|
476
|
+
eventId,
|
|
477
|
+
handler: delivery.handler,
|
|
478
|
+
key: stored.envelope.key,
|
|
479
|
+
sequence: stored.sequence,
|
|
480
|
+
status: "pending",
|
|
481
|
+
attempts: 0,
|
|
482
|
+
availableAt: delivery.availableAt,
|
|
483
|
+
leaseOwner: null,
|
|
484
|
+
leaseToken: null,
|
|
485
|
+
leaseExpiresAt: null,
|
|
486
|
+
lastError: null,
|
|
487
|
+
createdAt: now,
|
|
488
|
+
updatedAt: now,
|
|
489
|
+
completedAt: null
|
|
490
|
+
});
|
|
491
|
+
}
|
|
492
|
+
}
|
|
560
493
|
async claim(request) {
|
|
561
494
|
const handlers = new Set(request.handlers);
|
|
562
495
|
const due = [...this.#deliveries.values()].filter((delivery) => handlers.has(delivery.handler) && isDue(delivery, request.now)).sort(compareDue);
|
|
@@ -585,12 +518,14 @@ var InMemoryEventStore = class {
|
|
|
585
518
|
}
|
|
586
519
|
return claimed;
|
|
587
520
|
}
|
|
521
|
+
// eslint-disable-next-line relaybus/max-two-params -- EventStore port signature
|
|
588
522
|
async extendLease(lease, leaseExpiresAt, now) {
|
|
589
523
|
return this.#updateLeased(lease, (delivery) => {
|
|
590
524
|
delivery.leaseExpiresAt = leaseExpiresAt;
|
|
591
525
|
delivery.updatedAt = now;
|
|
592
526
|
});
|
|
593
527
|
}
|
|
528
|
+
// eslint-disable-next-line relaybus/max-two-params -- EventStore port signature
|
|
594
529
|
async complete(lease, now, transaction) {
|
|
595
530
|
if (transaction === void 0) {
|
|
596
531
|
return this.#updateLeased(lease, (delivery) => this.#finish(delivery, now));
|
|
@@ -609,6 +544,7 @@ var InMemoryEventStore = class {
|
|
|
609
544
|
async completeMany(leases, now) {
|
|
610
545
|
return Promise.all(leases.map((lease) => this.complete(lease, now)));
|
|
611
546
|
}
|
|
547
|
+
// eslint-disable-next-line relaybus/max-two-params -- EventStore port signature
|
|
612
548
|
async reschedule(lease, availableAt, error, now) {
|
|
613
549
|
return this.#updateLeased(lease, (delivery) => {
|
|
614
550
|
delivery.status = "pending";
|
|
@@ -627,6 +563,7 @@ var InMemoryEventStore = class {
|
|
|
627
563
|
clearLease(delivery);
|
|
628
564
|
});
|
|
629
565
|
}
|
|
566
|
+
// eslint-disable-next-line relaybus/max-two-params -- EventStore port signature
|
|
630
567
|
async bury(lease, error, now) {
|
|
631
568
|
return this.#updateLeased(lease, (delivery) => {
|
|
632
569
|
delivery.status = "dead";
|
|
@@ -636,8 +573,10 @@ var InMemoryEventStore = class {
|
|
|
636
573
|
});
|
|
637
574
|
}
|
|
638
575
|
async listDeliveries(query) {
|
|
639
|
-
const limit = query.limit ??
|
|
640
|
-
return [...this.#deliveries.values()].filter(
|
|
576
|
+
const limit = query.limit ?? DEFAULT_LIST_LIMIT;
|
|
577
|
+
return [...this.#deliveries.values()].filter(
|
|
578
|
+
(delivery) => matchesQuery(delivery, { event: this.#events.get(delivery.eventId), query })
|
|
579
|
+
).sort(
|
|
641
580
|
(left, right) => left.createdAt.getTime() - right.createdAt.getTime() || compareIds(left.id, right.id)
|
|
642
581
|
).slice(0, limit).map((delivery) => ({
|
|
643
582
|
id: delivery.id,
|
|
@@ -679,12 +618,14 @@ var InMemoryEventStore = class {
|
|
|
679
618
|
deletedDeliveries += 1;
|
|
680
619
|
}
|
|
681
620
|
}
|
|
621
|
+
return { deletedDeliveries, deletedEvents: this.#purgeEvents(request, touchedEventIds) };
|
|
622
|
+
}
|
|
623
|
+
/** Deletes the events no delivery references whose retention ran out; returns how many. */
|
|
624
|
+
#purgeEvents(request, touchedEventIds) {
|
|
682
625
|
const referencedEventIds = new Set(
|
|
683
626
|
[...this.#deliveries.values()].map((delivery) => delivery.eventId)
|
|
684
627
|
);
|
|
685
|
-
const
|
|
686
|
-
const orphanCutoff = cutoffs.length === 0 ? Number.NEGATIVE_INFINITY : Math.min(...cutoffs);
|
|
687
|
-
const completedCutoff = request.completedBefore?.getTime() ?? Number.NEGATIVE_INFINITY;
|
|
628
|
+
const { orphanCutoff, completedCutoff } = eventCutoffs(request);
|
|
688
629
|
let deletedEvents = 0;
|
|
689
630
|
for (const [eventId, event] of [...this.#events]) {
|
|
690
631
|
if (referencedEventIds.has(eventId) || this.#locks.has(`event:${eventId}`)) {
|
|
@@ -696,7 +637,7 @@ var InMemoryEventStore = class {
|
|
|
696
637
|
deletedEvents += 1;
|
|
697
638
|
}
|
|
698
639
|
}
|
|
699
|
-
return
|
|
640
|
+
return deletedEvents;
|
|
700
641
|
}
|
|
701
642
|
async statistics(now) {
|
|
702
643
|
const counts = {
|
|
@@ -771,12 +712,7 @@ var InMemoryEventStore = class {
|
|
|
771
712
|
return false;
|
|
772
713
|
}
|
|
773
714
|
for (const other of this.#deliveries.values()) {
|
|
774
|
-
if (other
|
|
775
|
-
continue;
|
|
776
|
-
}
|
|
777
|
-
const unfinishedPredecessor = other.sequence < delivery.sequence && (other.status === "pending" || other.status === "processing");
|
|
778
|
-
const runningUnderLiveLease = other.status === "processing" && other.leaseExpiresAt > now;
|
|
779
|
-
if (unfinishedPredecessor || runningUnderLiveLease) {
|
|
715
|
+
if (blocksKey(other, { delivery, now })) {
|
|
780
716
|
return true;
|
|
781
717
|
}
|
|
782
718
|
}
|
|
@@ -818,6 +754,21 @@ var InMemoryEventStore = class {
|
|
|
818
754
|
return { ...copy, occurredAt: new Date(copy.occurredAt) };
|
|
819
755
|
}
|
|
820
756
|
};
|
|
757
|
+
function blocksKey(other, { delivery, now }) {
|
|
758
|
+
if (other === delivery || other.handler !== delivery.handler || other.key !== delivery.key) {
|
|
759
|
+
return false;
|
|
760
|
+
}
|
|
761
|
+
const unfinishedPredecessor = other.sequence < delivery.sequence && (other.status === "pending" || other.status === "processing");
|
|
762
|
+
const runningUnderLiveLease = other.status === "processing" && other.leaseExpiresAt > now;
|
|
763
|
+
return unfinishedPredecessor || runningUnderLiveLease;
|
|
764
|
+
}
|
|
765
|
+
function eventCutoffs(request) {
|
|
766
|
+
const cutoffs = [request.completedBefore, request.deadBefore].filter((cutoff) => cutoff !== void 0).map((cutoff) => cutoff.getTime());
|
|
767
|
+
return {
|
|
768
|
+
orphanCutoff: cutoffs.length === 0 ? Number.NEGATIVE_INFINITY : Math.min(...cutoffs),
|
|
769
|
+
completedCutoff: request.completedBefore?.getTime() ?? Number.NEGATIVE_INFINITY
|
|
770
|
+
};
|
|
771
|
+
}
|
|
821
772
|
function serializeEnvelope(envelope) {
|
|
822
773
|
return JSON.parse(JSON.stringify(envelope));
|
|
823
774
|
}
|
|
@@ -842,7 +793,7 @@ function clearLease(delivery) {
|
|
|
842
793
|
delivery.leaseToken = null;
|
|
843
794
|
delivery.leaseExpiresAt = null;
|
|
844
795
|
}
|
|
845
|
-
function matchesQuery(delivery, event, query) {
|
|
796
|
+
function matchesQuery(delivery, { event, query }) {
|
|
846
797
|
return (query.status === void 0 || delivery.status === query.status) && (query.handler === void 0 || delivery.handler === query.handler) && (query.eventType === void 0 || event.envelope.type === query.eventType) && (query.eventId === void 0 || delivery.eventId === query.eventId);
|
|
847
798
|
}
|
|
848
799
|
function isPurgeable(delivery, request) {
|
|
@@ -855,6 +806,276 @@ function isPurgeable(delivery, request) {
|
|
|
855
806
|
return false;
|
|
856
807
|
}
|
|
857
808
|
|
|
809
|
+
// src/tracing.ts
|
|
810
|
+
var TRACEPARENT_HEADER = "traceparent";
|
|
811
|
+
var TRACESTATE_HEADER = "tracestate";
|
|
812
|
+
var TRACEPARENT_FIELDS = "(?!0{32})[\\da-f]{32}-(?!0{16})[\\da-f]{16}-[\\da-f]{2}";
|
|
813
|
+
var TRACEPARENT = new RegExp(
|
|
814
|
+
`^(?:00-${TRACEPARENT_FIELDS}|(?!00|ff)[\\da-f]{2}-${TRACEPARENT_FIELDS}(?:-.*)?)$`
|
|
815
|
+
);
|
|
816
|
+
function traceContextOf(headers) {
|
|
817
|
+
const traceparent = headers[TRACEPARENT_HEADER];
|
|
818
|
+
if (traceparent === void 0 || !TRACEPARENT.test(traceparent)) {
|
|
819
|
+
return void 0;
|
|
820
|
+
}
|
|
821
|
+
const tracestate = headers[TRACESTATE_HEADER];
|
|
822
|
+
return tracestate === void 0 ? { traceparent } : { traceparent, tracestate };
|
|
823
|
+
}
|
|
824
|
+
|
|
825
|
+
// src/bus-observation.ts
|
|
826
|
+
function withTraceHeaders(options, headers) {
|
|
827
|
+
return Object.keys(headers).length === 0 ? options : { ...options, headers: { ...headers, ...options.headers } };
|
|
828
|
+
}
|
|
829
|
+
var BusObservation = class _BusObservation {
|
|
830
|
+
#notifier;
|
|
831
|
+
#tracer;
|
|
832
|
+
#clock;
|
|
833
|
+
#publishes;
|
|
834
|
+
#receives;
|
|
835
|
+
#sends;
|
|
836
|
+
constructor(input) {
|
|
837
|
+
this.#notifier = input.notifier;
|
|
838
|
+
this.#tracer = input.tracer;
|
|
839
|
+
this.#clock = input.clock;
|
|
840
|
+
this.#publishes = input.notifier.wants("onPublishFinished");
|
|
841
|
+
this.#receives = input.notifier.wants("onReceiveFinished");
|
|
842
|
+
this.#sends = input.notifier.wants("onTransportSent");
|
|
843
|
+
}
|
|
844
|
+
/** An observation, or undefined when no observer hook and no tracer would see anything. */
|
|
845
|
+
static create(input) {
|
|
846
|
+
const observed = input.tracer !== void 0 || ["onPublishFinished", "onReceiveFinished", "onTransportSent", "onBacklog"].some(
|
|
847
|
+
(method) => input.notifier.wants(method)
|
|
848
|
+
);
|
|
849
|
+
return observed ? new _BusObservation(input) : void 0;
|
|
850
|
+
}
|
|
851
|
+
/** Runs a publish call inside the tracer's publish span, reporting its duration and outcome. */
|
|
852
|
+
async publish(input, run) {
|
|
853
|
+
const startedAt = this.#publishes ? this.#now() : 0;
|
|
854
|
+
const tracer = this.#tracer;
|
|
855
|
+
const traced = tracer?.publish === void 0 ? run({}) : tracer.publish(input, run);
|
|
856
|
+
if (!this.#publishes) {
|
|
857
|
+
return traced;
|
|
858
|
+
}
|
|
859
|
+
const report = (outcome, error) => {
|
|
860
|
+
this.#notifier.notify("onPublishFinished", {
|
|
861
|
+
eventTypes: input.eventTypes,
|
|
862
|
+
durationMs: this.#now() - startedAt,
|
|
863
|
+
outcome,
|
|
864
|
+
...error === void 0 ? {} : { error: chunk3OHMEMZA_cjs.toErrorInfo(error) }
|
|
865
|
+
});
|
|
866
|
+
};
|
|
867
|
+
try {
|
|
868
|
+
const result = await traced;
|
|
869
|
+
report("success");
|
|
870
|
+
return result;
|
|
871
|
+
} catch (error) {
|
|
872
|
+
report("error", error);
|
|
873
|
+
throw error;
|
|
874
|
+
}
|
|
875
|
+
}
|
|
876
|
+
/** Runs `receive` for one event, reporting where it came from and what became of it. */
|
|
877
|
+
async receive(input, run) {
|
|
878
|
+
if (!this.#receives) {
|
|
879
|
+
return run();
|
|
880
|
+
}
|
|
881
|
+
const startedAt = this.#now();
|
|
882
|
+
try {
|
|
883
|
+
const outcome = await run();
|
|
884
|
+
this.#reportReceive(input, { outcome, startedAt, failure: void 0 });
|
|
885
|
+
return outcome;
|
|
886
|
+
} catch (error) {
|
|
887
|
+
const outcome = error instanceof chunk3OHMEMZA_cjs.InvalidEnvelopeError ? "invalid" : "error";
|
|
888
|
+
this.#reportReceive(input, { outcome, startedAt, failure: error });
|
|
889
|
+
throw error;
|
|
890
|
+
}
|
|
891
|
+
}
|
|
892
|
+
#reportReceive({ envelope, origin }, report) {
|
|
893
|
+
const { headers } = envelope;
|
|
894
|
+
this.#notifier.notify("onReceiveFinished", {
|
|
895
|
+
transport: origin?.transport.name,
|
|
896
|
+
system: origin === void 0 ? void 0 : systemOf(origin.transport),
|
|
897
|
+
destination: origin?.destination,
|
|
898
|
+
eventType: textOrUndefined(envelope.type),
|
|
899
|
+
source: textOrUndefined(envelope.source),
|
|
900
|
+
traceContext: isObject(headers) ? traceContextOf(headers) : void 0,
|
|
901
|
+
outcome: report.outcome,
|
|
902
|
+
durationMs: this.#now() - report.startedAt,
|
|
903
|
+
...report.failure === void 0 ? {} : { error: chunk3OHMEMZA_cjs.toErrorInfo(report.failure) }
|
|
904
|
+
});
|
|
905
|
+
}
|
|
906
|
+
/** The handler of a route: sends inside the tracer's send span and reports each send. */
|
|
907
|
+
sender(transport, destination) {
|
|
908
|
+
if (this.#tracer?.send === void 0 && !this.#sends) {
|
|
909
|
+
return (envelope) => transport.publish(envelope, destination);
|
|
910
|
+
}
|
|
911
|
+
return (envelope) => this.#send({ envelope, transport, destination });
|
|
912
|
+
}
|
|
913
|
+
async #send({ envelope, transport, destination }) {
|
|
914
|
+
const startedAt = this.#sends ? this.#now() : 0;
|
|
915
|
+
const system = systemOf(transport);
|
|
916
|
+
const run = (headers) => transport.publish(
|
|
917
|
+
Object.keys(headers).length === 0 ? envelope : { ...envelope, headers: { ...envelope.headers, ...headers } },
|
|
918
|
+
destination
|
|
919
|
+
);
|
|
920
|
+
const tracer = this.#tracer;
|
|
921
|
+
const sent = tracer?.send === void 0 ? run({}) : tracer.send({ envelope, transport: transport.name, system, destination }, run);
|
|
922
|
+
if (!this.#sends) {
|
|
923
|
+
return sent;
|
|
924
|
+
}
|
|
925
|
+
const report = (outcome, error) => {
|
|
926
|
+
this.#notifier.notify("onTransportSent", {
|
|
927
|
+
envelope,
|
|
928
|
+
transport: transport.name,
|
|
929
|
+
system,
|
|
930
|
+
destination,
|
|
931
|
+
durationMs: this.#now() - startedAt,
|
|
932
|
+
outcome,
|
|
933
|
+
...error === void 0 ? {} : { error: chunk3OHMEMZA_cjs.toErrorInfo(error) }
|
|
934
|
+
});
|
|
935
|
+
};
|
|
936
|
+
try {
|
|
937
|
+
await sent;
|
|
938
|
+
report("success");
|
|
939
|
+
} catch (error) {
|
|
940
|
+
report("error", error);
|
|
941
|
+
throw error;
|
|
942
|
+
}
|
|
943
|
+
}
|
|
944
|
+
/**
|
|
945
|
+
* Reads the backlog every `intervalMs` and reports it, when an observer wants it. A reading that
|
|
946
|
+
* is still running when the next one is due is not overlapped. Returns the cancel of the timer.
|
|
947
|
+
*/
|
|
948
|
+
startBacklog({ intervalMs, statistics }) {
|
|
949
|
+
if (!this.#notifier.wants("onBacklog")) {
|
|
950
|
+
return void 0;
|
|
951
|
+
}
|
|
952
|
+
let reading = false;
|
|
953
|
+
return chunk3OHMEMZA_cjs.schedule(this.#clock, {
|
|
954
|
+
run: () => {
|
|
955
|
+
if (reading) {
|
|
956
|
+
return;
|
|
957
|
+
}
|
|
958
|
+
reading = true;
|
|
959
|
+
statistics().then((result) => this.#reportBacklog(result)).catch((error) => this.#notifier.failure("backlog", error)).finally(() => {
|
|
960
|
+
reading = false;
|
|
961
|
+
});
|
|
962
|
+
},
|
|
963
|
+
delayMs: intervalMs,
|
|
964
|
+
repeat: true,
|
|
965
|
+
unref: true
|
|
966
|
+
});
|
|
967
|
+
}
|
|
968
|
+
#reportBacklog(statistics) {
|
|
969
|
+
const oldestDueAt = statistics.oldestDueAt;
|
|
970
|
+
this.#notifier.notify("onBacklog", {
|
|
971
|
+
pending: statistics.pending,
|
|
972
|
+
oldestPendingAgeMs: oldestDueAt === null ? 0 : Math.max(0, this.#now() - oldestDueAt.getTime()),
|
|
973
|
+
statistics
|
|
974
|
+
});
|
|
975
|
+
}
|
|
976
|
+
#now() {
|
|
977
|
+
return this.#clock.now().getTime();
|
|
978
|
+
}
|
|
979
|
+
};
|
|
980
|
+
function systemOf(transport) {
|
|
981
|
+
return transport.system ?? transport.name;
|
|
982
|
+
}
|
|
983
|
+
function textOrUndefined(value) {
|
|
984
|
+
return typeof value === "string" ? value : void 0;
|
|
985
|
+
}
|
|
986
|
+
function isObject(value) {
|
|
987
|
+
return typeof value === "object" && value !== null;
|
|
988
|
+
}
|
|
989
|
+
|
|
990
|
+
// src/dispatch-observation.ts
|
|
991
|
+
var DispatchObservation = class _DispatchObservation {
|
|
992
|
+
#notifier;
|
|
993
|
+
#tracer;
|
|
994
|
+
#clock;
|
|
995
|
+
#claims;
|
|
996
|
+
#runs;
|
|
997
|
+
constructor(input) {
|
|
998
|
+
this.#notifier = input.notifier;
|
|
999
|
+
this.#tracer = input.tracer;
|
|
1000
|
+
this.#clock = input.clock;
|
|
1001
|
+
this.#claims = input.notifier.wants("onClaimFinished");
|
|
1002
|
+
this.#runs = input.notifier.wants("onHandlerStarted") || input.notifier.wants("onHandlerFinished");
|
|
1003
|
+
}
|
|
1004
|
+
/** An observation, or undefined when no observer hook and no tracer would see anything. */
|
|
1005
|
+
static create(input) {
|
|
1006
|
+
const observation = new _DispatchObservation(input);
|
|
1007
|
+
return observation.#claims || observation.#runs || input.tracer?.process !== void 0 ? observation : void 0;
|
|
1008
|
+
}
|
|
1009
|
+
/** Claims through `store`, reporting the query's size, lanes and duration. */
|
|
1010
|
+
async claim(store, request) {
|
|
1011
|
+
if (!this.#claims) {
|
|
1012
|
+
return store.claim(request);
|
|
1013
|
+
}
|
|
1014
|
+
const startedAt = this.#clock.now().getTime();
|
|
1015
|
+
const report = { handlers: request.handlers, requested: request.limit };
|
|
1016
|
+
try {
|
|
1017
|
+
const claimed = await store.claim(request);
|
|
1018
|
+
this.#notifier.notify("onClaimFinished", {
|
|
1019
|
+
...report,
|
|
1020
|
+
claimed: claimed.length,
|
|
1021
|
+
lanes: new Set(claimed.map((delivery) => delivery.leaseToken)).size,
|
|
1022
|
+
durationMs: this.#clock.now().getTime() - startedAt,
|
|
1023
|
+
outcome: "success"
|
|
1024
|
+
});
|
|
1025
|
+
return claimed;
|
|
1026
|
+
} catch (error) {
|
|
1027
|
+
this.#notifier.notify("onClaimFinished", {
|
|
1028
|
+
...report,
|
|
1029
|
+
claimed: 0,
|
|
1030
|
+
lanes: 0,
|
|
1031
|
+
durationMs: this.#clock.now().getTime() - startedAt,
|
|
1032
|
+
outcome: "error",
|
|
1033
|
+
error: chunk3OHMEMZA_cjs.toErrorInfo(error)
|
|
1034
|
+
});
|
|
1035
|
+
throw error;
|
|
1036
|
+
}
|
|
1037
|
+
}
|
|
1038
|
+
/** Runs a local handler inside the tracer's process span; routes are traced as sends instead. */
|
|
1039
|
+
process(run, handle) {
|
|
1040
|
+
const tracer = this.#tracer;
|
|
1041
|
+
if (tracer?.process === void 0 || run.registration.forwardsToTransport) {
|
|
1042
|
+
return handle();
|
|
1043
|
+
}
|
|
1044
|
+
return tracer.process({ delivery: run.delivery }, async () => handle());
|
|
1045
|
+
}
|
|
1046
|
+
/** Reports the start of a handler run now and its end when `settled` settles, passing it through. */
|
|
1047
|
+
async observe(run, settled) {
|
|
1048
|
+
if (!this.#runs) {
|
|
1049
|
+
await settled;
|
|
1050
|
+
return;
|
|
1051
|
+
}
|
|
1052
|
+
const { delivery } = run;
|
|
1053
|
+
const kind = run.registration.forwardsToTransport ? "route" : "handler";
|
|
1054
|
+
const startedAt = this.#clock.now().getTime();
|
|
1055
|
+
this.#notifier.notify("onHandlerStarted", {
|
|
1056
|
+
delivery,
|
|
1057
|
+
kind,
|
|
1058
|
+
endToEndLatencyMs: startedAt - delivery.event.occurredAt.getTime()
|
|
1059
|
+
});
|
|
1060
|
+
try {
|
|
1061
|
+
await settled;
|
|
1062
|
+
} catch (error) {
|
|
1063
|
+
const durationMs2 = this.#clock.now().getTime() - startedAt;
|
|
1064
|
+
const errorInfo = chunk3OHMEMZA_cjs.toErrorInfo(error);
|
|
1065
|
+
this.#notifier.notify("onHandlerFinished", {
|
|
1066
|
+
delivery,
|
|
1067
|
+
kind,
|
|
1068
|
+
durationMs: durationMs2,
|
|
1069
|
+
outcome: "error",
|
|
1070
|
+
error: errorInfo
|
|
1071
|
+
});
|
|
1072
|
+
throw error;
|
|
1073
|
+
}
|
|
1074
|
+
const durationMs = this.#clock.now().getTime() - startedAt;
|
|
1075
|
+
this.#notifier.notify("onHandlerFinished", { delivery, kind, durationMs, outcome: "success" });
|
|
1076
|
+
}
|
|
1077
|
+
};
|
|
1078
|
+
|
|
858
1079
|
// src/completion-batcher.ts
|
|
859
1080
|
var CompletionBatcher = class {
|
|
860
1081
|
#store;
|
|
@@ -884,7 +1105,10 @@ var CompletionBatcher = class {
|
|
|
884
1105
|
if (this.#closing || this.#pending.length >= this.#settings.maxBatchSize || this.#pending.length >= this.#runningCount()) {
|
|
885
1106
|
this.#flush();
|
|
886
1107
|
} else {
|
|
887
|
-
this.#timer ??=
|
|
1108
|
+
this.#timer ??= chunk3OHMEMZA_cjs.schedule(this.#clock, {
|
|
1109
|
+
run: () => this.#flush(),
|
|
1110
|
+
delayMs: this.#settings.windowMs
|
|
1111
|
+
});
|
|
888
1112
|
}
|
|
889
1113
|
});
|
|
890
1114
|
}
|
|
@@ -899,7 +1123,7 @@ var CompletionBatcher = class {
|
|
|
899
1123
|
}
|
|
900
1124
|
}
|
|
901
1125
|
#flush() {
|
|
902
|
-
|
|
1126
|
+
this.#timer?.();
|
|
903
1127
|
this.#timer = void 0;
|
|
904
1128
|
const batch = this.#pending;
|
|
905
1129
|
this.#pending = [];
|
|
@@ -914,6 +1138,57 @@ var CompletionBatcher = class {
|
|
|
914
1138
|
);
|
|
915
1139
|
}
|
|
916
1140
|
};
|
|
1141
|
+
|
|
1142
|
+
// src/delivery-timers.ts
|
|
1143
|
+
var DeliveryTimers = class {
|
|
1144
|
+
#clock;
|
|
1145
|
+
/** Cancel of every timer still waiting, with what its cancellation must also do. */
|
|
1146
|
+
#waiting = /* @__PURE__ */ new Map();
|
|
1147
|
+
constructor(clock) {
|
|
1148
|
+
this.#clock = clock;
|
|
1149
|
+
}
|
|
1150
|
+
/** Runs `task.run` after `task.delayMs`; returns what cancels it. */
|
|
1151
|
+
start(task) {
|
|
1152
|
+
return this.#start(task, () => void 0);
|
|
1153
|
+
}
|
|
1154
|
+
/** Resolves after `milliseconds`, or at once when {@link DeliveryTimers.cancelAll} runs. */
|
|
1155
|
+
sleep(milliseconds) {
|
|
1156
|
+
return new Promise((resolve) => {
|
|
1157
|
+
this.#start({ run: resolve, delayMs: milliseconds }, resolve);
|
|
1158
|
+
});
|
|
1159
|
+
}
|
|
1160
|
+
cancelAll() {
|
|
1161
|
+
for (const [cancel, onCancel] of [...this.#waiting]) {
|
|
1162
|
+
cancel();
|
|
1163
|
+
onCancel();
|
|
1164
|
+
}
|
|
1165
|
+
}
|
|
1166
|
+
#start(task, onCancel) {
|
|
1167
|
+
const cancel = () => {
|
|
1168
|
+
cancelTimer();
|
|
1169
|
+
this.#waiting.delete(cancel);
|
|
1170
|
+
};
|
|
1171
|
+
const cancelTimer = chunk3OHMEMZA_cjs.schedule(this.#clock, {
|
|
1172
|
+
run: () => {
|
|
1173
|
+
this.#waiting.delete(cancel);
|
|
1174
|
+
task.run();
|
|
1175
|
+
},
|
|
1176
|
+
delayMs: task.delayMs
|
|
1177
|
+
});
|
|
1178
|
+
this.#waiting.set(cancel, onCancel);
|
|
1179
|
+
return cancel;
|
|
1180
|
+
}
|
|
1181
|
+
};
|
|
1182
|
+
|
|
1183
|
+
// src/dispatcher-support.ts
|
|
1184
|
+
function pendingKey(delivery) {
|
|
1185
|
+
return `${delivery.deliveryId}\0${delivery.leaseToken}`;
|
|
1186
|
+
}
|
|
1187
|
+
function rejectOnAbort(signal) {
|
|
1188
|
+
return new Promise((_, reject) => {
|
|
1189
|
+
signal.addEventListener("abort", () => reject(signal.reason), { once: true });
|
|
1190
|
+
});
|
|
1191
|
+
}
|
|
917
1192
|
var storage = new async_hooks.AsyncLocalStorage();
|
|
918
1193
|
function runInScope(scope, work) {
|
|
919
1194
|
return storage.run({ ...storage.getStore(), ...scope }, work);
|
|
@@ -925,6 +1200,102 @@ function getCurrentScope() {
|
|
|
925
1200
|
return storage.getStore();
|
|
926
1201
|
}
|
|
927
1202
|
|
|
1203
|
+
// src/shutdown-deadline.ts
|
|
1204
|
+
var DRAIN_SHARE = 0.8;
|
|
1205
|
+
var ShutdownDeadline = class {
|
|
1206
|
+
#controller = new AbortController();
|
|
1207
|
+
#drained;
|
|
1208
|
+
#expired;
|
|
1209
|
+
#cancelTimers;
|
|
1210
|
+
constructor(clock, timeoutMs) {
|
|
1211
|
+
let drain;
|
|
1212
|
+
this.#drained = new Promise((resolve) => {
|
|
1213
|
+
drain = () => resolve("timeout");
|
|
1214
|
+
});
|
|
1215
|
+
const signal = this.#controller.signal;
|
|
1216
|
+
this.#expired = new Promise((resolve) => {
|
|
1217
|
+
signal.addEventListener("abort", () => resolve("timeout"), { once: true });
|
|
1218
|
+
});
|
|
1219
|
+
this.#cancelTimers = [
|
|
1220
|
+
chunk3OHMEMZA_cjs.schedule(clock, { run: drain, delayMs: Math.floor(timeoutMs * DRAIN_SHARE) }),
|
|
1221
|
+
chunk3OHMEMZA_cjs.schedule(clock, { run: () => this.#controller.abort(), delayMs: timeoutMs })
|
|
1222
|
+
];
|
|
1223
|
+
}
|
|
1224
|
+
/** Aborted when the deadline passes. */
|
|
1225
|
+
get signal() {
|
|
1226
|
+
return this.#controller.signal;
|
|
1227
|
+
}
|
|
1228
|
+
get passed() {
|
|
1229
|
+
return this.#controller.signal.aborted;
|
|
1230
|
+
}
|
|
1231
|
+
/** Waits for `work` until the deadline. Rejects when `work` rejects before it. */
|
|
1232
|
+
race(work) {
|
|
1233
|
+
return Promise.race([work.then(() => "done"), this.#expired]);
|
|
1234
|
+
}
|
|
1235
|
+
/** Waits for running handlers until the drain point, when they are aborted. */
|
|
1236
|
+
raceDrain(work) {
|
|
1237
|
+
return Promise.race([work.then(() => "done"), this.#drained]);
|
|
1238
|
+
}
|
|
1239
|
+
/** Cancels the timers once the shutdown is over. */
|
|
1240
|
+
dispose() {
|
|
1241
|
+
for (const cancel of this.#cancelTimers) {
|
|
1242
|
+
cancel();
|
|
1243
|
+
}
|
|
1244
|
+
}
|
|
1245
|
+
};
|
|
1246
|
+
async function withinDeadline({ clock, timeoutMs }, work) {
|
|
1247
|
+
const startedAt = clock.now().getTime();
|
|
1248
|
+
const deadline = new ShutdownDeadline(clock, timeoutMs);
|
|
1249
|
+
try {
|
|
1250
|
+
const abandonedDeliveries = await work(deadline);
|
|
1251
|
+
return {
|
|
1252
|
+
graceful: !deadline.passed,
|
|
1253
|
+
abandonedDeliveries,
|
|
1254
|
+
durationMs: clock.now().getTime() - startedAt
|
|
1255
|
+
};
|
|
1256
|
+
} finally {
|
|
1257
|
+
deadline.dispose();
|
|
1258
|
+
}
|
|
1259
|
+
}
|
|
1260
|
+
|
|
1261
|
+
// src/lease-heartbeat.ts
|
|
1262
|
+
var RENEWALS_PER_LEASE = 3;
|
|
1263
|
+
function startLeaseHeartbeat({ store, clock, notifier, settings }, { delivery, controller }) {
|
|
1264
|
+
let confirmedExpiry = delivery.leaseExpiresAt.getTime();
|
|
1265
|
+
let renewing = false;
|
|
1266
|
+
const abortLeaseLost = (storeConfirmed = false) => {
|
|
1267
|
+
controller.abort(
|
|
1268
|
+
new chunk3OHMEMZA_cjs.LeaseLostError(`Lease on delivery ${delivery.deliveryId} was lost`, { storeConfirmed })
|
|
1269
|
+
);
|
|
1270
|
+
};
|
|
1271
|
+
return chunk3OHMEMZA_cjs.schedule(clock, {
|
|
1272
|
+
run: () => {
|
|
1273
|
+
const now = clock.now();
|
|
1274
|
+
if (now.getTime() >= confirmedExpiry) {
|
|
1275
|
+
abortLeaseLost();
|
|
1276
|
+
return;
|
|
1277
|
+
}
|
|
1278
|
+
if (renewing) {
|
|
1279
|
+
return;
|
|
1280
|
+
}
|
|
1281
|
+
renewing = true;
|
|
1282
|
+
const expiry = now.getTime() + settings.leaseDurationMs;
|
|
1283
|
+
store.extendLease(delivery, new Date(expiry), now).then((extended) => {
|
|
1284
|
+
if (extended) {
|
|
1285
|
+
confirmedExpiry = expiry;
|
|
1286
|
+
} else {
|
|
1287
|
+
abortLeaseLost(true);
|
|
1288
|
+
}
|
|
1289
|
+
}).catch((error) => notifier.failure("extend-lease", error)).finally(() => {
|
|
1290
|
+
renewing = false;
|
|
1291
|
+
});
|
|
1292
|
+
},
|
|
1293
|
+
delayMs: Math.max(1, Math.floor(settings.leaseDurationMs / RENEWALS_PER_LEASE)),
|
|
1294
|
+
repeat: true,
|
|
1295
|
+
unref: true
|
|
1296
|
+
});
|
|
1297
|
+
}
|
|
1298
|
+
|
|
928
1299
|
// src/retry-policy.ts
|
|
929
1300
|
var DEFAULT_RETRY_POLICY = {
|
|
930
1301
|
attempts: 10,
|
|
@@ -944,35 +1315,42 @@ var DEFAULT_ROUTE_RETRY_POLICY = {
|
|
|
944
1315
|
};
|
|
945
1316
|
var JITTER_STRATEGIES = ["full", "equal", "none"];
|
|
946
1317
|
function createRetryPolicy(options, base = DEFAULT_RETRY_POLICY) {
|
|
947
|
-
const
|
|
948
|
-
|
|
949
|
-
|
|
1318
|
+
const policy = mergeRetryPolicy(options, base);
|
|
1319
|
+
assertValidRetryPolicy(policy);
|
|
1320
|
+
return policy;
|
|
1321
|
+
}
|
|
1322
|
+
function mergeRetryPolicy(options, base) {
|
|
1323
|
+
const overrides = options ?? {};
|
|
1324
|
+
const initialDelayMs = overrides.initialDelay === void 0 ? base.initialDelayMs : toMilliseconds(overrides.initialDelay, "retry.initialDelay");
|
|
1325
|
+
return {
|
|
1326
|
+
attempts: overrides.attempts ?? base.attempts,
|
|
950
1327
|
initialDelayMs,
|
|
951
|
-
factor:
|
|
952
|
-
maxDelayMs:
|
|
1328
|
+
factor: overrides.factor ?? base.factor,
|
|
1329
|
+
maxDelayMs: overrides.maxDelay === void 0 ? (
|
|
953
1330
|
// Raising only the initial delay must not trip the inherited cap.
|
|
954
1331
|
Math.max(base.maxDelayMs, initialDelayMs)
|
|
955
|
-
) : toMilliseconds(
|
|
956
|
-
jitter:
|
|
957
|
-
retryIf:
|
|
1332
|
+
) : toMilliseconds(overrides.maxDelay, "retry.maxDelay"),
|
|
1333
|
+
jitter: overrides.jitter ?? base.jitter,
|
|
1334
|
+
retryIf: overrides.retryIf ?? base.retryIf
|
|
958
1335
|
};
|
|
1336
|
+
}
|
|
1337
|
+
function assertValidRetryPolicy(policy) {
|
|
959
1338
|
if (policy.attempts !== Number.POSITIVE_INFINITY && (!Number.isInteger(policy.attempts) || policy.attempts < 1)) {
|
|
960
|
-
throw new ConfigurationError(
|
|
1339
|
+
throw new chunk3OHMEMZA_cjs.ConfigurationError(
|
|
961
1340
|
`retry.attempts must be a positive integer or Infinity, received ${policy.attempts}`
|
|
962
1341
|
);
|
|
963
1342
|
}
|
|
964
1343
|
if (!Number.isFinite(policy.factor) || policy.factor < 1) {
|
|
965
|
-
throw new ConfigurationError(`retry.factor must be at least 1, received ${policy.factor}`);
|
|
1344
|
+
throw new chunk3OHMEMZA_cjs.ConfigurationError(`retry.factor must be at least 1, received ${policy.factor}`);
|
|
966
1345
|
}
|
|
967
1346
|
if (policy.maxDelayMs < policy.initialDelayMs) {
|
|
968
|
-
throw new ConfigurationError("retry.maxDelay must not be smaller than retry.initialDelay");
|
|
1347
|
+
throw new chunk3OHMEMZA_cjs.ConfigurationError("retry.maxDelay must not be smaller than retry.initialDelay");
|
|
969
1348
|
}
|
|
970
1349
|
if (!JITTER_STRATEGIES.includes(policy.jitter)) {
|
|
971
|
-
throw new ConfigurationError(
|
|
1350
|
+
throw new chunk3OHMEMZA_cjs.ConfigurationError(
|
|
972
1351
|
`retry.jitter must be one of ${JITTER_STRATEGIES.join(", ")}, received '${String(policy.jitter)}'`
|
|
973
1352
|
);
|
|
974
1353
|
}
|
|
975
|
-
return policy;
|
|
976
1354
|
}
|
|
977
1355
|
function computeRetryDelay(policy, failedAttempts, random = Math.random) {
|
|
978
1356
|
const exponential = policy.initialDelayMs * policy.factor ** (failedAttempts - 1);
|
|
@@ -987,7 +1365,7 @@ function computeRetryDelay(policy, failedAttempts, random = Math.random) {
|
|
|
987
1365
|
}
|
|
988
1366
|
}
|
|
989
1367
|
function isRetryableError(policy, error) {
|
|
990
|
-
if (error instanceof NonRetryableError) {
|
|
1368
|
+
if (error instanceof chunk3OHMEMZA_cjs.NonRetryableError) {
|
|
991
1369
|
return false;
|
|
992
1370
|
}
|
|
993
1371
|
if (policy.retryIf === void 0) {
|
|
@@ -1001,11 +1379,17 @@ function isRetryableError(policy, error) {
|
|
|
1001
1379
|
}
|
|
1002
1380
|
|
|
1003
1381
|
// src/dispatcher.ts
|
|
1004
|
-
var
|
|
1382
|
+
var FIRST_COMPLETION_RETRY_MS = 50;
|
|
1383
|
+
var SECOND_COMPLETION_RETRY_MS = 250;
|
|
1384
|
+
var LAST_COMPLETION_RETRY_MS = 1e3;
|
|
1385
|
+
var COMPLETION_RETRY_DELAYS_MS = [
|
|
1386
|
+
FIRST_COMPLETION_RETRY_MS,
|
|
1387
|
+
SECOND_COMPLETION_RETRY_MS,
|
|
1388
|
+
LAST_COMPLETION_RETRY_MS
|
|
1389
|
+
];
|
|
1005
1390
|
var WAKE_COOLDOWN_MS = 5;
|
|
1006
1391
|
var QUIET_REFILL_MS = 10;
|
|
1007
1392
|
var REFILL_FRACTION = 0.7;
|
|
1008
|
-
var defaultSleep = (milliseconds) => new Promise((resolve) => setTimeout(resolve, milliseconds));
|
|
1009
1393
|
var Dispatcher = class {
|
|
1010
1394
|
#dependencies;
|
|
1011
1395
|
#sleep;
|
|
@@ -1031,9 +1415,15 @@ var Dispatcher = class {
|
|
|
1031
1415
|
#quietRefillTimer;
|
|
1032
1416
|
#unwatch;
|
|
1033
1417
|
#watching;
|
|
1418
|
+
/** Set when a stop gave up at its deadline: the store may be closed, so nothing more is started or retried. */
|
|
1419
|
+
#abandoned = false;
|
|
1420
|
+
/** Stops the lease renewal (or transactional lease guard) of each delivery whose handler runs. */
|
|
1421
|
+
#leaseKeepers = /* @__PURE__ */ new Map();
|
|
1422
|
+
#timers;
|
|
1034
1423
|
constructor(dependencies) {
|
|
1035
1424
|
this.#dependencies = dependencies;
|
|
1036
|
-
this.#
|
|
1425
|
+
this.#timers = new DeliveryTimers(dependencies.clock);
|
|
1426
|
+
this.#sleep = dependencies.sleep ?? ((milliseconds) => this.#timers.sleep(milliseconds));
|
|
1037
1427
|
const { store, clock, settings } = dependencies;
|
|
1038
1428
|
this.#refillThreshold = Math.ceil(
|
|
1039
1429
|
Math.min(settings.concurrency, settings.batchSize) * REFILL_FRACTION
|
|
@@ -1082,10 +1472,13 @@ var Dispatcher = class {
|
|
|
1082
1472
|
this.#requestClaim();
|
|
1083
1473
|
return;
|
|
1084
1474
|
}
|
|
1085
|
-
this.#cooldownTimer ??=
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1475
|
+
this.#cooldownTimer ??= chunk3OHMEMZA_cjs.schedule(this.#dependencies.clock, {
|
|
1476
|
+
run: () => {
|
|
1477
|
+
this.#cooldownTimer = void 0;
|
|
1478
|
+
this.#requestClaim();
|
|
1479
|
+
},
|
|
1480
|
+
delayMs: wait
|
|
1481
|
+
});
|
|
1089
1482
|
}
|
|
1090
1483
|
/** Ends the current wait of the claim loop, or the next one at once, for a claim once `minimumFree` slots are free. */
|
|
1091
1484
|
#signal(minimumFree) {
|
|
@@ -1124,33 +1517,66 @@ var Dispatcher = class {
|
|
|
1124
1517
|
}
|
|
1125
1518
|
}
|
|
1126
1519
|
/**
|
|
1127
|
-
* Stops claiming
|
|
1128
|
-
* then aborts the rest and releases their deliveries for another
|
|
1520
|
+
* Stops claiming and waits for running handlers until the drain point of `deadline` (default: the
|
|
1521
|
+
* shutdown timeout from now), then aborts the rest and releases their deliveries for another
|
|
1522
|
+
* process. Deliveries still in flight when the deadline itself passes are abandoned: their leases
|
|
1523
|
+
* expire and they run again elsewhere. Resolves with how many were abandoned.
|
|
1129
1524
|
*/
|
|
1130
|
-
async stop() {
|
|
1525
|
+
async stop(deadline) {
|
|
1526
|
+
const { clock, settings } = this.#dependencies;
|
|
1527
|
+
const own = deadline ?? new ShutdownDeadline(clock, settings.shutdownTimeoutMs);
|
|
1528
|
+
try {
|
|
1529
|
+
return await this.#stopWithin(own);
|
|
1530
|
+
} finally {
|
|
1531
|
+
if (deadline === void 0) {
|
|
1532
|
+
own.dispose();
|
|
1533
|
+
}
|
|
1534
|
+
}
|
|
1535
|
+
}
|
|
1536
|
+
async #stopWithin(deadline) {
|
|
1131
1537
|
if (!this.#running) {
|
|
1132
|
-
await this.#closeRecovery(
|
|
1133
|
-
return;
|
|
1538
|
+
await this.#closeRecovery(deadline);
|
|
1539
|
+
return 0;
|
|
1134
1540
|
}
|
|
1135
1541
|
this.#running = false;
|
|
1136
1542
|
this.#completions?.setClosing(true);
|
|
1137
|
-
|
|
1543
|
+
this.#cooldownTimer?.();
|
|
1138
1544
|
this.#cooldownTimer = void 0;
|
|
1139
|
-
|
|
1545
|
+
this.#quietRefillTimer?.();
|
|
1140
1546
|
this.#quietRefillTimer = void 0;
|
|
1141
1547
|
this.#signal(this.#refillThreshold);
|
|
1142
|
-
|
|
1143
|
-
await
|
|
1144
|
-
await this.#waitUntil(this.#loop, deadline);
|
|
1548
|
+
await deadline.race(this.#stopWatching());
|
|
1549
|
+
await deadline.race(this.#loop);
|
|
1145
1550
|
const settled = Promise.allSettled([...this.#inFlight].map((delivery) => delivery.settled));
|
|
1146
|
-
if (await
|
|
1551
|
+
if (await deadline.raceDrain(settled) === "timeout") {
|
|
1147
1552
|
for (const delivery of this.#inFlight) {
|
|
1148
|
-
delivery.controller.abort(new DispatcherStoppedError("relaybus dispatcher stopped"));
|
|
1553
|
+
delivery.controller.abort(new chunk3OHMEMZA_cjs.DispatcherStoppedError("relaybus dispatcher stopped"));
|
|
1554
|
+
}
|
|
1555
|
+
if (await deadline.race(settled) === "timeout") {
|
|
1556
|
+
return this.#abandon();
|
|
1149
1557
|
}
|
|
1150
|
-
await settled;
|
|
1151
1558
|
}
|
|
1152
1559
|
await this.#closeRecovery(deadline);
|
|
1153
|
-
await
|
|
1560
|
+
await deadline.race(this.#relinquish());
|
|
1561
|
+
return 0;
|
|
1562
|
+
}
|
|
1563
|
+
/**
|
|
1564
|
+
* The deadline passed with deliveries still in flight (aborted, their release not answered): forgets
|
|
1565
|
+
* them and every settlement still to retry. Whatever they release or complete from now on may reach a
|
|
1566
|
+
* closed store; a delivery whose state change is lost stays leased until the lease expires and then
|
|
1567
|
+
* runs again (at-least-once).
|
|
1568
|
+
*/
|
|
1569
|
+
#abandon() {
|
|
1570
|
+
this.#abandoned = true;
|
|
1571
|
+
for (const stopKeeping of this.#leaseKeepers.values()) {
|
|
1572
|
+
stopKeeping();
|
|
1573
|
+
}
|
|
1574
|
+
this.#leaseKeepers.clear();
|
|
1575
|
+
this.#timers.cancelAll();
|
|
1576
|
+
this.#recoveryTimer?.();
|
|
1577
|
+
this.#recoveryTimer = void 0;
|
|
1578
|
+
this.#pending.clear();
|
|
1579
|
+
return this.#inFlight.size;
|
|
1154
1580
|
}
|
|
1155
1581
|
/**
|
|
1156
1582
|
* Gives the settlements still pending one last try and forgets them. A pass of the timer may be inside the
|
|
@@ -1158,13 +1584,13 @@ var Dispatcher = class {
|
|
|
1158
1584
|
* further call, since it may be closed right after.
|
|
1159
1585
|
*/
|
|
1160
1586
|
async #closeRecovery(deadline) {
|
|
1161
|
-
|
|
1587
|
+
this.#recoveryTimer?.();
|
|
1162
1588
|
this.#recoveryTimer = void 0;
|
|
1163
1589
|
const lastTry = (async () => {
|
|
1164
1590
|
await this.#recovery;
|
|
1165
1591
|
await this.#recoverSettlements();
|
|
1166
1592
|
})();
|
|
1167
|
-
await
|
|
1593
|
+
await deadline.race(lastTry);
|
|
1168
1594
|
this.#pending.clear();
|
|
1169
1595
|
}
|
|
1170
1596
|
/**
|
|
@@ -1195,12 +1621,16 @@ var Dispatcher = class {
|
|
|
1195
1621
|
}
|
|
1196
1622
|
}
|
|
1197
1623
|
#retain(entry) {
|
|
1624
|
+
if (this.#abandoned) {
|
|
1625
|
+
return;
|
|
1626
|
+
}
|
|
1198
1627
|
this.#pending.set(pendingKey(entry.delivery), entry);
|
|
1199
|
-
this.#recoveryTimer ??=
|
|
1200
|
-
() => void this.#recoverSettlements(),
|
|
1201
|
-
this.#dependencies.settings.pollIntervalMs
|
|
1202
|
-
|
|
1203
|
-
|
|
1628
|
+
this.#recoveryTimer ??= chunk3OHMEMZA_cjs.schedule(this.#dependencies.clock, {
|
|
1629
|
+
run: () => void this.#recoverSettlements(),
|
|
1630
|
+
delayMs: this.#dependencies.settings.pollIntervalMs,
|
|
1631
|
+
repeat: true,
|
|
1632
|
+
unref: true
|
|
1633
|
+
});
|
|
1204
1634
|
}
|
|
1205
1635
|
/** Gives lease groups the owner no longer works on back to the store, so other workers need not wait for expiry. */
|
|
1206
1636
|
async #relinquish() {
|
|
@@ -1218,12 +1648,15 @@ var Dispatcher = class {
|
|
|
1218
1648
|
return started.length;
|
|
1219
1649
|
}
|
|
1220
1650
|
async #waitUntil(work, deadline) {
|
|
1221
|
-
let
|
|
1651
|
+
let cancel;
|
|
1222
1652
|
const timedOut = new Promise((resolve) => {
|
|
1223
|
-
|
|
1653
|
+
cancel = chunk3OHMEMZA_cjs.schedule(this.#dependencies.clock, {
|
|
1654
|
+
run: () => resolve("timeout"),
|
|
1655
|
+
delayMs: Math.max(0, deadline - Date.now())
|
|
1656
|
+
});
|
|
1224
1657
|
});
|
|
1225
1658
|
const outcome = await Promise.race([work.then(() => "done"), timedOut]);
|
|
1226
|
-
|
|
1659
|
+
cancel?.();
|
|
1227
1660
|
return outcome;
|
|
1228
1661
|
}
|
|
1229
1662
|
/**
|
|
@@ -1257,12 +1690,15 @@ var Dispatcher = class {
|
|
|
1257
1690
|
async #waitForWakeUp() {
|
|
1258
1691
|
if (this.#signalledMinimum === void 0) {
|
|
1259
1692
|
await new Promise((resolve) => {
|
|
1260
|
-
const
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1693
|
+
const cancel = chunk3OHMEMZA_cjs.schedule(this.#dependencies.clock, {
|
|
1694
|
+
run: () => {
|
|
1695
|
+
this.#signalledMinimum = 1;
|
|
1696
|
+
resolve();
|
|
1697
|
+
},
|
|
1698
|
+
delayMs: this.#dependencies.settings.pollIntervalMs
|
|
1699
|
+
});
|
|
1264
1700
|
this.#wakeUp = () => {
|
|
1265
|
-
|
|
1701
|
+
cancel();
|
|
1266
1702
|
resolve();
|
|
1267
1703
|
};
|
|
1268
1704
|
});
|
|
@@ -1298,13 +1734,13 @@ var Dispatcher = class {
|
|
|
1298
1734
|
});
|
|
1299
1735
|
}
|
|
1300
1736
|
} else if (free > 0) {
|
|
1301
|
-
this.#quietRefillTimer ??=
|
|
1302
|
-
() => {
|
|
1737
|
+
this.#quietRefillTimer ??= chunk3OHMEMZA_cjs.schedule(this.#dependencies.clock, {
|
|
1738
|
+
run: () => {
|
|
1303
1739
|
this.#quietRefillTimer = void 0;
|
|
1304
1740
|
this.#signal(1);
|
|
1305
1741
|
},
|
|
1306
|
-
Math.max(0, this.#lastClaimStartedAt + QUIET_REFILL_MS - Date.now())
|
|
1307
|
-
);
|
|
1742
|
+
delayMs: Math.max(0, this.#lastClaimStartedAt + QUIET_REFILL_MS - Date.now())
|
|
1743
|
+
});
|
|
1308
1744
|
}
|
|
1309
1745
|
}
|
|
1310
1746
|
#freeSlots() {
|
|
@@ -1312,23 +1748,24 @@ var Dispatcher = class {
|
|
|
1312
1748
|
}
|
|
1313
1749
|
/** Claims for every handler with room and starts what was claimed. */
|
|
1314
1750
|
async #claimAndStart() {
|
|
1315
|
-
const { store, registry, clock, settings } = this.#dependencies;
|
|
1751
|
+
const { store, registry, clock, settings, observation } = this.#dependencies;
|
|
1316
1752
|
const started = [];
|
|
1317
1753
|
let requested = 0;
|
|
1318
1754
|
for (const request of this.#planClaims(registry.all())) {
|
|
1319
1755
|
const now = clock.now();
|
|
1320
1756
|
const wakeGeneration = this.#wakeGeneration;
|
|
1321
1757
|
this.#lastClaimStartedAt = Date.now();
|
|
1322
|
-
|
|
1758
|
+
this.#quietRefillTimer?.();
|
|
1323
1759
|
this.#quietRefillTimer = void 0;
|
|
1324
1760
|
requested += request.limit;
|
|
1325
|
-
const
|
|
1761
|
+
const claim = {
|
|
1326
1762
|
handlers: request.handlers,
|
|
1327
1763
|
limit: request.limit,
|
|
1328
1764
|
leaseOwner: settings.owner,
|
|
1329
1765
|
leaseExpiresAt: new Date(now.getTime() + settings.leaseDurationMs),
|
|
1330
1766
|
now
|
|
1331
|
-
}
|
|
1767
|
+
};
|
|
1768
|
+
const claimed = await (observation === void 0 ? store.claim(claim) : observation.claim(store, claim));
|
|
1332
1769
|
for (const handler of request.handlers) {
|
|
1333
1770
|
if (claimed.length < request.limit && wakeGeneration === this.#wakeGeneration) {
|
|
1334
1771
|
this.#exhaustedHandlers.add(handler);
|
|
@@ -1336,7 +1773,7 @@ var Dispatcher = class {
|
|
|
1336
1773
|
this.#exhaustedHandlers.delete(handler);
|
|
1337
1774
|
}
|
|
1338
1775
|
}
|
|
1339
|
-
for (const delivery of claimed) {
|
|
1776
|
+
for (const delivery of this.#abandoned ? [] : claimed) {
|
|
1340
1777
|
started.push(this.#start(delivery));
|
|
1341
1778
|
}
|
|
1342
1779
|
}
|
|
@@ -1351,22 +1788,25 @@ var Dispatcher = class {
|
|
|
1351
1788
|
if (registration.concurrency === null || free === 0) {
|
|
1352
1789
|
continue;
|
|
1353
1790
|
}
|
|
1354
|
-
const room = registration.concurrency - this.#countInFlight(registration
|
|
1791
|
+
const room = registration.concurrency - this.#countInFlight(registration);
|
|
1355
1792
|
const limit = Math.min(room, free);
|
|
1356
1793
|
if (limit > 0) {
|
|
1357
|
-
plans.push({ handlers: [registration.
|
|
1794
|
+
plans.push({ handlers: [...registration.claimIds], limit });
|
|
1358
1795
|
free -= limit;
|
|
1359
1796
|
}
|
|
1360
1797
|
}
|
|
1361
1798
|
if (unlimited.length > 0 && free > 0) {
|
|
1362
|
-
plans.push({
|
|
1799
|
+
plans.push({
|
|
1800
|
+
handlers: unlimited.flatMap((registration) => registration.claimIds),
|
|
1801
|
+
limit: free
|
|
1802
|
+
});
|
|
1363
1803
|
}
|
|
1364
1804
|
return plans;
|
|
1365
1805
|
}
|
|
1366
|
-
#countInFlight(
|
|
1806
|
+
#countInFlight(registration) {
|
|
1367
1807
|
let count = 0;
|
|
1368
1808
|
for (const delivery of this.#inFlight) {
|
|
1369
|
-
if (delivery.handler
|
|
1809
|
+
if (registration.claimIds.includes(delivery.handler)) {
|
|
1370
1810
|
count += 1;
|
|
1371
1811
|
}
|
|
1372
1812
|
}
|
|
@@ -1402,7 +1842,7 @@ var Dispatcher = class {
|
|
|
1402
1842
|
if (delivery.attempt > registration.retry.attempts) {
|
|
1403
1843
|
await this.#bury(
|
|
1404
1844
|
delivery,
|
|
1405
|
-
new
|
|
1845
|
+
new chunk3OHMEMZA_cjs.AttemptsExhaustedError(
|
|
1406
1846
|
`Delivery reached attempt ${delivery.attempt} of ${registration.retry.attempts} after its lease expired`
|
|
1407
1847
|
)
|
|
1408
1848
|
);
|
|
@@ -1410,8 +1850,9 @@ var Dispatcher = class {
|
|
|
1410
1850
|
}
|
|
1411
1851
|
let stopHeartbeat = () => void 0;
|
|
1412
1852
|
try {
|
|
1413
|
-
stopHeartbeat = registration.transactional ? await this.#guardTransactionalLease(registration, delivery, controller) : this.#
|
|
1414
|
-
|
|
1853
|
+
stopHeartbeat = registration.transactional ? await this.#guardTransactionalLease(registration, { delivery, controller }) : startLeaseHeartbeat(this.#dependencies, { delivery, controller });
|
|
1854
|
+
this.#leaseKeepers.set(delivery, stopHeartbeat);
|
|
1855
|
+
const outcome = await this.#runHandler(registration, { delivery, controller });
|
|
1415
1856
|
const durationMs = clock.now().getTime() - startedAt;
|
|
1416
1857
|
if (outcome === "completed") {
|
|
1417
1858
|
notifier.notify("onDeliverySucceeded", { delivery, durationMs });
|
|
@@ -1424,24 +1865,31 @@ var Dispatcher = class {
|
|
|
1424
1865
|
}
|
|
1425
1866
|
return true;
|
|
1426
1867
|
} catch (error) {
|
|
1427
|
-
if (
|
|
1868
|
+
if (this.#abandoned) {
|
|
1869
|
+
return false;
|
|
1870
|
+
}
|
|
1871
|
+
if (error instanceof chunk3OHMEMZA_cjs.LeaseLostError) {
|
|
1428
1872
|
notifier.notify("onLeaseLost", { delivery });
|
|
1429
1873
|
if (!error.storeConfirmed) {
|
|
1430
|
-
await this.#giveBack(registration, delivery, error);
|
|
1874
|
+
await this.#giveBack(registration, { delivery, error });
|
|
1431
1875
|
}
|
|
1432
|
-
} else if (error instanceof DispatcherStoppedError) {
|
|
1876
|
+
} else if (error instanceof chunk3OHMEMZA_cjs.DispatcherStoppedError) {
|
|
1433
1877
|
await this.#release(delivery);
|
|
1434
1878
|
} else {
|
|
1435
|
-
await this.#fail(registration, delivery, error);
|
|
1879
|
+
await this.#fail(registration, { delivery, error });
|
|
1436
1880
|
}
|
|
1437
1881
|
return false;
|
|
1438
1882
|
} finally {
|
|
1439
1883
|
stopHeartbeat();
|
|
1884
|
+
this.#leaseKeepers.delete(delivery);
|
|
1440
1885
|
}
|
|
1441
1886
|
}
|
|
1442
|
-
async #runHandler(registration,
|
|
1887
|
+
async #runHandler(registration, {
|
|
1888
|
+
delivery,
|
|
1889
|
+
controller
|
|
1890
|
+
}) {
|
|
1443
1891
|
const { store, clock } = this.#dependencies;
|
|
1444
|
-
const payload = await validatePayload(
|
|
1892
|
+
const payload = await chunk3OHMEMZA_cjs.validatePayload(
|
|
1445
1893
|
delivery.event.type,
|
|
1446
1894
|
registration.schema,
|
|
1447
1895
|
delivery.event.payload
|
|
@@ -1449,16 +1897,16 @@ var Dispatcher = class {
|
|
|
1449
1897
|
const event = { ...delivery.event, payload };
|
|
1450
1898
|
if (registration.transactional) {
|
|
1451
1899
|
await store.transaction(async (transaction) => {
|
|
1452
|
-
await this.#invoke(registration, event, delivery, controller, transaction);
|
|
1900
|
+
await this.#invoke({ registration, event, delivery, controller, transaction });
|
|
1453
1901
|
if (!await store.complete(delivery, clock.now(), transaction)) {
|
|
1454
|
-
throw new LeaseLostError(`Lease on delivery ${delivery.deliveryId} was lost`, {
|
|
1902
|
+
throw new chunk3OHMEMZA_cjs.LeaseLostError(`Lease on delivery ${delivery.deliveryId} was lost`, {
|
|
1455
1903
|
storeConfirmed: true
|
|
1456
1904
|
});
|
|
1457
1905
|
}
|
|
1458
1906
|
});
|
|
1459
1907
|
return "completed";
|
|
1460
1908
|
}
|
|
1461
|
-
await this.#invoke(registration, event, delivery, controller, void 0);
|
|
1909
|
+
await this.#invoke({ registration, event, delivery, controller, transaction: void 0 });
|
|
1462
1910
|
return this.#confirmCompletion(delivery);
|
|
1463
1911
|
}
|
|
1464
1912
|
/**
|
|
@@ -1468,6 +1916,9 @@ var Dispatcher = class {
|
|
|
1468
1916
|
async #confirmCompletion(delivery) {
|
|
1469
1917
|
const { notifier } = this.#dependencies;
|
|
1470
1918
|
for (let attempt = 0; ; attempt += 1) {
|
|
1919
|
+
if (this.#abandoned) {
|
|
1920
|
+
return "unconfirmed";
|
|
1921
|
+
}
|
|
1471
1922
|
let completed;
|
|
1472
1923
|
try {
|
|
1473
1924
|
completed = await this.#complete(delivery);
|
|
@@ -1481,7 +1932,7 @@ var Dispatcher = class {
|
|
|
1481
1932
|
continue;
|
|
1482
1933
|
}
|
|
1483
1934
|
if (!completed) {
|
|
1484
|
-
throw new LeaseLostError(`Lease on delivery ${delivery.deliveryId} was lost`, {
|
|
1935
|
+
throw new chunk3OHMEMZA_cjs.LeaseLostError(`Lease on delivery ${delivery.deliveryId} was lost`, {
|
|
1485
1936
|
storeConfirmed: true
|
|
1486
1937
|
});
|
|
1487
1938
|
}
|
|
@@ -1493,7 +1944,8 @@ var Dispatcher = class {
|
|
|
1493
1944
|
const { store, clock } = this.#dependencies;
|
|
1494
1945
|
return this.#completions === void 0 ? store.complete(delivery, clock.now()) : this.#completions.submit(delivery);
|
|
1495
1946
|
}
|
|
1496
|
-
async #invoke(
|
|
1947
|
+
async #invoke(input) {
|
|
1948
|
+
const { registration, event, delivery, controller, transaction } = input;
|
|
1497
1949
|
const context = {
|
|
1498
1950
|
deliveryId: delivery.deliveryId,
|
|
1499
1951
|
handler: delivery.handler,
|
|
@@ -1506,28 +1958,37 @@ var Dispatcher = class {
|
|
|
1506
1958
|
if (controller.signal.aborted) {
|
|
1507
1959
|
throw controller.signal.reason;
|
|
1508
1960
|
}
|
|
1961
|
+
const handle = () => runInIsolatedScope(scope, () => registration.handle(event, context));
|
|
1962
|
+
const { observation } = this.#dependencies;
|
|
1509
1963
|
const work = Promise.resolve().then(
|
|
1510
|
-
|
|
1964
|
+
observation === void 0 ? handle : () => observation.process({ registration, delivery }, handle)
|
|
1511
1965
|
);
|
|
1512
1966
|
work.catch(() => void 0);
|
|
1513
|
-
let
|
|
1967
|
+
let cancelTimeout;
|
|
1514
1968
|
if (registration.timeoutMs !== null) {
|
|
1515
1969
|
const timeoutMs = registration.timeoutMs;
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
|
|
1970
|
+
cancelTimeout = this.#timers.start({
|
|
1971
|
+
run: () => {
|
|
1972
|
+
controller.abort(
|
|
1973
|
+
new chunk3OHMEMZA_cjs.HandlerTimeoutError(
|
|
1974
|
+
`Handler '${registration.id}' did not settle within ${timeoutMs}ms`
|
|
1975
|
+
)
|
|
1976
|
+
);
|
|
1977
|
+
},
|
|
1978
|
+
delayMs: timeoutMs
|
|
1979
|
+
});
|
|
1523
1980
|
}
|
|
1524
1981
|
try {
|
|
1525
|
-
|
|
1982
|
+
const settled = Promise.race([work, rejectOnAbort(controller.signal)]);
|
|
1983
|
+
await (observation === void 0 ? settled : observation.observe({ registration, delivery }, settled));
|
|
1526
1984
|
} catch (error) {
|
|
1527
|
-
await this.#awaitAbandonedHandler(work,
|
|
1985
|
+
await this.#awaitAbandonedHandler(work, {
|
|
1986
|
+
reason: error,
|
|
1987
|
+
transactional: transaction !== void 0
|
|
1988
|
+
});
|
|
1528
1989
|
throw error;
|
|
1529
1990
|
} finally {
|
|
1530
|
-
|
|
1991
|
+
cancelTimeout?.();
|
|
1531
1992
|
}
|
|
1532
1993
|
}
|
|
1533
1994
|
/**
|
|
@@ -1537,7 +1998,7 @@ var Dispatcher = class {
|
|
|
1537
1998
|
* Transactional handlers are always awaited; others for one lease duration,
|
|
1538
1999
|
* except at shutdown, which already waited.
|
|
1539
2000
|
*/
|
|
1540
|
-
async #awaitAbandonedHandler(work, reason, transactional) {
|
|
2001
|
+
async #awaitAbandonedHandler(work, { reason, transactional }) {
|
|
1541
2002
|
const settledWork = work.then(
|
|
1542
2003
|
() => void 0,
|
|
1543
2004
|
() => void 0
|
|
@@ -1546,7 +2007,7 @@ var Dispatcher = class {
|
|
|
1546
2007
|
await settledWork;
|
|
1547
2008
|
return;
|
|
1548
2009
|
}
|
|
1549
|
-
if (reason instanceof DispatcherStoppedError) {
|
|
2010
|
+
if (reason instanceof chunk3OHMEMZA_cjs.DispatcherStoppedError) {
|
|
1550
2011
|
return;
|
|
1551
2012
|
}
|
|
1552
2013
|
const deadline = Date.now() + this.#dependencies.settings.leaseDurationMs;
|
|
@@ -1559,81 +2020,42 @@ var Dispatcher = class {
|
|
|
1559
2020
|
* lease is extended once to cover the handler timeout, and the handler is
|
|
1560
2021
|
* aborted when that lease ends.
|
|
1561
2022
|
*/
|
|
1562
|
-
async #guardTransactionalLease(registration,
|
|
2023
|
+
async #guardTransactionalLease(registration, {
|
|
2024
|
+
delivery,
|
|
2025
|
+
controller
|
|
2026
|
+
}) {
|
|
1563
2027
|
const { store, clock, settings } = this.#dependencies;
|
|
1564
2028
|
const now = clock.now();
|
|
1565
2029
|
let expiry = delivery.leaseExpiresAt.getTime();
|
|
1566
2030
|
if (registration.timeoutMs !== null && now.getTime() + registration.timeoutMs >= expiry) {
|
|
1567
2031
|
expiry = now.getTime() + registration.timeoutMs + settings.leaseDurationMs;
|
|
1568
2032
|
if (!await store.extendLease(delivery, new Date(expiry), now)) {
|
|
1569
|
-
throw new LeaseLostError(`Lease on delivery ${delivery.deliveryId} was lost`, {
|
|
2033
|
+
throw new chunk3OHMEMZA_cjs.LeaseLostError(`Lease on delivery ${delivery.deliveryId} was lost`, {
|
|
1570
2034
|
storeConfirmed: true
|
|
1571
2035
|
});
|
|
1572
2036
|
}
|
|
1573
2037
|
}
|
|
1574
|
-
|
|
1575
|
-
() => {
|
|
1576
|
-
controller.abort(new LeaseLostError(`Lease on delivery ${delivery.deliveryId} expired`));
|
|
2038
|
+
return chunk3OHMEMZA_cjs.schedule(clock, {
|
|
2039
|
+
run: () => {
|
|
2040
|
+
controller.abort(new chunk3OHMEMZA_cjs.LeaseLostError(`Lease on delivery ${delivery.deliveryId} expired`));
|
|
1577
2041
|
},
|
|
1578
|
-
Math.max(0, expiry - clock.now().getTime())
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
return () => clearTimeout(timer);
|
|
2042
|
+
delayMs: Math.max(0, expiry - clock.now().getTime()),
|
|
2043
|
+
unref: true
|
|
2044
|
+
});
|
|
1582
2045
|
}
|
|
1583
|
-
|
|
1584
|
-
* Renews the lease every third of its duration. A worker that cannot renew
|
|
1585
|
-
* aborts its handler once the last confirmed expiry passes, because another
|
|
1586
|
-
* worker may then claim the delivery (self-fencing).
|
|
1587
|
-
*/
|
|
1588
|
-
#startHeartbeat(delivery, controller) {
|
|
1589
|
-
const { store, clock, notifier, settings } = this.#dependencies;
|
|
1590
|
-
let confirmedExpiry = delivery.leaseExpiresAt.getTime();
|
|
1591
|
-
let renewing = false;
|
|
1592
|
-
const abortLeaseLost = (storeConfirmed = false) => {
|
|
1593
|
-
controller.abort(
|
|
1594
|
-
new LeaseLostError(`Lease on delivery ${delivery.deliveryId} was lost`, { storeConfirmed })
|
|
1595
|
-
);
|
|
1596
|
-
};
|
|
1597
|
-
const timer = setInterval(
|
|
1598
|
-
() => {
|
|
1599
|
-
const now = clock.now();
|
|
1600
|
-
if (now.getTime() >= confirmedExpiry) {
|
|
1601
|
-
abortLeaseLost();
|
|
1602
|
-
return;
|
|
1603
|
-
}
|
|
1604
|
-
if (renewing) {
|
|
1605
|
-
return;
|
|
1606
|
-
}
|
|
1607
|
-
renewing = true;
|
|
1608
|
-
const expiry = now.getTime() + settings.leaseDurationMs;
|
|
1609
|
-
store.extendLease(delivery, new Date(expiry), now).then((extended) => {
|
|
1610
|
-
if (extended) {
|
|
1611
|
-
confirmedExpiry = expiry;
|
|
1612
|
-
} else {
|
|
1613
|
-
abortLeaseLost(true);
|
|
1614
|
-
}
|
|
1615
|
-
}).catch((error) => notifier.failure("extend-lease", error)).finally(() => {
|
|
1616
|
-
renewing = false;
|
|
1617
|
-
});
|
|
1618
|
-
},
|
|
1619
|
-
Math.max(1, Math.floor(settings.leaseDurationMs / 3))
|
|
1620
|
-
);
|
|
1621
|
-
timer.unref();
|
|
1622
|
-
return () => clearInterval(timer);
|
|
1623
|
-
}
|
|
1624
|
-
async #fail(registration, delivery, error) {
|
|
2046
|
+
async #fail(registration, { delivery, error }) {
|
|
1625
2047
|
const policy = registration.retry;
|
|
1626
2048
|
if (delivery.attempt >= policy.attempts || !isRetryableError(policy, error)) {
|
|
1627
2049
|
await this.#bury(delivery, error);
|
|
1628
2050
|
return;
|
|
1629
2051
|
}
|
|
1630
|
-
await this.#reschedule(registration, delivery, error);
|
|
2052
|
+
await this.#reschedule(registration, { delivery, error });
|
|
1631
2053
|
}
|
|
1632
2054
|
/** Schedules the next attempt with the retry policy's backoff. */
|
|
1633
|
-
async #reschedule(registration, delivery, error) {
|
|
2055
|
+
async #reschedule(registration, { delivery, error }) {
|
|
1634
2056
|
const { store, clock, random, notifier } = this.#dependencies;
|
|
1635
2057
|
const policy = registration.retry;
|
|
1636
|
-
const errorInfo = toErrorInfo(error);
|
|
2058
|
+
const errorInfo = chunk3OHMEMZA_cjs.toErrorInfo(error);
|
|
1637
2059
|
const now = clock.now();
|
|
1638
2060
|
const availableAt = new Date(
|
|
1639
2061
|
now.getTime() + computeRetryDelay(policy, delivery.attempt, random)
|
|
@@ -1658,7 +2080,7 @@ var Dispatcher = class {
|
|
|
1658
2080
|
}
|
|
1659
2081
|
async #bury(delivery, error) {
|
|
1660
2082
|
const { store, clock, notifier } = this.#dependencies;
|
|
1661
|
-
const errorInfo = toErrorInfo(error);
|
|
2083
|
+
const errorInfo = chunk3OHMEMZA_cjs.toErrorInfo(error);
|
|
1662
2084
|
let buried;
|
|
1663
2085
|
try {
|
|
1664
2086
|
buried = await store.bury(delivery, errorInfo, clock.now());
|
|
@@ -1685,11 +2107,11 @@ var Dispatcher = class {
|
|
|
1685
2107
|
* outlives its lease cannot run for ever at attempt 1. If the lease is gone for real the store
|
|
1686
2108
|
* refuses and nothing changes. The lost lease was reported already.
|
|
1687
2109
|
*/
|
|
1688
|
-
async #giveBack(registration, delivery, error) {
|
|
2110
|
+
async #giveBack(registration, { delivery, error }) {
|
|
1689
2111
|
if (delivery.attempt >= registration.retry.attempts) {
|
|
1690
2112
|
await this.#bury(delivery, error);
|
|
1691
2113
|
} else {
|
|
1692
|
-
await this.#reschedule(registration, delivery, error);
|
|
2114
|
+
await this.#reschedule(registration, { delivery, error });
|
|
1693
2115
|
}
|
|
1694
2116
|
}
|
|
1695
2117
|
async #release(delivery) {
|
|
@@ -1713,54 +2135,64 @@ var Dispatcher = class {
|
|
|
1713
2135
|
}
|
|
1714
2136
|
}
|
|
1715
2137
|
};
|
|
1716
|
-
function pendingKey(delivery) {
|
|
1717
|
-
return `${delivery.deliveryId}\0${delivery.leaseToken}`;
|
|
1718
|
-
}
|
|
1719
|
-
function rejectOnAbort(signal) {
|
|
1720
|
-
return new Promise((_, reject) => {
|
|
1721
|
-
signal.addEventListener("abort", () => reject(signal.reason), { once: true });
|
|
1722
|
-
});
|
|
1723
|
-
}
|
|
1724
2138
|
|
|
1725
2139
|
// src/handler-registry.ts
|
|
1726
2140
|
var HANDLER_ID_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._:/-]{0,254}$/;
|
|
1727
2141
|
var HandlerRegistry = class {
|
|
1728
2142
|
#byId = /* @__PURE__ */ new Map();
|
|
2143
|
+
#byClaimId = /* @__PURE__ */ new Map();
|
|
1729
2144
|
#byEventType = /* @__PURE__ */ new Map();
|
|
1730
2145
|
register(input, defaults) {
|
|
1731
2146
|
const { options } = input;
|
|
1732
2147
|
if (!HANDLER_ID_PATTERN.test(options.id)) {
|
|
1733
|
-
throw new ConfigurationError(
|
|
2148
|
+
throw new chunk3OHMEMZA_cjs.ConfigurationError(
|
|
1734
2149
|
`Handler id '${options.id}' must be 1-255 characters of letters, digits, '.', '_', ':', '/' or '-'`
|
|
1735
2150
|
);
|
|
1736
2151
|
}
|
|
1737
|
-
|
|
1738
|
-
|
|
1739
|
-
|
|
1740
|
-
|
|
1741
|
-
throw new ConfigurationError(
|
|
1742
|
-
`Concurrency of handler '${options.id}' must be a positive integer, received ${options.concurrency}`
|
|
1743
|
-
);
|
|
2152
|
+
const claimIds = [options.id, ...input.legacyIds ?? []];
|
|
2153
|
+
this.#assertUnregistered(claimIds);
|
|
2154
|
+
if (options.concurrency !== void 0) {
|
|
2155
|
+
chunk3OHMEMZA_cjs.assertPositiveInteger(options.concurrency, `Concurrency of handler '${options.id}'`);
|
|
1744
2156
|
}
|
|
1745
2157
|
const registration = {
|
|
1746
2158
|
id: options.id,
|
|
1747
|
-
|
|
2159
|
+
eventTypes: input.eventTypes,
|
|
2160
|
+
claimIds,
|
|
1748
2161
|
schema: input.schema,
|
|
1749
2162
|
handle: input.handle,
|
|
1750
2163
|
retry: createRetryPolicy(options.retry, defaults.retry),
|
|
1751
|
-
timeoutMs: resolveTimeout(options.timeout,
|
|
2164
|
+
timeoutMs: resolveTimeout(options.timeout, {
|
|
2165
|
+
fallback: defaults.timeoutMs,
|
|
2166
|
+
handlerId: options.id
|
|
2167
|
+
}),
|
|
1752
2168
|
concurrency: options.concurrency ?? null,
|
|
1753
2169
|
transactional: options.transactional ?? false,
|
|
1754
2170
|
forwardsToTransport: input.forwardsToTransport
|
|
1755
2171
|
};
|
|
1756
|
-
this.#
|
|
1757
|
-
const forType = this.#byEventType.get(registration.eventType) ?? [];
|
|
1758
|
-
forType.push(registration);
|
|
1759
|
-
this.#byEventType.set(registration.eventType, forType);
|
|
2172
|
+
this.#index(registration);
|
|
1760
2173
|
return registration;
|
|
1761
2174
|
}
|
|
2175
|
+
#assertUnregistered(claimIds) {
|
|
2176
|
+
for (const id of claimIds) {
|
|
2177
|
+
if (this.#byClaimId.has(id)) {
|
|
2178
|
+
throw new chunk3OHMEMZA_cjs.ConfigurationError(`Handler id '${id}' is already registered`);
|
|
2179
|
+
}
|
|
2180
|
+
}
|
|
2181
|
+
}
|
|
2182
|
+
#index(registration) {
|
|
2183
|
+
this.#byId.set(registration.id, registration);
|
|
2184
|
+
for (const id of registration.claimIds) {
|
|
2185
|
+
this.#byClaimId.set(id, registration);
|
|
2186
|
+
}
|
|
2187
|
+
for (const eventType of registration.eventTypes) {
|
|
2188
|
+
const forType = this.#byEventType.get(eventType) ?? [];
|
|
2189
|
+
forType.push(registration);
|
|
2190
|
+
this.#byEventType.set(eventType, forType);
|
|
2191
|
+
}
|
|
2192
|
+
}
|
|
2193
|
+
/** The registration that runs deliveries stored under `id`, its own id or a legacy one. */
|
|
1762
2194
|
get(id) {
|
|
1763
|
-
return this.#
|
|
2195
|
+
return this.#byClaimId.get(id);
|
|
1764
2196
|
}
|
|
1765
2197
|
forEventType(eventType) {
|
|
1766
2198
|
return this.#byEventType.get(eventType) ?? [];
|
|
@@ -1769,7 +2201,7 @@ var HandlerRegistry = class {
|
|
|
1769
2201
|
return [...this.#byId.values()];
|
|
1770
2202
|
}
|
|
1771
2203
|
};
|
|
1772
|
-
function resolveTimeout(timeout, fallback, handlerId) {
|
|
2204
|
+
function resolveTimeout(timeout, { fallback, handlerId }) {
|
|
1773
2205
|
if (timeout === void 0) {
|
|
1774
2206
|
return fallback;
|
|
1775
2207
|
}
|
|
@@ -1783,9 +2215,18 @@ function resolveTimeout(timeout, fallback, handlerId) {
|
|
|
1783
2215
|
var Notifier = class {
|
|
1784
2216
|
#observers;
|
|
1785
2217
|
#logger;
|
|
2218
|
+
/** Hooks at least one observer implements, so callers skip building events nobody reads. */
|
|
2219
|
+
#implemented;
|
|
1786
2220
|
constructor(observers, logger) {
|
|
1787
2221
|
this.#observers = observers;
|
|
1788
2222
|
this.#logger = logger;
|
|
2223
|
+
this.#implemented = new Set(
|
|
2224
|
+
observers.flatMap((observer) => hookNames(observer))
|
|
2225
|
+
);
|
|
2226
|
+
}
|
|
2227
|
+
/** True when some observer implements `method`. */
|
|
2228
|
+
wants(method) {
|
|
2229
|
+
return this.#implemented.has(method);
|
|
1789
2230
|
}
|
|
1790
2231
|
notify(method, argument) {
|
|
1791
2232
|
for (const observer of this.#observers) {
|
|
@@ -1800,7 +2241,7 @@ var Notifier = class {
|
|
|
1800
2241
|
}
|
|
1801
2242
|
}
|
|
1802
2243
|
failure(stage, error) {
|
|
1803
|
-
const errorInfo = toErrorInfo(error);
|
|
2244
|
+
const errorInfo = chunk3OHMEMZA_cjs.toErrorInfo(error);
|
|
1804
2245
|
this.#logger.error(
|
|
1805
2246
|
`[Notifier](failure) relaybus ${stage} failed | ${errorInfo.name}: ${errorInfo.message}`
|
|
1806
2247
|
);
|
|
@@ -1814,7 +2255,7 @@ var Notifier = class {
|
|
|
1814
2255
|
}
|
|
1815
2256
|
#reportObserverFailure(failedObserver, error) {
|
|
1816
2257
|
this.#logObserverFailure(error);
|
|
1817
|
-
const errorInfo = toErrorInfo(error);
|
|
2258
|
+
const errorInfo = chunk3OHMEMZA_cjs.toErrorInfo(error);
|
|
1818
2259
|
for (const observer of this.#observers) {
|
|
1819
2260
|
if (observer === failedObserver) {
|
|
1820
2261
|
continue;
|
|
@@ -1827,14 +2268,60 @@ var Notifier = class {
|
|
|
1827
2268
|
}
|
|
1828
2269
|
}
|
|
1829
2270
|
#logObserverFailure(error) {
|
|
1830
|
-
const errorInfo = toErrorInfo(error);
|
|
2271
|
+
const errorInfo = chunk3OHMEMZA_cjs.toErrorInfo(error);
|
|
1831
2272
|
this.#logger.warn(
|
|
1832
|
-
`[Notifier](
|
|
2273
|
+
`[Notifier](logObserverFailure) relaybus observer threw | ${errorInfo.name}: ${errorInfo.message}`
|
|
1833
2274
|
);
|
|
1834
2275
|
}
|
|
1835
2276
|
};
|
|
2277
|
+
function hookNames(observer) {
|
|
2278
|
+
const names = [];
|
|
2279
|
+
for (let target = observer; target !== null && target !== Object.prototype; target = Object.getPrototypeOf(target)) {
|
|
2280
|
+
names.push(
|
|
2281
|
+
...Object.getOwnPropertyNames(target).filter(
|
|
2282
|
+
(name) => typeof observer[name] === "function"
|
|
2283
|
+
)
|
|
2284
|
+
);
|
|
2285
|
+
}
|
|
2286
|
+
return names;
|
|
2287
|
+
}
|
|
1836
2288
|
|
|
1837
|
-
// src/
|
|
2289
|
+
// src/received-envelope.ts
|
|
2290
|
+
function normalizeEnvelope(envelope) {
|
|
2291
|
+
if (typeof envelope.id !== "string" || envelope.id.length === 0) {
|
|
2292
|
+
throw new chunk3OHMEMZA_cjs.InvalidEnvelopeError("Received event has no id");
|
|
2293
|
+
}
|
|
2294
|
+
if (typeof envelope.type !== "string") {
|
|
2295
|
+
throw new chunk3OHMEMZA_cjs.InvalidEnvelopeError(`Received event ${envelope.id} has no type`);
|
|
2296
|
+
}
|
|
2297
|
+
try {
|
|
2298
|
+
chunk3OHMEMZA_cjs.assertEventType(envelope.type);
|
|
2299
|
+
} catch (error) {
|
|
2300
|
+
throw new chunk3OHMEMZA_cjs.InvalidEnvelopeError(chunk3OHMEMZA_cjs.toErrorInfo(error).message);
|
|
2301
|
+
}
|
|
2302
|
+
const occurredAt = toDate(envelope.occurredAt);
|
|
2303
|
+
if (occurredAt === void 0) {
|
|
2304
|
+
throw new chunk3OHMEMZA_cjs.InvalidEnvelopeError(`Received event ${envelope.id} has an invalid occurredAt`);
|
|
2305
|
+
}
|
|
2306
|
+
return {
|
|
2307
|
+
id: envelope.id,
|
|
2308
|
+
type: envelope.type,
|
|
2309
|
+
source: typeof envelope.source === "string" ? envelope.source : "unknown",
|
|
2310
|
+
key: optionalText(envelope.key),
|
|
2311
|
+
payload: envelope.payload,
|
|
2312
|
+
headers: { ...envelope.headers },
|
|
2313
|
+
correlationId: optionalText(envelope.correlationId),
|
|
2314
|
+
causationId: optionalText(envelope.causationId),
|
|
2315
|
+
occurredAt
|
|
2316
|
+
};
|
|
2317
|
+
}
|
|
2318
|
+
function toDate(value) {
|
|
2319
|
+
const date = typeof value === "string" ? new Date(value) : value;
|
|
2320
|
+
return date instanceof Date && !Number.isNaN(date.getTime()) ? date : void 0;
|
|
2321
|
+
}
|
|
2322
|
+
function optionalText(value) {
|
|
2323
|
+
return typeof value === "string" && value.length > 0 ? value : null;
|
|
2324
|
+
}
|
|
1838
2325
|
var DEFAULTS = {
|
|
1839
2326
|
concurrency: 10,
|
|
1840
2327
|
pollInterval: "1s",
|
|
@@ -1844,14 +2331,195 @@ var DEFAULTS = {
|
|
|
1844
2331
|
completionBatchSize: 200,
|
|
1845
2332
|
completedRetention: "7d",
|
|
1846
2333
|
retentionInterval: "1h",
|
|
1847
|
-
routeTimeoutMs: 3e4
|
|
2334
|
+
routeTimeoutMs: 3e4,
|
|
2335
|
+
transactionTimeout: "5s"
|
|
1848
2336
|
};
|
|
2337
|
+
function oneOf(value, { allowed, name }) {
|
|
2338
|
+
if (!allowed.includes(value)) {
|
|
2339
|
+
throw new chunk3OHMEMZA_cjs.ConfigurationError(
|
|
2340
|
+
`${name} must be one of ${allowed.map((option) => `'${option}'`).join(", ")}, received '${String(value)}'`
|
|
2341
|
+
);
|
|
2342
|
+
}
|
|
2343
|
+
return value;
|
|
2344
|
+
}
|
|
2345
|
+
function isDispatcherEnabled(options) {
|
|
2346
|
+
return options.dispatcher?.enabled ?? true;
|
|
2347
|
+
}
|
|
2348
|
+
function resolveBacklogInterval(options) {
|
|
2349
|
+
return options.backlogInterval === void 0 ? void 0 : toTimerMilliseconds(options.backlogInterval, "backlogInterval");
|
|
2350
|
+
}
|
|
2351
|
+
function resolveTransactionTimeout(options) {
|
|
2352
|
+
return toTimerMilliseconds(
|
|
2353
|
+
options.transactionTimeout ?? DEFAULTS.transactionTimeout,
|
|
2354
|
+
"transactionTimeout"
|
|
2355
|
+
);
|
|
2356
|
+
}
|
|
2357
|
+
function createHandlerDefaults(options) {
|
|
2358
|
+
return {
|
|
2359
|
+
retry: createRetryPolicy(options.retry),
|
|
2360
|
+
timeoutMs: options.handlerTimeout === void 0 || options.handlerTimeout === false ? null : toTimerMilliseconds(options.handlerTimeout, "handlerTimeout")
|
|
2361
|
+
};
|
|
2362
|
+
}
|
|
2363
|
+
function createDispatcherSettings(options, generateIdentifier) {
|
|
2364
|
+
const dispatcher = options ?? {};
|
|
2365
|
+
const concurrency = chunk3OHMEMZA_cjs.assertPositiveInteger(
|
|
2366
|
+
dispatcher.concurrency ?? DEFAULTS.concurrency,
|
|
2367
|
+
"dispatcher.concurrency"
|
|
2368
|
+
);
|
|
2369
|
+
return {
|
|
2370
|
+
owner: `${os.hostname()}:${process.pid}:${generateIdentifier()}`,
|
|
2371
|
+
concurrency,
|
|
2372
|
+
batchSize: chunk3OHMEMZA_cjs.assertPositiveInteger(dispatcher.batchSize ?? concurrency, "dispatcher.batchSize"),
|
|
2373
|
+
...createDispatcherTimings(dispatcher)
|
|
2374
|
+
};
|
|
2375
|
+
}
|
|
2376
|
+
function createDispatcherTimings(dispatcher) {
|
|
2377
|
+
return {
|
|
2378
|
+
pollIntervalMs: toTimerMilliseconds(
|
|
2379
|
+
dispatcher.pollInterval ?? DEFAULTS.pollInterval,
|
|
2380
|
+
"dispatcher.pollInterval"
|
|
2381
|
+
),
|
|
2382
|
+
leaseDurationMs: toTimerMilliseconds(
|
|
2383
|
+
dispatcher.leaseDuration ?? DEFAULTS.leaseDuration,
|
|
2384
|
+
"dispatcher.leaseDuration"
|
|
2385
|
+
),
|
|
2386
|
+
shutdownTimeoutMs: toTimerMilliseconds(
|
|
2387
|
+
dispatcher.shutdownTimeout ?? DEFAULTS.shutdownTimeout,
|
|
2388
|
+
"dispatcher.shutdownTimeout"
|
|
2389
|
+
),
|
|
2390
|
+
completionBatchWindowMs: toMilliseconds(
|
|
2391
|
+
dispatcher.completionBatchWindow ?? DEFAULTS.completionBatchWindow,
|
|
2392
|
+
"dispatcher.completionBatchWindow"
|
|
2393
|
+
),
|
|
2394
|
+
completionBatchSize: chunk3OHMEMZA_cjs.assertPositiveInteger(
|
|
2395
|
+
dispatcher.completionBatchSize ?? DEFAULTS.completionBatchSize,
|
|
2396
|
+
"dispatcher.completionBatchSize"
|
|
2397
|
+
)
|
|
2398
|
+
};
|
|
2399
|
+
}
|
|
2400
|
+
function createRetentionSettings(options) {
|
|
2401
|
+
const completed = options?.completed ?? DEFAULTS.completedRetention;
|
|
2402
|
+
const dead = options?.dead ?? false;
|
|
2403
|
+
return {
|
|
2404
|
+
completedMs: completed === false ? null : toMilliseconds(completed, "retention.completed"),
|
|
2405
|
+
deadMs: dead === false ? null : toMilliseconds(dead, "retention.dead"),
|
|
2406
|
+
intervalMs: toTimerMilliseconds(
|
|
2407
|
+
options?.interval ?? DEFAULTS.retentionInterval,
|
|
2408
|
+
"retention.interval"
|
|
2409
|
+
)
|
|
2410
|
+
};
|
|
2411
|
+
}
|
|
2412
|
+
|
|
2413
|
+
// src/route-groups.ts
|
|
1849
2414
|
var TRANSPORT_HANDLER_PREFIX = "relaybus.transport";
|
|
2415
|
+
function groupRoutes(routes) {
|
|
2416
|
+
const groups = /* @__PURE__ */ new Map();
|
|
2417
|
+
for (const route of routes) {
|
|
2418
|
+
const type = routedType(route.event);
|
|
2419
|
+
const destination = route.destination ?? type;
|
|
2420
|
+
const id = `${TRANSPORT_HANDLER_PREFIX}:${route.transport}:${destination}`;
|
|
2421
|
+
const group = groups.get(id);
|
|
2422
|
+
if (group === void 0) {
|
|
2423
|
+
groups.set(id, createGroup(route, { id, destination, type }));
|
|
2424
|
+
} else {
|
|
2425
|
+
addRoute(group, { route, type, destination });
|
|
2426
|
+
}
|
|
2427
|
+
}
|
|
2428
|
+
return [...groups.values()];
|
|
2429
|
+
}
|
|
2430
|
+
function createGroup(route, {
|
|
2431
|
+
id,
|
|
2432
|
+
destination,
|
|
2433
|
+
type
|
|
2434
|
+
}) {
|
|
2435
|
+
return {
|
|
2436
|
+
transport: route.transport,
|
|
2437
|
+
destination,
|
|
2438
|
+
id,
|
|
2439
|
+
eventTypes: [type],
|
|
2440
|
+
legacyIds: [`${id}:${type}`],
|
|
2441
|
+
route,
|
|
2442
|
+
...resolveSettings(route, id)
|
|
2443
|
+
};
|
|
2444
|
+
}
|
|
2445
|
+
function addRoute(group, {
|
|
2446
|
+
route,
|
|
2447
|
+
type,
|
|
2448
|
+
destination
|
|
2449
|
+
}) {
|
|
2450
|
+
const target = `'${group.destination}' of transport '${group.transport}'`;
|
|
2451
|
+
if (route.transport !== group.transport) {
|
|
2452
|
+
throw new chunk3OHMEMZA_cjs.ConfigurationError(
|
|
2453
|
+
`Routes to ${target} and to '${destination}' of transport '${route.transport}' would share the handler id '${group.id}'; rename the transport or the destination`
|
|
2454
|
+
);
|
|
2455
|
+
}
|
|
2456
|
+
if (group.eventTypes.includes(type)) {
|
|
2457
|
+
throw new chunk3OHMEMZA_cjs.ConfigurationError(`Event type ${type} is routed to ${target} twice`);
|
|
2458
|
+
}
|
|
2459
|
+
const { retry, timeoutMs } = resolveSettings(route, group.id);
|
|
2460
|
+
if (timeoutMs !== group.timeoutMs || !sameRetryPolicy(retry, group.retry)) {
|
|
2461
|
+
throw new chunk3OHMEMZA_cjs.ConfigurationError(
|
|
2462
|
+
`Routes to ${target} must share retry and timeout, because their events are sent in one queue per key | types=${group.eventTypes[0]},${type}`
|
|
2463
|
+
);
|
|
2464
|
+
}
|
|
2465
|
+
group.eventTypes.push(type);
|
|
2466
|
+
group.legacyIds.push(`${group.id}:${type}`);
|
|
2467
|
+
}
|
|
2468
|
+
function resolveSettings(route, id) {
|
|
2469
|
+
return {
|
|
2470
|
+
retry: createRetryPolicy(route.retry, DEFAULT_ROUTE_RETRY_POLICY),
|
|
2471
|
+
timeoutMs: route.timeout === void 0 ? DEFAULTS.routeTimeoutMs : toTimerMilliseconds(route.timeout, `Timeout of handler '${id}'`)
|
|
2472
|
+
};
|
|
2473
|
+
}
|
|
2474
|
+
function sameRetryPolicy(left, right) {
|
|
2475
|
+
return Object.keys(left).every(
|
|
2476
|
+
(field) => left[field] === right[field]
|
|
2477
|
+
);
|
|
2478
|
+
}
|
|
2479
|
+
function routedType(event) {
|
|
2480
|
+
const type = typeof event === "string" ? event : event.type;
|
|
2481
|
+
chunk3OHMEMZA_cjs.assertEventType(type);
|
|
2482
|
+
return type;
|
|
2483
|
+
}
|
|
2484
|
+
|
|
2485
|
+
// src/transaction-budget.ts
|
|
2486
|
+
async function runBudgetedTransaction(store, { clock, timeoutMs, work }) {
|
|
2487
|
+
const startedAt = clock.now().getTime();
|
|
2488
|
+
const overrun = () => clock.now().getTime() - startedAt >= timeoutMs;
|
|
2489
|
+
let committing = false;
|
|
2490
|
+
try {
|
|
2491
|
+
return await store.transaction(
|
|
2492
|
+
async (transaction) => {
|
|
2493
|
+
committing = false;
|
|
2494
|
+
const result = await work(transaction);
|
|
2495
|
+
if (overrun()) {
|
|
2496
|
+
throw createTimeoutError(timeoutMs, void 0);
|
|
2497
|
+
}
|
|
2498
|
+
committing = true;
|
|
2499
|
+
return result;
|
|
2500
|
+
},
|
|
2501
|
+
{ timeoutMs }
|
|
2502
|
+
);
|
|
2503
|
+
} catch (error) {
|
|
2504
|
+
if (error instanceof chunk3OHMEMZA_cjs.TransactionTimeoutError || committing || !overrun()) {
|
|
2505
|
+
throw error;
|
|
2506
|
+
}
|
|
2507
|
+
throw createTimeoutError(timeoutMs, error);
|
|
2508
|
+
}
|
|
2509
|
+
}
|
|
2510
|
+
function createTimeoutError(timeoutMs, cause) {
|
|
2511
|
+
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`;
|
|
2512
|
+
return cause === void 0 ? new chunk3OHMEMZA_cjs.TransactionTimeoutError(message) : new chunk3OHMEMZA_cjs.TransactionTimeoutError(`${message} | cause=${chunk3OHMEMZA_cjs.toErrorInfo(cause).message}`, { cause });
|
|
2513
|
+
}
|
|
2514
|
+
|
|
2515
|
+
// src/relay-bus.ts
|
|
2516
|
+
var DEFAULT_DRAIN_BATCHES = 1e3;
|
|
1850
2517
|
var WAKE_DELAY_MS = 25;
|
|
1851
2518
|
var RelayBus = class {
|
|
1852
2519
|
#store;
|
|
1853
2520
|
#source;
|
|
1854
2521
|
#transactionMode;
|
|
2522
|
+
#transactionTimeoutMs;
|
|
1855
2523
|
#transactionResolver;
|
|
1856
2524
|
#unroutedEvents;
|
|
1857
2525
|
#registry = new HandlerRegistry();
|
|
@@ -1868,61 +2536,79 @@ var RelayBus = class {
|
|
|
1868
2536
|
#setupStore;
|
|
1869
2537
|
#retention;
|
|
1870
2538
|
#retentionTimer;
|
|
2539
|
+
#observation;
|
|
2540
|
+
#backlogIntervalMs;
|
|
2541
|
+
#backlogTimer;
|
|
2542
|
+
#shutdownTimeoutMs;
|
|
1871
2543
|
#state = "idle";
|
|
1872
2544
|
#starting;
|
|
1873
2545
|
#stopping;
|
|
1874
2546
|
constructor(options) {
|
|
1875
2547
|
this.#store = options.store;
|
|
1876
2548
|
this.#source = options.source ?? "relaybus";
|
|
1877
|
-
this.#transactionMode = oneOf(
|
|
1878
|
-
|
|
1879
|
-
|
|
1880
|
-
|
|
1881
|
-
|
|
1882
|
-
|
|
1883
|
-
|
|
1884
|
-
|
|
1885
|
-
"unroutedEvents"
|
|
1886
|
-
);
|
|
2549
|
+
this.#transactionMode = oneOf(options.transactionMode ?? "required", {
|
|
2550
|
+
allowed: ["required", "optional"],
|
|
2551
|
+
name: "transactionMode"
|
|
2552
|
+
});
|
|
2553
|
+
this.#unroutedEvents = oneOf(options.unroutedEvents ?? "warn", {
|
|
2554
|
+
allowed: ["warn", "throw"],
|
|
2555
|
+
name: "unroutedEvents"
|
|
2556
|
+
});
|
|
1887
2557
|
this.#transactionResolver = options.transactionResolver;
|
|
1888
|
-
this.#
|
|
1889
|
-
|
|
1890
|
-
timeoutMs: options.handlerTimeout === void 0 || options.handlerTimeout === false ? null : toTimerMilliseconds(options.handlerTimeout, "handlerTimeout")
|
|
1891
|
-
};
|
|
2558
|
+
this.#transactionTimeoutMs = resolveTransactionTimeout(options);
|
|
2559
|
+
this.#defaults = createHandlerDefaults(options);
|
|
1892
2560
|
this.#logger = options.logger ?? console;
|
|
1893
2561
|
this.#notifier = new Notifier(options.observers ?? [], this.#logger);
|
|
1894
2562
|
this.#clock = options.clock ?? systemClock;
|
|
2563
|
+
this.#observation = BusObservation.create({
|
|
2564
|
+
notifier: this.#notifier,
|
|
2565
|
+
tracer: options.tracer,
|
|
2566
|
+
clock: this.#clock
|
|
2567
|
+
});
|
|
2568
|
+
this.#backlogIntervalMs = resolveBacklogInterval(options);
|
|
1895
2569
|
this.#generateIdentifier = options.generateIdentifier ?? (() => createUuidV7());
|
|
1896
2570
|
this.#setupStore = options.setupStore ?? true;
|
|
1897
2571
|
this.#retention = createRetentionSettings(options.retention);
|
|
2572
|
+
this.#subscriptions = options.subscriptions ?? [];
|
|
2573
|
+
this.#registerConnections(options);
|
|
2574
|
+
this.#dispatcherEnabled = isDispatcherEnabled(options);
|
|
2575
|
+
const settings = createDispatcherSettings(options.dispatcher, this.#generateIdentifier);
|
|
2576
|
+
this.#shutdownTimeoutMs = settings.shutdownTimeoutMs;
|
|
2577
|
+
this.#dispatcher = this.#createDispatcher(settings, options);
|
|
2578
|
+
}
|
|
2579
|
+
#createDispatcher(settings, options) {
|
|
2580
|
+
return new Dispatcher({
|
|
2581
|
+
store: this.#store,
|
|
2582
|
+
registry: this.#registry,
|
|
2583
|
+
clock: this.#clock,
|
|
2584
|
+
random: options.random ?? Math.random,
|
|
2585
|
+
notifier: this.#notifier,
|
|
2586
|
+
settings,
|
|
2587
|
+
observation: DispatchObservation.create({
|
|
2588
|
+
notifier: this.#notifier,
|
|
2589
|
+
tracer: options.tracer,
|
|
2590
|
+
clock: this.#clock
|
|
2591
|
+
})
|
|
2592
|
+
});
|
|
2593
|
+
}
|
|
2594
|
+
/** Registers the transports, then the routes, checks the subscriptions and registers the catalogs. */
|
|
2595
|
+
#registerConnections(options) {
|
|
1898
2596
|
for (const transport of options.transports ?? []) {
|
|
1899
2597
|
if (this.#transports.has(transport.name)) {
|
|
1900
|
-
throw new ConfigurationError(`Transport name '${transport.name}' is used twice`);
|
|
2598
|
+
throw new chunk3OHMEMZA_cjs.ConfigurationError(`Transport name '${transport.name}' is used twice`);
|
|
1901
2599
|
}
|
|
1902
2600
|
this.#transports.set(transport.name, transport);
|
|
1903
2601
|
}
|
|
1904
|
-
|
|
1905
|
-
this.#registerRoute(route);
|
|
1906
|
-
}
|
|
1907
|
-
this.#subscriptions = options.subscriptions ?? [];
|
|
2602
|
+
this.#registerRoutes(options.routes ?? []);
|
|
1908
2603
|
for (const subscription of this.#subscriptions) {
|
|
1909
2604
|
const transport = this.#requireTransport(subscription.transport);
|
|
1910
2605
|
if (transport.subscribe === void 0) {
|
|
1911
|
-
throw new ConfigurationError(`Transport '${transport.name}' cannot subscribe`);
|
|
2606
|
+
throw new chunk3OHMEMZA_cjs.ConfigurationError(`Transport '${transport.name}' cannot subscribe`);
|
|
1912
2607
|
}
|
|
1913
2608
|
}
|
|
1914
2609
|
for (const catalog of options.catalogs ?? []) {
|
|
1915
2610
|
this.registerCatalog(catalog);
|
|
1916
2611
|
}
|
|
1917
|
-
this.#dispatcherEnabled = options.dispatcher?.enabled ?? true;
|
|
1918
|
-
this.#dispatcher = new Dispatcher({
|
|
1919
|
-
store: this.#store,
|
|
1920
|
-
registry: this.#registry,
|
|
1921
|
-
clock: this.#clock,
|
|
1922
|
-
random: options.random ?? Math.random,
|
|
1923
|
-
notifier: this.#notifier,
|
|
1924
|
-
settings: createDispatcherSettings(options.dispatcher, this.#generateIdentifier)
|
|
1925
|
-
});
|
|
1926
2612
|
}
|
|
1927
2613
|
get store() {
|
|
1928
2614
|
return this.#store;
|
|
@@ -1942,11 +2628,12 @@ var RelayBus = class {
|
|
|
1942
2628
|
}
|
|
1943
2629
|
return true;
|
|
1944
2630
|
}
|
|
2631
|
+
// eslint-disable-next-line relaybus/max-two-params -- public RelayBus.subscribe signature
|
|
1945
2632
|
subscribe(event, handler, options) {
|
|
1946
2633
|
const { type, schema } = describeEvent(event);
|
|
1947
2634
|
this.#registry.register(
|
|
1948
2635
|
{
|
|
1949
|
-
|
|
2636
|
+
eventTypes: [type],
|
|
1950
2637
|
schema,
|
|
1951
2638
|
handle: handler,
|
|
1952
2639
|
options,
|
|
@@ -1955,8 +2642,9 @@ var RelayBus = class {
|
|
|
1955
2642
|
this.#defaults
|
|
1956
2643
|
);
|
|
1957
2644
|
}
|
|
2645
|
+
// eslint-disable-next-line relaybus/max-two-params -- public RelayBus.subscribeCatalog signature
|
|
1958
2646
|
subscribeCatalog(catalog, handlers, options = {}) {
|
|
1959
|
-
for (const binding of resolveCatalogHandlers(catalog, handlers, options)) {
|
|
2647
|
+
for (const binding of resolveCatalogHandlers(catalog, { handlers, options })) {
|
|
1960
2648
|
this.subscribe(binding.eventClass, binding.handle, binding.options);
|
|
1961
2649
|
}
|
|
1962
2650
|
}
|
|
@@ -1979,7 +2667,9 @@ var RelayBus = class {
|
|
|
1979
2667
|
}
|
|
1980
2668
|
/** Registered handlers and routes, for diagnostics. */
|
|
1981
2669
|
handlers() {
|
|
1982
|
-
return this.#registry.all().
|
|
2670
|
+
return this.#registry.all().flatMap(
|
|
2671
|
+
(registration) => registration.eventTypes.map((eventType) => toDescriptor(registration, eventType))
|
|
2672
|
+
);
|
|
1983
2673
|
}
|
|
1984
2674
|
/**
|
|
1985
2675
|
* Stores an event in the current transaction. Handlers run after the
|
|
@@ -1987,21 +2677,32 @@ var RelayBus = class {
|
|
|
1987
2677
|
* have been written when the id already exists.
|
|
1988
2678
|
*/
|
|
1989
2679
|
async publish(event, options = {}) {
|
|
1990
|
-
const [envelope] = await this.#
|
|
2680
|
+
const [envelope] = await this.#observedPublish([event], { options, id: options.id });
|
|
1991
2681
|
return envelope;
|
|
1992
2682
|
}
|
|
1993
2683
|
/** Stores several events atomically, in order, sharing one correlation id. */
|
|
1994
2684
|
async publishAll(events, options = {}) {
|
|
1995
|
-
return this.#
|
|
2685
|
+
return this.#observedPublish(events, { options, id: void 0 });
|
|
1996
2686
|
}
|
|
1997
2687
|
/**
|
|
1998
2688
|
* Runs `work` in a new store transaction. `publish` calls inside it join the
|
|
1999
|
-
* transaction without passing it explicitly.
|
|
2689
|
+
* transaction without passing it explicitly. The transaction is held to `transactionTimeout`
|
|
2690
|
+
* and rolled back with a {@link TransactionTimeoutError} when it runs past it.
|
|
2000
2691
|
*/
|
|
2001
2692
|
async transaction(work) {
|
|
2002
|
-
return this.#store
|
|
2003
|
-
|
|
2004
|
-
|
|
2693
|
+
return runBudgetedTransaction(this.#store, {
|
|
2694
|
+
clock: this.#clock,
|
|
2695
|
+
timeoutMs: this.#transactionTimeoutMs,
|
|
2696
|
+
work: (transaction) => runInScope({ transaction }, () => work(transaction))
|
|
2697
|
+
});
|
|
2698
|
+
}
|
|
2699
|
+
/**
|
|
2700
|
+
* The transaction `publish` would join right now: the one of the enclosing `bus.transaction`
|
|
2701
|
+
* (or `@RelayBusTransaction()` method, or transactional handler), else the one
|
|
2702
|
+
* `transactionResolver` finds. Repositories use it to write in the same transaction.
|
|
2703
|
+
*/
|
|
2704
|
+
currentTransaction() {
|
|
2705
|
+
return getCurrentScope()?.transaction ?? this.#transactionResolver?.();
|
|
2005
2706
|
}
|
|
2006
2707
|
/**
|
|
2007
2708
|
* Stores an event received from a broker and schedules it for local handlers.
|
|
@@ -2009,7 +2710,13 @@ var RelayBus = class {
|
|
|
2009
2710
|
* only after it resolves. Throws {@link InvalidEnvelopeError} for malformed
|
|
2010
2711
|
* events, which transports should dead-letter instead of redelivering.
|
|
2011
2712
|
*/
|
|
2012
|
-
|
|
2713
|
+
receive(envelope) {
|
|
2714
|
+
return this.#observation === void 0 ? this.#receive(envelope) : this.#observedReceive(envelope, void 0);
|
|
2715
|
+
}
|
|
2716
|
+
#observedReceive(envelope, origin) {
|
|
2717
|
+
return this.#observation.receive({ envelope, origin }, () => this.#receive(envelope));
|
|
2718
|
+
}
|
|
2719
|
+
async #receive(envelope) {
|
|
2013
2720
|
const normalized = normalizeEnvelope(envelope);
|
|
2014
2721
|
const registrations = this.#registry.forEventType(normalized.type).filter((registration) => !registration.forwardsToTransport);
|
|
2015
2722
|
if (registrations.length === 0) {
|
|
@@ -2038,7 +2745,7 @@ var RelayBus = class {
|
|
|
2038
2745
|
*/
|
|
2039
2746
|
async start() {
|
|
2040
2747
|
if (this.#state !== "idle") {
|
|
2041
|
-
throw new
|
|
2748
|
+
throw new chunk3OHMEMZA_cjs.LifecycleError(
|
|
2042
2749
|
this.#state === "stopped" ? "A stopped relaybus cannot be started again; create a new instance" : `Cannot start a relaybus that is ${this.#state}`
|
|
2043
2750
|
);
|
|
2044
2751
|
}
|
|
@@ -2052,25 +2759,43 @@ var RelayBus = class {
|
|
|
2052
2759
|
}
|
|
2053
2760
|
}
|
|
2054
2761
|
/**
|
|
2055
|
-
* Stops
|
|
2056
|
-
*
|
|
2762
|
+
* Stops claiming, waits for running handlers, hands back leases, closes transports and the store,
|
|
2763
|
+
* all within `options.timeout` (default `dispatcher.shutdownTimeout`). When the deadline passes it
|
|
2764
|
+
* aborts the handlers still running, force-closes what relaybus opened itself (never pools or
|
|
2765
|
+
* clients the application passed in) and resolves anyway: it never rejects because of the
|
|
2766
|
+
* deadline. Abandoned deliveries stay leased until their lease expires and then run again
|
|
2767
|
+
* (at-least-once). Waits for a start in progress first. Safe to call more than once: later calls
|
|
2768
|
+
* get the result of the first.
|
|
2057
2769
|
*/
|
|
2058
|
-
async stop() {
|
|
2770
|
+
async stop(options = {}) {
|
|
2771
|
+
const timeoutMs = options.timeout === void 0 ? this.#shutdownTimeoutMs : toTimerMilliseconds(options.timeout, "timeout");
|
|
2059
2772
|
if (this.#stopping !== void 0) {
|
|
2060
2773
|
return this.#stopping;
|
|
2061
2774
|
}
|
|
2062
|
-
if (this.#state
|
|
2063
|
-
|
|
2775
|
+
if (this.#state !== "running" && this.#state !== "starting") {
|
|
2776
|
+
return this.#stopOutsideRun(timeoutMs);
|
|
2064
2777
|
}
|
|
2065
|
-
|
|
2066
|
-
|
|
2067
|
-
|
|
2778
|
+
this.#stopping = this.#stopComponents(timeoutMs);
|
|
2779
|
+
const result = await this.#stopping;
|
|
2780
|
+
if (!result.graceful) {
|
|
2781
|
+
this.#logger.warn(
|
|
2782
|
+
`[RelayBus](stop) Shutdown deadline passed, forced close | abandoned=${result.abandonedDeliveries} | timeoutMs=${timeoutMs}`
|
|
2783
|
+
);
|
|
2068
2784
|
}
|
|
2069
|
-
|
|
2070
|
-
|
|
2785
|
+
return result;
|
|
2786
|
+
}
|
|
2787
|
+
/**
|
|
2788
|
+
* A bus that is not running: an idle one only settles what batches run by hand left behind (and
|
|
2789
|
+
* stays startable); a stopped one has nothing left to do.
|
|
2790
|
+
*/
|
|
2791
|
+
async #stopOutsideRun(timeoutMs) {
|
|
2792
|
+
if (this.#state !== "idle") {
|
|
2793
|
+
return { graceful: true, abandonedDeliveries: 0, durationMs: 0 };
|
|
2071
2794
|
}
|
|
2072
|
-
|
|
2073
|
-
|
|
2795
|
+
return withinDeadline(
|
|
2796
|
+
{ clock: this.#clock, timeoutMs },
|
|
2797
|
+
(deadline) => this.#dispatcher.stop(deadline)
|
|
2798
|
+
);
|
|
2074
2799
|
}
|
|
2075
2800
|
/**
|
|
2076
2801
|
* Claims and runs one batch of due deliveries, resolving when they settle.
|
|
@@ -2083,7 +2808,7 @@ var RelayBus = class {
|
|
|
2083
2808
|
* Runs batches until nothing is due. Deliveries scheduled in the future
|
|
2084
2809
|
* (retries, delays) are not waited for. Intended for tests.
|
|
2085
2810
|
*/
|
|
2086
|
-
async drain(maxBatches =
|
|
2811
|
+
async drain(maxBatches = DEFAULT_DRAIN_BATCHES) {
|
|
2087
2812
|
let total = 0;
|
|
2088
2813
|
for (let batch = 0; batch < maxBatches; batch += 1) {
|
|
2089
2814
|
const processed = await this.runOnce();
|
|
@@ -2092,7 +2817,7 @@ var RelayBus = class {
|
|
|
2092
2817
|
}
|
|
2093
2818
|
total += processed;
|
|
2094
2819
|
}
|
|
2095
|
-
throw new
|
|
2820
|
+
throw new chunk3OHMEMZA_cjs.DrainIncompleteError(`drain did not finish within ${maxBatches} batches`);
|
|
2096
2821
|
}
|
|
2097
2822
|
listDeliveries(query = {}) {
|
|
2098
2823
|
return this.#store.listDeliveries(query);
|
|
@@ -2134,41 +2859,128 @@ var RelayBus = class {
|
|
|
2134
2859
|
}
|
|
2135
2860
|
for (const subscription of this.#subscriptions) {
|
|
2136
2861
|
const transport = this.#transports.get(subscription.transport);
|
|
2137
|
-
await transport.subscribe(
|
|
2138
|
-
|
|
2139
|
-
|
|
2862
|
+
await transport.subscribe(
|
|
2863
|
+
subscription.destinations,
|
|
2864
|
+
async (envelope, message) => {
|
|
2865
|
+
await (this.#observation === void 0 ? this.#receive(envelope) : this.#observedReceive(envelope, { transport, destination: message?.destination }));
|
|
2866
|
+
},
|
|
2867
|
+
this.#subscriptionHooks(transport)
|
|
2868
|
+
);
|
|
2140
2869
|
}
|
|
2141
2870
|
} catch (error) {
|
|
2142
|
-
await
|
|
2871
|
+
await withinDeadline(
|
|
2872
|
+
{ clock: this.#clock, timeoutMs: this.#shutdownTimeoutMs },
|
|
2873
|
+
async (deadline) => {
|
|
2874
|
+
await this.#closeTransports(deadline);
|
|
2875
|
+
return 0;
|
|
2876
|
+
}
|
|
2877
|
+
);
|
|
2143
2878
|
this.#state = "stopped";
|
|
2144
2879
|
throw error;
|
|
2145
2880
|
}
|
|
2881
|
+
if (this.#state !== "starting") {
|
|
2882
|
+
throw new chunk3OHMEMZA_cjs.LifecycleError("relaybus was stopped before it finished starting");
|
|
2883
|
+
}
|
|
2146
2884
|
if (this.#dispatcherEnabled) {
|
|
2147
2885
|
this.#dispatcher.start();
|
|
2148
2886
|
}
|
|
2149
|
-
|
|
2150
|
-
|
|
2151
|
-
this.purgeExpired().catch(() => void 0);
|
|
2152
|
-
}, this.#retention.intervalMs);
|
|
2153
|
-
this.#retentionTimer.unref();
|
|
2154
|
-
}
|
|
2887
|
+
this.#scheduleRetention();
|
|
2888
|
+
this.#scheduleBacklog();
|
|
2155
2889
|
this.#state = "running";
|
|
2156
2890
|
this.#logger.log(
|
|
2157
|
-
`[RelayBus](
|
|
2891
|
+
`[RelayBus](startComponents) relaybus started | handlers=${this.#registry.all().length} dispatcher=${this.#dispatcherEnabled}`
|
|
2158
2892
|
);
|
|
2159
2893
|
}
|
|
2160
|
-
|
|
2161
|
-
|
|
2162
|
-
|
|
2894
|
+
/** What a transport may report back while subscribed; undefined when no observer wants it. */
|
|
2895
|
+
#subscriptionHooks(transport) {
|
|
2896
|
+
if (!this.#notifier.wants("onInvalidMessage")) {
|
|
2897
|
+
return void 0;
|
|
2898
|
+
}
|
|
2899
|
+
return {
|
|
2900
|
+
onInvalidMessage: ({ destination, error }) => {
|
|
2901
|
+
const errorInfo = chunk3OHMEMZA_cjs.toErrorInfo(error);
|
|
2902
|
+
this.#notifier.notify("onInvalidMessage", {
|
|
2903
|
+
transport: transport.name,
|
|
2904
|
+
system: transport.system ?? transport.name,
|
|
2905
|
+
destination,
|
|
2906
|
+
reason: errorInfo.message,
|
|
2907
|
+
error: errorInfo
|
|
2908
|
+
});
|
|
2909
|
+
}
|
|
2910
|
+
};
|
|
2911
|
+
}
|
|
2912
|
+
/** Applies the retention policy every `retention.interval` while the bus runs, when it deletes anything. */
|
|
2913
|
+
#scheduleRetention() {
|
|
2914
|
+
if (this.#retention.completedMs !== null || this.#retention.deadMs !== null) {
|
|
2915
|
+
this.#retentionTimer = chunk3OHMEMZA_cjs.schedule(this.#clock, {
|
|
2916
|
+
run: () => {
|
|
2917
|
+
this.purgeExpired().catch(() => void 0);
|
|
2918
|
+
},
|
|
2919
|
+
delayMs: this.#retention.intervalMs,
|
|
2920
|
+
repeat: true,
|
|
2921
|
+
unref: true
|
|
2922
|
+
});
|
|
2923
|
+
}
|
|
2924
|
+
}
|
|
2925
|
+
/** Reports the backlog every `backlogInterval` while the bus runs, when an observer wants it. */
|
|
2926
|
+
#scheduleBacklog() {
|
|
2927
|
+
if (this.#backlogIntervalMs !== void 0) {
|
|
2928
|
+
this.#backlogTimer = this.#observation?.startBacklog({
|
|
2929
|
+
intervalMs: this.#backlogIntervalMs,
|
|
2930
|
+
statistics: () => this.statistics()
|
|
2931
|
+
});
|
|
2932
|
+
}
|
|
2933
|
+
}
|
|
2934
|
+
/**
|
|
2935
|
+
* Waits for a start in progress (a failed start leaves nothing to stop), then stops the dispatcher
|
|
2936
|
+
* and closes the transports and the store. A start still running at the deadline is given up: the
|
|
2937
|
+
* transports and the store are force-closed and the start rejects when it resumes.
|
|
2938
|
+
*/
|
|
2939
|
+
async #stopComponents(timeoutMs) {
|
|
2163
2940
|
try {
|
|
2164
|
-
await this.#
|
|
2165
|
-
|
|
2166
|
-
|
|
2941
|
+
return await withinDeadline({ clock: this.#clock, timeoutMs }, async (deadline) => {
|
|
2942
|
+
if (this.#state === "starting") {
|
|
2943
|
+
const started = await deadline.race(this.#starting.catch(() => void 0));
|
|
2944
|
+
if (started === "timeout") {
|
|
2945
|
+
this.#state = "stopping";
|
|
2946
|
+
await this.#closeConnections(deadline);
|
|
2947
|
+
return 0;
|
|
2948
|
+
}
|
|
2949
|
+
}
|
|
2950
|
+
if (this.#state !== "running") {
|
|
2951
|
+
return 0;
|
|
2952
|
+
}
|
|
2953
|
+
this.#state = "stopping";
|
|
2954
|
+
this.#retentionTimer?.();
|
|
2955
|
+
this.#backlogTimer?.();
|
|
2956
|
+
const abandoned = await this.#dispatcher.stop(deadline);
|
|
2957
|
+
await this.#closeConnections(deadline);
|
|
2958
|
+
return abandoned;
|
|
2959
|
+
});
|
|
2167
2960
|
} finally {
|
|
2168
2961
|
this.#state = "stopped";
|
|
2169
2962
|
}
|
|
2170
2963
|
}
|
|
2171
|
-
async #
|
|
2964
|
+
async #closeConnections(deadline) {
|
|
2965
|
+
await this.#closeTransports(deadline);
|
|
2966
|
+
const options = { signal: deadline.signal };
|
|
2967
|
+
await deadline.race(Promise.resolve(this.#store.close?.(options)));
|
|
2968
|
+
}
|
|
2969
|
+
/** Publishes inside the tracer's span, with its trace headers, when the bus is observed. */
|
|
2970
|
+
#observedPublish(events, input) {
|
|
2971
|
+
const observation = this.#observation;
|
|
2972
|
+
if (observation === void 0) {
|
|
2973
|
+
return this.#publish(events, input);
|
|
2974
|
+
}
|
|
2975
|
+
return observation.publish(
|
|
2976
|
+
{ eventTypes: events.map((event) => String(event.type)) },
|
|
2977
|
+
(headers) => this.#publish(events, { ...input, options: withTraceHeaders(input.options, headers) })
|
|
2978
|
+
);
|
|
2979
|
+
}
|
|
2980
|
+
async #publish(events, {
|
|
2981
|
+
options,
|
|
2982
|
+
id
|
|
2983
|
+
}) {
|
|
2172
2984
|
if (events.length === 0) {
|
|
2173
2985
|
return [];
|
|
2174
2986
|
}
|
|
@@ -2176,10 +2988,27 @@ var RelayBus = class {
|
|
|
2176
2988
|
const delayMs = options.delay === void 0 ? 0 : toMilliseconds(options.delay, "delay");
|
|
2177
2989
|
const availableAt = new Date(now.getTime() + delayMs);
|
|
2178
2990
|
const scope = getCurrentScope();
|
|
2991
|
+
const entries = await this.#createEntries({ events, options, id, scope, now, availableAt });
|
|
2992
|
+
const deliverable = entries.filter((entry) => entry.deliveries.length > 0);
|
|
2993
|
+
if (deliverable.length > 0) {
|
|
2994
|
+
await this.#appendDeliverable({ entries: deliverable, options, scope, now, delayMs });
|
|
2995
|
+
}
|
|
2996
|
+
return entries.map((entry) => entry.event);
|
|
2997
|
+
}
|
|
2998
|
+
/** One entry per event, in order: its envelope and a delivery for every registration of its type. */
|
|
2999
|
+
async #createEntries(input) {
|
|
3000
|
+
const { events, options, id, scope, now, availableAt } = input;
|
|
2179
3001
|
const entries = [];
|
|
2180
3002
|
let correlationId = options.correlationId ?? scope?.event?.correlationId ?? scope?.event?.id;
|
|
2181
3003
|
for (const event of events) {
|
|
2182
|
-
const envelope = await this.#createEnvelope(
|
|
3004
|
+
const envelope = await this.#createEnvelope({
|
|
3005
|
+
event,
|
|
3006
|
+
options,
|
|
3007
|
+
id,
|
|
3008
|
+
scope,
|
|
3009
|
+
now,
|
|
3010
|
+
correlationId
|
|
3011
|
+
});
|
|
2183
3012
|
correlationId = envelope.correlationId;
|
|
2184
3013
|
const registrations = this.#registry.forEventType(envelope.type);
|
|
2185
3014
|
if (registrations.length === 0) {
|
|
@@ -2192,37 +3021,42 @@ var RelayBus = class {
|
|
|
2192
3021
|
)
|
|
2193
3022
|
});
|
|
2194
3023
|
}
|
|
2195
|
-
|
|
2196
|
-
|
|
2197
|
-
|
|
2198
|
-
|
|
2199
|
-
|
|
2200
|
-
|
|
2201
|
-
|
|
2202
|
-
|
|
2203
|
-
|
|
2204
|
-
|
|
2205
|
-
|
|
2206
|
-
|
|
2207
|
-
|
|
3024
|
+
return entries;
|
|
3025
|
+
}
|
|
3026
|
+
/** Appends the entries that have deliveries, reports what was new and wakes the dispatcher when it is due now. */
|
|
3027
|
+
async #appendDeliverable(input) {
|
|
3028
|
+
const { entries, options, scope, now, delayMs } = input;
|
|
3029
|
+
const result = await this.#appendInTransaction(entries, { options, scope, now });
|
|
3030
|
+
const appended = new Set(result.appendedEventIds);
|
|
3031
|
+
const published = entries.map((entry) => entry.event).filter((envelope) => appended.has(envelope.id));
|
|
3032
|
+
if (published.length > 0) {
|
|
3033
|
+
this.#notifier.notify("onPublished", {
|
|
3034
|
+
envelopes: published,
|
|
3035
|
+
deliveries: result.appendedDeliveryCount
|
|
3036
|
+
});
|
|
3037
|
+
if (delayMs === 0) {
|
|
3038
|
+
this.#scheduleWake();
|
|
2208
3039
|
}
|
|
2209
3040
|
}
|
|
2210
|
-
return entries.map((entry) => entry.event);
|
|
2211
3041
|
}
|
|
2212
3042
|
#reportUnrouted(envelope) {
|
|
2213
3043
|
const message = `No handler or route for event type, event not stored | type=${envelope.type} id=${envelope.id}`;
|
|
2214
3044
|
if (this.#unroutedEvents === "throw") {
|
|
2215
|
-
throw new ConfigurationError(message);
|
|
3045
|
+
throw new chunk3OHMEMZA_cjs.ConfigurationError(message);
|
|
2216
3046
|
}
|
|
2217
|
-
this.#logger.warn(`[RelayBus](
|
|
3047
|
+
this.#logger.warn(`[RelayBus](reportUnrouted) ${message}`);
|
|
2218
3048
|
}
|
|
2219
|
-
async #appendInTransaction(entries,
|
|
3049
|
+
async #appendInTransaction(entries, {
|
|
3050
|
+
options,
|
|
3051
|
+
scope,
|
|
3052
|
+
now
|
|
3053
|
+
}) {
|
|
2220
3054
|
const transaction = options.transaction ?? scope?.transaction ?? this.#transactionResolver?.();
|
|
2221
3055
|
if (transaction !== void 0) {
|
|
2222
3056
|
return this.#store.append(transaction, entries, now);
|
|
2223
3057
|
}
|
|
2224
3058
|
if (this.#transactionMode === "required") {
|
|
2225
|
-
throw new TransactionRequiredError(
|
|
3059
|
+
throw new chunk3OHMEMZA_cjs.TransactionRequiredError(
|
|
2226
3060
|
"publish found no transaction. Pass { transaction }, publish inside bus.transaction(), configure transactionResolver, or set transactionMode to optional"
|
|
2227
3061
|
);
|
|
2228
3062
|
}
|
|
@@ -2230,24 +3064,25 @@ var RelayBus = class {
|
|
|
2230
3064
|
(ownTransaction) => this.#store.append(ownTransaction, entries, now)
|
|
2231
3065
|
);
|
|
2232
3066
|
}
|
|
2233
|
-
async #createEnvelope(
|
|
3067
|
+
async #createEnvelope(input) {
|
|
3068
|
+
const { event, options, id, scope, now, correlationId } = input;
|
|
2234
3069
|
if (typeof event.type !== "string") {
|
|
2235
|
-
throw new ConfigurationError(
|
|
3070
|
+
throw new chunk3OHMEMZA_cjs.ConfigurationError(
|
|
2236
3071
|
"publish expects an instance of a class created with defineEvent"
|
|
2237
3072
|
);
|
|
2238
3073
|
}
|
|
2239
|
-
assertEventType(event.type);
|
|
3074
|
+
chunk3OHMEMZA_cjs.assertEventType(event.type);
|
|
2240
3075
|
const schema = event.constructor.schema;
|
|
2241
|
-
const payload = await validatePayload(event.type, schema, event.payload);
|
|
3076
|
+
const payload = await chunk3OHMEMZA_cjs.validatePayload(event.type, schema, event.payload);
|
|
2242
3077
|
if (options.key !== void 0 && options.key.length === 0) {
|
|
2243
|
-
throw new ConfigurationError("key must not be empty");
|
|
3078
|
+
throw new chunk3OHMEMZA_cjs.ConfigurationError("key must not be empty");
|
|
2244
3079
|
}
|
|
2245
3080
|
const eventId = id ?? this.#generateIdentifier();
|
|
2246
3081
|
return {
|
|
2247
3082
|
id: eventId,
|
|
2248
3083
|
type: event.type,
|
|
2249
3084
|
source: this.#source,
|
|
2250
|
-
key: options.key ??
|
|
3085
|
+
key: options.key ?? chunk3OHMEMZA_cjs.keyFromDefinition(event, payload),
|
|
2251
3086
|
payload,
|
|
2252
3087
|
headers: { ...options.headers },
|
|
2253
3088
|
correlationId: correlationId ?? eventId,
|
|
@@ -2263,17 +3098,26 @@ var RelayBus = class {
|
|
|
2263
3098
|
const routes = registrations.filter((registration) => registration.forwardsToTransport).length;
|
|
2264
3099
|
return { handlers: registrations.length - routes, routes };
|
|
2265
3100
|
}
|
|
2266
|
-
#
|
|
2267
|
-
const
|
|
2268
|
-
|
|
2269
|
-
|
|
3101
|
+
#registerRoutes(routes) {
|
|
3102
|
+
for (const route of routes) {
|
|
3103
|
+
this.#requireTransport(route.transport);
|
|
3104
|
+
}
|
|
3105
|
+
for (const group of groupRoutes(routes)) {
|
|
3106
|
+
this.#registerRoute(group);
|
|
3107
|
+
}
|
|
3108
|
+
}
|
|
3109
|
+
/** Registers one handler for every route to a destination, so a key's events reach it in commit order. */
|
|
3110
|
+
#registerRoute(group) {
|
|
3111
|
+
const transport = this.#transports.get(group.transport);
|
|
3112
|
+
const { route, destination } = group;
|
|
2270
3113
|
this.#registry.register(
|
|
2271
3114
|
{
|
|
2272
|
-
|
|
3115
|
+
eventTypes: group.eventTypes,
|
|
3116
|
+
legacyIds: group.legacyIds,
|
|
2273
3117
|
schema: void 0,
|
|
2274
|
-
handle: (envelope) => transport.publish(envelope, destination),
|
|
3118
|
+
handle: this.#observation?.sender(transport, destination) ?? ((envelope) => transport.publish(envelope, destination)),
|
|
2275
3119
|
options: {
|
|
2276
|
-
id:
|
|
3120
|
+
id: group.id,
|
|
2277
3121
|
...route.retry === void 0 ? {} : { retry: route.retry },
|
|
2278
3122
|
...route.timeout === void 0 ? {} : { timeout: route.timeout }
|
|
2279
3123
|
},
|
|
@@ -2285,7 +3129,7 @@ var RelayBus = class {
|
|
|
2285
3129
|
#requireTransport(name) {
|
|
2286
3130
|
const transport = this.#transports.get(name);
|
|
2287
3131
|
if (transport === void 0) {
|
|
2288
|
-
throw new ConfigurationError(`No transport named '${name}' is configured`);
|
|
3132
|
+
throw new chunk3OHMEMZA_cjs.ConfigurationError(`No transport named '${name}' is configured`);
|
|
2289
3133
|
}
|
|
2290
3134
|
return transport;
|
|
2291
3135
|
}
|
|
@@ -2293,24 +3137,30 @@ var RelayBus = class {
|
|
|
2293
3137
|
if (this.#state !== "running" || this.#dispatcher.watchesNewDeliveries) {
|
|
2294
3138
|
return;
|
|
2295
3139
|
}
|
|
2296
|
-
|
|
3140
|
+
chunk3OHMEMZA_cjs.schedule(this.#clock, {
|
|
3141
|
+
run: () => this.#dispatcher.wake(),
|
|
3142
|
+
delayMs: WAKE_DELAY_MS,
|
|
3143
|
+
unref: true
|
|
3144
|
+
});
|
|
2297
3145
|
}
|
|
2298
|
-
|
|
3146
|
+
/** Closes every transport, each at most until the deadline; past it they are asked to force. */
|
|
3147
|
+
async #closeTransports(deadline) {
|
|
3148
|
+
const options = { signal: deadline.signal };
|
|
2299
3149
|
for (const transport of this.#transports.values()) {
|
|
2300
3150
|
try {
|
|
2301
|
-
await transport.close();
|
|
3151
|
+
await deadline.race(transport.close(options));
|
|
2302
3152
|
} catch (error) {
|
|
2303
3153
|
this.#logger.error(
|
|
2304
|
-
`[RelayBus](closeTransports) Failed to close transport | transport=${transport.name} error=${toErrorInfo(error).message}`
|
|
3154
|
+
`[RelayBus](closeTransports) Failed to close transport | transport=${transport.name} error=${chunk3OHMEMZA_cjs.toErrorInfo(error).message}`
|
|
2305
3155
|
);
|
|
2306
3156
|
}
|
|
2307
3157
|
}
|
|
2308
3158
|
}
|
|
2309
3159
|
};
|
|
2310
|
-
function toDescriptor(registration) {
|
|
3160
|
+
function toDescriptor(registration, eventType) {
|
|
2311
3161
|
return {
|
|
2312
3162
|
id: registration.id,
|
|
2313
|
-
eventType
|
|
3163
|
+
eventType,
|
|
2314
3164
|
retry: registration.retry,
|
|
2315
3165
|
timeoutMs: registration.timeoutMs,
|
|
2316
3166
|
concurrency: registration.concurrency,
|
|
@@ -2320,136 +3170,102 @@ function toDescriptor(registration) {
|
|
|
2320
3170
|
}
|
|
2321
3171
|
function describeEvent(event) {
|
|
2322
3172
|
if (typeof event === "string") {
|
|
2323
|
-
assertEventType(event);
|
|
3173
|
+
chunk3OHMEMZA_cjs.assertEventType(event);
|
|
2324
3174
|
return { type: event, schema: void 0 };
|
|
2325
3175
|
}
|
|
2326
|
-
assertEventType(event.type);
|
|
3176
|
+
chunk3OHMEMZA_cjs.assertEventType(event.type);
|
|
2327
3177
|
return { type: event.type, schema: event.schema };
|
|
2328
3178
|
}
|
|
2329
|
-
function normalizeEnvelope(envelope) {
|
|
2330
|
-
if (typeof envelope.id !== "string" || envelope.id.length === 0) {
|
|
2331
|
-
throw new InvalidEnvelopeError("Received event has no id");
|
|
2332
|
-
}
|
|
2333
|
-
if (typeof envelope.type !== "string") {
|
|
2334
|
-
throw new InvalidEnvelopeError(`Received event ${envelope.id} has no type`);
|
|
2335
|
-
}
|
|
2336
|
-
try {
|
|
2337
|
-
assertEventType(envelope.type);
|
|
2338
|
-
} catch (error) {
|
|
2339
|
-
throw new InvalidEnvelopeError(toErrorInfo(error).message);
|
|
2340
|
-
}
|
|
2341
|
-
const occurredAt = toDate(envelope.occurredAt);
|
|
2342
|
-
if (occurredAt === void 0) {
|
|
2343
|
-
throw new InvalidEnvelopeError(`Received event ${envelope.id} has an invalid occurredAt`);
|
|
2344
|
-
}
|
|
2345
|
-
return {
|
|
2346
|
-
id: envelope.id,
|
|
2347
|
-
type: envelope.type,
|
|
2348
|
-
source: typeof envelope.source === "string" ? envelope.source : "unknown",
|
|
2349
|
-
key: optionalText(envelope.key),
|
|
2350
|
-
payload: envelope.payload,
|
|
2351
|
-
headers: { ...envelope.headers },
|
|
2352
|
-
correlationId: optionalText(envelope.correlationId),
|
|
2353
|
-
causationId: optionalText(envelope.causationId),
|
|
2354
|
-
occurredAt
|
|
2355
|
-
};
|
|
2356
|
-
}
|
|
2357
|
-
function toDate(value) {
|
|
2358
|
-
const date = typeof value === "string" ? new Date(value) : value;
|
|
2359
|
-
return date instanceof Date && !Number.isNaN(date.getTime()) ? date : void 0;
|
|
2360
|
-
}
|
|
2361
|
-
function optionalText(value) {
|
|
2362
|
-
return typeof value === "string" && value.length > 0 ? value : null;
|
|
2363
|
-
}
|
|
2364
|
-
function oneOf(value, allowed, name) {
|
|
2365
|
-
if (!allowed.includes(value)) {
|
|
2366
|
-
throw new ConfigurationError(
|
|
2367
|
-
`${name} must be one of ${allowed.map((option) => `'${option}'`).join(", ")}, received '${String(value)}'`
|
|
2368
|
-
);
|
|
2369
|
-
}
|
|
2370
|
-
return value;
|
|
2371
|
-
}
|
|
2372
|
-
function positiveInteger(value, name) {
|
|
2373
|
-
if (!Number.isInteger(value) || value < 1) {
|
|
2374
|
-
throw new ConfigurationError(`${name} must be a positive integer, received ${value}`);
|
|
2375
|
-
}
|
|
2376
|
-
return value;
|
|
2377
|
-
}
|
|
2378
|
-
function createDispatcherSettings(options, generateIdentifier) {
|
|
2379
|
-
const concurrency = positiveInteger(
|
|
2380
|
-
options?.concurrency ?? DEFAULTS.concurrency,
|
|
2381
|
-
"dispatcher.concurrency"
|
|
2382
|
-
);
|
|
2383
|
-
return {
|
|
2384
|
-
owner: `${os.hostname()}:${process.pid}:${generateIdentifier()}`,
|
|
2385
|
-
concurrency,
|
|
2386
|
-
batchSize: positiveInteger(options?.batchSize ?? concurrency, "dispatcher.batchSize"),
|
|
2387
|
-
pollIntervalMs: toTimerMilliseconds(
|
|
2388
|
-
options?.pollInterval ?? DEFAULTS.pollInterval,
|
|
2389
|
-
"dispatcher.pollInterval"
|
|
2390
|
-
),
|
|
2391
|
-
leaseDurationMs: toTimerMilliseconds(
|
|
2392
|
-
options?.leaseDuration ?? DEFAULTS.leaseDuration,
|
|
2393
|
-
"dispatcher.leaseDuration"
|
|
2394
|
-
),
|
|
2395
|
-
shutdownTimeoutMs: toTimerMilliseconds(
|
|
2396
|
-
options?.shutdownTimeout ?? DEFAULTS.shutdownTimeout,
|
|
2397
|
-
"dispatcher.shutdownTimeout"
|
|
2398
|
-
),
|
|
2399
|
-
completionBatchWindowMs: toMilliseconds(
|
|
2400
|
-
options?.completionBatchWindow ?? DEFAULTS.completionBatchWindow,
|
|
2401
|
-
"dispatcher.completionBatchWindow"
|
|
2402
|
-
),
|
|
2403
|
-
completionBatchSize: positiveInteger(
|
|
2404
|
-
options?.completionBatchSize ?? DEFAULTS.completionBatchSize,
|
|
2405
|
-
"dispatcher.completionBatchSize"
|
|
2406
|
-
)
|
|
2407
|
-
};
|
|
2408
|
-
}
|
|
2409
|
-
function createRetentionSettings(options) {
|
|
2410
|
-
const completed = options?.completed ?? DEFAULTS.completedRetention;
|
|
2411
|
-
const dead = options?.dead ?? false;
|
|
2412
|
-
return {
|
|
2413
|
-
completedMs: completed === false ? null : toMilliseconds(completed, "retention.completed"),
|
|
2414
|
-
deadMs: dead === false ? null : toMilliseconds(dead, "retention.dead"),
|
|
2415
|
-
intervalMs: toTimerMilliseconds(
|
|
2416
|
-
options?.interval ?? DEFAULTS.retentionInterval,
|
|
2417
|
-
"retention.interval"
|
|
2418
|
-
)
|
|
2419
|
-
};
|
|
2420
|
-
}
|
|
2421
3179
|
|
|
3180
|
+
Object.defineProperty(exports, "AttemptsExhaustedError", {
|
|
3181
|
+
enumerable: true,
|
|
3182
|
+
get: function () { return chunk3OHMEMZA_cjs.AttemptsExhaustedError; }
|
|
3183
|
+
});
|
|
3184
|
+
Object.defineProperty(exports, "ConfigurationError", {
|
|
3185
|
+
enumerable: true,
|
|
3186
|
+
get: function () { return chunk3OHMEMZA_cjs.ConfigurationError; }
|
|
3187
|
+
});
|
|
3188
|
+
Object.defineProperty(exports, "DispatcherStoppedError", {
|
|
3189
|
+
enumerable: true,
|
|
3190
|
+
get: function () { return chunk3OHMEMZA_cjs.DispatcherStoppedError; }
|
|
3191
|
+
});
|
|
3192
|
+
Object.defineProperty(exports, "DrainIncompleteError", {
|
|
3193
|
+
enumerable: true,
|
|
3194
|
+
get: function () { return chunk3OHMEMZA_cjs.DrainIncompleteError; }
|
|
3195
|
+
});
|
|
3196
|
+
Object.defineProperty(exports, "EventValidationError", {
|
|
3197
|
+
enumerable: true,
|
|
3198
|
+
get: function () { return chunk3OHMEMZA_cjs.EventValidationError; }
|
|
3199
|
+
});
|
|
3200
|
+
Object.defineProperty(exports, "HandlerTimeoutError", {
|
|
3201
|
+
enumerable: true,
|
|
3202
|
+
get: function () { return chunk3OHMEMZA_cjs.HandlerTimeoutError; }
|
|
3203
|
+
});
|
|
3204
|
+
Object.defineProperty(exports, "InvalidEnvelopeError", {
|
|
3205
|
+
enumerable: true,
|
|
3206
|
+
get: function () { return chunk3OHMEMZA_cjs.InvalidEnvelopeError; }
|
|
3207
|
+
});
|
|
3208
|
+
Object.defineProperty(exports, "LeaseLostError", {
|
|
3209
|
+
enumerable: true,
|
|
3210
|
+
get: function () { return chunk3OHMEMZA_cjs.LeaseLostError; }
|
|
3211
|
+
});
|
|
3212
|
+
Object.defineProperty(exports, "LifecycleError", {
|
|
3213
|
+
enumerable: true,
|
|
3214
|
+
get: function () { return chunk3OHMEMZA_cjs.LifecycleError; }
|
|
3215
|
+
});
|
|
3216
|
+
Object.defineProperty(exports, "NonRetryableError", {
|
|
3217
|
+
enumerable: true,
|
|
3218
|
+
get: function () { return chunk3OHMEMZA_cjs.NonRetryableError; }
|
|
3219
|
+
});
|
|
3220
|
+
Object.defineProperty(exports, "RelayBusError", {
|
|
3221
|
+
enumerable: true,
|
|
3222
|
+
get: function () { return chunk3OHMEMZA_cjs.RelayBusError; }
|
|
3223
|
+
});
|
|
3224
|
+
Object.defineProperty(exports, "TransactionRequiredError", {
|
|
3225
|
+
enumerable: true,
|
|
3226
|
+
get: function () { return chunk3OHMEMZA_cjs.TransactionRequiredError; }
|
|
3227
|
+
});
|
|
3228
|
+
Object.defineProperty(exports, "TransactionTimeoutError", {
|
|
3229
|
+
enumerable: true,
|
|
3230
|
+
get: function () { return chunk3OHMEMZA_cjs.TransactionTimeoutError; }
|
|
3231
|
+
});
|
|
3232
|
+
Object.defineProperty(exports, "assertEventType", {
|
|
3233
|
+
enumerable: true,
|
|
3234
|
+
get: function () { return chunk3OHMEMZA_cjs.assertEventType; }
|
|
3235
|
+
});
|
|
3236
|
+
Object.defineProperty(exports, "defineEvent", {
|
|
3237
|
+
enumerable: true,
|
|
3238
|
+
get: function () { return chunk3OHMEMZA_cjs.defineEvent; }
|
|
3239
|
+
});
|
|
3240
|
+
Object.defineProperty(exports, "toErrorInfo", {
|
|
3241
|
+
enumerable: true,
|
|
3242
|
+
get: function () { return chunk3OHMEMZA_cjs.toErrorInfo; }
|
|
3243
|
+
});
|
|
3244
|
+
Object.defineProperty(exports, "validatePayload", {
|
|
3245
|
+
enumerable: true,
|
|
3246
|
+
get: function () { return chunk3OHMEMZA_cjs.validatePayload; }
|
|
3247
|
+
});
|
|
2422
3248
|
exports.CatalogPublisher = CatalogPublisher;
|
|
2423
|
-
exports.ConfigurationError = ConfigurationError;
|
|
2424
3249
|
exports.DEFAULT_RETRY_POLICY = DEFAULT_RETRY_POLICY;
|
|
2425
3250
|
exports.DEFAULT_ROUTE_RETRY_POLICY = DEFAULT_ROUTE_RETRY_POLICY;
|
|
2426
3251
|
exports.DELIVERY_STATUSES = DELIVERY_STATUSES;
|
|
2427
|
-
exports.DispatcherStoppedError = DispatcherStoppedError;
|
|
2428
|
-
exports.EventValidationError = EventValidationError;
|
|
2429
|
-
exports.HandlerTimeoutError = HandlerTimeoutError;
|
|
2430
3252
|
exports.InMemoryEventStore = InMemoryEventStore;
|
|
2431
3253
|
exports.InMemoryTransaction = InMemoryTransaction;
|
|
2432
|
-
exports.InvalidEnvelopeError = InvalidEnvelopeError;
|
|
2433
|
-
exports.LeaseLostError = LeaseLostError;
|
|
2434
3254
|
exports.MAX_TIMER_DELAY_MS = MAX_TIMER_DELAY_MS;
|
|
2435
|
-
exports.NonRetryableError = NonRetryableError;
|
|
2436
3255
|
exports.RelayBus = RelayBus;
|
|
2437
|
-
exports.
|
|
2438
|
-
exports.
|
|
2439
|
-
exports.assertEventType = assertEventType;
|
|
3256
|
+
exports.TRACEPARENT_HEADER = TRACEPARENT_HEADER;
|
|
3257
|
+
exports.TRACESTATE_HEADER = TRACESTATE_HEADER;
|
|
2440
3258
|
exports.computeRetryDelay = computeRetryDelay;
|
|
2441
3259
|
exports.consume = consume;
|
|
2442
3260
|
exports.createRetryPolicy = createRetryPolicy;
|
|
2443
3261
|
exports.createUuidV7 = createUuidV7;
|
|
2444
3262
|
exports.defineCatalog = defineCatalog;
|
|
2445
|
-
exports.defineEvent = defineEvent;
|
|
2446
3263
|
exports.defineHandlers = defineHandlers;
|
|
2447
3264
|
exports.describeCatalog = describeCatalog;
|
|
2448
3265
|
exports.isRetryableError = isRetryableError;
|
|
2449
3266
|
exports.produce = produce;
|
|
2450
3267
|
exports.systemClock = systemClock;
|
|
2451
|
-
exports.toErrorInfo = toErrorInfo;
|
|
2452
3268
|
exports.toMilliseconds = toMilliseconds;
|
|
2453
|
-
exports.
|
|
3269
|
+
exports.traceContextOf = traceContextOf;
|
|
2454
3270
|
//# sourceMappingURL=index.cjs.map
|
|
2455
3271
|
//# sourceMappingURL=index.cjs.map
|