@relaybus/core 0.3.0 → 0.4.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 +6 -1
- package/dist/{chunk-3OHMEMZA.cjs → chunk-J76M7XTS.cjs} +127 -9
- package/dist/chunk-J76M7XTS.cjs.map +1 -0
- package/dist/{chunk-WS274DL3.js → chunk-NOR5JHVN.js} +121 -9
- package/dist/chunk-NOR5JHVN.js.map +1 -0
- package/dist/index.cjs +2301 -1903
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +88 -475
- package/dist/index.d.ts +88 -475
- package/dist/index.js +2316 -1924
- package/dist/index.js.map +1 -1
- package/dist/internal/index.cjs +492 -140
- package/dist/internal/index.cjs.map +1 -1
- package/dist/internal/index.d.cts +321 -9
- package/dist/internal/index.d.ts +321 -9
- package/dist/internal/index.js +450 -124
- package/dist/internal/index.js.map +1 -1
- package/dist/transport-DIFdeXQY.d.cts +536 -0
- package/dist/transport-DIFdeXQY.d.ts +536 -0
- package/package.json +1 -1
- package/dist/chunk-3OHMEMZA.cjs.map +0 -1
- package/dist/chunk-WS274DL3.js.map +0 -1
- package/dist/event-DCRTuYKI.d.cts +0 -142
- package/dist/event-DCRTuYKI.d.ts +0 -142
package/dist/internal/index.js
CHANGED
|
@@ -1,5 +1,10 @@
|
|
|
1
|
-
import { ConfigurationError, assertEventType } from '../chunk-
|
|
2
|
-
export {
|
|
1
|
+
import { PositiveInteger, ConfigurationError, assertEventType, DeliveryLeaseIdentity } from '../chunk-NOR5JHVN.js';
|
|
2
|
+
export { CompositeKey, DeliveryLeaseIdentity, LeaseSettlement, Milliseconds, PositiveInteger, schedule, sleep } from '../chunk-NOR5JHVN.js';
|
|
3
|
+
|
|
4
|
+
// src/internal/validation.ts
|
|
5
|
+
function assertPositiveInteger(value, name) {
|
|
6
|
+
return PositiveInteger.of(value, name);
|
|
7
|
+
}
|
|
3
8
|
|
|
4
9
|
// src/internal/field-extractors.ts
|
|
5
10
|
function fromHeader(name) {
|
|
@@ -84,7 +89,7 @@ function writeStructuredCloudEvent(envelope, { time, data }) {
|
|
|
84
89
|
return `${JSON.stringify(attributes).slice(0, -1)},"data":${data}}`;
|
|
85
90
|
}
|
|
86
91
|
|
|
87
|
-
// src/internal/json-envelope.ts
|
|
92
|
+
// src/internal/json-envelope-plan.ts
|
|
88
93
|
var OPTIONAL_NAMES = [
|
|
89
94
|
"key",
|
|
90
95
|
"source",
|
|
@@ -92,19 +97,8 @@ var OPTIONAL_NAMES = [
|
|
|
92
97
|
"correlationId",
|
|
93
98
|
"causationId"
|
|
94
99
|
];
|
|
95
|
-
var
|
|
96
|
-
var RFC3339_PATTERN = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d+)?(?:Z|[+-]\d{2}:\d{2})$/i;
|
|
100
|
+
var TIME_FORMATS = ["rfc3339", "epoch-millis", "epoch-seconds"];
|
|
97
101
|
var ARRAY_INDEX = /^(?:0|[1-9]\d*)$/;
|
|
98
|
-
function createJsonEnvelopeFormat(mapping, {
|
|
99
|
-
transportIds,
|
|
100
|
-
failures
|
|
101
|
-
}) {
|
|
102
|
-
const plan = compile(mapping, transportIds);
|
|
103
|
-
return {
|
|
104
|
-
write: (envelope) => writeMessage(envelope, { plan, failures }),
|
|
105
|
-
read: (message) => readMessage(message, { plan, failures })
|
|
106
|
-
};
|
|
107
|
-
}
|
|
108
102
|
function compile(mapping, transportIds) {
|
|
109
103
|
if (!isJsonObject(mapping)) {
|
|
110
104
|
throw new ConfigurationError("jsonEnvelope needs a mapping object");
|
|
@@ -116,16 +110,55 @@ function compile(mapping, transportIds) {
|
|
|
116
110
|
optional[name] = toLocation(field, name);
|
|
117
111
|
}
|
|
118
112
|
}
|
|
113
|
+
const payload = compilePayload(mapping.payload);
|
|
114
|
+
const topLevelHeaders = mapping.headers === "top-level-fields";
|
|
115
|
+
if (topLevelHeaders && payload === void 0) {
|
|
116
|
+
throw new ConfigurationError("jsonEnvelope headers 'top-level-fields' needs a body payload");
|
|
117
|
+
}
|
|
119
118
|
const plan = {
|
|
120
119
|
type: compileType(mapping.type),
|
|
121
120
|
id: compileId(mapping.id, transportIds),
|
|
121
|
+
idWhenMissing: isJsonObject(mapping.id) && "whenMissing" in mapping.id,
|
|
122
122
|
optional,
|
|
123
|
-
payload
|
|
124
|
-
headers: compileHeaders(mapping.headers),
|
|
125
|
-
|
|
123
|
+
payload,
|
|
124
|
+
headers: topLevelHeaders ? void 0 : compileHeaders(mapping.headers),
|
|
125
|
+
topLevelHeaders,
|
|
126
|
+
topLevelNames: /* @__PURE__ */ new Set(),
|
|
127
|
+
mappedHeaders: /* @__PURE__ */ new Set(),
|
|
128
|
+
timeFormat: compileTimeFormat(mapping.occurredAt),
|
|
129
|
+
...compileExtras(mapping, payload !== void 0)
|
|
126
130
|
};
|
|
127
131
|
return assertDisjoint(plan);
|
|
128
132
|
}
|
|
133
|
+
function compileTimeFormat(field) {
|
|
134
|
+
const format = isJsonObject(field) ? field["format"] : void 0;
|
|
135
|
+
if (format === void 0) {
|
|
136
|
+
return "rfc3339";
|
|
137
|
+
}
|
|
138
|
+
if (!TIME_FORMATS.includes(format)) {
|
|
139
|
+
throw new ConfigurationError(
|
|
140
|
+
`jsonEnvelope occurredAt format must be one of ${TIME_FORMATS.map((name) => `'${name}'`).join(", ")}`
|
|
141
|
+
);
|
|
142
|
+
}
|
|
143
|
+
return format;
|
|
144
|
+
}
|
|
145
|
+
function compileExtras(mapping, hasBody) {
|
|
146
|
+
const extra = mapping.extra ?? {};
|
|
147
|
+
const bodyOrder = mapping.bodyOrder ?? [];
|
|
148
|
+
if (!hasBody && (extra.body !== void 0 || bodyOrder.length > 0)) {
|
|
149
|
+
throw new ConfigurationError(
|
|
150
|
+
"jsonEnvelope with a whole-body payload has no body for extra values or a body order"
|
|
151
|
+
);
|
|
152
|
+
}
|
|
153
|
+
return {
|
|
154
|
+
extraBody: Object.entries(extra.body ?? {}).map(([path, value]) => ({
|
|
155
|
+
path: toPath(path, "extra body value"),
|
|
156
|
+
value
|
|
157
|
+
})),
|
|
158
|
+
extraHeaders: Object.entries(extra.headers ?? {}),
|
|
159
|
+
bodyOrder
|
|
160
|
+
};
|
|
161
|
+
}
|
|
129
162
|
function compileType(type) {
|
|
130
163
|
if (isJsonObject(type) && typeof type["fixed"] === "string") {
|
|
131
164
|
assertEventType(type["fixed"]);
|
|
@@ -140,11 +173,15 @@ function compileId(id, transportIds) {
|
|
|
140
173
|
if (typeof id === "string" && transportIds.includes(id)) {
|
|
141
174
|
return void 0;
|
|
142
175
|
}
|
|
176
|
+
const names = transportIds.map((name) => `'${name}'`).join(", ");
|
|
143
177
|
if (!isJsonObject(id)) {
|
|
144
178
|
throw new ConfigurationError(
|
|
145
|
-
`jsonEnvelope needs an id: { body }, { header } or one of ${
|
|
179
|
+
`jsonEnvelope needs an id: { body }, { header } or one of ${names}`
|
|
146
180
|
);
|
|
147
181
|
}
|
|
182
|
+
if ("whenMissing" in id && !transportIds.includes(id["whenMissing"])) {
|
|
183
|
+
throw new ConfigurationError(`jsonEnvelope id whenMissing must be one of ${names}`);
|
|
184
|
+
}
|
|
148
185
|
return toLocation(id, "id");
|
|
149
186
|
}
|
|
150
187
|
function compilePayload(payload) {
|
|
@@ -193,17 +230,25 @@ function assertDisjoint(plan) {
|
|
|
193
230
|
const locations = [plan.type, plan.id, ...Object.values(plan.optional)].filter(
|
|
194
231
|
(location) => location !== void 0 && !("fixed" in location)
|
|
195
232
|
);
|
|
196
|
-
const headers =
|
|
233
|
+
const headers = [
|
|
234
|
+
...locations.flatMap((location) => "header" in location ? [location.header] : []),
|
|
235
|
+
...plan.extraHeaders.map(([name]) => name)
|
|
236
|
+
];
|
|
197
237
|
const duplicate = headers.find((name, index) => headers.indexOf(name) !== index);
|
|
198
238
|
if (duplicate !== void 0) {
|
|
199
239
|
throw new ConfigurationError(`jsonEnvelope maps header '${duplicate}' twice`);
|
|
200
240
|
}
|
|
201
241
|
const paths = [
|
|
202
242
|
...locations.flatMap((location) => "path" in location ? [location.path] : []),
|
|
203
|
-
...[plan.payload, plan.headers].filter((path) => path !== void 0)
|
|
243
|
+
...[plan.payload, plan.headers].filter((path) => path !== void 0),
|
|
244
|
+
...plan.extraBody.map(({ path }) => path)
|
|
204
245
|
];
|
|
205
246
|
assertNoNestedPaths(paths);
|
|
206
|
-
return {
|
|
247
|
+
return {
|
|
248
|
+
...plan,
|
|
249
|
+
mappedHeaders: new Set(headers),
|
|
250
|
+
topLevelNames: new Set(paths.map((path) => path[0]))
|
|
251
|
+
};
|
|
207
252
|
}
|
|
208
253
|
function assertNoNestedPaths(paths) {
|
|
209
254
|
paths.forEach((path, index) => {
|
|
@@ -222,44 +267,198 @@ function assertCompatiblePaths(path, other) {
|
|
|
222
267
|
throw new ConfigurationError(`jsonEnvelope body paths ${both} mix an array index and a name`);
|
|
223
268
|
}
|
|
224
269
|
}
|
|
270
|
+
|
|
271
|
+
// src/internal/json-envelope-read.ts
|
|
272
|
+
var RFC3339_PATTERN = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d+)?(?:Z|[+-]\d{2}:\d{2})$/i;
|
|
273
|
+
var EPOCH_PATTERN = /^\d+$/;
|
|
274
|
+
var MILLISECONDS_PER_SECOND = 1e3;
|
|
275
|
+
function readMessage(message, context) {
|
|
276
|
+
const { plan, failures } = context;
|
|
277
|
+
if (plan.payload !== void 0 && !isJsonObject(message.body)) {
|
|
278
|
+
throw failures.invalid("The message body must be a JSON object");
|
|
279
|
+
}
|
|
280
|
+
const read = (location) => location === void 0 ? void 0 : readText(message, { location, failures });
|
|
281
|
+
const fields = {
|
|
282
|
+
id: readId(read(plan.id), context),
|
|
283
|
+
type: readType(message, context),
|
|
284
|
+
payload: readPayload(message.body, context),
|
|
285
|
+
key: read(plan.optional.key),
|
|
286
|
+
source: read(plan.optional.source),
|
|
287
|
+
headers: readEventHeaders(message, plan),
|
|
288
|
+
correlationId: read(plan.optional.correlationId) ?? null,
|
|
289
|
+
causationId: read(plan.optional.causationId) ?? null,
|
|
290
|
+
occurredAt: readTime(read(plan.optional.occurredAt), context)
|
|
291
|
+
};
|
|
292
|
+
return Object.fromEntries(
|
|
293
|
+
Object.entries(fields).filter((entry) => entry[1] !== void 0)
|
|
294
|
+
);
|
|
295
|
+
}
|
|
296
|
+
function readId(id, { plan, failures }) {
|
|
297
|
+
if (plan.id === void 0 || plan.idWhenMissing) {
|
|
298
|
+
return id;
|
|
299
|
+
}
|
|
300
|
+
return requireText(id, { location: plan.id, failures });
|
|
301
|
+
}
|
|
302
|
+
function readType(message, { plan, failures }) {
|
|
303
|
+
if ("fixed" in plan.type) {
|
|
304
|
+
return plan.type.fixed;
|
|
305
|
+
}
|
|
306
|
+
const location = plan.type;
|
|
307
|
+
const type = requireText(readText(message, { location, failures }), { location, failures });
|
|
308
|
+
try {
|
|
309
|
+
assertEventType(type);
|
|
310
|
+
} catch (error) {
|
|
311
|
+
throw failures.invalid(`The event type is not a valid event type: ${error.message}`);
|
|
312
|
+
}
|
|
313
|
+
return type;
|
|
314
|
+
}
|
|
315
|
+
function readPayload(body, { plan, failures }) {
|
|
316
|
+
if (plan.payload === void 0) {
|
|
317
|
+
return body;
|
|
318
|
+
}
|
|
319
|
+
const payload = valueAt(body, plan.payload);
|
|
320
|
+
if (payload === void 0) {
|
|
321
|
+
throw failures.invalid(`The message has no payload at '${plan.payload.join(".")}'`);
|
|
322
|
+
}
|
|
323
|
+
return payload;
|
|
324
|
+
}
|
|
325
|
+
function readEventHeaders(message, plan) {
|
|
326
|
+
if (plan.topLevelHeaders) {
|
|
327
|
+
return textEntries(
|
|
328
|
+
Object.entries(message.body).filter(
|
|
329
|
+
([name]) => !plan.topLevelNames.has(name)
|
|
330
|
+
)
|
|
331
|
+
);
|
|
332
|
+
}
|
|
333
|
+
if (plan.headers === void 0) {
|
|
334
|
+
return Object.fromEntries(
|
|
335
|
+
Object.entries(message.headers).filter(([name]) => !plan.mappedHeaders.has(name))
|
|
336
|
+
);
|
|
337
|
+
}
|
|
338
|
+
const headers = valueAt(message.body, plan.headers);
|
|
339
|
+
return isJsonObject(headers) ? textEntries(Object.entries(headers)) : {};
|
|
340
|
+
}
|
|
341
|
+
function textEntries(entries) {
|
|
342
|
+
return Object.fromEntries(
|
|
343
|
+
entries.filter((entry) => typeof entry[1] === "string")
|
|
344
|
+
);
|
|
345
|
+
}
|
|
346
|
+
function readTime(text, { plan, failures }) {
|
|
347
|
+
if (text === void 0) {
|
|
348
|
+
return void 0;
|
|
349
|
+
}
|
|
350
|
+
if (plan.timeFormat !== "rfc3339") {
|
|
351
|
+
const time2 = new Date(
|
|
352
|
+
Number(text) * (plan.timeFormat === "epoch-seconds" ? MILLISECONDS_PER_SECOND : 1)
|
|
353
|
+
);
|
|
354
|
+
if (!EPOCH_PATTERN.test(text) || Number.isNaN(time2.getTime())) {
|
|
355
|
+
throw failures.invalid(`The event time is not a whole ${plan.timeFormat} number: '${text}'`);
|
|
356
|
+
}
|
|
357
|
+
return time2;
|
|
358
|
+
}
|
|
359
|
+
const time = new Date(text);
|
|
360
|
+
if (!RFC3339_PATTERN.test(text) || Number.isNaN(time.getTime())) {
|
|
361
|
+
throw failures.invalid(`The event time is not an RFC 3339 timestamp: '${text}'`);
|
|
362
|
+
}
|
|
363
|
+
return time;
|
|
364
|
+
}
|
|
365
|
+
function readText(message, { location, failures }) {
|
|
366
|
+
const value = "header" in location ? Object.hasOwn(message.headers, location.header) ? message.headers[location.header] : void 0 : valueAt(message.body, location.path);
|
|
367
|
+
if (value === void 0 || value === null || value === "") {
|
|
368
|
+
return void 0;
|
|
369
|
+
}
|
|
370
|
+
if (typeof value === "string") {
|
|
371
|
+
return value;
|
|
372
|
+
}
|
|
373
|
+
if (typeof value === "number" && Number.isSafeInteger(value)) {
|
|
374
|
+
return String(value);
|
|
375
|
+
}
|
|
376
|
+
throw failures.invalid(`The ${location.label} at ${describe(location)} must be a string`);
|
|
377
|
+
}
|
|
378
|
+
function requireText(value, { location, failures }) {
|
|
379
|
+
if (value === void 0) {
|
|
380
|
+
throw failures.invalid(`The message has no ${location.label} at ${describe(location)}`);
|
|
381
|
+
}
|
|
382
|
+
return value;
|
|
383
|
+
}
|
|
384
|
+
function describe(location) {
|
|
385
|
+
return "header" in location ? `header '${location.header}'` : `'${location.path.join(".")}'`;
|
|
386
|
+
}
|
|
387
|
+
function valueAt(body, path) {
|
|
388
|
+
let value = body;
|
|
389
|
+
for (const segment of path) {
|
|
390
|
+
if (typeof value !== "object" || value === null || !Object.hasOwn(value, segment)) {
|
|
391
|
+
return void 0;
|
|
392
|
+
}
|
|
393
|
+
value = value[segment];
|
|
394
|
+
}
|
|
395
|
+
return value;
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
// src/internal/json-envelope-write.ts
|
|
399
|
+
var MAXIMUM_RFC3339_YEAR = 9999;
|
|
400
|
+
var MILLISECONDS_PER_SECOND2 = 1e3;
|
|
225
401
|
function writeMessage(envelope, { plan, failures }) {
|
|
226
402
|
const root = {};
|
|
227
403
|
const headers = [];
|
|
228
404
|
const put = (location, value) => {
|
|
229
405
|
if ("header" in location) {
|
|
230
|
-
headers.push([location.header, value]);
|
|
406
|
+
headers.push([location.header, String(value)]);
|
|
231
407
|
} else {
|
|
232
408
|
setAt(root, { path: location.path, value });
|
|
233
409
|
}
|
|
234
410
|
};
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
`Event ${envelope.id} has type '${envelope.type}', but this route carries only '${plan.type.fixed}'`
|
|
239
|
-
);
|
|
240
|
-
}
|
|
241
|
-
} else {
|
|
242
|
-
put(plan.type, envelope.type);
|
|
411
|
+
const typeAt = typeLocation(envelope, { plan, failures });
|
|
412
|
+
if (typeAt !== void 0) {
|
|
413
|
+
put(typeAt, envelope.type);
|
|
243
414
|
}
|
|
244
415
|
if (plan.id !== void 0) {
|
|
245
416
|
put(plan.id, envelope.id);
|
|
246
417
|
}
|
|
247
|
-
for (const [name, value] of optionalValues(envelope, failures)) {
|
|
418
|
+
for (const [name, value] of optionalValues(envelope, { plan, failures })) {
|
|
248
419
|
const location = plan.optional[name];
|
|
249
420
|
if (location !== void 0 && value !== null) {
|
|
250
421
|
put(location, value);
|
|
251
422
|
}
|
|
252
423
|
}
|
|
424
|
+
const body = writeBody(envelope, { plan, root });
|
|
425
|
+
if (plan.topLevelHeaders) {
|
|
426
|
+
spreadHeaders(envelope, { plan, failures, root: body });
|
|
427
|
+
}
|
|
253
428
|
return {
|
|
254
|
-
body
|
|
255
|
-
headers: Object.fromEntries([
|
|
429
|
+
body,
|
|
430
|
+
headers: Object.fromEntries([
|
|
431
|
+
...eventHeaderEntries(envelope, { plan, failures }),
|
|
432
|
+
...headers,
|
|
433
|
+
...extraHeaderEntries(envelope, plan)
|
|
434
|
+
])
|
|
256
435
|
};
|
|
257
436
|
}
|
|
258
|
-
function
|
|
437
|
+
function typeLocation(envelope, { plan, failures }) {
|
|
438
|
+
if (!("fixed" in plan.type)) {
|
|
439
|
+
return plan.type;
|
|
440
|
+
}
|
|
441
|
+
if (envelope.type !== plan.type.fixed) {
|
|
442
|
+
throw failures.unencodable(
|
|
443
|
+
`Event ${envelope.id} has type '${envelope.type}', but this route carries only '${plan.type.fixed}'`
|
|
444
|
+
);
|
|
445
|
+
}
|
|
446
|
+
return void 0;
|
|
447
|
+
}
|
|
448
|
+
function extraHeaderEntries(envelope, plan) {
|
|
449
|
+
return plan.extraHeaders.map(([name, value]) => [
|
|
450
|
+
name,
|
|
451
|
+
typeof value === "function" ? value(envelope) : value
|
|
452
|
+
]);
|
|
453
|
+
}
|
|
454
|
+
function optionalValues(envelope, { plan, failures }) {
|
|
259
455
|
return [
|
|
260
456
|
["key", envelope.key],
|
|
261
457
|
["source", envelope.source],
|
|
262
|
-
[
|
|
458
|
+
[
|
|
459
|
+
"occurredAt",
|
|
460
|
+
timeValue(formatTime(envelope, failures), { envelope, format: plan.timeFormat })
|
|
461
|
+
],
|
|
263
462
|
["correlationId", envelope.correlationId],
|
|
264
463
|
["causationId", envelope.causationId]
|
|
265
464
|
];
|
|
@@ -275,10 +474,43 @@ function writeBody(envelope, { plan, root }) {
|
|
|
275
474
|
value: Object.fromEntries(Object.entries(envelope.headers))
|
|
276
475
|
});
|
|
277
476
|
}
|
|
278
|
-
|
|
477
|
+
for (const { path, value } of plan.extraBody) {
|
|
478
|
+
setAt(root, { path, value: typeof value === "function" ? value(envelope) : value });
|
|
479
|
+
}
|
|
480
|
+
return ordered(root, plan.bodyOrder);
|
|
481
|
+
}
|
|
482
|
+
function spreadHeaders(envelope, { plan, failures, root }) {
|
|
483
|
+
for (const [name, value] of Object.entries(envelope.headers)) {
|
|
484
|
+
if (plan.topLevelNames.has(name)) {
|
|
485
|
+
throw failures.unencodable(
|
|
486
|
+
`Event ${envelope.id} has a header '${name}', which the mapping uses for another body field`
|
|
487
|
+
);
|
|
488
|
+
}
|
|
489
|
+
setAt(root, { path: [name], value });
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
function timeValue(text, { envelope, format }) {
|
|
493
|
+
const milliseconds = envelope.occurredAt.getTime();
|
|
494
|
+
if (format === "epoch-millis") {
|
|
495
|
+
return milliseconds;
|
|
496
|
+
}
|
|
497
|
+
return format === "epoch-seconds" ? Math.floor(milliseconds / MILLISECONDS_PER_SECOND2) : text;
|
|
498
|
+
}
|
|
499
|
+
function ordered(root, order) {
|
|
500
|
+
if (order.length === 0) {
|
|
501
|
+
return root;
|
|
502
|
+
}
|
|
503
|
+
const result = {};
|
|
504
|
+
const names = [...order.filter((name) => Object.hasOwn(root, name)), ...Object.keys(root)];
|
|
505
|
+
for (const name of names) {
|
|
506
|
+
if (!Object.hasOwn(result, name)) {
|
|
507
|
+
defineValue(result, { name, value: root[name] });
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
return result;
|
|
279
511
|
}
|
|
280
512
|
function eventHeaderEntries(envelope, { plan, failures }) {
|
|
281
|
-
if (plan.headers !== void 0) {
|
|
513
|
+
if (plan.headers !== void 0 || plan.topLevelHeaders) {
|
|
282
514
|
return [];
|
|
283
515
|
}
|
|
284
516
|
const entries = Object.entries(envelope.headers);
|
|
@@ -320,106 +552,200 @@ function defineValue(target, { name, value }) {
|
|
|
320
552
|
configurable: true
|
|
321
553
|
});
|
|
322
554
|
}
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
const
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
key: read(plan.optional.key),
|
|
334
|
-
source: read(plan.optional.source),
|
|
335
|
-
headers: readEventHeaders(message, plan),
|
|
336
|
-
correlationId: read(plan.optional.correlationId) ?? null,
|
|
337
|
-
causationId: read(plan.optional.causationId) ?? null,
|
|
338
|
-
occurredAt: readTime(read(plan.optional.occurredAt), failures)
|
|
555
|
+
|
|
556
|
+
// src/internal/json-envelope.ts
|
|
557
|
+
function createJsonEnvelopeFormat(mapping, {
|
|
558
|
+
transportIds,
|
|
559
|
+
failures
|
|
560
|
+
}) {
|
|
561
|
+
const plan = compile(mapping, transportIds);
|
|
562
|
+
return {
|
|
563
|
+
write: (envelope) => writeMessage(envelope, { plan, failures }),
|
|
564
|
+
read: (message) => readMessage(message, { plan, failures })
|
|
339
565
|
};
|
|
340
|
-
return Object.fromEntries(
|
|
341
|
-
Object.entries(fields).filter((entry) => entry[1] !== void 0)
|
|
342
|
-
);
|
|
343
566
|
}
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
}
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
567
|
+
|
|
568
|
+
// src/store-kit/handed-out-ledger.ts
|
|
569
|
+
var HandedOutLedger = class {
|
|
570
|
+
#entries = /* @__PURE__ */ new Map();
|
|
571
|
+
register(lease, entry) {
|
|
572
|
+
this.#entries.set(DeliveryLeaseIdentity.of(lease.deliveryId, lease.leaseToken), entry);
|
|
573
|
+
}
|
|
574
|
+
/** Whether the claim with this identity ({@link DeliveryLeaseIdentity.of}) is handed out. */
|
|
575
|
+
has(identity) {
|
|
576
|
+
return this.#entries.has(identity);
|
|
577
|
+
}
|
|
578
|
+
forget(lease) {
|
|
579
|
+
this.#entries.delete(DeliveryLeaseIdentity.of(lease.deliveryId, lease.leaseToken));
|
|
580
|
+
}
|
|
581
|
+
/** Forgets every entry `settled` says is settled. */
|
|
582
|
+
forgetWhere(settled) {
|
|
583
|
+
for (const [identity, entry] of this.#entries) {
|
|
584
|
+
if (settled(entry, identity)) {
|
|
585
|
+
this.#entries.delete(identity);
|
|
586
|
+
}
|
|
587
|
+
}
|
|
360
588
|
}
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
589
|
+
/** The entries handed out to `owner`. */
|
|
590
|
+
entriesOf(owner) {
|
|
591
|
+
return [...this.#entries.values()].filter((entry) => entry.owner === owner);
|
|
364
592
|
}
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
593
|
+
};
|
|
594
|
+
|
|
595
|
+
// src/store-kit/owner-serialized-claims.ts
|
|
596
|
+
var OwnerSerializedClaims = class {
|
|
597
|
+
/** The last claim of each owner still running; the next one waits for it. */
|
|
598
|
+
#queues = /* @__PURE__ */ new Map();
|
|
599
|
+
claim(request) {
|
|
600
|
+
if (this.claimsNothing(request)) {
|
|
601
|
+
return Promise.resolve([]);
|
|
602
|
+
}
|
|
603
|
+
return this.#serialized(request.leaseOwner, () => this.claimInTurn(request));
|
|
604
|
+
}
|
|
605
|
+
/** Whether the request cannot claim anything: no handlers, or a limit that is not above 0. */
|
|
606
|
+
claimsNothing(request) {
|
|
607
|
+
return request.handlers.length === 0 || !(request.limit > 0);
|
|
608
|
+
}
|
|
609
|
+
#serialized(owner, work) {
|
|
610
|
+
const previous = this.#queues.get(owner) ?? Promise.resolve();
|
|
611
|
+
const run = previous.then(work, work);
|
|
612
|
+
const tail = run.then(
|
|
613
|
+
() => void 0,
|
|
614
|
+
() => void 0
|
|
371
615
|
);
|
|
616
|
+
this.#queues.set(owner, tail);
|
|
617
|
+
void tail.then(() => {
|
|
618
|
+
if (this.#queues.get(owner) === tail) {
|
|
619
|
+
this.#queues.delete(owner);
|
|
620
|
+
}
|
|
621
|
+
});
|
|
622
|
+
return run;
|
|
372
623
|
}
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
}
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
}
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
624
|
+
};
|
|
625
|
+
|
|
626
|
+
// src/transport-kit/decoded-event-reader.ts
|
|
627
|
+
function observeRejection(value) {
|
|
628
|
+
if (typeof value === "object" && value !== null && "then" in value) {
|
|
629
|
+
Promise.resolve(value).catch(() => void 0);
|
|
630
|
+
}
|
|
631
|
+
}
|
|
632
|
+
var DecodedEventReader = class {
|
|
633
|
+
/**
|
|
634
|
+
* Fills the fields a decoder left out and guards against a decoder that returned no object,
|
|
635
|
+
* which would otherwise surface as a retried `TypeError` instead of a malformed message.
|
|
636
|
+
*/
|
|
637
|
+
complete(decoded, message) {
|
|
638
|
+
if (typeof decoded !== "object" || decoded === null) {
|
|
639
|
+
throw this.invalid("The decoder must return an event object synchronously");
|
|
640
|
+
}
|
|
641
|
+
if ("then" in decoded) {
|
|
642
|
+
observeRejection(decoded);
|
|
643
|
+
throw this.invalid("The decoder must return an event object synchronously");
|
|
644
|
+
}
|
|
645
|
+
const event = decoded;
|
|
646
|
+
return {
|
|
647
|
+
id: event.id,
|
|
648
|
+
type: event.type,
|
|
649
|
+
source: event.source ?? this.defaultSource(message),
|
|
650
|
+
key: event.key === void 0 ? this.defaultKey(message) : event.key,
|
|
651
|
+
payload: event.payload,
|
|
652
|
+
headers: event.headers ?? {},
|
|
653
|
+
correlationId: event.correlationId ?? null,
|
|
654
|
+
causationId: event.causationId ?? null,
|
|
655
|
+
occurredAt: event.occurredAt ?? this.defaultTime(message)
|
|
656
|
+
};
|
|
657
|
+
}
|
|
658
|
+
/** The text an option gives for `plain`; undefined when it gives none or `''`. */
|
|
659
|
+
text(option, { plain, name }) {
|
|
660
|
+
const value = typeof option === "function" ? option(plain) : option;
|
|
661
|
+
if (value === void 0 || value === null) {
|
|
662
|
+
return void 0;
|
|
663
|
+
}
|
|
664
|
+
if (typeof value !== "string") {
|
|
665
|
+
observeRejection(value);
|
|
666
|
+
throw this.invalid(`The ${name} option returned ${typeof value}, expected a string`);
|
|
667
|
+
}
|
|
668
|
+
return value.length === 0 ? void 0 : value;
|
|
394
669
|
}
|
|
395
|
-
|
|
670
|
+
};
|
|
671
|
+
|
|
672
|
+
// src/transport-kit/subscribing-transport.ts
|
|
673
|
+
var SubscribingTransport = class {
|
|
674
|
+
/**
|
|
675
|
+
* Subscribes to `destinations`. Not async itself: the subscription's own promise is returned
|
|
676
|
+
* as it is, so its checks reject rather than throw.
|
|
677
|
+
*/
|
|
678
|
+
// eslint-disable-next-line relaybus/max-two-params -- the Transport.subscribe signature
|
|
679
|
+
subscribe(destinations, onMessage, hooks) {
|
|
680
|
+
return this.openSubscription({ destinations, onMessage, hooks });
|
|
681
|
+
}
|
|
682
|
+
};
|
|
683
|
+
|
|
684
|
+
// src/values/bounded-integer.ts
|
|
685
|
+
var BoundedInteger = class {
|
|
686
|
+
/** Returns `value` when it is an integer within the bounds, else throws a {@link ConfigurationError}. */
|
|
687
|
+
static of(value, { name, minimum, maximum }) {
|
|
688
|
+
if (!Number.isInteger(value) || value < minimum || value > maximum) {
|
|
689
|
+
throw new ConfigurationError(
|
|
690
|
+
`${name} must be an integer from ${minimum} to ${maximum}, received ${value}`
|
|
691
|
+
);
|
|
692
|
+
}
|
|
396
693
|
return value;
|
|
397
694
|
}
|
|
398
|
-
|
|
399
|
-
|
|
695
|
+
};
|
|
696
|
+
|
|
697
|
+
// src/values/exponential-backoff.ts
|
|
698
|
+
var ExponentialBackoff = class {
|
|
699
|
+
initialMs;
|
|
700
|
+
maxMs;
|
|
701
|
+
constructor({ initialMs, maxMs }) {
|
|
702
|
+
this.initialMs = initialMs;
|
|
703
|
+
this.maxMs = maxMs;
|
|
704
|
+
}
|
|
705
|
+
/** The delay after failed attempt `attempt` (1-based). */
|
|
706
|
+
delayFor(attempt) {
|
|
707
|
+
return Math.min(this.initialMs * 2 ** (attempt - 1), this.maxMs);
|
|
708
|
+
}
|
|
709
|
+
};
|
|
710
|
+
|
|
711
|
+
// src/values/lane-lease-token.ts
|
|
712
|
+
var LANE_DIGITS = 3;
|
|
713
|
+
var LaneLeaseTokenFormat = class {
|
|
714
|
+
#pattern;
|
|
715
|
+
#laneCount;
|
|
716
|
+
constructor(limits) {
|
|
717
|
+
this.#laneCount = limits.laneCount;
|
|
718
|
+
this.#pattern = new RegExp(
|
|
719
|
+
`^(\\d{1,${LANE_DIGITS}}):(\\d{1,${limits.epochDigits}}):(\\d{1,${limits.claimsDigits}})$`
|
|
720
|
+
);
|
|
400
721
|
}
|
|
401
|
-
|
|
402
|
-
}
|
|
403
|
-
function requireText(value, { location, failures }) {
|
|
404
|
-
if (value === void 0) {
|
|
405
|
-
throw failures.invalid(`The message has no ${location.label} at ${describe(location)}`);
|
|
722
|
+
format(lane, { epoch, claims }) {
|
|
723
|
+
return `${lane}:${epoch}:${claims}`;
|
|
406
724
|
}
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
}
|
|
412
|
-
function valueAt(body, path) {
|
|
413
|
-
let value = body;
|
|
414
|
-
for (const segment of path) {
|
|
415
|
-
if (typeof value !== "object" || value === null || !Object.hasOwn(value, segment)) {
|
|
725
|
+
/** The parts of `token`; undefined when the store cannot have issued it. */
|
|
726
|
+
parse(token) {
|
|
727
|
+
const match = this.#pattern.exec(token);
|
|
728
|
+
if (match === null || Number(match[1]) >= this.#laneCount) {
|
|
416
729
|
return void 0;
|
|
417
730
|
}
|
|
418
|
-
|
|
731
|
+
return { lane: Number(match[1]), epoch: match[2], claims: Number(match[3]) };
|
|
419
732
|
}
|
|
420
|
-
|
|
421
|
-
|
|
733
|
+
};
|
|
734
|
+
|
|
735
|
+
// src/values/non-empty-text.ts
|
|
736
|
+
var NonEmptyText = class _NonEmptyText {
|
|
737
|
+
static of(value, name) {
|
|
738
|
+
if (value.length === 0) {
|
|
739
|
+
throw new ConfigurationError(`${name} must not be empty`);
|
|
740
|
+
}
|
|
741
|
+
return value;
|
|
742
|
+
}
|
|
743
|
+
/** Like {@link NonEmptyText.of}, for an option that may be left out. */
|
|
744
|
+
static optional(value, name) {
|
|
745
|
+
return value === void 0 ? void 0 : _NonEmptyText.of(value, name);
|
|
746
|
+
}
|
|
747
|
+
};
|
|
422
748
|
|
|
423
|
-
export { createCloudEventReaders, createJsonEnvelopeFormat, fromHeader, fromPayloadField, isJsonObject, mediaTypeOf, writeStructuredCloudEvent };
|
|
749
|
+
export { BoundedInteger, DecodedEventReader, ExponentialBackoff, HandedOutLedger, LaneLeaseTokenFormat, NonEmptyText, OwnerSerializedClaims, SubscribingTransport, assertPositiveInteger, createCloudEventReaders, createJsonEnvelopeFormat, fromHeader, fromPayloadField, isJsonObject, mediaTypeOf, observeRejection, writeStructuredCloudEvent };
|
|
424
750
|
//# sourceMappingURL=index.js.map
|
|
425
751
|
//# sourceMappingURL=index.js.map
|