@relaybus/core 0.3.1 → 0.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +7 -1
- package/dist/index.cjs +77 -13
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +9 -3
- package/dist/index.d.ts +9 -3
- package/dist/index.js +78 -14
- package/dist/index.js.map +1 -1
- package/dist/internal/index.cjs +306 -43
- package/dist/internal/index.cjs.map +1 -1
- package/dist/internal/index.d.cts +75 -9
- package/dist/internal/index.d.ts +75 -9
- package/dist/internal/index.js +306 -43
- package/dist/internal/index.js.map +1 -1
- package/dist/{transport-BlfzIBZD.d.cts → transport-DIFdeXQY.d.cts} +2 -1
- package/dist/{transport-BlfzIBZD.d.ts → transport-DIFdeXQY.d.ts} +2 -1
- package/package.json +1 -1
package/dist/internal/index.cjs
CHANGED
|
@@ -90,6 +90,55 @@ function writeStructuredCloudEvent(envelope, { time, data }) {
|
|
|
90
90
|
return `${JSON.stringify(attributes).slice(0, -1)},"data":${data}}`;
|
|
91
91
|
}
|
|
92
92
|
|
|
93
|
+
// src/internal/json-envelope-wire-types.ts
|
|
94
|
+
function compileWireTypes(wireTypes, { fixed }) {
|
|
95
|
+
if (wireTypes === void 0) {
|
|
96
|
+
return { wireTypes: /* @__PURE__ */ new Map(), eventTypes: /* @__PURE__ */ new Map() };
|
|
97
|
+
}
|
|
98
|
+
if (!isJsonObject(wireTypes) || fixed) {
|
|
99
|
+
throw new chunkJ76M7XTS_cjs.ConfigurationError(
|
|
100
|
+
"jsonEnvelope wireTypes must be an object of event type to wire name, with a type at { body } or { header }"
|
|
101
|
+
);
|
|
102
|
+
}
|
|
103
|
+
const entries = Object.entries(wireTypes);
|
|
104
|
+
const eventTypes = /* @__PURE__ */ new Map();
|
|
105
|
+
for (const [type, wire] of entries) {
|
|
106
|
+
chunkJ76M7XTS_cjs.assertEventType(type);
|
|
107
|
+
if (typeof wire !== "string" || wire.length === 0 || eventTypes.has(wire)) {
|
|
108
|
+
throw new chunkJ76M7XTS_cjs.ConfigurationError(
|
|
109
|
+
`jsonEnvelope wireTypes needs a non-empty wire name for '${type}' that no other type uses`
|
|
110
|
+
);
|
|
111
|
+
}
|
|
112
|
+
eventTypes.set(wire, type);
|
|
113
|
+
}
|
|
114
|
+
return { wireTypes: new Map(entries), eventTypes };
|
|
115
|
+
}
|
|
116
|
+
function wireTypeOf(envelope, { plan, failures }) {
|
|
117
|
+
const wire = plan.wireTypes.get(envelope.type);
|
|
118
|
+
if (wire !== void 0) {
|
|
119
|
+
return wire;
|
|
120
|
+
}
|
|
121
|
+
const other = plan.eventTypes.get(envelope.type);
|
|
122
|
+
if (other !== void 0) {
|
|
123
|
+
throw failures.unencodable(
|
|
124
|
+
`Event ${envelope.id} has type '${envelope.type}', which wireTypes uses as the wire name of '${other}'`
|
|
125
|
+
);
|
|
126
|
+
}
|
|
127
|
+
return envelope.type;
|
|
128
|
+
}
|
|
129
|
+
function eventTypeOf(wire, { plan, failures }) {
|
|
130
|
+
const type = plan.eventTypes.get(wire);
|
|
131
|
+
if (type !== void 0) {
|
|
132
|
+
return type;
|
|
133
|
+
}
|
|
134
|
+
try {
|
|
135
|
+
chunkJ76M7XTS_cjs.assertEventType(wire);
|
|
136
|
+
} catch (error) {
|
|
137
|
+
throw failures.invalid(`The event type is not a valid event type: ${error.message}`);
|
|
138
|
+
}
|
|
139
|
+
return wire;
|
|
140
|
+
}
|
|
141
|
+
|
|
93
142
|
// src/internal/json-envelope-plan.ts
|
|
94
143
|
var OPTIONAL_NAMES = [
|
|
95
144
|
"key",
|
|
@@ -98,6 +147,7 @@ var OPTIONAL_NAMES = [
|
|
|
98
147
|
"correlationId",
|
|
99
148
|
"causationId"
|
|
100
149
|
];
|
|
150
|
+
var TIME_FORMATS = ["rfc3339", "epoch-millis", "epoch-seconds"];
|
|
101
151
|
var ARRAY_INDEX = /^(?:0|[1-9]\d*)$/;
|
|
102
152
|
function compile(mapping, transportIds) {
|
|
103
153
|
if (!isJsonObject(mapping)) {
|
|
@@ -110,16 +160,72 @@ function compile(mapping, transportIds) {
|
|
|
110
160
|
optional[name] = toLocation(field, name);
|
|
111
161
|
}
|
|
112
162
|
}
|
|
163
|
+
const payload = compilePayload(mapping.payload);
|
|
164
|
+
const topLevelHeaders = mapping.headers === "top-level-fields";
|
|
165
|
+
if (topLevelHeaders && payload === void 0) {
|
|
166
|
+
throw new chunkJ76M7XTS_cjs.ConfigurationError("jsonEnvelope headers 'top-level-fields' needs a body payload");
|
|
167
|
+
}
|
|
168
|
+
const type = compileType(mapping.type);
|
|
113
169
|
const plan = {
|
|
114
|
-
type
|
|
170
|
+
type,
|
|
115
171
|
id: compileId(mapping.id, transportIds),
|
|
172
|
+
idWhenMissing: isJsonObject(mapping.id) && "whenMissing" in mapping.id,
|
|
116
173
|
optional,
|
|
117
|
-
payload
|
|
118
|
-
headers: compileHeaders(mapping.headers),
|
|
119
|
-
|
|
174
|
+
payload,
|
|
175
|
+
headers: topLevelHeaders ? void 0 : compileHeaders(mapping.headers),
|
|
176
|
+
topLevelHeaders,
|
|
177
|
+
topLevelNames: /* @__PURE__ */ new Set(),
|
|
178
|
+
mappedHeaders: /* @__PURE__ */ new Set(),
|
|
179
|
+
timeFormat: compileTimeFormat(mapping.occurredAt),
|
|
180
|
+
otherFields: compileOtherFields(mapping, payload !== void 0),
|
|
181
|
+
...compileWireTypes(mapping.wireTypes, { fixed: "fixed" in type }),
|
|
182
|
+
...compileExtras(mapping, payload !== void 0)
|
|
120
183
|
};
|
|
121
184
|
return assertDisjoint(plan);
|
|
122
185
|
}
|
|
186
|
+
function compileTimeFormat(field) {
|
|
187
|
+
const format = isJsonObject(field) ? field["format"] : void 0;
|
|
188
|
+
if (format === void 0) {
|
|
189
|
+
return "rfc3339";
|
|
190
|
+
}
|
|
191
|
+
if (!TIME_FORMATS.includes(format)) {
|
|
192
|
+
throw new chunkJ76M7XTS_cjs.ConfigurationError(
|
|
193
|
+
`jsonEnvelope occurredAt format must be one of ${TIME_FORMATS.map((name) => `'${name}'`).join(", ")}`
|
|
194
|
+
);
|
|
195
|
+
}
|
|
196
|
+
return format;
|
|
197
|
+
}
|
|
198
|
+
function compileExtras(mapping, hasBody) {
|
|
199
|
+
const extra = mapping.extra ?? {};
|
|
200
|
+
const bodyOrder = mapping.bodyOrder ?? [];
|
|
201
|
+
if (!hasBody && (extra.body !== void 0 || bodyOrder.length > 0)) {
|
|
202
|
+
throw new chunkJ76M7XTS_cjs.ConfigurationError(
|
|
203
|
+
"jsonEnvelope with a whole-body payload has no body for extra values or a body order"
|
|
204
|
+
);
|
|
205
|
+
}
|
|
206
|
+
return {
|
|
207
|
+
extraBody: Object.entries(extra.body ?? {}).map(([path, value]) => ({
|
|
208
|
+
path: toPath(path, "extra body value"),
|
|
209
|
+
value
|
|
210
|
+
})),
|
|
211
|
+
extraHeaders: Object.entries(extra.headers ?? {}),
|
|
212
|
+
bodyOrder
|
|
213
|
+
};
|
|
214
|
+
}
|
|
215
|
+
function compileOtherFields(mapping, hasBody) {
|
|
216
|
+
const otherFields = mapping.otherFields;
|
|
217
|
+
if (otherFields === void 0) {
|
|
218
|
+
return void 0;
|
|
219
|
+
}
|
|
220
|
+
if (!hasBody) {
|
|
221
|
+
throw new chunkJ76M7XTS_cjs.ConfigurationError("jsonEnvelope otherFields needs a body payload");
|
|
222
|
+
}
|
|
223
|
+
const field = isJsonObject(otherFields) ? otherFields["payloadField"] : void 0;
|
|
224
|
+
if (typeof field !== "string" || field.length === 0) {
|
|
225
|
+
throw new chunkJ76M7XTS_cjs.ConfigurationError("jsonEnvelope otherFields must be { payloadField: '<name>' }");
|
|
226
|
+
}
|
|
227
|
+
return field;
|
|
228
|
+
}
|
|
123
229
|
function compileType(type) {
|
|
124
230
|
if (isJsonObject(type) && typeof type["fixed"] === "string") {
|
|
125
231
|
chunkJ76M7XTS_cjs.assertEventType(type["fixed"]);
|
|
@@ -134,11 +240,15 @@ function compileId(id, transportIds) {
|
|
|
134
240
|
if (typeof id === "string" && transportIds.includes(id)) {
|
|
135
241
|
return void 0;
|
|
136
242
|
}
|
|
243
|
+
const names = transportIds.map((name) => `'${name}'`).join(", ");
|
|
137
244
|
if (!isJsonObject(id)) {
|
|
138
245
|
throw new chunkJ76M7XTS_cjs.ConfigurationError(
|
|
139
|
-
`jsonEnvelope needs an id: { body }, { header } or one of ${
|
|
246
|
+
`jsonEnvelope needs an id: { body }, { header } or one of ${names}`
|
|
140
247
|
);
|
|
141
248
|
}
|
|
249
|
+
if ("whenMissing" in id && !transportIds.includes(id["whenMissing"])) {
|
|
250
|
+
throw new chunkJ76M7XTS_cjs.ConfigurationError(`jsonEnvelope id whenMissing must be one of ${names}`);
|
|
251
|
+
}
|
|
142
252
|
return toLocation(id, "id");
|
|
143
253
|
}
|
|
144
254
|
function compilePayload(payload) {
|
|
@@ -187,17 +297,25 @@ function assertDisjoint(plan) {
|
|
|
187
297
|
const locations = [plan.type, plan.id, ...Object.values(plan.optional)].filter(
|
|
188
298
|
(location) => location !== void 0 && !("fixed" in location)
|
|
189
299
|
);
|
|
190
|
-
const headers =
|
|
300
|
+
const headers = [
|
|
301
|
+
...locations.flatMap((location) => "header" in location ? [location.header] : []),
|
|
302
|
+
...plan.extraHeaders.map(([name]) => name)
|
|
303
|
+
];
|
|
191
304
|
const duplicate = headers.find((name, index) => headers.indexOf(name) !== index);
|
|
192
305
|
if (duplicate !== void 0) {
|
|
193
306
|
throw new chunkJ76M7XTS_cjs.ConfigurationError(`jsonEnvelope maps header '${duplicate}' twice`);
|
|
194
307
|
}
|
|
195
308
|
const paths = [
|
|
196
309
|
...locations.flatMap((location) => "path" in location ? [location.path] : []),
|
|
197
|
-
...[plan.payload, plan.headers].filter((path) => path !== void 0)
|
|
310
|
+
...[plan.payload, plan.headers].filter((path) => path !== void 0),
|
|
311
|
+
...plan.extraBody.map(({ path }) => path)
|
|
198
312
|
];
|
|
199
313
|
assertNoNestedPaths(paths);
|
|
200
|
-
return {
|
|
314
|
+
return {
|
|
315
|
+
...plan,
|
|
316
|
+
mappedHeaders: new Set(headers),
|
|
317
|
+
topLevelNames: new Set(paths.map((path) => path[0]))
|
|
318
|
+
};
|
|
201
319
|
}
|
|
202
320
|
function assertNoNestedPaths(paths) {
|
|
203
321
|
paths.forEach((path, index) => {
|
|
@@ -217,8 +335,60 @@ function assertCompatiblePaths(path, other) {
|
|
|
217
335
|
}
|
|
218
336
|
}
|
|
219
337
|
|
|
338
|
+
// src/internal/json-envelope-other-fields.ts
|
|
339
|
+
function unusedFields(body, { plan }) {
|
|
340
|
+
return Object.entries(body).filter(
|
|
341
|
+
([name, value]) => !plan.topLevelNames.has(name) && !(plan.topLevelHeaders && typeof value === "string")
|
|
342
|
+
);
|
|
343
|
+
}
|
|
344
|
+
function withOtherFields(payload, { body, context }) {
|
|
345
|
+
const field = context.plan.otherFields;
|
|
346
|
+
if (field === void 0) {
|
|
347
|
+
return payload;
|
|
348
|
+
}
|
|
349
|
+
const fields = unusedFields(body, context);
|
|
350
|
+
if (!isJsonObject(payload) && fields.length === 0) {
|
|
351
|
+
return payload;
|
|
352
|
+
}
|
|
353
|
+
if (!isJsonObject(payload) || Object.hasOwn(payload, field)) {
|
|
354
|
+
throw context.failures.invalid(
|
|
355
|
+
`otherFields keeps the unused top-level fields under the payload field '${field}', but the payload is not an object without that field`
|
|
356
|
+
);
|
|
357
|
+
}
|
|
358
|
+
return Object.fromEntries([...Object.entries(payload), [field, Object.fromEntries(fields)]]);
|
|
359
|
+
}
|
|
360
|
+
function splitOtherFields(envelope, context) {
|
|
361
|
+
const field = context.plan.otherFields;
|
|
362
|
+
const { payload } = envelope;
|
|
363
|
+
if (field === void 0 || !isJsonObject(payload) || !Object.hasOwn(payload, field)) {
|
|
364
|
+
return { payload, fields: [] };
|
|
365
|
+
}
|
|
366
|
+
const others = payload[field];
|
|
367
|
+
if (!isJsonObject(others)) {
|
|
368
|
+
throw context.failures.unencodable(
|
|
369
|
+
`Event ${envelope.id} has a payload field '${field}' that is not an object of top-level fields`
|
|
370
|
+
);
|
|
371
|
+
}
|
|
372
|
+
const fields = Object.entries(others);
|
|
373
|
+
const refused = fields.find((entry) => isRefused(entry, context));
|
|
374
|
+
if (refused !== void 0) {
|
|
375
|
+
throw context.failures.unencodable(
|
|
376
|
+
`Event ${envelope.id} keeps a top-level field '${refused[0]}' under '${field}' that the mapping reads as another value`
|
|
377
|
+
);
|
|
378
|
+
}
|
|
379
|
+
return {
|
|
380
|
+
payload: Object.fromEntries(Object.entries(payload).filter(([name]) => name !== field)),
|
|
381
|
+
fields
|
|
382
|
+
};
|
|
383
|
+
}
|
|
384
|
+
function isRefused([name, value], { plan }) {
|
|
385
|
+
return plan.topLevelNames.has(name) || plan.topLevelHeaders && typeof value === "string";
|
|
386
|
+
}
|
|
387
|
+
|
|
220
388
|
// src/internal/json-envelope-read.ts
|
|
221
389
|
var RFC3339_PATTERN = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d+)?(?:Z|[+-]\d{2}:\d{2})$/i;
|
|
390
|
+
var EPOCH_PATTERN = /^\d+$/;
|
|
391
|
+
var MILLISECONDS_PER_SECOND = 1e3;
|
|
222
392
|
function readMessage(message, context) {
|
|
223
393
|
const { plan, failures } = context;
|
|
224
394
|
if (plan.payload !== void 0 && !isJsonObject(message.body)) {
|
|
@@ -226,7 +396,7 @@ function readMessage(message, context) {
|
|
|
226
396
|
}
|
|
227
397
|
const read = (location) => location === void 0 ? void 0 : readText(message, { location, failures });
|
|
228
398
|
const fields = {
|
|
229
|
-
id:
|
|
399
|
+
id: readId(read(plan.id), context),
|
|
230
400
|
type: readType(message, context),
|
|
231
401
|
payload: readPayload(message.body, context),
|
|
232
402
|
key: read(plan.optional.key),
|
|
@@ -234,24 +404,25 @@ function readMessage(message, context) {
|
|
|
234
404
|
headers: readEventHeaders(message, plan),
|
|
235
405
|
correlationId: read(plan.optional.correlationId) ?? null,
|
|
236
406
|
causationId: read(plan.optional.causationId) ?? null,
|
|
237
|
-
occurredAt: readTime(read(plan.optional.occurredAt),
|
|
407
|
+
occurredAt: readTime(read(plan.optional.occurredAt), context)
|
|
238
408
|
};
|
|
239
409
|
return Object.fromEntries(
|
|
240
410
|
Object.entries(fields).filter((entry) => entry[1] !== void 0)
|
|
241
411
|
);
|
|
242
412
|
}
|
|
413
|
+
function readId(id, { plan, failures }) {
|
|
414
|
+
if (plan.id === void 0 || plan.idWhenMissing) {
|
|
415
|
+
return id;
|
|
416
|
+
}
|
|
417
|
+
return requireText(id, { location: plan.id, failures });
|
|
418
|
+
}
|
|
243
419
|
function readType(message, { plan, failures }) {
|
|
244
420
|
if ("fixed" in plan.type) {
|
|
245
421
|
return plan.type.fixed;
|
|
246
422
|
}
|
|
247
423
|
const location = plan.type;
|
|
248
|
-
const
|
|
249
|
-
|
|
250
|
-
chunkJ76M7XTS_cjs.assertEventType(type);
|
|
251
|
-
} catch (error) {
|
|
252
|
-
throw failures.invalid(`The event type is not a valid event type: ${error.message}`);
|
|
253
|
-
}
|
|
254
|
-
return type;
|
|
424
|
+
const wire = requireText(readText(message, { location, failures }), { location, failures });
|
|
425
|
+
return eventTypeOf(wire, { plan, failures });
|
|
255
426
|
}
|
|
256
427
|
function readPayload(body, { plan, failures }) {
|
|
257
428
|
if (plan.payload === void 0) {
|
|
@@ -261,25 +432,45 @@ function readPayload(body, { plan, failures }) {
|
|
|
261
432
|
if (payload === void 0) {
|
|
262
433
|
throw failures.invalid(`The message has no payload at '${plan.payload.join(".")}'`);
|
|
263
434
|
}
|
|
264
|
-
return payload
|
|
435
|
+
return withOtherFields(payload, {
|
|
436
|
+
body,
|
|
437
|
+
context: { plan, failures }
|
|
438
|
+
});
|
|
265
439
|
}
|
|
266
440
|
function readEventHeaders(message, plan) {
|
|
441
|
+
if (plan.topLevelHeaders) {
|
|
442
|
+
return textEntries(
|
|
443
|
+
Object.entries(message.body).filter(
|
|
444
|
+
([name]) => !plan.topLevelNames.has(name)
|
|
445
|
+
)
|
|
446
|
+
);
|
|
447
|
+
}
|
|
267
448
|
if (plan.headers === void 0) {
|
|
268
449
|
return Object.fromEntries(
|
|
269
450
|
Object.entries(message.headers).filter(([name]) => !plan.mappedHeaders.has(name))
|
|
270
451
|
);
|
|
271
452
|
}
|
|
272
453
|
const headers = valueAt(message.body, plan.headers);
|
|
273
|
-
return isJsonObject(headers) ? Object.
|
|
274
|
-
Object.entries(headers).filter(
|
|
275
|
-
(entry) => typeof entry[1] === "string"
|
|
276
|
-
)
|
|
277
|
-
) : {};
|
|
454
|
+
return isJsonObject(headers) ? textEntries(Object.entries(headers)) : {};
|
|
278
455
|
}
|
|
279
|
-
function
|
|
456
|
+
function textEntries(entries) {
|
|
457
|
+
return Object.fromEntries(
|
|
458
|
+
entries.filter((entry) => typeof entry[1] === "string")
|
|
459
|
+
);
|
|
460
|
+
}
|
|
461
|
+
function readTime(text, { plan, failures }) {
|
|
280
462
|
if (text === void 0) {
|
|
281
463
|
return void 0;
|
|
282
464
|
}
|
|
465
|
+
if (plan.timeFormat !== "rfc3339") {
|
|
466
|
+
const time2 = new Date(
|
|
467
|
+
Number(text) * (plan.timeFormat === "epoch-seconds" ? MILLISECONDS_PER_SECOND : 1)
|
|
468
|
+
);
|
|
469
|
+
if (!EPOCH_PATTERN.test(text) || Number.isNaN(time2.getTime())) {
|
|
470
|
+
throw failures.invalid(`The event time is not a whole ${plan.timeFormat} number: '${text}'`);
|
|
471
|
+
}
|
|
472
|
+
return time2;
|
|
473
|
+
}
|
|
283
474
|
const time = new Date(text);
|
|
284
475
|
if (!RFC3339_PATTERN.test(text) || Number.isNaN(time.getTime())) {
|
|
285
476
|
throw failures.invalid(`The event time is not an RFC 3339 timestamp: '${text}'`);
|
|
@@ -321,63 +512,135 @@ function valueAt(body, path) {
|
|
|
321
512
|
|
|
322
513
|
// src/internal/json-envelope-write.ts
|
|
323
514
|
var MAXIMUM_RFC3339_YEAR = 9999;
|
|
515
|
+
var MILLISECONDS_PER_SECOND2 = 1e3;
|
|
324
516
|
function writeMessage(envelope, { plan, failures }) {
|
|
325
517
|
const root = {};
|
|
326
518
|
const headers = [];
|
|
327
519
|
const put = (location, value) => {
|
|
328
520
|
if ("header" in location) {
|
|
329
|
-
headers.push([location.header, value]);
|
|
521
|
+
headers.push([location.header, String(value)]);
|
|
330
522
|
} else {
|
|
331
523
|
setAt(root, { path: location.path, value });
|
|
332
524
|
}
|
|
333
525
|
};
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
`Event ${envelope.id} has type '${envelope.type}', but this route carries only '${plan.type.fixed}'`
|
|
338
|
-
);
|
|
339
|
-
}
|
|
340
|
-
} else {
|
|
341
|
-
put(plan.type, envelope.type);
|
|
526
|
+
const typeAt = typeLocation(envelope, { plan, failures });
|
|
527
|
+
if (typeAt !== void 0) {
|
|
528
|
+
put(typeAt, wireTypeOf(envelope, { plan, failures }));
|
|
342
529
|
}
|
|
343
530
|
if (plan.id !== void 0) {
|
|
344
531
|
put(plan.id, envelope.id);
|
|
345
532
|
}
|
|
346
|
-
for (const [name, value] of optionalValues(envelope, failures)) {
|
|
533
|
+
for (const [name, value] of optionalValues(envelope, { plan, failures })) {
|
|
347
534
|
const location = plan.optional[name];
|
|
348
535
|
if (location !== void 0 && value !== null) {
|
|
349
536
|
put(location, value);
|
|
350
537
|
}
|
|
351
538
|
}
|
|
352
539
|
return {
|
|
353
|
-
body: writeBody(envelope, { plan, root }),
|
|
354
|
-
headers: Object.fromEntries([
|
|
540
|
+
body: writeBody(envelope, { plan, failures, root }),
|
|
541
|
+
headers: Object.fromEntries([
|
|
542
|
+
...eventHeaderEntries(envelope, { plan, failures }),
|
|
543
|
+
...headers,
|
|
544
|
+
...extraHeaderEntries(envelope, plan)
|
|
545
|
+
])
|
|
355
546
|
};
|
|
356
547
|
}
|
|
357
|
-
function
|
|
548
|
+
function typeLocation(envelope, { plan, failures }) {
|
|
549
|
+
if (!("fixed" in plan.type)) {
|
|
550
|
+
return plan.type;
|
|
551
|
+
}
|
|
552
|
+
if (envelope.type !== plan.type.fixed) {
|
|
553
|
+
throw failures.unencodable(
|
|
554
|
+
`Event ${envelope.id} has type '${envelope.type}', but this route carries only '${plan.type.fixed}'`
|
|
555
|
+
);
|
|
556
|
+
}
|
|
557
|
+
return void 0;
|
|
558
|
+
}
|
|
559
|
+
function extraHeaderEntries(envelope, plan) {
|
|
560
|
+
return plan.extraHeaders.map(([name, value]) => [
|
|
561
|
+
name,
|
|
562
|
+
typeof value === "function" ? value(envelope) : value
|
|
563
|
+
]);
|
|
564
|
+
}
|
|
565
|
+
function optionalValues(envelope, { plan, failures }) {
|
|
358
566
|
return [
|
|
359
567
|
["key", envelope.key],
|
|
360
568
|
["source", envelope.source],
|
|
361
|
-
[
|
|
569
|
+
[
|
|
570
|
+
"occurredAt",
|
|
571
|
+
timeValue(formatTime(envelope, failures), { envelope, format: plan.timeFormat })
|
|
572
|
+
],
|
|
362
573
|
["correlationId", envelope.correlationId],
|
|
363
574
|
["causationId", envelope.causationId]
|
|
364
575
|
];
|
|
365
576
|
}
|
|
366
|
-
function writeBody(envelope, { plan, root }) {
|
|
577
|
+
function writeBody(envelope, { plan, failures, root }) {
|
|
578
|
+
const { payload, fields } = splitOtherFields(envelope, { plan, failures });
|
|
367
579
|
if (plan.payload === void 0) {
|
|
368
|
-
return
|
|
580
|
+
return payload;
|
|
369
581
|
}
|
|
370
|
-
setAt(root, { path: plan.payload, value:
|
|
582
|
+
setAt(root, { path: plan.payload, value: payload });
|
|
371
583
|
if (plan.headers !== void 0) {
|
|
372
584
|
setAt(root, {
|
|
373
585
|
path: plan.headers,
|
|
374
586
|
value: Object.fromEntries(Object.entries(envelope.headers))
|
|
375
587
|
});
|
|
376
588
|
}
|
|
377
|
-
|
|
589
|
+
for (const { path, value } of plan.extraBody) {
|
|
590
|
+
setAt(root, { path, value: typeof value === "function" ? value(envelope) : value });
|
|
591
|
+
}
|
|
592
|
+
if (plan.topLevelHeaders) {
|
|
593
|
+
spreadHeaders(envelope, { plan, failures, root });
|
|
594
|
+
}
|
|
595
|
+
spreadOtherFields(envelope, { failures, fields, root });
|
|
596
|
+
return ordered(root, plan.bodyOrder);
|
|
597
|
+
}
|
|
598
|
+
function spreadHeaders(envelope, { plan, failures, root }) {
|
|
599
|
+
for (const [name, value] of Object.entries(envelope.headers)) {
|
|
600
|
+
if (plan.topLevelNames.has(name)) {
|
|
601
|
+
throw failures.unencodable(
|
|
602
|
+
`Event ${envelope.id} has a header '${name}', which the mapping uses for another body field`
|
|
603
|
+
);
|
|
604
|
+
}
|
|
605
|
+
setAt(root, { path: [name], value });
|
|
606
|
+
}
|
|
607
|
+
}
|
|
608
|
+
function spreadOtherFields(envelope, {
|
|
609
|
+
failures,
|
|
610
|
+
fields,
|
|
611
|
+
root
|
|
612
|
+
}) {
|
|
613
|
+
for (const [name, value] of fields) {
|
|
614
|
+
if (Object.hasOwn(root, name)) {
|
|
615
|
+
throw failures.unencodable(
|
|
616
|
+
`Event ${envelope.id} keeps a top-level field '${name}' that an event header also writes`
|
|
617
|
+
);
|
|
618
|
+
}
|
|
619
|
+
setAt(root, { path: [name], value });
|
|
620
|
+
}
|
|
621
|
+
}
|
|
622
|
+
function timeValue(text, { envelope, format }) {
|
|
623
|
+
const milliseconds = envelope.occurredAt.getTime();
|
|
624
|
+
if (format === "epoch-millis") {
|
|
625
|
+
return milliseconds;
|
|
626
|
+
}
|
|
627
|
+
return format === "epoch-seconds" ? Math.floor(milliseconds / MILLISECONDS_PER_SECOND2) : text;
|
|
628
|
+
}
|
|
629
|
+
function ordered(root, order) {
|
|
630
|
+
if (order.length === 0) {
|
|
631
|
+
return root;
|
|
632
|
+
}
|
|
633
|
+
const result = {};
|
|
634
|
+
const names = [...order.filter((name) => Object.hasOwn(root, name)), ...Object.keys(root)];
|
|
635
|
+
for (const name of names) {
|
|
636
|
+
if (!Object.hasOwn(result, name)) {
|
|
637
|
+
defineValue(result, { name, value: root[name] });
|
|
638
|
+
}
|
|
639
|
+
}
|
|
640
|
+
return result;
|
|
378
641
|
}
|
|
379
642
|
function eventHeaderEntries(envelope, { plan, failures }) {
|
|
380
|
-
if (plan.headers !== void 0) {
|
|
643
|
+
if (plan.headers !== void 0 || plan.topLevelHeaders) {
|
|
381
644
|
return [];
|
|
382
645
|
}
|
|
383
646
|
const entries = Object.entries(envelope.headers);
|