@relaybus/core 0.4.0 → 0.5.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.
@@ -1,5 +1,5 @@
1
- import { PositiveInteger, ConfigurationError, assertEventType, DeliveryLeaseIdentity } from '../chunk-NOR5JHVN.js';
2
- export { CompositeKey, DeliveryLeaseIdentity, LeaseSettlement, Milliseconds, PositiveInteger, schedule, sleep } from '../chunk-NOR5JHVN.js';
1
+ import { PositiveInteger, ConfigurationError, assertEventType, WireTypesUnsupportedError, WireTypeCollisionError, DeliveryLeaseIdentity } from '../chunk-6PQ673MW.js';
2
+ export { CompositeKey, DeliveryLeaseIdentity, LeaseSettlement, Milliseconds, PositiveInteger, schedule, sleep } from '../chunk-6PQ673MW.js';
3
3
 
4
4
  // src/internal/validation.ts
5
5
  function assertPositiveInteger(value, name) {
@@ -89,6 +89,93 @@ 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
+ function withDeclaredWireTypes(codec, {
141
+ wireTypes,
142
+ transport
143
+ }) {
144
+ if (codec.withWireTypes === void 0) {
145
+ throw new WireTypesUnsupportedError(
146
+ `The codec of transport '${transport}' cannot map event types to wire names; use jsonEnvelope with a type at { body } or { header }`
147
+ );
148
+ }
149
+ return codec.withWireTypes(wireTypes);
150
+ }
151
+ function mergeWireTypes(mapping, declared) {
152
+ if (isJsonObject(mapping.type) && "fixed" in mapping.type) {
153
+ throw new WireTypesUnsupportedError(
154
+ "A jsonEnvelope with a fixed type writes no type, so it has no wire names to declare"
155
+ );
156
+ }
157
+ const merged = new Map(Object.entries(mapping.wireTypes ?? {}));
158
+ for (const [type, wire] of Object.entries(declared)) {
159
+ assertDeclarable(merged, { type, wire });
160
+ merged.set(type, wire);
161
+ }
162
+ return Object.fromEntries(merged);
163
+ }
164
+ function assertDeclarable(wireTypes, { type, wire }) {
165
+ const current = wireTypes.get(type);
166
+ if (current !== void 0 && current !== wire) {
167
+ throw new WireTypeCollisionError(
168
+ `Event type '${type}' has the wire name '${current}' in the wireTypes of the codec and '${wire}' from a route or catalog`
169
+ );
170
+ }
171
+ const owner = [...wireTypes].find(([other, name]) => name === wire && other !== type);
172
+ if (owner !== void 0) {
173
+ throw new WireTypeCollisionError(
174
+ `Event types '${owner[0]}' and '${type}' share the wire name '${wire}' in the codec and a route or catalog`
175
+ );
176
+ }
177
+ }
178
+
92
179
  // src/internal/json-envelope-plan.ts
93
180
  var OPTIONAL_NAMES = [
94
181
  "key",
@@ -115,8 +202,9 @@ function compile(mapping, transportIds) {
115
202
  if (topLevelHeaders && payload === void 0) {
116
203
  throw new ConfigurationError("jsonEnvelope headers 'top-level-fields' needs a body payload");
117
204
  }
205
+ const type = compileType(mapping.type);
118
206
  const plan = {
119
- type: compileType(mapping.type),
207
+ type,
120
208
  id: compileId(mapping.id, transportIds),
121
209
  idWhenMissing: isJsonObject(mapping.id) && "whenMissing" in mapping.id,
122
210
  optional,
@@ -126,6 +214,8 @@ function compile(mapping, transportIds) {
126
214
  topLevelNames: /* @__PURE__ */ new Set(),
127
215
  mappedHeaders: /* @__PURE__ */ new Set(),
128
216
  timeFormat: compileTimeFormat(mapping.occurredAt),
217
+ otherFields: compileOtherFields(mapping, payload !== void 0),
218
+ ...compileWireTypes(mapping.wireTypes, { fixed: "fixed" in type }),
129
219
  ...compileExtras(mapping, payload !== void 0)
130
220
  };
131
221
  return assertDisjoint(plan);
@@ -159,6 +249,20 @@ function compileExtras(mapping, hasBody) {
159
249
  bodyOrder
160
250
  };
161
251
  }
252
+ function compileOtherFields(mapping, hasBody) {
253
+ const otherFields = mapping.otherFields;
254
+ if (otherFields === void 0) {
255
+ return void 0;
256
+ }
257
+ if (!hasBody) {
258
+ throw new ConfigurationError("jsonEnvelope otherFields needs a body payload");
259
+ }
260
+ const field = isJsonObject(otherFields) ? otherFields["payloadField"] : void 0;
261
+ if (typeof field !== "string" || field.length === 0) {
262
+ throw new ConfigurationError("jsonEnvelope otherFields must be { payloadField: '<name>' }");
263
+ }
264
+ return field;
265
+ }
162
266
  function compileType(type) {
163
267
  if (isJsonObject(type) && typeof type["fixed"] === "string") {
164
268
  assertEventType(type["fixed"]);
@@ -268,6 +372,56 @@ function assertCompatiblePaths(path, other) {
268
372
  }
269
373
  }
270
374
 
375
+ // src/internal/json-envelope-other-fields.ts
376
+ function unusedFields(body, { plan }) {
377
+ return Object.entries(body).filter(
378
+ ([name, value]) => !plan.topLevelNames.has(name) && !(plan.topLevelHeaders && typeof value === "string")
379
+ );
380
+ }
381
+ function withOtherFields(payload, { body, context }) {
382
+ const field = context.plan.otherFields;
383
+ if (field === void 0) {
384
+ return payload;
385
+ }
386
+ const fields = unusedFields(body, context);
387
+ if (!isJsonObject(payload) && fields.length === 0) {
388
+ return payload;
389
+ }
390
+ if (!isJsonObject(payload) || Object.hasOwn(payload, field)) {
391
+ throw context.failures.invalid(
392
+ `otherFields keeps the unused top-level fields under the payload field '${field}', but the payload is not an object without that field`
393
+ );
394
+ }
395
+ return Object.fromEntries([...Object.entries(payload), [field, Object.fromEntries(fields)]]);
396
+ }
397
+ function splitOtherFields(envelope, context) {
398
+ const field = context.plan.otherFields;
399
+ const { payload } = envelope;
400
+ if (field === void 0 || !isJsonObject(payload) || !Object.hasOwn(payload, field)) {
401
+ return { payload, fields: [] };
402
+ }
403
+ const others = payload[field];
404
+ if (!isJsonObject(others)) {
405
+ throw context.failures.unencodable(
406
+ `Event ${envelope.id} has a payload field '${field}' that is not an object of top-level fields`
407
+ );
408
+ }
409
+ const fields = Object.entries(others);
410
+ const refused = fields.find((entry) => isRefused(entry, context));
411
+ if (refused !== void 0) {
412
+ throw context.failures.unencodable(
413
+ `Event ${envelope.id} keeps a top-level field '${refused[0]}' under '${field}' that the mapping reads as another value`
414
+ );
415
+ }
416
+ return {
417
+ payload: Object.fromEntries(Object.entries(payload).filter(([name]) => name !== field)),
418
+ fields
419
+ };
420
+ }
421
+ function isRefused([name, value], { plan }) {
422
+ return plan.topLevelNames.has(name) || plan.topLevelHeaders && typeof value === "string";
423
+ }
424
+
271
425
  // src/internal/json-envelope-read.ts
272
426
  var RFC3339_PATTERN = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d+)?(?:Z|[+-]\d{2}:\d{2})$/i;
273
427
  var EPOCH_PATTERN = /^\d+$/;
@@ -304,13 +458,8 @@ function readType(message, { plan, failures }) {
304
458
  return plan.type.fixed;
305
459
  }
306
460
  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;
461
+ const wire = requireText(readText(message, { location, failures }), { location, failures });
462
+ return eventTypeOf(wire, { plan, failures });
314
463
  }
315
464
  function readPayload(body, { plan, failures }) {
316
465
  if (plan.payload === void 0) {
@@ -320,7 +469,10 @@ function readPayload(body, { plan, failures }) {
320
469
  if (payload === void 0) {
321
470
  throw failures.invalid(`The message has no payload at '${plan.payload.join(".")}'`);
322
471
  }
323
- return payload;
472
+ return withOtherFields(payload, {
473
+ body,
474
+ context: { plan, failures }
475
+ });
324
476
  }
325
477
  function readEventHeaders(message, plan) {
326
478
  if (plan.topLevelHeaders) {
@@ -410,7 +562,7 @@ function writeMessage(envelope, { plan, failures }) {
410
562
  };
411
563
  const typeAt = typeLocation(envelope, { plan, failures });
412
564
  if (typeAt !== void 0) {
413
- put(typeAt, envelope.type);
565
+ put(typeAt, wireTypeOf(envelope, { plan, failures }));
414
566
  }
415
567
  if (plan.id !== void 0) {
416
568
  put(plan.id, envelope.id);
@@ -421,12 +573,8 @@ function writeMessage(envelope, { plan, failures }) {
421
573
  put(location, value);
422
574
  }
423
575
  }
424
- const body = writeBody(envelope, { plan, root });
425
- if (plan.topLevelHeaders) {
426
- spreadHeaders(envelope, { plan, failures, root: body });
427
- }
428
576
  return {
429
- body,
577
+ body: writeBody(envelope, { plan, failures, root }),
430
578
  headers: Object.fromEntries([
431
579
  ...eventHeaderEntries(envelope, { plan, failures }),
432
580
  ...headers,
@@ -463,11 +611,12 @@ function optionalValues(envelope, { plan, failures }) {
463
611
  ["causationId", envelope.causationId]
464
612
  ];
465
613
  }
466
- function writeBody(envelope, { plan, root }) {
614
+ function writeBody(envelope, { plan, failures, root }) {
615
+ const { payload, fields } = splitOtherFields(envelope, { plan, failures });
467
616
  if (plan.payload === void 0) {
468
- return envelope.payload;
617
+ return payload;
469
618
  }
470
- setAt(root, { path: plan.payload, value: envelope.payload });
619
+ setAt(root, { path: plan.payload, value: payload });
471
620
  if (plan.headers !== void 0) {
472
621
  setAt(root, {
473
622
  path: plan.headers,
@@ -477,6 +626,10 @@ function writeBody(envelope, { plan, root }) {
477
626
  for (const { path, value } of plan.extraBody) {
478
627
  setAt(root, { path, value: typeof value === "function" ? value(envelope) : value });
479
628
  }
629
+ if (plan.topLevelHeaders) {
630
+ spreadHeaders(envelope, { plan, failures, root });
631
+ }
632
+ spreadOtherFields(envelope, { failures, fields, root });
480
633
  return ordered(root, plan.bodyOrder);
481
634
  }
482
635
  function spreadHeaders(envelope, { plan, failures, root }) {
@@ -489,6 +642,20 @@ function spreadHeaders(envelope, { plan, failures, root }) {
489
642
  setAt(root, { path: [name], value });
490
643
  }
491
644
  }
645
+ function spreadOtherFields(envelope, {
646
+ failures,
647
+ fields,
648
+ root
649
+ }) {
650
+ for (const [name, value] of fields) {
651
+ if (Object.hasOwn(root, name)) {
652
+ throw failures.unencodable(
653
+ `Event ${envelope.id} keeps a top-level field '${name}' that an event header also writes`
654
+ );
655
+ }
656
+ setAt(root, { path: [name], value });
657
+ }
658
+ }
492
659
  function timeValue(text, { envelope, format }) {
493
660
  const milliseconds = envelope.occurredAt.getTime();
494
661
  if (format === "epoch-millis") {
@@ -746,6 +913,6 @@ var NonEmptyText = class _NonEmptyText {
746
913
  }
747
914
  };
748
915
 
749
- export { BoundedInteger, DecodedEventReader, ExponentialBackoff, HandedOutLedger, LaneLeaseTokenFormat, NonEmptyText, OwnerSerializedClaims, SubscribingTransport, assertPositiveInteger, createCloudEventReaders, createJsonEnvelopeFormat, fromHeader, fromPayloadField, isJsonObject, mediaTypeOf, observeRejection, writeStructuredCloudEvent };
916
+ export { BoundedInteger, DecodedEventReader, ExponentialBackoff, HandedOutLedger, LaneLeaseTokenFormat, NonEmptyText, OwnerSerializedClaims, SubscribingTransport, assertPositiveInteger, createCloudEventReaders, createJsonEnvelopeFormat, fromHeader, fromPayloadField, isJsonObject, mediaTypeOf, mergeWireTypes, observeRejection, withDeclaredWireTypes, writeStructuredCloudEvent };
750
917
  //# sourceMappingURL=index.js.map
751
918
  //# sourceMappingURL=index.js.map