@relaybus/kafka 0.1.0 → 0.3.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 +166 -21
- package/dist/index.cjs +458 -96
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +245 -23
- package/dist/index.d.ts +245 -23
- package/dist/index.js +446 -98
- package/dist/index.js.map +1 -1
- package/package.json +22 -9
package/dist/index.cjs
CHANGED
|
@@ -1,16 +1,37 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
var core = require('@relaybus/core');
|
|
4
|
+
var internal = require('@relaybus/core/internal');
|
|
4
5
|
|
|
5
|
-
// src/
|
|
6
|
+
// src/errors.ts
|
|
6
7
|
var InvalidKafkaMessageError = class extends core.RelayBusError {
|
|
8
|
+
name = "InvalidKafkaMessageError";
|
|
9
|
+
code = "RELAYBUS_KAFKA_INVALID_MESSAGE";
|
|
7
10
|
};
|
|
8
11
|
var UnencodableEventError = class extends core.NonRetryableError {
|
|
9
12
|
name = "UnencodableEventError";
|
|
13
|
+
code = "RELAYBUS_KAFKA_UNENCODABLE_EVENT";
|
|
14
|
+
};
|
|
15
|
+
var PublishTimeoutError = class extends core.RelayBusError {
|
|
16
|
+
name = "PublishTimeoutError";
|
|
17
|
+
code = "RELAYBUS_KAFKA_PUBLISH_TIMEOUT";
|
|
18
|
+
};
|
|
19
|
+
var TransportClosedError = class extends core.RelayBusError {
|
|
20
|
+
name = "TransportClosedError";
|
|
21
|
+
code = "RELAYBUS_KAFKA_TRANSPORT_CLOSED";
|
|
22
|
+
};
|
|
23
|
+
var GroupJoinTimeoutError = class extends core.RelayBusError {
|
|
24
|
+
name = "GroupJoinTimeoutError";
|
|
25
|
+
code = "RELAYBUS_KAFKA_GROUP_JOIN_TIMEOUT";
|
|
26
|
+
};
|
|
27
|
+
var HandlerFailedError = class extends core.RelayBusError {
|
|
28
|
+
name = "HandlerFailedError";
|
|
29
|
+
code = "RELAYBUS_KAFKA_HANDLER_FAILED";
|
|
10
30
|
};
|
|
11
31
|
var SPEC_VERSION = "1.0";
|
|
12
32
|
var CONTENT_TYPE = "content-type";
|
|
13
33
|
var JSON_MEDIA_TYPE = "application/json";
|
|
34
|
+
var STRUCTURED_MEDIA_TYPE = "application/cloudevents+json";
|
|
14
35
|
var CLOUDEVENTS_PREFIX = "ce_";
|
|
15
36
|
var ESCAPE_PREFIX = "x-relaybus-h-";
|
|
16
37
|
var RESERVED_PREFIXES = [CLOUDEVENTS_PREFIX, "x-relaybus-"];
|
|
@@ -25,6 +46,7 @@ var HEADER = {
|
|
|
25
46
|
causationId: "ce_causationid"
|
|
26
47
|
};
|
|
27
48
|
var PROTOCOL_HEADERS = /* @__PURE__ */ new Set([...Object.values(HEADER), CONTENT_TYPE]);
|
|
49
|
+
var MAXIMUM_RFC3339_YEAR = 9999;
|
|
28
50
|
var RFC3339_PATTERN = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d+)?(?:Z|[+-]\d{2}:\d{2})$/i;
|
|
29
51
|
var strictUtf8 = new TextDecoder("utf-8", { fatal: true });
|
|
30
52
|
var lenientUtf8 = new TextDecoder("utf-8");
|
|
@@ -52,25 +74,39 @@ function encodeEnvelope(envelope) {
|
|
|
52
74
|
}
|
|
53
75
|
return { key: envelope.key, value, headers: Object.fromEntries(entries) };
|
|
54
76
|
}
|
|
77
|
+
function encodeStructuredEnvelope(envelope) {
|
|
78
|
+
const value = internal.writeStructuredCloudEvent(envelope, {
|
|
79
|
+
time: formatTime(envelope),
|
|
80
|
+
data: serializePayload(envelope)
|
|
81
|
+
});
|
|
82
|
+
const entries = Object.entries(envelope.headers).map(
|
|
83
|
+
([name, headerValue]) => [escapeUserHeaderName(name), headerValue]
|
|
84
|
+
);
|
|
85
|
+
entries.push([CONTENT_TYPE, STRUCTURED_MEDIA_TYPE]);
|
|
86
|
+
return { key: envelope.key, value, headers: Object.fromEntries(entries) };
|
|
87
|
+
}
|
|
55
88
|
function serializePayload(envelope) {
|
|
56
|
-
|
|
89
|
+
return serializeJson(envelope, envelope.payload);
|
|
90
|
+
}
|
|
91
|
+
function serializeJson(envelope, value) {
|
|
92
|
+
let text;
|
|
57
93
|
try {
|
|
58
|
-
|
|
94
|
+
text = JSON.stringify(value);
|
|
59
95
|
} catch (error) {
|
|
60
96
|
throw new UnencodableEventError(
|
|
61
97
|
`Event ${envelope.id} has a payload that JSON cannot represent: ${error.message}`
|
|
62
98
|
);
|
|
63
99
|
}
|
|
64
|
-
if (
|
|
100
|
+
if (text === void 0) {
|
|
65
101
|
throw new UnencodableEventError(
|
|
66
102
|
`Event ${envelope.id} has a payload that JSON cannot represent (undefined, a function or a symbol)`
|
|
67
103
|
);
|
|
68
104
|
}
|
|
69
|
-
return
|
|
105
|
+
return text;
|
|
70
106
|
}
|
|
71
107
|
function formatTime(envelope) {
|
|
72
108
|
const year = envelope.occurredAt.getUTCFullYear();
|
|
73
|
-
if (Number.isNaN(year) || year < 0 || year >
|
|
109
|
+
if (Number.isNaN(year) || year < 0 || year > MAXIMUM_RFC3339_YEAR) {
|
|
74
110
|
throw new UnencodableEventError(
|
|
75
111
|
`Event ${envelope.id} has an occurredAt that is not an RFC 3339 instant (invalid date or year outside 0..9999)`
|
|
76
112
|
);
|
|
@@ -118,11 +154,14 @@ function readHeaders(incoming) {
|
|
|
118
154
|
if (typeof raw !== "string" && !(raw instanceof Uint8Array)) {
|
|
119
155
|
throw invalid(`Header '${name}' is repeated`);
|
|
120
156
|
}
|
|
121
|
-
headers.set(
|
|
157
|
+
headers.set(
|
|
158
|
+
name,
|
|
159
|
+
decodeText(raw, { strict: PROTOCOL_HEADERS.has(name), label: `Header '${name}'` })
|
|
160
|
+
);
|
|
122
161
|
}
|
|
123
162
|
return headers;
|
|
124
163
|
}
|
|
125
|
-
function decodeText(raw, strict, label) {
|
|
164
|
+
function decodeText(raw, { strict, label }) {
|
|
126
165
|
if (typeof raw === "string") {
|
|
127
166
|
return raw;
|
|
128
167
|
}
|
|
@@ -145,21 +184,24 @@ function assertJsonContentType(contentType) {
|
|
|
145
184
|
throw invalid(`${CONTENT_TYPE} must be '${JSON_MEDIA_TYPE}', received '${contentType ?? ""}'`);
|
|
146
185
|
}
|
|
147
186
|
}
|
|
148
|
-
function resolveTime(text,
|
|
187
|
+
function resolveTime(text, recordTimestamp2) {
|
|
149
188
|
if (text !== void 0 && text.length > 0) {
|
|
150
189
|
return parseTime(text);
|
|
151
190
|
}
|
|
152
|
-
const
|
|
153
|
-
|
|
154
|
-
if (recordTimestamp === void 0 || recordTimestamp === "" || Number.isNaN(time.getTime())) {
|
|
191
|
+
const time = readRecordTimestamp(recordTimestamp2);
|
|
192
|
+
if (time === void 0) {
|
|
155
193
|
throw invalid(`${HEADER.time} is missing and the record has no usable timestamp`);
|
|
156
194
|
}
|
|
157
195
|
return time;
|
|
158
196
|
}
|
|
159
|
-
function
|
|
197
|
+
function readRecordTimestamp(recordTimestamp2) {
|
|
198
|
+
const time = new Date(Number(recordTimestamp2));
|
|
199
|
+
return recordTimestamp2 === void 0 || recordTimestamp2 === "" || Number.isNaN(time.getTime()) ? void 0 : time;
|
|
200
|
+
}
|
|
201
|
+
function parseTime(text, label = HEADER.time) {
|
|
160
202
|
const time = new Date(text);
|
|
161
203
|
if (!RFC3339_PATTERN.test(text) || Number.isNaN(time.getTime())) {
|
|
162
|
-
throw invalid(`${
|
|
204
|
+
throw invalid(`${label} is not an RFC 3339 timestamp: '${text}'`);
|
|
163
205
|
}
|
|
164
206
|
return time;
|
|
165
207
|
}
|
|
@@ -167,7 +209,7 @@ function parsePayload(value) {
|
|
|
167
209
|
if (value === null) {
|
|
168
210
|
throw invalid("Message has no value");
|
|
169
211
|
}
|
|
170
|
-
const text = decodeText(value, true, "Message value");
|
|
212
|
+
const text = decodeText(value, { strict: true, label: "Message value" });
|
|
171
213
|
try {
|
|
172
214
|
return JSON.parse(text);
|
|
173
215
|
} catch {
|
|
@@ -175,12 +217,15 @@ function parsePayload(value) {
|
|
|
175
217
|
}
|
|
176
218
|
}
|
|
177
219
|
function resolveKey(subject, recordKey) {
|
|
178
|
-
const keyText =
|
|
220
|
+
const keyText = readRecordKey(recordKey);
|
|
179
221
|
if (subject !== void 0 && keyText !== null && subject !== keyText) {
|
|
180
222
|
throw invalid(`${HEADER.subject} does not match the message key`);
|
|
181
223
|
}
|
|
182
224
|
return subject ?? keyText;
|
|
183
225
|
}
|
|
226
|
+
function readRecordKey(recordKey) {
|
|
227
|
+
return recordKey === null ? null : decodeText(recordKey, { strict: true, label: "Message key" });
|
|
228
|
+
}
|
|
184
229
|
function collectUserHeaders(headers) {
|
|
185
230
|
const entries = [];
|
|
186
231
|
for (const [name, value] of headers) {
|
|
@@ -208,12 +253,250 @@ function toOutgoingHeaders(incoming) {
|
|
|
208
253
|
function toBufferOrString(value) {
|
|
209
254
|
return typeof value === "string" ? value : Buffer.from(value);
|
|
210
255
|
}
|
|
211
|
-
var
|
|
212
|
-
|
|
213
|
-
};
|
|
214
|
-
var
|
|
215
|
-
|
|
216
|
-
|
|
256
|
+
var { optionalAttribute, requiredAttribute, assertValidType, assertJsonDataContentType } = internal.createCloudEventReaders(invalid);
|
|
257
|
+
var STRUCTURED_MEDIA_TYPE2 = "application/cloudevents+json";
|
|
258
|
+
var exactUtf8 = new TextDecoder("utf-8", { fatal: true, ignoreBOM: true });
|
|
259
|
+
var SPEC_VERSION2 = "1.0";
|
|
260
|
+
var SPEC_VERSION_HEADER = "ce_specversion";
|
|
261
|
+
var STRUCTURED_ATTRIBUTES = /* @__PURE__ */ new Set([
|
|
262
|
+
"specversion",
|
|
263
|
+
"id",
|
|
264
|
+
"type",
|
|
265
|
+
"source",
|
|
266
|
+
"time",
|
|
267
|
+
"subject",
|
|
268
|
+
"datacontenttype",
|
|
269
|
+
"dataschema",
|
|
270
|
+
"data",
|
|
271
|
+
"data_base64",
|
|
272
|
+
"correlationid",
|
|
273
|
+
"causationid"
|
|
274
|
+
]);
|
|
275
|
+
function decodeCloudEvent(record) {
|
|
276
|
+
const headers = readHeaders(record.headers);
|
|
277
|
+
if (internal.mediaTypeOf(headers.get(CONTENT_TYPE)) !== STRUCTURED_MEDIA_TYPE2) {
|
|
278
|
+
return decodeEnvelope(record);
|
|
279
|
+
}
|
|
280
|
+
const body = parsePayload(record.value);
|
|
281
|
+
if (!internal.isJsonObject(body)) {
|
|
282
|
+
throw invalid("A structured CloudEvent must be a JSON object");
|
|
283
|
+
}
|
|
284
|
+
if (body["specversion"] !== SPEC_VERSION2) {
|
|
285
|
+
throw invalid(`specversion must be '${SPEC_VERSION2}'`);
|
|
286
|
+
}
|
|
287
|
+
const type = requiredAttribute(body, "type");
|
|
288
|
+
assertValidType(type, "type");
|
|
289
|
+
assertJsonDataContentType(optionalAttribute(body, "datacontenttype"));
|
|
290
|
+
if (Object.hasOwn(body, "data_base64")) {
|
|
291
|
+
throw invalid("data_base64 is not supported: relaybus payloads are JSON, send them as data");
|
|
292
|
+
}
|
|
293
|
+
const time = optionalAttribute(body, "time");
|
|
294
|
+
return {
|
|
295
|
+
id: requiredAttribute(body, "id"),
|
|
296
|
+
type,
|
|
297
|
+
source: requiredAttribute(body, "source"),
|
|
298
|
+
key: optionalAttribute(body, "subject") ?? readRecordKey(record.key),
|
|
299
|
+
payload: Object.hasOwn(body, "data") ? body["data"] : null,
|
|
300
|
+
headers: { ...collectUserHeaders(headers), ...extensionAttributes(body) },
|
|
301
|
+
correlationId: optionalAttribute(body, "correlationid") ?? null,
|
|
302
|
+
causationId: optionalAttribute(body, "causationid") ?? null,
|
|
303
|
+
occurredAt: time === void 0 ? requireRecordTimestamp(record) : parseTime(time, "time")
|
|
304
|
+
};
|
|
305
|
+
}
|
|
306
|
+
function plainJsonDecoder(options) {
|
|
307
|
+
if (typeof options.type === "string") {
|
|
308
|
+
core.assertEventType(options.type);
|
|
309
|
+
}
|
|
310
|
+
return (record) => {
|
|
311
|
+
const headers = readHeadersLeniently(record.headers);
|
|
312
|
+
if (record.headers?.[SPEC_VERSION_HEADER] !== void 0 || internal.mediaTypeOf(headers[CONTENT_TYPE]) === STRUCTURED_MEDIA_TYPE2) {
|
|
313
|
+
return decodeCloudEvent(record);
|
|
314
|
+
}
|
|
315
|
+
const plain = {
|
|
316
|
+
topic: record.topic,
|
|
317
|
+
partition: record.partition,
|
|
318
|
+
offset: record.offset,
|
|
319
|
+
key: readKeyLeniently(record.key),
|
|
320
|
+
headers,
|
|
321
|
+
payload: parsePayload(record.value),
|
|
322
|
+
timestamp: record.timestamp
|
|
323
|
+
};
|
|
324
|
+
const type = resolveText(options.type, { record: plain, name: "type" });
|
|
325
|
+
if (type === void 0) {
|
|
326
|
+
throw invalid("Record has no event type: the type option found none");
|
|
327
|
+
}
|
|
328
|
+
assertValidType(type, "The event type");
|
|
329
|
+
return {
|
|
330
|
+
id: resolveText(options.id, { record: plain, name: "id" }) ?? `kafka:${record.topic}:${record.partition}:${record.offset}`,
|
|
331
|
+
type,
|
|
332
|
+
source: resolveText(options.source, { record: plain, name: "source" }) ?? record.topic,
|
|
333
|
+
key: options.key === void 0 ? plain.key : resolveText(options.key, { record: plain, name: "key" }) ?? null,
|
|
334
|
+
payload: plain.payload,
|
|
335
|
+
headers,
|
|
336
|
+
correlationId: null,
|
|
337
|
+
causationId: null,
|
|
338
|
+
occurredAt: resolveOccurredAt(options.occurredAt, { plain, record })
|
|
339
|
+
};
|
|
340
|
+
};
|
|
341
|
+
}
|
|
342
|
+
function completeDecodedEvent(decoded, record) {
|
|
343
|
+
if (typeof decoded !== "object" || decoded === null) {
|
|
344
|
+
throw invalid("The decoder must return an event object synchronously");
|
|
345
|
+
}
|
|
346
|
+
if ("then" in decoded) {
|
|
347
|
+
observeRejection(decoded);
|
|
348
|
+
throw invalid("The decoder must return an event object synchronously");
|
|
349
|
+
}
|
|
350
|
+
const event = decoded;
|
|
351
|
+
return {
|
|
352
|
+
id: event.id,
|
|
353
|
+
type: event.type,
|
|
354
|
+
source: event.source ?? record.topic,
|
|
355
|
+
key: event.key === void 0 ? readRecordKey(record.key) : event.key,
|
|
356
|
+
payload: event.payload,
|
|
357
|
+
headers: event.headers ?? {},
|
|
358
|
+
correlationId: event.correlationId ?? null,
|
|
359
|
+
causationId: event.causationId ?? null,
|
|
360
|
+
occurredAt: event.occurredAt ?? requireRecordTimestamp(record)
|
|
361
|
+
};
|
|
362
|
+
}
|
|
363
|
+
function extensionAttributes(body) {
|
|
364
|
+
return Object.fromEntries(
|
|
365
|
+
Object.entries(body).filter(
|
|
366
|
+
(entry) => !STRUCTURED_ATTRIBUTES.has(entry[0]) && typeof entry[1] === "string"
|
|
367
|
+
)
|
|
368
|
+
);
|
|
369
|
+
}
|
|
370
|
+
function requireRecordTimestamp(record) {
|
|
371
|
+
const time = readRecordTimestamp(record.timestamp);
|
|
372
|
+
if (time === void 0) {
|
|
373
|
+
throw invalid("The event has no time and the record has no usable timestamp");
|
|
374
|
+
}
|
|
375
|
+
return time;
|
|
376
|
+
}
|
|
377
|
+
function readHeadersLeniently(incoming) {
|
|
378
|
+
const entries = [];
|
|
379
|
+
for (const [name, raw] of Object.entries(incoming ?? {})) {
|
|
380
|
+
const value = Array.isArray(raw) ? raw.at(-1) : raw;
|
|
381
|
+
const text = value === void 0 ? void 0 : decodeUtf8OrUndefined(value);
|
|
382
|
+
if (text !== void 0) {
|
|
383
|
+
entries.push([name, text]);
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
return Object.fromEntries(entries);
|
|
387
|
+
}
|
|
388
|
+
function decodeUtf8OrUndefined(value) {
|
|
389
|
+
if (typeof value === "string") {
|
|
390
|
+
return value;
|
|
391
|
+
}
|
|
392
|
+
try {
|
|
393
|
+
return exactUtf8.decode(value);
|
|
394
|
+
} catch {
|
|
395
|
+
return void 0;
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
function readKeyLeniently(key) {
|
|
399
|
+
if (key === null) {
|
|
400
|
+
return null;
|
|
401
|
+
}
|
|
402
|
+
try {
|
|
403
|
+
return readRecordKey(key);
|
|
404
|
+
} catch {
|
|
405
|
+
return Buffer.from(key).toString("base64");
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
function resolveText(option, { record, name }) {
|
|
409
|
+
const value = typeof option === "function" ? option(record) : option;
|
|
410
|
+
if (value === void 0 || value === null) {
|
|
411
|
+
return void 0;
|
|
412
|
+
}
|
|
413
|
+
if (typeof value !== "string") {
|
|
414
|
+
observeRejection(value);
|
|
415
|
+
throw invalid(`The ${name} option returned ${typeof value}, expected a string`);
|
|
416
|
+
}
|
|
417
|
+
return value.length === 0 ? void 0 : value;
|
|
418
|
+
}
|
|
419
|
+
function resolveOccurredAt(option, { plain, record }) {
|
|
420
|
+
const value = option?.(plain);
|
|
421
|
+
if (value === void 0) {
|
|
422
|
+
return requireRecordTimestamp(record);
|
|
423
|
+
}
|
|
424
|
+
if (!(value instanceof Date) || Number.isNaN(value.getTime())) {
|
|
425
|
+
observeRejection(value);
|
|
426
|
+
throw invalid("The occurredAt option returned no valid Date");
|
|
427
|
+
}
|
|
428
|
+
return value;
|
|
429
|
+
}
|
|
430
|
+
function observeRejection(value) {
|
|
431
|
+
if (typeof value === "object" && value !== null && "then" in value) {
|
|
432
|
+
Promise.resolve(value).catch(() => void 0);
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
// src/codecs.ts
|
|
437
|
+
var cloudEventsBinary = Object.freeze({
|
|
438
|
+
encode: encodeEnvelope,
|
|
439
|
+
decode: decodeEnvelope
|
|
440
|
+
});
|
|
441
|
+
var cloudEventsStructured = Object.freeze({
|
|
442
|
+
encode: encodeStructuredEnvelope,
|
|
443
|
+
decode: decodeCloudEvent
|
|
444
|
+
});
|
|
445
|
+
var RECORD_POSITION = "record-position";
|
|
446
|
+
var EVENT_ID_HEADER = "relaybus-event-id";
|
|
447
|
+
var DEFAULT_CONTENT_TYPE = "application/json";
|
|
448
|
+
function jsonEnvelope(mapping) {
|
|
449
|
+
const format = internal.createJsonEnvelopeFormat(mapping, {
|
|
450
|
+
transportIds: [RECORD_POSITION],
|
|
451
|
+
failures: { invalid, unencodable: (reason) => new UnencodableEventError(reason) }
|
|
452
|
+
});
|
|
453
|
+
const contentType = mapping.contentType ?? DEFAULT_CONTENT_TYPE;
|
|
454
|
+
const stampsTime = mapping.occurredAt === void 0;
|
|
455
|
+
const ownHeaders = (envelope) => mapping.id === RECORD_POSITION ? { [CONTENT_TYPE]: contentType, [EVENT_ID_HEADER]: envelope.id } : { [CONTENT_TYPE]: contentType };
|
|
456
|
+
return {
|
|
457
|
+
encode: (envelope) => {
|
|
458
|
+
assertRepresentable(envelope);
|
|
459
|
+
const { body, headers } = format.write(envelope);
|
|
460
|
+
const taken = [CONTENT_TYPE, EVENT_ID_HEADER].find((name) => Object.hasOwn(headers, name));
|
|
461
|
+
if (taken !== void 0) {
|
|
462
|
+
throw new UnencodableEventError(
|
|
463
|
+
`Event ${envelope.id} has a header '${taken}', which the codec writes itself`
|
|
464
|
+
);
|
|
465
|
+
}
|
|
466
|
+
return {
|
|
467
|
+
key: envelope.key,
|
|
468
|
+
value: serializeJson(envelope, body),
|
|
469
|
+
headers: { ...headers, ...ownHeaders(envelope) },
|
|
470
|
+
...stampsTime ? { timestamp: recordTimestamp(envelope) } : {}
|
|
471
|
+
};
|
|
472
|
+
},
|
|
473
|
+
decode: (record) => {
|
|
474
|
+
const { [EVENT_ID_HEADER]: eventId, ...headers } = withoutContentType(
|
|
475
|
+
readHeadersLeniently(record.headers)
|
|
476
|
+
);
|
|
477
|
+
const fields = format.read({ body: parsePayload(record.value), headers });
|
|
478
|
+
return {
|
|
479
|
+
...fields,
|
|
480
|
+
id: fields.id ?? (eventId === void 0 || eventId === "" ? `kafka:${record.topic}:${record.partition}:${record.offset}` : eventId)
|
|
481
|
+
};
|
|
482
|
+
}
|
|
483
|
+
};
|
|
484
|
+
}
|
|
485
|
+
function withoutContentType(headers) {
|
|
486
|
+
return Object.fromEntries(Object.entries(headers).filter(([name]) => name !== CONTENT_TYPE));
|
|
487
|
+
}
|
|
488
|
+
function assertRepresentable(envelope) {
|
|
489
|
+
const kind = typeof envelope.payload;
|
|
490
|
+
if (kind === "undefined" || kind === "function" || kind === "symbol") {
|
|
491
|
+
throw new UnencodableEventError(
|
|
492
|
+
`Event ${envelope.id} has a payload that JSON cannot represent (undefined, a function or a symbol)`
|
|
493
|
+
);
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
function recordTimestamp(envelope) {
|
|
497
|
+
formatTime(envelope);
|
|
498
|
+
return String(envelope.occurredAt.getTime());
|
|
499
|
+
}
|
|
217
500
|
var DEFAULTS = {
|
|
218
501
|
name: "kafka",
|
|
219
502
|
maxInFlightRequests: 1,
|
|
@@ -228,6 +511,7 @@ var DEFAULTS = {
|
|
|
228
511
|
};
|
|
229
512
|
var KafkaTransport = class {
|
|
230
513
|
name;
|
|
514
|
+
system = "kafka";
|
|
231
515
|
#kafka;
|
|
232
516
|
#groupId;
|
|
233
517
|
#fromBeginning;
|
|
@@ -235,6 +519,7 @@ var KafkaTransport = class {
|
|
|
235
519
|
#producerConfig;
|
|
236
520
|
#consumerConfig;
|
|
237
521
|
#invalidPolicy;
|
|
522
|
+
#codec;
|
|
238
523
|
#logger;
|
|
239
524
|
#settings;
|
|
240
525
|
#sleepers = /* @__PURE__ */ new Set();
|
|
@@ -253,15 +538,13 @@ var KafkaTransport = class {
|
|
|
253
538
|
this.#kafka = options.kafka;
|
|
254
539
|
this.#groupId = options.groupId;
|
|
255
540
|
this.#fromBeginning = options.fromBeginning ?? true;
|
|
256
|
-
this.#maxInFlightRequests =
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
`maxInFlightRequests must be an integer from 1 to 5, received ${this.#maxInFlightRequests}`
|
|
260
|
-
);
|
|
261
|
-
}
|
|
541
|
+
this.#maxInFlightRequests = assertMaxInFlightRequests(
|
|
542
|
+
options.maxInFlightRequests ?? DEFAULTS.maxInFlightRequests
|
|
543
|
+
);
|
|
262
544
|
this.#producerConfig = options.producerConfig ?? {};
|
|
263
545
|
this.#consumerConfig = options.consumerConfig ?? {};
|
|
264
546
|
this.#invalidPolicy = options.onInvalidMessage ?? "dead-letter";
|
|
547
|
+
this.#codec = resolveCodec(options);
|
|
265
548
|
this.#logger = options.logger ?? console;
|
|
266
549
|
this.#settings = resolveSettings(options);
|
|
267
550
|
}
|
|
@@ -291,11 +574,10 @@ var KafkaTransport = class {
|
|
|
291
574
|
}
|
|
292
575
|
async publish(envelope, destination) {
|
|
293
576
|
const producer = await this.#connectedProducer();
|
|
294
|
-
const { key, value, headers } = encodeEnvelope(envelope);
|
|
295
577
|
await this.#send(producer, {
|
|
296
578
|
topic: destination,
|
|
297
579
|
acks: -1,
|
|
298
|
-
messages: [
|
|
580
|
+
messages: [this.#codec.encode(envelope)]
|
|
299
581
|
});
|
|
300
582
|
}
|
|
301
583
|
/**
|
|
@@ -305,13 +587,14 @@ var KafkaTransport = class {
|
|
|
305
587
|
* @throws ConfigurationError when `groupId` is missing, `destinations` is empty
|
|
306
588
|
* or the transport already subscribed (create one transport per subscription).
|
|
307
589
|
*/
|
|
308
|
-
|
|
590
|
+
// eslint-disable-next-line relaybus/max-two-params -- the Transport.subscribe signature
|
|
591
|
+
async subscribe(destinations, onMessage, hooks) {
|
|
309
592
|
this.#assertCanSubscribe(destinations);
|
|
310
593
|
this.#state = "subscribed";
|
|
311
594
|
const consumer = this.#kafka.consumer({ ...this.#consumerConfig, groupId: this.#groupId });
|
|
312
595
|
this.#consumer = consumer;
|
|
313
596
|
this.#observeConsumer(consumer);
|
|
314
|
-
const start = () => this.#startConsumer(consumer, [...destinations], onMessage);
|
|
597
|
+
const start = () => this.#startConsumer(consumer, { destinations: [...destinations], onMessage, hooks });
|
|
315
598
|
this.#startConsuming = start;
|
|
316
599
|
try {
|
|
317
600
|
await start();
|
|
@@ -327,12 +610,18 @@ var KafkaTransport = class {
|
|
|
327
610
|
`[KafkaTransport](subscribe) Consuming | transport=${this.name} group=${this.#groupId} topics=${destinations.join(",")}`
|
|
328
611
|
);
|
|
329
612
|
}
|
|
330
|
-
async #startConsumer(consumer,
|
|
613
|
+
async #startConsumer(consumer, {
|
|
614
|
+
destinations,
|
|
615
|
+
onMessage,
|
|
616
|
+
hooks
|
|
617
|
+
}) {
|
|
331
618
|
await consumer.connect();
|
|
332
619
|
await consumer.subscribe({ topics: destinations, fromBeginning: this.#fromBeginning });
|
|
333
620
|
const joined = this.#waitForGroupJoin(consumer);
|
|
334
621
|
try {
|
|
335
|
-
await consumer.run({
|
|
622
|
+
await consumer.run({
|
|
623
|
+
eachMessage: (payload) => this.#handleMessage(payload, { onMessage, hooks })
|
|
624
|
+
});
|
|
336
625
|
await joined.promise;
|
|
337
626
|
} finally {
|
|
338
627
|
joined.dispose();
|
|
@@ -350,7 +639,7 @@ var KafkaTransport = class {
|
|
|
350
639
|
for (const wake of this.#sleepers) {
|
|
351
640
|
wake();
|
|
352
641
|
}
|
|
353
|
-
this.#joinWaiter?.reject(new
|
|
642
|
+
this.#joinWaiter?.reject(new TransportClosedError(`Transport '${this.name}' is closed`));
|
|
354
643
|
await this.#restartLoop;
|
|
355
644
|
const producer = await pendingProducer?.catch(() => void 0);
|
|
356
645
|
const results = await Promise.allSettled([
|
|
@@ -364,7 +653,7 @@ var KafkaTransport = class {
|
|
|
364
653
|
}
|
|
365
654
|
#assertCanSubscribe(destinations) {
|
|
366
655
|
if (this.#state === "closed") {
|
|
367
|
-
throw new
|
|
656
|
+
throw new TransportClosedError(`Transport '${this.name}' is closed`);
|
|
368
657
|
}
|
|
369
658
|
if (this.#state === "subscribed") {
|
|
370
659
|
throw new core.ConfigurationError(
|
|
@@ -400,27 +689,30 @@ var KafkaTransport = class {
|
|
|
400
689
|
}
|
|
401
690
|
/** Sends with the publish timeout; the late outcome of a timed-out send is ignored. */
|
|
402
691
|
async #send(producer, record) {
|
|
403
|
-
let
|
|
692
|
+
let cancel;
|
|
404
693
|
const timeout = new Promise((_resolve, reject) => {
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
694
|
+
cancel = internal.schedule(core.systemClock, {
|
|
695
|
+
run: () => {
|
|
696
|
+
reject(
|
|
697
|
+
new PublishTimeoutError(
|
|
698
|
+
`Transport '${this.name}' got no broker ack for topic '${record.topic}' within ${this.#settings.publishTimeoutMs}ms`
|
|
699
|
+
)
|
|
700
|
+
);
|
|
701
|
+
},
|
|
702
|
+
delayMs: this.#settings.publishTimeoutMs
|
|
703
|
+
});
|
|
412
704
|
});
|
|
413
705
|
const sending = producer.send(record);
|
|
414
706
|
sending.catch(() => void 0);
|
|
415
707
|
try {
|
|
416
708
|
await Promise.race([sending, timeout]);
|
|
417
709
|
} finally {
|
|
418
|
-
|
|
710
|
+
cancel?.();
|
|
419
711
|
}
|
|
420
712
|
}
|
|
421
713
|
#connectedProducer() {
|
|
422
714
|
if (this.#state === "closed") {
|
|
423
|
-
return Promise.reject(new
|
|
715
|
+
return Promise.reject(new TransportClosedError(`Transport '${this.name}' is closed`));
|
|
424
716
|
}
|
|
425
717
|
this.#producer ??= this.#openProducer();
|
|
426
718
|
return this.#producer;
|
|
@@ -440,40 +732,63 @@ var KafkaTransport = class {
|
|
|
440
732
|
this.#producerConnected = this.#state !== "closed";
|
|
441
733
|
return producer;
|
|
442
734
|
}
|
|
443
|
-
async #handleMessage(payload,
|
|
735
|
+
async #handleMessage(payload, {
|
|
736
|
+
onMessage,
|
|
737
|
+
hooks
|
|
738
|
+
}) {
|
|
444
739
|
let envelope;
|
|
445
740
|
try {
|
|
446
|
-
envelope =
|
|
741
|
+
envelope = this.#decodeRecord(payload);
|
|
447
742
|
} catch (error) {
|
|
448
|
-
if (!(error instanceof InvalidKafkaMessageError)) {
|
|
743
|
+
if (this.#codec === cloudEventsBinary && !(error instanceof InvalidKafkaMessageError)) {
|
|
449
744
|
throw toHandlerFailure(error);
|
|
450
745
|
}
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
"handleInvalid",
|
|
454
|
-
() => this.#handleInvalid(payload, error
|
|
455
|
-
);
|
|
746
|
+
hooks?.onInvalidMessage({ destination: payload.topic, error });
|
|
747
|
+
await this.#withRetries(payload, {
|
|
748
|
+
method: "handleInvalid",
|
|
749
|
+
operation: () => this.#handleInvalid(payload, error)
|
|
750
|
+
});
|
|
456
751
|
return;
|
|
457
752
|
}
|
|
458
|
-
await this.#withRetries(payload,
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
753
|
+
await this.#withRetries(payload, {
|
|
754
|
+
method: "deliver",
|
|
755
|
+
operation: async () => {
|
|
756
|
+
try {
|
|
757
|
+
await onMessage(envelope, { destination: payload.topic });
|
|
758
|
+
} catch (error) {
|
|
759
|
+
if (!(error instanceof core.NonRetryableError)) {
|
|
760
|
+
throw error;
|
|
761
|
+
}
|
|
762
|
+
await this.#handleInvalid(payload, error);
|
|
464
763
|
}
|
|
465
|
-
await this.#handleInvalid(payload, error.message);
|
|
466
764
|
}
|
|
467
765
|
});
|
|
468
766
|
}
|
|
469
|
-
|
|
767
|
+
#decodeRecord({ topic, partition, message }) {
|
|
768
|
+
if (this.#codec === cloudEventsBinary) {
|
|
769
|
+
return decodeEnvelope(message);
|
|
770
|
+
}
|
|
771
|
+
const record = {
|
|
772
|
+
topic,
|
|
773
|
+
partition,
|
|
774
|
+
offset: message.offset,
|
|
775
|
+
key: message.key,
|
|
776
|
+
value: message.value,
|
|
777
|
+
headers: message.headers,
|
|
778
|
+
timestamp: message.timestamp
|
|
779
|
+
};
|
|
780
|
+
return completeDecodedEvent(this.#codec.decode(record), record);
|
|
781
|
+
}
|
|
782
|
+
async #handleInvalid(payload, error) {
|
|
470
783
|
const { topic, partition, message } = payload;
|
|
784
|
+
const { message: reason, name, code } = core.toErrorInfo(error);
|
|
471
785
|
const location = `topic=${topic} partition=${partition} offset=${message.offset}`;
|
|
786
|
+
const errorFields = `errorName=${name}${code === void 0 ? "" : ` code=${code}`}`;
|
|
472
787
|
const policy = this.#invalidPolicy;
|
|
473
788
|
const deadLetterTopic = this.#deadLetterTopicFor(topic);
|
|
474
789
|
if (policy === "skip") {
|
|
475
790
|
this.#logger.error(
|
|
476
|
-
`[KafkaTransport](handleInvalid) Skipping malformed message | ${location} reason=${reason}`
|
|
791
|
+
`[KafkaTransport](handleInvalid) Skipping malformed message | ${location} reason=${reason} ${errorFields}`
|
|
477
792
|
);
|
|
478
793
|
} else if (typeof policy === "function") {
|
|
479
794
|
await policy({
|
|
@@ -485,6 +800,19 @@ var KafkaTransport = class {
|
|
|
485
800
|
value: message.value
|
|
486
801
|
});
|
|
487
802
|
} else {
|
|
803
|
+
const headers = {
|
|
804
|
+
...toOutgoingHeaders(message.headers),
|
|
805
|
+
"relaybus-invalid-reason": reason,
|
|
806
|
+
"relaybus-source-topic": topic,
|
|
807
|
+
"relaybus-source-partition": String(partition),
|
|
808
|
+
"relaybus-source-offset": message.offset,
|
|
809
|
+
"relaybus-error-name": name
|
|
810
|
+
};
|
|
811
|
+
if (code === void 0) {
|
|
812
|
+
delete headers["relaybus-error-code"];
|
|
813
|
+
} else {
|
|
814
|
+
headers["relaybus-error-code"] = code;
|
|
815
|
+
}
|
|
488
816
|
const producer = await this.#connectedProducer();
|
|
489
817
|
await this.#send(producer, {
|
|
490
818
|
topic: deadLetterTopic,
|
|
@@ -493,18 +821,12 @@ var KafkaTransport = class {
|
|
|
493
821
|
{
|
|
494
822
|
key: message.key === null ? null : Buffer.from(message.key),
|
|
495
823
|
value: message.value === null ? null : Buffer.from(message.value),
|
|
496
|
-
headers
|
|
497
|
-
...toOutgoingHeaders(message.headers),
|
|
498
|
-
"relaybus-invalid-reason": reason,
|
|
499
|
-
"relaybus-source-topic": topic,
|
|
500
|
-
"relaybus-source-partition": String(partition),
|
|
501
|
-
"relaybus-source-offset": message.offset
|
|
502
|
-
}
|
|
824
|
+
headers
|
|
503
825
|
}
|
|
504
826
|
]
|
|
505
827
|
});
|
|
506
828
|
this.#logger.error(
|
|
507
|
-
`[KafkaTransport](handleInvalid) Dead-lettered malformed message | ${location} deadLetterTopic=${deadLetterTopic} reason=${reason}`
|
|
829
|
+
`[KafkaTransport](handleInvalid) Dead-lettered malformed message | ${location} deadLetterTopic=${deadLetterTopic} reason=${reason} ${errorFields}`
|
|
508
830
|
);
|
|
509
831
|
}
|
|
510
832
|
}
|
|
@@ -513,7 +835,7 @@ var KafkaTransport = class {
|
|
|
513
835
|
* last error is rethrown so the client leaves the offset uncommitted and
|
|
514
836
|
* redelivers the record; heartbeats between attempts keep the session alive.
|
|
515
837
|
*/
|
|
516
|
-
async #withRetries(payload, method, operation) {
|
|
838
|
+
async #withRetries(payload, { method, operation }) {
|
|
517
839
|
const { maxAttempts, initialBackoffMs, maxBackoffMs } = this.#settings;
|
|
518
840
|
const location = `topic=${payload.topic} partition=${payload.partition} offset=${payload.message.offset}`;
|
|
519
841
|
const partitionKey = `${payload.topic}:${payload.partition}`;
|
|
@@ -527,12 +849,12 @@ var KafkaTransport = class {
|
|
|
527
849
|
if (attempt >= maxAttempts || this.#state === "closed") {
|
|
528
850
|
this.#stalledPartitions.add(partitionKey);
|
|
529
851
|
this.#logger.error(
|
|
530
|
-
`[KafkaTransport](
|
|
852
|
+
`[KafkaTransport](withRetries) Giving up for now, the record stays uncommitted and will be redelivered | operation=${method} ${location} attempts=${attempt} error=${reason}`
|
|
531
853
|
);
|
|
532
854
|
throw toHandlerFailure(error);
|
|
533
855
|
}
|
|
534
856
|
this.#logger.warn(
|
|
535
|
-
`[KafkaTransport](
|
|
857
|
+
`[KafkaTransport](withRetries) Attempt failed, retrying | operation=${method} ${location} attempt=${attempt} error=${reason}`
|
|
536
858
|
);
|
|
537
859
|
await this.#sleepWithHeartbeats(
|
|
538
860
|
Math.min(initialBackoffMs * 2 ** (attempt - 1), maxBackoffMs),
|
|
@@ -554,11 +876,11 @@ var KafkaTransport = class {
|
|
|
554
876
|
#sleep(milliseconds) {
|
|
555
877
|
return new Promise((resolve) => {
|
|
556
878
|
const wake = () => {
|
|
557
|
-
|
|
879
|
+
cancel();
|
|
558
880
|
this.#sleepers.delete(wake);
|
|
559
881
|
resolve();
|
|
560
882
|
};
|
|
561
|
-
const
|
|
883
|
+
const cancel = internal.schedule(core.systemClock, { run: wake, delayMs: milliseconds });
|
|
562
884
|
this.#sleepers.add(wake);
|
|
563
885
|
});
|
|
564
886
|
}
|
|
@@ -640,42 +962,53 @@ var KafkaTransport = class {
|
|
|
640
962
|
if (!this.#tracksMembership(consumer)) {
|
|
641
963
|
return { promise: Promise.resolve(), dispose: () => void 0 };
|
|
642
964
|
}
|
|
643
|
-
let
|
|
965
|
+
let cancel;
|
|
644
966
|
const promise = new Promise((resolve, reject) => {
|
|
645
967
|
this.#joinWaiter = { resolve, reject };
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
968
|
+
cancel = internal.schedule(core.systemClock, {
|
|
969
|
+
run: () => {
|
|
970
|
+
reject(
|
|
971
|
+
new GroupJoinTimeoutError(
|
|
972
|
+
`Consumer of transport '${this.name}' did not join group '${this.#groupId}' within ${this.#settings.startupTimeoutMs}ms`
|
|
973
|
+
)
|
|
974
|
+
);
|
|
975
|
+
},
|
|
976
|
+
delayMs: this.#settings.startupTimeoutMs
|
|
977
|
+
});
|
|
653
978
|
});
|
|
654
979
|
promise.catch(() => void 0);
|
|
655
980
|
return {
|
|
656
981
|
promise,
|
|
657
982
|
dispose: () => {
|
|
658
|
-
|
|
983
|
+
cancel?.();
|
|
659
984
|
this.#joinWaiter = void 0;
|
|
660
985
|
}
|
|
661
986
|
};
|
|
662
987
|
}
|
|
663
988
|
};
|
|
664
|
-
function
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
throw new core.ConfigurationError(
|
|
668
|
-
`processing.maxAttempts must be a positive integer, received ${maxAttempts}`
|
|
669
|
-
);
|
|
989
|
+
function resolveCodec({ codec, decode }) {
|
|
990
|
+
if (decode === void 0) {
|
|
991
|
+
return codec ?? cloudEventsBinary;
|
|
670
992
|
}
|
|
993
|
+
if (codec !== void 0) {
|
|
994
|
+
throw new core.ConfigurationError("Give 'codec' or the deprecated 'decode', not both");
|
|
995
|
+
}
|
|
996
|
+
return { encode: encodeEnvelope, decode };
|
|
997
|
+
}
|
|
998
|
+
function resolveSettings(options) {
|
|
999
|
+
const processing = options.processing ?? {};
|
|
1000
|
+
const maxAttempts = internal.assertPositiveInteger(
|
|
1001
|
+
processing.maxAttempts ?? DEFAULTS.maxAttempts,
|
|
1002
|
+
"processing.maxAttempts"
|
|
1003
|
+
);
|
|
671
1004
|
return {
|
|
672
1005
|
maxAttempts,
|
|
673
1006
|
initialBackoffMs: core.toMilliseconds(
|
|
674
|
-
|
|
1007
|
+
processing.initialBackoff ?? DEFAULTS.initialBackoff,
|
|
675
1008
|
"processing.initialBackoff"
|
|
676
1009
|
),
|
|
677
1010
|
maxBackoffMs: core.toMilliseconds(
|
|
678
|
-
|
|
1011
|
+
processing.maxBackoff ?? DEFAULTS.maxBackoff,
|
|
679
1012
|
"processing.maxBackoff"
|
|
680
1013
|
),
|
|
681
1014
|
startupTimeoutMs: core.toMilliseconds(
|
|
@@ -689,31 +1022,60 @@ function resolveSettings(options) {
|
|
|
689
1022
|
heartbeatIntervalMs: Math.max(
|
|
690
1023
|
1,
|
|
691
1024
|
core.toMilliseconds(
|
|
692
|
-
|
|
1025
|
+
processing.heartbeatInterval ?? DEFAULTS.heartbeatInterval,
|
|
693
1026
|
"processing.heartbeatInterval"
|
|
694
1027
|
)
|
|
695
1028
|
),
|
|
1029
|
+
...resolveRestartSettings(options.restart ?? {})
|
|
1030
|
+
};
|
|
1031
|
+
}
|
|
1032
|
+
function resolveRestartSettings(restart) {
|
|
1033
|
+
return {
|
|
696
1034
|
restartInitialBackoffMs: core.toMilliseconds(
|
|
697
|
-
|
|
1035
|
+
restart.initialBackoff ?? DEFAULTS.restartInitialBackoff,
|
|
698
1036
|
"restart.initialBackoff"
|
|
699
1037
|
),
|
|
700
1038
|
restartMaxBackoffMs: core.toMilliseconds(
|
|
701
|
-
|
|
1039
|
+
restart.maxBackoff ?? DEFAULTS.restartMaxBackoff,
|
|
702
1040
|
"restart.maxBackoff"
|
|
703
1041
|
)
|
|
704
1042
|
};
|
|
705
1043
|
}
|
|
1044
|
+
var MAXIMUM_IN_FLIGHT_REQUESTS = 5;
|
|
1045
|
+
function assertMaxInFlightRequests(value) {
|
|
1046
|
+
if (!Number.isInteger(value) || value < 1 || value > MAXIMUM_IN_FLIGHT_REQUESTS) {
|
|
1047
|
+
throw new core.ConfigurationError(
|
|
1048
|
+
`maxInFlightRequests must be an integer from 1 to 5, received ${value}`
|
|
1049
|
+
);
|
|
1050
|
+
}
|
|
1051
|
+
return value;
|
|
1052
|
+
}
|
|
706
1053
|
function toHandlerFailure(error) {
|
|
707
1054
|
const { name, message } = core.toErrorInfo(error);
|
|
708
1055
|
return new HandlerFailedError(`${name}: ${message}`);
|
|
709
1056
|
}
|
|
710
1057
|
|
|
1058
|
+
Object.defineProperty(exports, "fromHeader", {
|
|
1059
|
+
enumerable: true,
|
|
1060
|
+
get: function () { return internal.fromHeader; }
|
|
1061
|
+
});
|
|
1062
|
+
Object.defineProperty(exports, "fromPayloadField", {
|
|
1063
|
+
enumerable: true,
|
|
1064
|
+
get: function () { return internal.fromPayloadField; }
|
|
1065
|
+
});
|
|
1066
|
+
exports.GroupJoinTimeoutError = GroupJoinTimeoutError;
|
|
711
1067
|
exports.HandlerFailedError = HandlerFailedError;
|
|
712
1068
|
exports.InvalidKafkaMessageError = InvalidKafkaMessageError;
|
|
713
1069
|
exports.KafkaTransport = KafkaTransport;
|
|
714
1070
|
exports.PublishTimeoutError = PublishTimeoutError;
|
|
1071
|
+
exports.TransportClosedError = TransportClosedError;
|
|
715
1072
|
exports.UnencodableEventError = UnencodableEventError;
|
|
1073
|
+
exports.cloudEventsBinary = cloudEventsBinary;
|
|
1074
|
+
exports.cloudEventsStructured = cloudEventsStructured;
|
|
1075
|
+
exports.decodeCloudEvent = decodeCloudEvent;
|
|
716
1076
|
exports.decodeEnvelope = decodeEnvelope;
|
|
717
1077
|
exports.encodeEnvelope = encodeEnvelope;
|
|
1078
|
+
exports.jsonEnvelope = jsonEnvelope;
|
|
1079
|
+
exports.plainJsonDecoder = plainJsonDecoder;
|
|
718
1080
|
//# sourceMappingURL=index.cjs.map
|
|
719
1081
|
//# sourceMappingURL=index.cjs.map
|