@relaybus/kafka 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/dist/index.d.cts CHANGED
@@ -63,17 +63,55 @@ interface KafkaOutgoingMessage {
63
63
  interface KafkaProducerRecord {
64
64
  readonly topic: string;
65
65
  readonly messages: KafkaOutgoingMessage[];
66
- /** Always `-1`: wait for all in-sync replicas. */
67
- readonly acks: number;
66
+ /**
67
+ * kafkajs: always `-1`, wait for all in-sync replicas. Absent for Confluent, which rejects
68
+ * `acks` here; the producer is created with `acks: -1` instead.
69
+ */
70
+ readonly acks?: number;
68
71
  }
69
- /** Producer settings this package decides itself; user settings are merged below them. */
70
- interface KafkaProducerConfig {
72
+ /** Producer settings of Confluent's KafkaJS-compatible API that this package decides itself. */
73
+ interface KafkaConfluentProducerSettings {
71
74
  readonly idempotent: boolean;
72
75
  readonly maxInFlightRequests: number;
76
+ readonly acks: number;
73
77
  }
74
- /** Consumer settings this package decides itself; user settings are merged below them. */
75
- interface KafkaConsumerConfig {
78
+ /**
79
+ * Producer settings this package decides itself; user settings are merged below them.
80
+ * kafkajs gets them at the top level; Confluent inside the `kafkaJS` block and again as
81
+ * librdkafka properties, which win over the block.
82
+ */
83
+ interface KafkaProducerConfig {
84
+ readonly idempotent?: boolean;
85
+ readonly maxInFlightRequests?: number;
86
+ readonly kafkaJS?: KafkaConfluentProducerSettings;
87
+ readonly 'enable.idempotence'?: boolean;
88
+ readonly 'max.in.flight'?: number;
89
+ readonly acks?: number;
90
+ }
91
+ /** Consumer settings of Confluent's KafkaJS-compatible API that this package decides itself. */
92
+ interface KafkaConfluentConsumerSettings {
76
93
  readonly groupId: string;
94
+ readonly fromBeginning: boolean;
95
+ }
96
+ /** A partition as Confluent hands it to `rebalance_cb`. */
97
+ interface KafkaTopicPartition {
98
+ readonly topic: string;
99
+ readonly partition: number;
100
+ }
101
+ /**
102
+ * Consumer settings this package decides itself; user settings are merged below them.
103
+ * kafkajs gets `groupId` at the top level; Confluent gets `groupId` and `fromBeginning`
104
+ * inside the `kafkaJS` block and again as librdkafka properties, and reports group membership
105
+ * through `rebalance_cb`.
106
+ */
107
+ interface KafkaConsumerConfig {
108
+ readonly groupId?: string;
109
+ readonly kafkaJS?: KafkaConfluentConsumerSettings;
110
+ readonly 'group.id'?: string;
111
+ readonly 'auto.offset.reset'?: 'earliest' | 'latest';
112
+ readonly rebalance_cb?: (error: {
113
+ readonly code: number;
114
+ }, assignment: readonly KafkaTopicPartition[]) => unknown;
77
115
  }
78
116
  /** Subset of the producer API: `connect`, `send`, `disconnect`. */
79
117
  interface KafkaProducerLike {
@@ -122,9 +160,10 @@ interface KafkaConsumerLike {
122
160
  readonly events?: KafkaConsumerEventNames | undefined;
123
161
  on?(eventName: string, listener: (event: KafkaConsumerEvent) => void): unknown;
124
162
  connect(): Promise<void>;
163
+ /** `fromBeginning` is given to kafkajs only; Confluent takes it in the consumer config. */
125
164
  subscribe(subscription: {
126
165
  topics: string[];
127
- fromBeginning: boolean;
166
+ fromBeginning?: boolean;
128
167
  }): Promise<void>;
129
168
  run(config: KafkaRunConfig): Promise<void>;
130
169
  disconnect(): Promise<void>;
@@ -327,13 +366,27 @@ declare const cloudEventsStructured: KafkaCodec;
327
366
  * event id in a `relaybus-event-id` header, which wins on read, so an outbox
328
367
  * retry that sends an event twice is still deduplicated.
329
368
  */
330
- type KafkaJsonEnvelopeMapping = JsonEnvelopeMapping<'record-position'>;
369
+ type KafkaJsonEnvelopeMapping = JsonEnvelopeMapping<'record-position'> & KafkaRecordOptions;
370
+ /**
371
+ * The record key of an event: `'event-key'` (the event key, default), `'none'` (no key) or a
372
+ * function of the event. The event key itself, and with it the outbox's per-key order, is
373
+ * unchanged: only the wire key differs. Records without a key are spread over partitions by the
374
+ * producer, so the per-key order holds up to the broker but not across partitions.
375
+ */
376
+ type KafkaRecordKey = 'event-key' | 'none' | ((envelope: EventEnvelope) => string | null);
377
+ /** Record-level choices of {@link jsonEnvelope} besides the mapping. */
378
+ interface KafkaRecordOptions {
379
+ /** Default `'event-key'`. */
380
+ readonly recordKey?: KafkaRecordKey;
381
+ /** Whether the record gets a `content-type` header. Default true. */
382
+ readonly contentTypeHeader?: boolean;
383
+ }
331
384
  /**
332
385
  * A JSON wire format declared by a mapping, in both directions: for topics
333
386
  * shared with services that wrap events in their own envelope
334
387
  * (`{ "eventName": ..., "data": {...} }`, MassTransit) or send the bare
335
- * payload. The record key is always the event key; when `occurredAt` is not
336
- * mapped, the record timestamp carries it. Event headers that are message
388
+ * payload. The record key is the event key unless `recordKey` says otherwise;
389
+ * when `occurredAt` is not mapped, the record timestamp carries it. Event headers that are message
337
390
  * headers are written as they are, so a name the mapping uses for another
338
391
  * field is refused with {@link UnencodableEventError}, as are `content-type`
339
392
  * and `relaybus-event-id`.
@@ -351,6 +404,9 @@ type KafkaJsonEnvelopeMapping = JsonEnvelopeMapping<'record-position'>;
351
404
  */
352
405
  declare function jsonEnvelope(mapping: KafkaJsonEnvelopeMapping): KafkaCodec;
353
406
 
407
+ /** The Kafka client a transport drives: `kafkajs`, or `@confluentinc/kafka-javascript`'s KafkaJS API. */
408
+ type KafkaClientName = 'kafkajs' | 'confluent';
409
+
354
410
  /** A record the transport refused to hand to the bus because it is malformed. */
355
411
  interface InvalidKafkaMessage {
356
412
  readonly topic: string;
@@ -398,9 +454,33 @@ interface KafkaRestartOptions {
398
454
  /** Upper bound of the delay between restart attempts. Default `'30s'`. */
399
455
  readonly maxBackoff?: Duration;
400
456
  }
457
+ /**
458
+ * A producer the application already connected and owns, such as the one of its own Kafka
459
+ * service: a kafkajs `Producer` or a Confluent `KafkaJS.Producer`. Only `send` is called.
460
+ */
461
+ type KafkaInjectedProducer = Pick<KafkaProducerLike, 'send'>;
401
462
  interface KafkaTransportOptions {
402
- /** A `kafkajs` `Kafka` or `@confluentinc/kafka-javascript` `KafkaJS.Kafka` instance. */
403
- readonly kafka: KafkaClientLike;
463
+ /**
464
+ * A `kafkajs` `Kafka` or `@confluentinc/kafka-javascript` `KafkaJS.Kafka` instance: the
465
+ * factory of the transport's own consumer and, unless `producer` is given, producer.
466
+ * Required unless `producer` is given; required to subscribe.
467
+ */
468
+ readonly kafka?: KafkaClientLike;
469
+ /**
470
+ * Publish (and dead-letter) through this already-connected producer instead of one of
471
+ * `kafka`, reusing the application's connection and its SSL/SASL settings. The transport never
472
+ * connects or disconnects it; `close` leaves it open. Make it idempotent with `acks` -1 (all
473
+ * in-sync replicas) and at most 5 in-flight requests (1 to keep retries from reordering), as
474
+ * the transport's own producer is: `producerConfig` and `maxInFlightRequests` cannot be given
475
+ * with it. With Confluent, set `client: 'confluent'` too.
476
+ */
477
+ readonly producer?: KafkaInjectedProducer;
478
+ /**
479
+ * Which client `kafka` is. Default `'kafkajs'`; set `'confluent'` for a
480
+ * `@confluentinc/kafka-javascript` `KafkaJS.Kafka`. The two KafkaJS-style APIs differ in where
481
+ * settings go and how the consumer reports its group membership.
482
+ */
483
+ readonly client?: KafkaClientName;
404
484
  /** Name referenced by routes and subscriptions. Default `'kafka'`. */
405
485
  readonly name?: string;
406
486
  /** Consumer group id. Required as soon as `subscribe` is used. */
@@ -412,9 +492,17 @@ interface KafkaTransportOptions {
412
492
  readonly fromBeginning?: boolean;
413
493
  /** Producer `maxInFlightRequests`, 1 to 5. Default 1, which keeps retries from reordering a key. */
414
494
  readonly maxInFlightRequests?: number;
415
- /** Extra producer settings; `idempotent` and `maxInFlightRequests` always win. */
495
+ /**
496
+ * Extra producer settings; `idempotent` and `maxInFlightRequests` always win. With Confluent,
497
+ * KafkaJS-style settings go in a `kafkaJS` block and librdkafka properties at the top level;
498
+ * `acks: -1` also wins, and so do `enable.idempotence`, `max.in.flight` and `acks` there.
499
+ */
416
500
  readonly producerConfig?: object;
417
- /** Extra consumer settings; `groupId` always wins. */
501
+ /**
502
+ * Extra consumer settings; `groupId` always wins. With Confluent, KafkaJS-style settings go in
503
+ * a `kafkaJS` block and librdkafka properties at the top level; `fromBeginning` also wins, and
504
+ * so do `group.id` and `auto.offset.reset` there. A `rebalance_cb` of yours is still called.
505
+ */
418
506
  readonly consumerConfig?: object;
419
507
  readonly processing?: KafkaProcessingOptions;
420
508
  /** How long `subscribe` waits for the consumer group join before failing. Default `'30s'`. */
@@ -456,8 +544,8 @@ interface KafkaTransportOptions {
456
544
  * Publishing: idempotent producer, `acks=-1`, resolves after the broker ack and
457
545
  * rejects with {@link PublishTimeoutError} after `publishTimeout`. A timed-out
458
546
  * send may still complete later; the outbox then retries it and the duplicate is
459
- * absorbed by the consumer's inbox dedup. The record key is the envelope key, so events sharing a key land on one
460
- * partition and keep their order; events without a key have no ordering.
547
+ * absorbed by the consumer's inbox dedup. The record key is the codec's: the event key by default, so events
548
+ * sharing a key land on one partition and keep their order; records without a key have no ordering.
461
549
  *
462
550
  * Subscribing: one consumer, one group, one `subscribe` call. `eachMessage`
463
551
  * awaits the handler; the client commits the offset only after it resolved. A
@@ -503,4 +591,12 @@ declare class KafkaTransport extends SubscribingTransport {
503
591
  close(): Promise<void>;
504
592
  }
505
593
 
506
- export { type DecodableMessage, type DecodedEvent, type EncodedMessage, GroupJoinTimeoutError, HandlerFailedError, type InvalidKafkaMessage, InvalidKafkaMessageError, type InvalidMessagePolicy, type KafkaBytes, type KafkaClientLike, type KafkaCodec, type KafkaConsumerConfig, type KafkaConsumerEvent, type KafkaConsumerEventNames, type KafkaConsumerLike, type KafkaEachMessagePayload, type KafkaEncodedRecord, type KafkaInboundRecord, type KafkaIncomingHeaders, type KafkaIncomingMessage, type KafkaJsonEnvelopeMapping, type KafkaMessageDecoder, type KafkaOutgoingHeaders, type KafkaOutgoingMessage, type KafkaProcessingOptions, type KafkaProducerConfig, type KafkaProducerLike, type KafkaProducerRecord, type KafkaRestartOptions, type KafkaRunConfig, KafkaTransport, type KafkaTransportOptions, type PlainJsonDecoderOptions, type PlainJsonRecord, PublishTimeoutError, TransportClosedError, UnencodableEventError, cloudEventsBinary, cloudEventsStructured, decodeCloudEvent, decodeEnvelope, encodeEnvelope, jsonEnvelope, plainJsonDecoder };
594
+ /**
595
+ * The event a {@link KafkaTransport} with `codec` hands to the bus for `record`: the codec's
596
+ * `decode`, with what it leaves out filled in (see `DecodedEvent`). Throws for a malformed
597
+ * record. For codec contract tests (`kafkaCodecContract` of `@relaybus/testing`).
598
+ */
599
+ declare function decodeKafkaRecord(codec: KafkaCodec, record: KafkaInboundRecord): EventEnvelope;
600
+
601
+ export { GroupJoinTimeoutError, HandlerFailedError, InvalidKafkaMessageError, KafkaTransport, PublishTimeoutError, TransportClosedError, UnencodableEventError, cloudEventsBinary, cloudEventsStructured, decodeCloudEvent, decodeEnvelope, decodeKafkaRecord, encodeEnvelope, jsonEnvelope, plainJsonDecoder };
602
+ export type { DecodableMessage, DecodedEvent, EncodedMessage, InvalidKafkaMessage, InvalidMessagePolicy, KafkaBytes, KafkaClientLike, KafkaCodec, KafkaConsumerConfig, KafkaConsumerEvent, KafkaConsumerEventNames, KafkaConsumerLike, KafkaEachMessagePayload, KafkaEncodedRecord, KafkaInboundRecord, KafkaIncomingHeaders, KafkaIncomingMessage, KafkaInjectedProducer, KafkaJsonEnvelopeMapping, KafkaMessageDecoder, KafkaOutgoingHeaders, KafkaOutgoingMessage, KafkaProcessingOptions, KafkaProducerConfig, KafkaProducerLike, KafkaProducerRecord, KafkaRecordKey, KafkaRecordOptions, KafkaRestartOptions, KafkaRunConfig, KafkaTransportOptions, PlainJsonDecoderOptions, PlainJsonRecord };
package/dist/index.d.ts CHANGED
@@ -63,17 +63,55 @@ interface KafkaOutgoingMessage {
63
63
  interface KafkaProducerRecord {
64
64
  readonly topic: string;
65
65
  readonly messages: KafkaOutgoingMessage[];
66
- /** Always `-1`: wait for all in-sync replicas. */
67
- readonly acks: number;
66
+ /**
67
+ * kafkajs: always `-1`, wait for all in-sync replicas. Absent for Confluent, which rejects
68
+ * `acks` here; the producer is created with `acks: -1` instead.
69
+ */
70
+ readonly acks?: number;
68
71
  }
69
- /** Producer settings this package decides itself; user settings are merged below them. */
70
- interface KafkaProducerConfig {
72
+ /** Producer settings of Confluent's KafkaJS-compatible API that this package decides itself. */
73
+ interface KafkaConfluentProducerSettings {
71
74
  readonly idempotent: boolean;
72
75
  readonly maxInFlightRequests: number;
76
+ readonly acks: number;
73
77
  }
74
- /** Consumer settings this package decides itself; user settings are merged below them. */
75
- interface KafkaConsumerConfig {
78
+ /**
79
+ * Producer settings this package decides itself; user settings are merged below them.
80
+ * kafkajs gets them at the top level; Confluent inside the `kafkaJS` block and again as
81
+ * librdkafka properties, which win over the block.
82
+ */
83
+ interface KafkaProducerConfig {
84
+ readonly idempotent?: boolean;
85
+ readonly maxInFlightRequests?: number;
86
+ readonly kafkaJS?: KafkaConfluentProducerSettings;
87
+ readonly 'enable.idempotence'?: boolean;
88
+ readonly 'max.in.flight'?: number;
89
+ readonly acks?: number;
90
+ }
91
+ /** Consumer settings of Confluent's KafkaJS-compatible API that this package decides itself. */
92
+ interface KafkaConfluentConsumerSettings {
76
93
  readonly groupId: string;
94
+ readonly fromBeginning: boolean;
95
+ }
96
+ /** A partition as Confluent hands it to `rebalance_cb`. */
97
+ interface KafkaTopicPartition {
98
+ readonly topic: string;
99
+ readonly partition: number;
100
+ }
101
+ /**
102
+ * Consumer settings this package decides itself; user settings are merged below them.
103
+ * kafkajs gets `groupId` at the top level; Confluent gets `groupId` and `fromBeginning`
104
+ * inside the `kafkaJS` block and again as librdkafka properties, and reports group membership
105
+ * through `rebalance_cb`.
106
+ */
107
+ interface KafkaConsumerConfig {
108
+ readonly groupId?: string;
109
+ readonly kafkaJS?: KafkaConfluentConsumerSettings;
110
+ readonly 'group.id'?: string;
111
+ readonly 'auto.offset.reset'?: 'earliest' | 'latest';
112
+ readonly rebalance_cb?: (error: {
113
+ readonly code: number;
114
+ }, assignment: readonly KafkaTopicPartition[]) => unknown;
77
115
  }
78
116
  /** Subset of the producer API: `connect`, `send`, `disconnect`. */
79
117
  interface KafkaProducerLike {
@@ -122,9 +160,10 @@ interface KafkaConsumerLike {
122
160
  readonly events?: KafkaConsumerEventNames | undefined;
123
161
  on?(eventName: string, listener: (event: KafkaConsumerEvent) => void): unknown;
124
162
  connect(): Promise<void>;
163
+ /** `fromBeginning` is given to kafkajs only; Confluent takes it in the consumer config. */
125
164
  subscribe(subscription: {
126
165
  topics: string[];
127
- fromBeginning: boolean;
166
+ fromBeginning?: boolean;
128
167
  }): Promise<void>;
129
168
  run(config: KafkaRunConfig): Promise<void>;
130
169
  disconnect(): Promise<void>;
@@ -327,13 +366,27 @@ declare const cloudEventsStructured: KafkaCodec;
327
366
  * event id in a `relaybus-event-id` header, which wins on read, so an outbox
328
367
  * retry that sends an event twice is still deduplicated.
329
368
  */
330
- type KafkaJsonEnvelopeMapping = JsonEnvelopeMapping<'record-position'>;
369
+ type KafkaJsonEnvelopeMapping = JsonEnvelopeMapping<'record-position'> & KafkaRecordOptions;
370
+ /**
371
+ * The record key of an event: `'event-key'` (the event key, default), `'none'` (no key) or a
372
+ * function of the event. The event key itself, and with it the outbox's per-key order, is
373
+ * unchanged: only the wire key differs. Records without a key are spread over partitions by the
374
+ * producer, so the per-key order holds up to the broker but not across partitions.
375
+ */
376
+ type KafkaRecordKey = 'event-key' | 'none' | ((envelope: EventEnvelope) => string | null);
377
+ /** Record-level choices of {@link jsonEnvelope} besides the mapping. */
378
+ interface KafkaRecordOptions {
379
+ /** Default `'event-key'`. */
380
+ readonly recordKey?: KafkaRecordKey;
381
+ /** Whether the record gets a `content-type` header. Default true. */
382
+ readonly contentTypeHeader?: boolean;
383
+ }
331
384
  /**
332
385
  * A JSON wire format declared by a mapping, in both directions: for topics
333
386
  * shared with services that wrap events in their own envelope
334
387
  * (`{ "eventName": ..., "data": {...} }`, MassTransit) or send the bare
335
- * payload. The record key is always the event key; when `occurredAt` is not
336
- * mapped, the record timestamp carries it. Event headers that are message
388
+ * payload. The record key is the event key unless `recordKey` says otherwise;
389
+ * when `occurredAt` is not mapped, the record timestamp carries it. Event headers that are message
337
390
  * headers are written as they are, so a name the mapping uses for another
338
391
  * field is refused with {@link UnencodableEventError}, as are `content-type`
339
392
  * and `relaybus-event-id`.
@@ -351,6 +404,9 @@ type KafkaJsonEnvelopeMapping = JsonEnvelopeMapping<'record-position'>;
351
404
  */
352
405
  declare function jsonEnvelope(mapping: KafkaJsonEnvelopeMapping): KafkaCodec;
353
406
 
407
+ /** The Kafka client a transport drives: `kafkajs`, or `@confluentinc/kafka-javascript`'s KafkaJS API. */
408
+ type KafkaClientName = 'kafkajs' | 'confluent';
409
+
354
410
  /** A record the transport refused to hand to the bus because it is malformed. */
355
411
  interface InvalidKafkaMessage {
356
412
  readonly topic: string;
@@ -398,9 +454,33 @@ interface KafkaRestartOptions {
398
454
  /** Upper bound of the delay between restart attempts. Default `'30s'`. */
399
455
  readonly maxBackoff?: Duration;
400
456
  }
457
+ /**
458
+ * A producer the application already connected and owns, such as the one of its own Kafka
459
+ * service: a kafkajs `Producer` or a Confluent `KafkaJS.Producer`. Only `send` is called.
460
+ */
461
+ type KafkaInjectedProducer = Pick<KafkaProducerLike, 'send'>;
401
462
  interface KafkaTransportOptions {
402
- /** A `kafkajs` `Kafka` or `@confluentinc/kafka-javascript` `KafkaJS.Kafka` instance. */
403
- readonly kafka: KafkaClientLike;
463
+ /**
464
+ * A `kafkajs` `Kafka` or `@confluentinc/kafka-javascript` `KafkaJS.Kafka` instance: the
465
+ * factory of the transport's own consumer and, unless `producer` is given, producer.
466
+ * Required unless `producer` is given; required to subscribe.
467
+ */
468
+ readonly kafka?: KafkaClientLike;
469
+ /**
470
+ * Publish (and dead-letter) through this already-connected producer instead of one of
471
+ * `kafka`, reusing the application's connection and its SSL/SASL settings. The transport never
472
+ * connects or disconnects it; `close` leaves it open. Make it idempotent with `acks` -1 (all
473
+ * in-sync replicas) and at most 5 in-flight requests (1 to keep retries from reordering), as
474
+ * the transport's own producer is: `producerConfig` and `maxInFlightRequests` cannot be given
475
+ * with it. With Confluent, set `client: 'confluent'` too.
476
+ */
477
+ readonly producer?: KafkaInjectedProducer;
478
+ /**
479
+ * Which client `kafka` is. Default `'kafkajs'`; set `'confluent'` for a
480
+ * `@confluentinc/kafka-javascript` `KafkaJS.Kafka`. The two KafkaJS-style APIs differ in where
481
+ * settings go and how the consumer reports its group membership.
482
+ */
483
+ readonly client?: KafkaClientName;
404
484
  /** Name referenced by routes and subscriptions. Default `'kafka'`. */
405
485
  readonly name?: string;
406
486
  /** Consumer group id. Required as soon as `subscribe` is used. */
@@ -412,9 +492,17 @@ interface KafkaTransportOptions {
412
492
  readonly fromBeginning?: boolean;
413
493
  /** Producer `maxInFlightRequests`, 1 to 5. Default 1, which keeps retries from reordering a key. */
414
494
  readonly maxInFlightRequests?: number;
415
- /** Extra producer settings; `idempotent` and `maxInFlightRequests` always win. */
495
+ /**
496
+ * Extra producer settings; `idempotent` and `maxInFlightRequests` always win. With Confluent,
497
+ * KafkaJS-style settings go in a `kafkaJS` block and librdkafka properties at the top level;
498
+ * `acks: -1` also wins, and so do `enable.idempotence`, `max.in.flight` and `acks` there.
499
+ */
416
500
  readonly producerConfig?: object;
417
- /** Extra consumer settings; `groupId` always wins. */
501
+ /**
502
+ * Extra consumer settings; `groupId` always wins. With Confluent, KafkaJS-style settings go in
503
+ * a `kafkaJS` block and librdkafka properties at the top level; `fromBeginning` also wins, and
504
+ * so do `group.id` and `auto.offset.reset` there. A `rebalance_cb` of yours is still called.
505
+ */
418
506
  readonly consumerConfig?: object;
419
507
  readonly processing?: KafkaProcessingOptions;
420
508
  /** How long `subscribe` waits for the consumer group join before failing. Default `'30s'`. */
@@ -456,8 +544,8 @@ interface KafkaTransportOptions {
456
544
  * Publishing: idempotent producer, `acks=-1`, resolves after the broker ack and
457
545
  * rejects with {@link PublishTimeoutError} after `publishTimeout`. A timed-out
458
546
  * send may still complete later; the outbox then retries it and the duplicate is
459
- * absorbed by the consumer's inbox dedup. The record key is the envelope key, so events sharing a key land on one
460
- * partition and keep their order; events without a key have no ordering.
547
+ * absorbed by the consumer's inbox dedup. The record key is the codec's: the event key by default, so events
548
+ * sharing a key land on one partition and keep their order; records without a key have no ordering.
461
549
  *
462
550
  * Subscribing: one consumer, one group, one `subscribe` call. `eachMessage`
463
551
  * awaits the handler; the client commits the offset only after it resolved. A
@@ -503,4 +591,12 @@ declare class KafkaTransport extends SubscribingTransport {
503
591
  close(): Promise<void>;
504
592
  }
505
593
 
506
- export { type DecodableMessage, type DecodedEvent, type EncodedMessage, GroupJoinTimeoutError, HandlerFailedError, type InvalidKafkaMessage, InvalidKafkaMessageError, type InvalidMessagePolicy, type KafkaBytes, type KafkaClientLike, type KafkaCodec, type KafkaConsumerConfig, type KafkaConsumerEvent, type KafkaConsumerEventNames, type KafkaConsumerLike, type KafkaEachMessagePayload, type KafkaEncodedRecord, type KafkaInboundRecord, type KafkaIncomingHeaders, type KafkaIncomingMessage, type KafkaJsonEnvelopeMapping, type KafkaMessageDecoder, type KafkaOutgoingHeaders, type KafkaOutgoingMessage, type KafkaProcessingOptions, type KafkaProducerConfig, type KafkaProducerLike, type KafkaProducerRecord, type KafkaRestartOptions, type KafkaRunConfig, KafkaTransport, type KafkaTransportOptions, type PlainJsonDecoderOptions, type PlainJsonRecord, PublishTimeoutError, TransportClosedError, UnencodableEventError, cloudEventsBinary, cloudEventsStructured, decodeCloudEvent, decodeEnvelope, encodeEnvelope, jsonEnvelope, plainJsonDecoder };
594
+ /**
595
+ * The event a {@link KafkaTransport} with `codec` hands to the bus for `record`: the codec's
596
+ * `decode`, with what it leaves out filled in (see `DecodedEvent`). Throws for a malformed
597
+ * record. For codec contract tests (`kafkaCodecContract` of `@relaybus/testing`).
598
+ */
599
+ declare function decodeKafkaRecord(codec: KafkaCodec, record: KafkaInboundRecord): EventEnvelope;
600
+
601
+ export { GroupJoinTimeoutError, HandlerFailedError, InvalidKafkaMessageError, KafkaTransport, PublishTimeoutError, TransportClosedError, UnencodableEventError, cloudEventsBinary, cloudEventsStructured, decodeCloudEvent, decodeEnvelope, decodeKafkaRecord, encodeEnvelope, jsonEnvelope, plainJsonDecoder };
602
+ export type { DecodableMessage, DecodedEvent, EncodedMessage, InvalidKafkaMessage, InvalidMessagePolicy, KafkaBytes, KafkaClientLike, KafkaCodec, KafkaConsumerConfig, KafkaConsumerEvent, KafkaConsumerEventNames, KafkaConsumerLike, KafkaEachMessagePayload, KafkaEncodedRecord, KafkaInboundRecord, KafkaIncomingHeaders, KafkaIncomingMessage, KafkaInjectedProducer, KafkaJsonEnvelopeMapping, KafkaMessageDecoder, KafkaOutgoingHeaders, KafkaOutgoingMessage, KafkaProcessingOptions, KafkaProducerConfig, KafkaProducerLike, KafkaProducerRecord, KafkaRecordKey, KafkaRecordOptions, KafkaRestartOptions, KafkaRunConfig, KafkaTransportOptions, PlainJsonDecoderOptions, PlainJsonRecord };