@relaybus/core 0.4.0 → 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 +1 -0
- package/dist/internal/index.cjs +147 -18
- package/dist/internal/index.cjs.map +1 -1
- package/dist/internal/index.d.cts +22 -1
- package/dist/internal/index.d.ts +22 -1
- package/dist/internal/index.js +147 -18
- package/dist/internal/index.js.map +1 -1
- package/package.json +1 -1
package/dist/internal/index.js
CHANGED
|
@@ -89,6 +89,55 @@ function writeStructuredCloudEvent(envelope, { time, data }) {
|
|
|
89
89
|
return `${JSON.stringify(attributes).slice(0, -1)},"data":${data}}`;
|
|
90
90
|
}
|
|
91
91
|
|
|
92
|
+
// src/internal/json-envelope-wire-types.ts
|
|
93
|
+
function compileWireTypes(wireTypes, { fixed }) {
|
|
94
|
+
if (wireTypes === void 0) {
|
|
95
|
+
return { wireTypes: /* @__PURE__ */ new Map(), eventTypes: /* @__PURE__ */ new Map() };
|
|
96
|
+
}
|
|
97
|
+
if (!isJsonObject(wireTypes) || fixed) {
|
|
98
|
+
throw new ConfigurationError(
|
|
99
|
+
"jsonEnvelope wireTypes must be an object of event type to wire name, with a type at { body } or { header }"
|
|
100
|
+
);
|
|
101
|
+
}
|
|
102
|
+
const entries = Object.entries(wireTypes);
|
|
103
|
+
const eventTypes = /* @__PURE__ */ new Map();
|
|
104
|
+
for (const [type, wire] of entries) {
|
|
105
|
+
assertEventType(type);
|
|
106
|
+
if (typeof wire !== "string" || wire.length === 0 || eventTypes.has(wire)) {
|
|
107
|
+
throw new ConfigurationError(
|
|
108
|
+
`jsonEnvelope wireTypes needs a non-empty wire name for '${type}' that no other type uses`
|
|
109
|
+
);
|
|
110
|
+
}
|
|
111
|
+
eventTypes.set(wire, type);
|
|
112
|
+
}
|
|
113
|
+
return { wireTypes: new Map(entries), eventTypes };
|
|
114
|
+
}
|
|
115
|
+
function wireTypeOf(envelope, { plan, failures }) {
|
|
116
|
+
const wire = plan.wireTypes.get(envelope.type);
|
|
117
|
+
if (wire !== void 0) {
|
|
118
|
+
return wire;
|
|
119
|
+
}
|
|
120
|
+
const other = plan.eventTypes.get(envelope.type);
|
|
121
|
+
if (other !== void 0) {
|
|
122
|
+
throw failures.unencodable(
|
|
123
|
+
`Event ${envelope.id} has type '${envelope.type}', which wireTypes uses as the wire name of '${other}'`
|
|
124
|
+
);
|
|
125
|
+
}
|
|
126
|
+
return envelope.type;
|
|
127
|
+
}
|
|
128
|
+
function eventTypeOf(wire, { plan, failures }) {
|
|
129
|
+
const type = plan.eventTypes.get(wire);
|
|
130
|
+
if (type !== void 0) {
|
|
131
|
+
return type;
|
|
132
|
+
}
|
|
133
|
+
try {
|
|
134
|
+
assertEventType(wire);
|
|
135
|
+
} catch (error) {
|
|
136
|
+
throw failures.invalid(`The event type is not a valid event type: ${error.message}`);
|
|
137
|
+
}
|
|
138
|
+
return wire;
|
|
139
|
+
}
|
|
140
|
+
|
|
92
141
|
// src/internal/json-envelope-plan.ts
|
|
93
142
|
var OPTIONAL_NAMES = [
|
|
94
143
|
"key",
|
|
@@ -115,8 +164,9 @@ function compile(mapping, transportIds) {
|
|
|
115
164
|
if (topLevelHeaders && payload === void 0) {
|
|
116
165
|
throw new ConfigurationError("jsonEnvelope headers 'top-level-fields' needs a body payload");
|
|
117
166
|
}
|
|
167
|
+
const type = compileType(mapping.type);
|
|
118
168
|
const plan = {
|
|
119
|
-
type
|
|
169
|
+
type,
|
|
120
170
|
id: compileId(mapping.id, transportIds),
|
|
121
171
|
idWhenMissing: isJsonObject(mapping.id) && "whenMissing" in mapping.id,
|
|
122
172
|
optional,
|
|
@@ -126,6 +176,8 @@ function compile(mapping, transportIds) {
|
|
|
126
176
|
topLevelNames: /* @__PURE__ */ new Set(),
|
|
127
177
|
mappedHeaders: /* @__PURE__ */ new Set(),
|
|
128
178
|
timeFormat: compileTimeFormat(mapping.occurredAt),
|
|
179
|
+
otherFields: compileOtherFields(mapping, payload !== void 0),
|
|
180
|
+
...compileWireTypes(mapping.wireTypes, { fixed: "fixed" in type }),
|
|
129
181
|
...compileExtras(mapping, payload !== void 0)
|
|
130
182
|
};
|
|
131
183
|
return assertDisjoint(plan);
|
|
@@ -159,6 +211,20 @@ function compileExtras(mapping, hasBody) {
|
|
|
159
211
|
bodyOrder
|
|
160
212
|
};
|
|
161
213
|
}
|
|
214
|
+
function compileOtherFields(mapping, hasBody) {
|
|
215
|
+
const otherFields = mapping.otherFields;
|
|
216
|
+
if (otherFields === void 0) {
|
|
217
|
+
return void 0;
|
|
218
|
+
}
|
|
219
|
+
if (!hasBody) {
|
|
220
|
+
throw new ConfigurationError("jsonEnvelope otherFields needs a body payload");
|
|
221
|
+
}
|
|
222
|
+
const field = isJsonObject(otherFields) ? otherFields["payloadField"] : void 0;
|
|
223
|
+
if (typeof field !== "string" || field.length === 0) {
|
|
224
|
+
throw new ConfigurationError("jsonEnvelope otherFields must be { payloadField: '<name>' }");
|
|
225
|
+
}
|
|
226
|
+
return field;
|
|
227
|
+
}
|
|
162
228
|
function compileType(type) {
|
|
163
229
|
if (isJsonObject(type) && typeof type["fixed"] === "string") {
|
|
164
230
|
assertEventType(type["fixed"]);
|
|
@@ -268,6 +334,56 @@ function assertCompatiblePaths(path, other) {
|
|
|
268
334
|
}
|
|
269
335
|
}
|
|
270
336
|
|
|
337
|
+
// src/internal/json-envelope-other-fields.ts
|
|
338
|
+
function unusedFields(body, { plan }) {
|
|
339
|
+
return Object.entries(body).filter(
|
|
340
|
+
([name, value]) => !plan.topLevelNames.has(name) && !(plan.topLevelHeaders && typeof value === "string")
|
|
341
|
+
);
|
|
342
|
+
}
|
|
343
|
+
function withOtherFields(payload, { body, context }) {
|
|
344
|
+
const field = context.plan.otherFields;
|
|
345
|
+
if (field === void 0) {
|
|
346
|
+
return payload;
|
|
347
|
+
}
|
|
348
|
+
const fields = unusedFields(body, context);
|
|
349
|
+
if (!isJsonObject(payload) && fields.length === 0) {
|
|
350
|
+
return payload;
|
|
351
|
+
}
|
|
352
|
+
if (!isJsonObject(payload) || Object.hasOwn(payload, field)) {
|
|
353
|
+
throw context.failures.invalid(
|
|
354
|
+
`otherFields keeps the unused top-level fields under the payload field '${field}', but the payload is not an object without that field`
|
|
355
|
+
);
|
|
356
|
+
}
|
|
357
|
+
return Object.fromEntries([...Object.entries(payload), [field, Object.fromEntries(fields)]]);
|
|
358
|
+
}
|
|
359
|
+
function splitOtherFields(envelope, context) {
|
|
360
|
+
const field = context.plan.otherFields;
|
|
361
|
+
const { payload } = envelope;
|
|
362
|
+
if (field === void 0 || !isJsonObject(payload) || !Object.hasOwn(payload, field)) {
|
|
363
|
+
return { payload, fields: [] };
|
|
364
|
+
}
|
|
365
|
+
const others = payload[field];
|
|
366
|
+
if (!isJsonObject(others)) {
|
|
367
|
+
throw context.failures.unencodable(
|
|
368
|
+
`Event ${envelope.id} has a payload field '${field}' that is not an object of top-level fields`
|
|
369
|
+
);
|
|
370
|
+
}
|
|
371
|
+
const fields = Object.entries(others);
|
|
372
|
+
const refused = fields.find((entry) => isRefused(entry, context));
|
|
373
|
+
if (refused !== void 0) {
|
|
374
|
+
throw context.failures.unencodable(
|
|
375
|
+
`Event ${envelope.id} keeps a top-level field '${refused[0]}' under '${field}' that the mapping reads as another value`
|
|
376
|
+
);
|
|
377
|
+
}
|
|
378
|
+
return {
|
|
379
|
+
payload: Object.fromEntries(Object.entries(payload).filter(([name]) => name !== field)),
|
|
380
|
+
fields
|
|
381
|
+
};
|
|
382
|
+
}
|
|
383
|
+
function isRefused([name, value], { plan }) {
|
|
384
|
+
return plan.topLevelNames.has(name) || plan.topLevelHeaders && typeof value === "string";
|
|
385
|
+
}
|
|
386
|
+
|
|
271
387
|
// src/internal/json-envelope-read.ts
|
|
272
388
|
var RFC3339_PATTERN = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d+)?(?:Z|[+-]\d{2}:\d{2})$/i;
|
|
273
389
|
var EPOCH_PATTERN = /^\d+$/;
|
|
@@ -304,13 +420,8 @@ function readType(message, { plan, failures }) {
|
|
|
304
420
|
return plan.type.fixed;
|
|
305
421
|
}
|
|
306
422
|
const location = plan.type;
|
|
307
|
-
const
|
|
308
|
-
|
|
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;
|
|
423
|
+
const wire = requireText(readText(message, { location, failures }), { location, failures });
|
|
424
|
+
return eventTypeOf(wire, { plan, failures });
|
|
314
425
|
}
|
|
315
426
|
function readPayload(body, { plan, failures }) {
|
|
316
427
|
if (plan.payload === void 0) {
|
|
@@ -320,7 +431,10 @@ function readPayload(body, { plan, failures }) {
|
|
|
320
431
|
if (payload === void 0) {
|
|
321
432
|
throw failures.invalid(`The message has no payload at '${plan.payload.join(".")}'`);
|
|
322
433
|
}
|
|
323
|
-
return payload
|
|
434
|
+
return withOtherFields(payload, {
|
|
435
|
+
body,
|
|
436
|
+
context: { plan, failures }
|
|
437
|
+
});
|
|
324
438
|
}
|
|
325
439
|
function readEventHeaders(message, plan) {
|
|
326
440
|
if (plan.topLevelHeaders) {
|
|
@@ -410,7 +524,7 @@ function writeMessage(envelope, { plan, failures }) {
|
|
|
410
524
|
};
|
|
411
525
|
const typeAt = typeLocation(envelope, { plan, failures });
|
|
412
526
|
if (typeAt !== void 0) {
|
|
413
|
-
put(typeAt, envelope
|
|
527
|
+
put(typeAt, wireTypeOf(envelope, { plan, failures }));
|
|
414
528
|
}
|
|
415
529
|
if (plan.id !== void 0) {
|
|
416
530
|
put(plan.id, envelope.id);
|
|
@@ -421,12 +535,8 @@ function writeMessage(envelope, { plan, failures }) {
|
|
|
421
535
|
put(location, value);
|
|
422
536
|
}
|
|
423
537
|
}
|
|
424
|
-
const body = writeBody(envelope, { plan, root });
|
|
425
|
-
if (plan.topLevelHeaders) {
|
|
426
|
-
spreadHeaders(envelope, { plan, failures, root: body });
|
|
427
|
-
}
|
|
428
538
|
return {
|
|
429
|
-
body,
|
|
539
|
+
body: writeBody(envelope, { plan, failures, root }),
|
|
430
540
|
headers: Object.fromEntries([
|
|
431
541
|
...eventHeaderEntries(envelope, { plan, failures }),
|
|
432
542
|
...headers,
|
|
@@ -463,11 +573,12 @@ function optionalValues(envelope, { plan, failures }) {
|
|
|
463
573
|
["causationId", envelope.causationId]
|
|
464
574
|
];
|
|
465
575
|
}
|
|
466
|
-
function writeBody(envelope, { plan, root }) {
|
|
576
|
+
function writeBody(envelope, { plan, failures, root }) {
|
|
577
|
+
const { payload, fields } = splitOtherFields(envelope, { plan, failures });
|
|
467
578
|
if (plan.payload === void 0) {
|
|
468
|
-
return
|
|
579
|
+
return payload;
|
|
469
580
|
}
|
|
470
|
-
setAt(root, { path: plan.payload, value:
|
|
581
|
+
setAt(root, { path: plan.payload, value: payload });
|
|
471
582
|
if (plan.headers !== void 0) {
|
|
472
583
|
setAt(root, {
|
|
473
584
|
path: plan.headers,
|
|
@@ -477,6 +588,10 @@ function writeBody(envelope, { plan, root }) {
|
|
|
477
588
|
for (const { path, value } of plan.extraBody) {
|
|
478
589
|
setAt(root, { path, value: typeof value === "function" ? value(envelope) : value });
|
|
479
590
|
}
|
|
591
|
+
if (plan.topLevelHeaders) {
|
|
592
|
+
spreadHeaders(envelope, { plan, failures, root });
|
|
593
|
+
}
|
|
594
|
+
spreadOtherFields(envelope, { failures, fields, root });
|
|
480
595
|
return ordered(root, plan.bodyOrder);
|
|
481
596
|
}
|
|
482
597
|
function spreadHeaders(envelope, { plan, failures, root }) {
|
|
@@ -489,6 +604,20 @@ function spreadHeaders(envelope, { plan, failures, root }) {
|
|
|
489
604
|
setAt(root, { path: [name], value });
|
|
490
605
|
}
|
|
491
606
|
}
|
|
607
|
+
function spreadOtherFields(envelope, {
|
|
608
|
+
failures,
|
|
609
|
+
fields,
|
|
610
|
+
root
|
|
611
|
+
}) {
|
|
612
|
+
for (const [name, value] of fields) {
|
|
613
|
+
if (Object.hasOwn(root, name)) {
|
|
614
|
+
throw failures.unencodable(
|
|
615
|
+
`Event ${envelope.id} keeps a top-level field '${name}' that an event header also writes`
|
|
616
|
+
);
|
|
617
|
+
}
|
|
618
|
+
setAt(root, { path: [name], value });
|
|
619
|
+
}
|
|
620
|
+
}
|
|
492
621
|
function timeValue(text, { envelope, format }) {
|
|
493
622
|
const milliseconds = envelope.occurredAt.getTime();
|
|
494
623
|
if (format === "epoch-millis") {
|