@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/README.md
CHANGED
|
@@ -394,6 +394,7 @@ never renamed or reused once released, so branch on them in alerts and retry rul
|
|
|
394
394
|
| `RELAYBUS_RABBITMQ_UNROUTABLE_MESSAGE` | `UnroutableMessageError` | `@relaybus/rabbitmq` |
|
|
395
395
|
| `RELAYBUS_RABBITMQ_MALFORMED_MESSAGE` | `MalformedMessageError` | `@relaybus/rabbitmq` |
|
|
396
396
|
| `RELAYBUS_RABBITMQ_UNSERIALIZABLE_PAYLOAD` | `UnserializablePayloadError` | `@relaybus/rabbitmq` |
|
|
397
|
+
| `RELAYBUS_TESTING_CODEC_CONTRACT` | `CodecContractError` | `@relaybus/testing` |
|
|
397
398
|
| `RELAYBUS_RABBITMQ_UNENCODABLE_EVENT` | `RabbitMqUnencodableEventError` | `@relaybus/rabbitmq` |
|
|
398
399
|
| `RELAYBUS_RABBITMQ_AMQP_LIMIT_EXCEEDED` | `AmqpLimitExceededError` | `@relaybus/rabbitmq` |
|
|
399
400
|
|
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",
|
|
@@ -116,8 +165,9 @@ function compile(mapping, transportIds) {
|
|
|
116
165
|
if (topLevelHeaders && payload === void 0) {
|
|
117
166
|
throw new chunkJ76M7XTS_cjs.ConfigurationError("jsonEnvelope headers 'top-level-fields' needs a body payload");
|
|
118
167
|
}
|
|
168
|
+
const type = compileType(mapping.type);
|
|
119
169
|
const plan = {
|
|
120
|
-
type
|
|
170
|
+
type,
|
|
121
171
|
id: compileId(mapping.id, transportIds),
|
|
122
172
|
idWhenMissing: isJsonObject(mapping.id) && "whenMissing" in mapping.id,
|
|
123
173
|
optional,
|
|
@@ -127,6 +177,8 @@ function compile(mapping, transportIds) {
|
|
|
127
177
|
topLevelNames: /* @__PURE__ */ new Set(),
|
|
128
178
|
mappedHeaders: /* @__PURE__ */ new Set(),
|
|
129
179
|
timeFormat: compileTimeFormat(mapping.occurredAt),
|
|
180
|
+
otherFields: compileOtherFields(mapping, payload !== void 0),
|
|
181
|
+
...compileWireTypes(mapping.wireTypes, { fixed: "fixed" in type }),
|
|
130
182
|
...compileExtras(mapping, payload !== void 0)
|
|
131
183
|
};
|
|
132
184
|
return assertDisjoint(plan);
|
|
@@ -160,6 +212,20 @@ function compileExtras(mapping, hasBody) {
|
|
|
160
212
|
bodyOrder
|
|
161
213
|
};
|
|
162
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
|
+
}
|
|
163
229
|
function compileType(type) {
|
|
164
230
|
if (isJsonObject(type) && typeof type["fixed"] === "string") {
|
|
165
231
|
chunkJ76M7XTS_cjs.assertEventType(type["fixed"]);
|
|
@@ -269,6 +335,56 @@ function assertCompatiblePaths(path, other) {
|
|
|
269
335
|
}
|
|
270
336
|
}
|
|
271
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
|
+
|
|
272
388
|
// src/internal/json-envelope-read.ts
|
|
273
389
|
var RFC3339_PATTERN = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d+)?(?:Z|[+-]\d{2}:\d{2})$/i;
|
|
274
390
|
var EPOCH_PATTERN = /^\d+$/;
|
|
@@ -305,13 +421,8 @@ function readType(message, { plan, failures }) {
|
|
|
305
421
|
return plan.type.fixed;
|
|
306
422
|
}
|
|
307
423
|
const location = plan.type;
|
|
308
|
-
const
|
|
309
|
-
|
|
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;
|
|
424
|
+
const wire = requireText(readText(message, { location, failures }), { location, failures });
|
|
425
|
+
return eventTypeOf(wire, { plan, failures });
|
|
315
426
|
}
|
|
316
427
|
function readPayload(body, { plan, failures }) {
|
|
317
428
|
if (plan.payload === void 0) {
|
|
@@ -321,7 +432,10 @@ function readPayload(body, { plan, failures }) {
|
|
|
321
432
|
if (payload === void 0) {
|
|
322
433
|
throw failures.invalid(`The message has no payload at '${plan.payload.join(".")}'`);
|
|
323
434
|
}
|
|
324
|
-
return payload
|
|
435
|
+
return withOtherFields(payload, {
|
|
436
|
+
body,
|
|
437
|
+
context: { plan, failures }
|
|
438
|
+
});
|
|
325
439
|
}
|
|
326
440
|
function readEventHeaders(message, plan) {
|
|
327
441
|
if (plan.topLevelHeaders) {
|
|
@@ -411,7 +525,7 @@ function writeMessage(envelope, { plan, failures }) {
|
|
|
411
525
|
};
|
|
412
526
|
const typeAt = typeLocation(envelope, { plan, failures });
|
|
413
527
|
if (typeAt !== void 0) {
|
|
414
|
-
put(typeAt, envelope
|
|
528
|
+
put(typeAt, wireTypeOf(envelope, { plan, failures }));
|
|
415
529
|
}
|
|
416
530
|
if (plan.id !== void 0) {
|
|
417
531
|
put(plan.id, envelope.id);
|
|
@@ -422,12 +536,8 @@ function writeMessage(envelope, { plan, failures }) {
|
|
|
422
536
|
put(location, value);
|
|
423
537
|
}
|
|
424
538
|
}
|
|
425
|
-
const body = writeBody(envelope, { plan, root });
|
|
426
|
-
if (plan.topLevelHeaders) {
|
|
427
|
-
spreadHeaders(envelope, { plan, failures, root: body });
|
|
428
|
-
}
|
|
429
539
|
return {
|
|
430
|
-
body,
|
|
540
|
+
body: writeBody(envelope, { plan, failures, root }),
|
|
431
541
|
headers: Object.fromEntries([
|
|
432
542
|
...eventHeaderEntries(envelope, { plan, failures }),
|
|
433
543
|
...headers,
|
|
@@ -464,11 +574,12 @@ function optionalValues(envelope, { plan, failures }) {
|
|
|
464
574
|
["causationId", envelope.causationId]
|
|
465
575
|
];
|
|
466
576
|
}
|
|
467
|
-
function writeBody(envelope, { plan, root }) {
|
|
577
|
+
function writeBody(envelope, { plan, failures, root }) {
|
|
578
|
+
const { payload, fields } = splitOtherFields(envelope, { plan, failures });
|
|
468
579
|
if (plan.payload === void 0) {
|
|
469
|
-
return
|
|
580
|
+
return payload;
|
|
470
581
|
}
|
|
471
|
-
setAt(root, { path: plan.payload, value:
|
|
582
|
+
setAt(root, { path: plan.payload, value: payload });
|
|
472
583
|
if (plan.headers !== void 0) {
|
|
473
584
|
setAt(root, {
|
|
474
585
|
path: plan.headers,
|
|
@@ -478,6 +589,10 @@ function writeBody(envelope, { plan, root }) {
|
|
|
478
589
|
for (const { path, value } of plan.extraBody) {
|
|
479
590
|
setAt(root, { path, value: typeof value === "function" ? value(envelope) : value });
|
|
480
591
|
}
|
|
592
|
+
if (plan.topLevelHeaders) {
|
|
593
|
+
spreadHeaders(envelope, { plan, failures, root });
|
|
594
|
+
}
|
|
595
|
+
spreadOtherFields(envelope, { failures, fields, root });
|
|
481
596
|
return ordered(root, plan.bodyOrder);
|
|
482
597
|
}
|
|
483
598
|
function spreadHeaders(envelope, { plan, failures, root }) {
|
|
@@ -490,6 +605,20 @@ function spreadHeaders(envelope, { plan, failures, root }) {
|
|
|
490
605
|
setAt(root, { path: [name], value });
|
|
491
606
|
}
|
|
492
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
|
+
}
|
|
493
622
|
function timeValue(text, { envelope, format }) {
|
|
494
623
|
const milliseconds = envelope.occurredAt.getTime();
|
|
495
624
|
if (format === "epoch-millis") {
|