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