@platformatic/kafka 1.27.0-alpha.2 → 1.27.0-alpha.3

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.
Files changed (55) hide show
  1. package/README.md +0 -58
  2. package/dist/clients/base/base.js +6 -2
  3. package/dist/clients/base/options.d.ts +9 -2
  4. package/dist/clients/base/options.js +1 -1
  5. package/dist/clients/base/types.d.ts +2 -1
  6. package/dist/clients/consumer/consumer.js +1 -24
  7. package/dist/clients/consumer/messages-stream.js +10 -66
  8. package/dist/clients/consumer/options.d.ts +0 -24
  9. package/dist/clients/consumer/options.js +1 -3
  10. package/dist/clients/consumer/types.d.ts +1 -4
  11. package/dist/clients/producer/options.d.ts +18 -2
  12. package/dist/clients/producer/options.js +1 -3
  13. package/dist/clients/producer/producer.js +15 -75
  14. package/dist/clients/producer/types.d.ts +1 -4
  15. package/dist/clients/serde.d.ts +6 -11
  16. package/dist/errors.d.ts +1 -5
  17. package/dist/errors.js +0 -8
  18. package/dist/index.d.ts +0 -1
  19. package/dist/index.js +0 -2
  20. package/dist/network/connection.d.ts +6 -7
  21. package/dist/protocol/definitions.js +1 -1
  22. package/dist/protocol/reader.js +1 -1
  23. package/dist/protocol/records.d.ts +18 -7
  24. package/dist/protocol/records.js +6 -2
  25. package/dist/protocol/sasl/oauth-bearer.d.ts +3 -3
  26. package/dist/protocol/sasl/plain.d.ts +3 -3
  27. package/dist/protocol/sasl/scram-sha.d.ts +3 -3
  28. package/dist/protocol/sasl/utils.d.ts +3 -3
  29. package/dist/protocol/writer.js +1 -1
  30. package/dist/typescript-4/dist/clients/base/options.d.ts +9 -2
  31. package/dist/typescript-4/dist/clients/base/types.d.ts +2 -1
  32. package/dist/typescript-4/dist/clients/consumer/options.d.ts +0 -24
  33. package/dist/typescript-4/dist/clients/consumer/types.d.ts +1 -4
  34. package/dist/typescript-4/dist/clients/producer/options.d.ts +18 -2
  35. package/dist/typescript-4/dist/clients/producer/types.d.ts +1 -4
  36. package/dist/typescript-4/dist/clients/serde.d.ts +6 -11
  37. package/dist/typescript-4/dist/errors.d.ts +1 -5
  38. package/dist/typescript-4/dist/index.d.ts +1 -2
  39. package/dist/typescript-4/dist/network/connection.d.ts +6 -7
  40. package/dist/typescript-4/dist/protocol/records.d.ts +18 -7
  41. package/dist/typescript-4/dist/protocol/sasl/oauth-bearer.d.ts +3 -3
  42. package/dist/typescript-4/dist/protocol/sasl/plain.d.ts +3 -3
  43. package/dist/typescript-4/dist/protocol/sasl/scram-sha.d.ts +3 -3
  44. package/dist/typescript-4/dist/protocol/sasl/utils.d.ts +3 -3
  45. package/dist/version.js +1 -1
  46. package/package.json +3 -4
  47. package/dist/registries/abstract.d.ts +0 -22
  48. package/dist/registries/abstract.js +0 -38
  49. package/dist/registries/confluent-schema-registry.d.ts +0 -41
  50. package/dist/registries/confluent-schema-registry.js +0 -222
  51. package/dist/registries/index.d.ts +0 -2
  52. package/dist/registries/index.js +0 -2
  53. package/dist/typescript-4/dist/registries/abstract.d.ts +0 -22
  54. package/dist/typescript-4/dist/registries/confluent-schema-registry.d.ts +0 -41
  55. package/dist/typescript-4/dist/registries/index.d.ts +0 -2
package/README.md CHANGED
@@ -10,7 +10,6 @@ A modern, high-performance, pure TypeScript/JavaScript type safe client for Apac
10
10
  - **Flexible API**: You can use promises or callbacks on all APIs.
11
11
  - **Streaming or Event-based Consumers**: Thanks to Node.js streams, you can choose your preferred consuming method.
12
12
  - **Flexible Serialisation**: Pluggable serialisers and deserialisers.
13
- - **Schema Registry Support**: Built-in Confluent Schema Registry integration with AVRO, Protobuf, and JSON Schema support.
14
13
  - **Connection Management**: Automatic connection pooling and recovery.
15
14
  - **Low Dependencies**: Minimal external dependencies.
16
15
 
@@ -142,63 +141,6 @@ await admin.deleteTopics({ topics: ['my-topic'] })
142
141
  await admin.close()
143
142
  ```
144
143
 
145
- ### Schema Registry
146
-
147
- The library includes built-in support for Confluent Schema Registry with AVRO, Protocol Buffers, and JSON Schema:
148
-
149
- ```typescript
150
- import { Producer, Consumer } from '@platformatic/kafka'
151
- import { ConfluentSchemaRegistry } from '@platformatic/kafka/registries'
152
-
153
- // Create a schema registry instance
154
- const registry = new ConfluentSchemaRegistry({
155
- url: 'http://localhost:8081',
156
- auth: {
157
- username: 'user',
158
- password: 'password'
159
- }
160
- })
161
-
162
- // Producer with schema registry
163
- const producer = new Producer({
164
- clientId: 'schema-producer',
165
- bootstrapBrokers: ['localhost:9092'],
166
- registry // Automatic serialization with schemas
167
- })
168
-
169
- // Send messages with schema IDs
170
- await producer.send({
171
- messages: [{
172
- topic: 'users',
173
- value: { id: 1, name: 'Alice' },
174
- metadata: {
175
- schemas: {
176
- value: 100 // Schema ID in the registry
177
- }
178
- }
179
- }]
180
- })
181
-
182
- // Consumer with schema registry
183
- const consumer = new Consumer({
184
- groupId: 'schema-consumers',
185
- clientId: 'schema-consumer',
186
- bootstrapBrokers: ['localhost:9092'],
187
- registry // Automatic deserialization with schemas
188
- })
189
-
190
- const stream = await consumer.consume({
191
- topics: ['users']
192
- })
193
-
194
- // Messages are automatically deserialized
195
- for await (const message of stream) {
196
- console.log('User:', message.value) // Typed object
197
- }
198
- ```
199
-
200
- For more details, see the [Confluent Schema Registry documentation](./docs/confluent-schema-registry.md).
201
-
202
144
  ## TLS and SASL
203
145
 
204
146
  See the relevant sections in the the [Base Client](./docs/base.md) page.
@@ -230,16 +230,20 @@ export class Base extends TypedEventEmitter {
230
230
  const retriable = !genericError.findBy?.('canRetry', false);
231
231
  errors.push(error);
232
232
  if (attempt < retries && retriable && !shouldSkipRetry?.(error)) {
233
- this.emitWithDebug('client', 'performWithRetry:retry', operationId, attempt, retries);
234
233
  function onClose() {
235
234
  clearTimeout(timeout);
236
235
  errors.push(new UserError(`Client closed while retrying ${operationId}.`));
237
236
  callback(new MultipleErrors(`${operationId} failed ${attempt + 1} times.`, errors));
238
237
  }
238
+ let delay = this[kOptions].retryDelay;
239
+ if (typeof delay === 'function') {
240
+ delay = delay(this, operationId, attempt + 1, retries, error);
241
+ }
242
+ this.emitWithDebug('client', 'performWithRetry:retry', operationId, attempt, retries, delay);
239
243
  const timeout = setTimeout(() => {
240
244
  this.removeListener('client:close', onClose);
241
245
  this[kPerformWithRetry](operationId, operation, callback, attempt + 1, errors, shouldSkipRetry);
242
- }, this[kOptions].retryDelay);
246
+ }, delay);
243
247
  this.once('client:close', onClose);
244
248
  }
245
249
  else {
@@ -67,8 +67,15 @@ export declare const baseOptionsSchema: {
67
67
  })[];
68
68
  };
69
69
  retryDelay: {
70
- type: string;
71
- minimum: number;
70
+ oneOf: ({
71
+ type: string;
72
+ minimum: number;
73
+ function?: undefined;
74
+ } | {
75
+ function: boolean;
76
+ type?: undefined;
77
+ minimum?: undefined;
78
+ })[];
72
79
  };
73
80
  maxInflights: {
74
81
  type: string;
@@ -29,7 +29,7 @@ export const baseOptionsSchema = {
29
29
  timeout: { type: 'number', minimum: 0 },
30
30
  connectTimeout: { type: 'number', minimum: 0 },
31
31
  retries: { oneOf: [{ type: 'number', minimum: 0 }, { type: 'boolean' }] },
32
- retryDelay: { type: 'number', minimum: 0 },
32
+ retryDelay: { oneOf: [{ type: 'number', minimum: 0 }, { function: true }] },
33
33
  maxInflights: { type: 'number', minimum: 0 },
34
34
  handleBackPressure: { type: 'boolean', default: false },
35
35
  tls: { type: 'object', additionalProperties: true }, // No validation as they come from Node.js
@@ -29,12 +29,13 @@ export interface ClusterMetadata {
29
29
  topics: Map<string, ClusterTopicMetadata>;
30
30
  lastUpdate: number;
31
31
  }
32
+ export type RetryDelayGetter<Owner = object> = (client: Owner, operationId: string, attempt: number, retries: number, error: Error) => number;
32
33
  export interface BaseOptions extends ConnectionOptions {
33
34
  clientId: string;
34
35
  bootstrapBrokers: Broker[] | string[];
35
36
  timeout?: number;
36
37
  retries?: number | boolean;
37
- retryDelay?: number;
38
+ retryDelay?: number | RetryDelayGetter;
38
39
  metadataMaxAge?: number;
39
40
  autocreateTopics?: boolean;
40
41
  strict?: boolean;
@@ -40,15 +40,6 @@ export class Consumer extends Base {
40
40
  constructor(options) {
41
41
  super({ ...defaultConsumerOptions, ...options });
42
42
  this[kValidateOptions](options, consumerOptionsValidator, '/options');
43
- if (options.registry) {
44
- if (options.beforeDeserialization) {
45
- throw new UserError('/options/beforeDeserialization cannot be provided when /options/registry is provided.');
46
- }
47
- else if (options.deserializers) {
48
- throw new UserError('/options/deserializers cannot be provided when /options/registry is provided.');
49
- }
50
- options.registry.getDeserializers();
51
- }
52
43
  this.groupId = options.groupId;
53
44
  this.groupInstanceId = options.groupInstanceId ?? null;
54
45
  this.generationId = 0;
@@ -159,22 +150,8 @@ export class Consumer extends Base {
159
150
  }
160
151
  options.autocommit ??= this[kOptions].autocommit ?? true;
161
152
  options.maxBytes ??= this[kOptions].maxBytes;
153
+ options.deserializers = Object.assign({}, options.deserializers, this[kOptions].deserializers);
162
154
  options.highWaterMark ??= this[kOptions].highWaterMark;
163
- options.registry ??= this[kOptions].registry;
164
- options.beforeDeserialization ??= this[kOptions].beforeDeserialization;
165
- if (options.registry) {
166
- if (options.beforeDeserialization) {
167
- throw new UserError('/options/beforeDeserialization cannot be provided when /options/registry is provided.');
168
- /* c8 ignore next - Hard to test */
169
- }
170
- else if (options.deserializers || this[kOptions].deserializers) {
171
- throw new UserError('/options/deserializers cannot be provided when /options/registry is provided.');
172
- }
173
- options.deserializers = options.registry.getDeserializers();
174
- }
175
- else {
176
- options.deserializers = Object.assign({}, options.deserializers, this[kOptions].deserializers);
177
- }
178
155
  this.#consume(options, callback);
179
156
  return callback[kCallbackPromise];
180
157
  }
@@ -4,7 +4,6 @@ import { ListOffsetTimestamps } from "../../apis/enumerations.js";
4
4
  import { consumerReceivesChannel, createDiagnosticContext, notifyCreation } from "../../diagnostic.js";
5
5
  import { UserError } from "../../errors.js";
6
6
  import { IS_CONTROL } from "../../protocol/records.js";
7
- import { runAsyncSeries } from "../../registries/abstract.js";
8
7
  import { kAutocommit, kInstance, kRefreshOffsetsAndFetch } from "../../symbols.js";
9
8
  import { kConnections, kCreateConnectionPool, kInspect, kPrometheus } from "../base/base.js";
10
9
  import { ensureMetric } from "../metrics.js";
@@ -57,7 +56,6 @@ export class MessagesStream extends Readable {
57
56
  #closeCallbacks;
58
57
  #metricsConsumedMessages;
59
58
  #corruptedMessageHandler;
60
- #pushRecordsOperation;
61
59
  [kInstance];
62
60
  /*
63
61
  The following requests are blocking in Kafka:
@@ -76,7 +74,7 @@ export class MessagesStream extends Readable {
76
74
  */
77
75
  [kConnections];
78
76
  constructor(consumer, options) {
79
- const { autocommit, mode, fallbackMode, maxFetches, offsets, deserializers, onCorruptedMessage, registry, beforeDeserialization,
77
+ const { autocommit, mode, fallbackMode, maxFetches, offsets, deserializers, onCorruptedMessage,
80
78
  // The options below are only destructured to avoid being part of structuredClone below
81
79
  partitionAssigner: _partitionAssigner, ...otherOptions } = options;
82
80
  if (offsets && mode !== MessagesStreamModes.MANUAL) {
@@ -103,10 +101,8 @@ export class MessagesStream extends Readable {
103
101
  this.#maxFetches = maxFetches ?? 0;
104
102
  this.#topics = structuredClone(options.topics);
105
103
  this.#inflightNodes = new Set();
106
- this.#keyDeserializer =
107
- deserializers?.key ?? noopDeserializer;
108
- this.#valueDeserializer =
109
- deserializers?.value ?? noopDeserializer;
104
+ this.#keyDeserializer = deserializers?.key ?? noopDeserializer;
105
+ this.#valueDeserializer = deserializers?.value ?? noopDeserializer;
110
106
  this.#headerKeyDeserializer = deserializers?.headerKey ?? noopDeserializer;
111
107
  this.#headerValueDeserializer = deserializers?.headerValue ?? noopDeserializer;
112
108
  this.#autocommitEnabled = !!options.autocommit;
@@ -114,15 +110,6 @@ export class MessagesStream extends Readable {
114
110
  this.#closed = false;
115
111
  this.#closeCallbacks = [];
116
112
  this.#corruptedMessageHandler = onCorruptedMessage ?? defaultCorruptedMessageHandler;
117
- if (registry) {
118
- this.#pushRecordsOperation = this.#beforeDeserialization.bind(this, registry.getBeforeDeserializationHook());
119
- }
120
- else if (beforeDeserialization) {
121
- this.#pushRecordsOperation = this.#beforeDeserialization.bind(this, beforeDeserialization);
122
- }
123
- else {
124
- this.#pushRecordsOperation = this.#pushRecords.bind(this);
125
- }
126
113
  // Restore offsets
127
114
  this.#offsetsToFetch = new Map();
128
115
  if (offsets) {
@@ -375,7 +362,10 @@ export class MessagesStream extends Readable {
375
362
  }
376
363
  return;
377
364
  }
378
- this.#pushRecordsOperation(metadata, topicIds, response, requestedOffsets);
365
+ this.#pushRecords(metadata, topicIds, response, requestedOffsets);
366
+ if (this.#maxFetches > 0 && ++this.#fetches >= this.#maxFetches) {
367
+ this.push(null);
368
+ }
379
369
  });
380
370
  }
381
371
  });
@@ -429,7 +419,6 @@ export class MessagesStream extends Readable {
429
419
  }
430
420
  // Process messages
431
421
  for (const record of batch.records) {
432
- const messageToConsume = { ...record, topic, partition };
433
422
  const offset = batch.firstOffset + BigInt(record.offsetDelta);
434
423
  if (offset < requestedOffsets.get(`${topic}:${partition}`)) {
435
424
  // Thi is a duplicate message, ignore it
@@ -448,10 +437,10 @@ export class MessagesStream extends Readable {
448
437
  try {
449
438
  const headers = new Map();
450
439
  for (const [headerKey, headerValue] of record.headers) {
451
- headers.set(headerKeyDeserializer(headerKey, messageToConsume), headerValueDeserializer(headerValue, messageToConsume));
440
+ headers.set(headerKeyDeserializer(headerKey), headerValueDeserializer(headerValue));
452
441
  }
453
- const key = keyDeserializer(record.key, headers, messageToConsume);
454
- const value = valueDeserializer(record.value, headers, messageToConsume);
442
+ const key = keyDeserializer(record.key, headers);
443
+ const value = valueDeserializer(record.value, headers);
455
444
  this.#metricsConsumedMessages?.inc();
456
445
  const message = {
457
446
  key,
@@ -494,9 +483,6 @@ export class MessagesStream extends Readable {
494
483
  this.#fetch();
495
484
  });
496
485
  }
497
- if (this.#maxFetches > 0 && ++this.#fetches >= this.#maxFetches) {
498
- this.push(null);
499
- }
500
486
  }
501
487
  #updateCommittedOffset(topic, partition, offset) {
502
488
  const key = `${topic}:${partition}`;
@@ -651,46 +637,4 @@ export class MessagesStream extends Readable {
651
637
  [kInspect](...args) {
652
638
  this.#consumer[kInspect](...args);
653
639
  }
654
- #beforeDeserialization(hook, metadata, topicIds, response, requestedOffsets) {
655
- const requests = [];
656
- // Create the pre-deserialization requests
657
- for (const topicResponse of response.responses) {
658
- for (const { records: recordsBatches, partitionIndex: partition } of topicResponse.partitions) {
659
- /* c8 ignore next 3 - Hard to test */
660
- if (!recordsBatches) {
661
- continue;
662
- }
663
- for (const batch of recordsBatches) {
664
- // Filter control markers
665
- /* c8 ignore next 3 - Hard to test */
666
- if (batch.attributes & IS_CONTROL) {
667
- continue;
668
- }
669
- for (const message of batch.records) {
670
- message.topic = topicIds.get(topicResponse.topicId);
671
- message.partition = partition;
672
- requests.push([message.key, 'key', message]);
673
- requests.push([message.value, 'value', message]);
674
- for (const [headerKey, headerValue] of message.headers) {
675
- requests.push([headerKey, 'headerKey', message]);
676
- requests.push([headerValue, 'headerValue', message]);
677
- }
678
- }
679
- }
680
- }
681
- }
682
- runAsyncSeries((request, cb) => {
683
- const [data, type, message] = request;
684
- const result = hook(data, type, message, cb);
685
- if (typeof result?.then === 'function') {
686
- result.then(() => cb(null), cb);
687
- }
688
- }, requests, 0, error => {
689
- if (error) {
690
- this.destroy(error);
691
- return;
692
- }
693
- this.#pushRecords(metadata, topicIds, response, requestedOffsets);
694
- });
695
- }
696
640
  }
@@ -127,12 +127,6 @@ export declare const consumeOptionsProperties: {
127
127
  type: string;
128
128
  minimum: number;
129
129
  };
130
- beforeDeserialization: {
131
- function: boolean;
132
- };
133
- registry: {
134
- type: string;
135
- };
136
130
  };
137
131
  export declare const groupOptionsSchema: {
138
132
  type: string;
@@ -247,12 +241,6 @@ export declare const consumeOptionsSchema: {
247
241
  type: string;
248
242
  minimum: number;
249
243
  };
250
- beforeDeserialization: {
251
- function: boolean;
252
- };
253
- registry: {
254
- type: string;
255
- };
256
244
  groupInstanceId: {
257
245
  type: string;
258
246
  pattern: string;
@@ -408,12 +396,6 @@ export declare const consumerOptionsSchema: {
408
396
  type: string;
409
397
  minimum: number;
410
398
  };
411
- beforeDeserialization: {
412
- function: boolean;
413
- };
414
- registry: {
415
- type: string;
416
- };
417
399
  groupInstanceId: {
418
400
  type: string;
419
401
  pattern: string;
@@ -529,12 +511,6 @@ export declare const fetchOptionsSchema: {
529
511
  type: string;
530
512
  minimum: number;
531
513
  };
532
- beforeDeserialization: {
533
- function: boolean;
534
- };
535
- registry: {
536
- type: string;
537
- };
538
514
  groupInstanceId: {
539
515
  type: string;
540
516
  pattern: string;
@@ -61,9 +61,7 @@ export const consumeOptionsProperties = {
61
61
  maxWaitTime: { type: 'number', minimum: 0 },
62
62
  isolationLevel: { type: 'number', enum: allowedFetchIsolationLevels },
63
63
  deserializers: serdeProperties,
64
- highWaterMark: { type: 'number', minimum: 1 },
65
- beforeDeserialization: { function: true },
66
- registry: { type: 'object' }
64
+ highWaterMark: { type: 'number', minimum: 1 }
67
65
  };
68
66
  export const groupOptionsSchema = {
69
67
  type: 'object',
@@ -2,9 +2,8 @@ import { type FetchRequestTopic } from '../../apis/consumer/fetch-v17.ts';
2
2
  import { type GroupProtocols } from '../../apis/enumerations.ts';
3
3
  import { type ConnectionPool } from '../../network/connection-pool.ts';
4
4
  import { type KafkaRecord, type Message } from '../../protocol/records.ts';
5
- import { type SchemaRegistry } from '../../registries/abstract.ts';
6
5
  import { type BaseOptions, type ClusterMetadata, type TopicWithPartitionAndOffset } from '../base/types.ts';
7
- import { type BeforeDeserializationHook, type Deserializers } from '../serde.ts';
6
+ import { type Deserializers } from '../serde.ts';
8
7
  export interface GroupProtocolSubscription {
9
8
  name: string;
10
9
  version: number;
@@ -69,8 +68,6 @@ export interface ConsumeBaseOptions<Key, Value, HeaderKey, HeaderValue> {
69
68
  isolationLevel?: number;
70
69
  deserializers?: Partial<Deserializers<Key, Value, HeaderKey, HeaderValue>>;
71
70
  highWaterMark?: number;
72
- beforeDeserialization?: BeforeDeserializationHook;
73
- registry?: SchemaRegistry<unknown, unknown, Key, Value, HeaderKey, HeaderValue>;
74
71
  }
75
72
  export interface StreamOptions {
76
73
  topics: string[];
@@ -121,8 +121,24 @@ export declare const sendOptionsSchema: {
121
121
  items: {
122
122
  type: string;
123
123
  properties: {
124
- key: boolean;
125
- value: boolean;
124
+ key: {
125
+ oneOf: ({
126
+ type: string;
127
+ buffer?: undefined;
128
+ } | {
129
+ buffer: boolean;
130
+ type?: undefined;
131
+ })[];
132
+ };
133
+ value: {
134
+ oneOf: ({
135
+ type: string;
136
+ buffer?: undefined;
137
+ } | {
138
+ buffer: boolean;
139
+ type?: undefined;
140
+ })[];
141
+ };
126
142
  headers: {
127
143
  anyOf: ({
128
144
  map: boolean;
@@ -36,9 +36,7 @@ export const producerOptionsValidator = ajv.compile({
36
36
  type: 'object',
37
37
  properties: {
38
38
  ...produceOptionsProperties,
39
- serializers: serdeProperties,
40
- beforeSerialization: { function: true },
41
- registry: { type: 'object' }
39
+ serializers: serdeProperties
42
40
  },
43
41
  additionalProperties: true
44
42
  });
@@ -4,7 +4,6 @@ import { FindCoordinatorKeyTypes, ProduceAcks } from "../../apis/enumerations.js
4
4
  import { createDiagnosticContext, producerInitIdempotentChannel, producerSendsChannel, producerTransactionsChannel } from "../../diagnostic.js";
5
5
  import { UserError } from "../../errors.js";
6
6
  import { murmur2 } from "../../protocol/murmur2.js";
7
- import { runAsyncSeries } from "../../registries/abstract.js";
8
7
  import { kInstance, kTransaction, kTransactionAddOffsets, kTransactionAddPartitions, kTransactionCancel, kTransactionCommitOffset, kTransactionEnd, kTransactionFindCoordinator, kTransactionPrepare } from "../../symbols.js";
9
8
  import { NumericMap } from "../../utils.js";
10
9
  import { Base, kAfterCreate, kCheckNotClosed, kClosed, kGetApi, kGetBootstrapConnection, kGetConnection, kMetadata, kOptions, kPerformDeduplicated, kPerformWithRetry, kPrometheus, kValidateOptions } from "../base/base.js";
@@ -29,7 +28,6 @@ export class Producer extends Base {
29
28
  #metricsProducedMessages;
30
29
  #coordinatorId;
31
30
  #transaction;
32
- #sendOperation;
33
31
  constructor(options) {
34
32
  if (options.idempotent) {
35
33
  options.maxInflights = 1;
@@ -41,37 +39,18 @@ export class Producer extends Base {
41
39
  }
42
40
  options.repeatOnStaleMetadata ??= true;
43
41
  super(options);
44
- this[kValidateOptions](options, producerOptionsValidator, '/options');
45
- let serializers = options.serializers;
46
- if (options.registry) {
47
- if (options.beforeSerialization) {
48
- throw new UserError('/options/beforeSerialization cannot be provided when /options/registry is provided.');
49
- }
50
- else if (options.serializers) {
51
- throw new UserError('/options/serializers cannot be provided when /options/registry is provided.');
52
- }
53
- serializers = options.registry.getSerializers();
54
- }
55
42
  this.#partitionsRoundRobin = new NumericMap();
56
43
  this.#sequences = new NumericMap();
57
- this.#keySerializer = serializers?.key ?? noopSerializer;
58
- this.#valueSerializer = serializers?.value ?? noopSerializer;
59
- this.#headerKeySerializer = serializers?.headerKey ?? noopSerializer;
60
- this.#headerValueSerializer = serializers?.headerValue ?? noopSerializer;
44
+ this.#keySerializer = options.serializers?.key ?? noopSerializer;
45
+ this.#valueSerializer = options.serializers?.value ?? noopSerializer;
46
+ this.#headerKeySerializer = options.serializers?.headerKey ?? noopSerializer;
47
+ this.#headerValueSerializer = options.serializers?.headerValue ?? noopSerializer;
61
48
  this[kOptions].transactionalId ??= randomUUID();
49
+ this[kValidateOptions](options, producerOptionsValidator, '/options');
62
50
  if (this[kPrometheus]) {
63
51
  ensureMetric(this[kPrometheus], 'Gauge', 'kafka_producers', 'Number of active Kafka producers').inc();
64
52
  this.#metricsProducedMessages = ensureMetric(this[kPrometheus], 'Counter', 'kafka_produced_messages', 'Number of produced Kafka messages');
65
53
  }
66
- if (options.registry) {
67
- this.#sendOperation = this.#beforeSerialization.bind(this, options.registry.getBeforeSerializationHook());
68
- }
69
- else if (options.beforeSerialization) {
70
- this.#sendOperation = this.#beforeSerialization.bind(this, options.beforeSerialization);
71
- }
72
- else {
73
- this.#sendOperation = this.#send.bind(this);
74
- }
75
54
  this[kAfterCreate]('producer');
76
55
  }
77
56
  get producerId() {
@@ -153,7 +132,7 @@ export class Producer extends Base {
153
132
  }
154
133
  }
155
134
  options.acks ??= idempotent ? ProduceAcks.ALL : ProduceAcks.LEADER;
156
- producerSendsChannel.traceCallback(this.#sendOperation, 1, createDiagnosticContext({ client: this, operation: 'send', options }), this, options, callback);
135
+ producerSendsChannel.traceCallback(this.#send, 1, createDiagnosticContext({ client: this, operation: 'send', options }), this, options, callback);
157
136
  return callback[kCallbackPromise];
158
137
  }
159
138
  beginTransaction(options, callback) {
@@ -451,28 +430,19 @@ export class Producer extends Base {
451
430
  const messages = [];
452
431
  for (const message of options.messages) {
453
432
  const topic = message.topic;
454
- let key;
455
- let value;
456
433
  let headers = new Map();
457
434
  const serializedHeaders = new Map();
458
- const metadata = message.metadata;
459
- try {
460
- if (message.headers) {
461
- headers =
462
- message.headers instanceof Map
463
- ? message.headers
464
- : new Map(Object.entries(message.headers));
465
- for (const [key, value] of headers) {
466
- serializedHeaders.set(this.#headerKeySerializer(key, metadata), this.#headerValueSerializer(value, metadata));
467
- }
435
+ if (message.headers) {
436
+ headers =
437
+ message.headers instanceof Map
438
+ ? message.headers
439
+ : new Map(Object.entries(message.headers));
440
+ for (const [key, value] of headers) {
441
+ serializedHeaders.set(this.#headerKeySerializer(key), this.#headerValueSerializer(value));
468
442
  }
469
- key = this.#keySerializer(message.key, headers, message);
470
- value = this.#valueSerializer(message.value, headers, message);
471
- }
472
- catch (error) {
473
- callback(new UserError('Failed to serialize a message.', { cause: error }));
474
- return;
475
443
  }
444
+ const key = this.#keySerializer(message.key, headers);
445
+ const value = this.#valueSerializer(message.value, headers);
476
446
  let partition = 0;
477
447
  if (typeof message.partition !== 'number') {
478
448
  if (partitioner) {
@@ -652,34 +622,4 @@ export class Producer extends Base {
652
622
  this.#transaction = undefined;
653
623
  }
654
624
  }
655
- #beforeSerialization(hook, options, callback) {
656
- // Create the pre-serialization requests
657
- const requests = [];
658
- for (const message of options.messages) {
659
- requests.push([message.key, 'key', message]);
660
- requests.push([message.value, 'value', message]);
661
- if (typeof message.headers !== 'undefined') {
662
- const headers = message.headers instanceof Map
663
- ? message.headers
664
- : new Map(Object.entries(message.headers));
665
- for (const [headerKey, headerValue] of headers) {
666
- requests.push([headerKey, 'headerKey', message]);
667
- requests.push([headerValue, 'headerValue', message]);
668
- }
669
- }
670
- }
671
- runAsyncSeries((request, cb) => {
672
- const [data, type, message] = request;
673
- const result = hook(data, type, message, cb);
674
- if (typeof result?.then === 'function') {
675
- result.then(() => cb(null), cb);
676
- }
677
- }, requests, 0, error => {
678
- if (error) {
679
- callback(error);
680
- return;
681
- }
682
- this.#send(options, callback);
683
- });
684
- }
685
625
  }
@@ -1,8 +1,7 @@
1
1
  import { type CompressionAlgorithmValue } from '../../protocol/compression.ts';
2
2
  import { type MessageToProduce } from '../../protocol/records.ts';
3
- import { type SchemaRegistry } from '../../registries/abstract.ts';
4
3
  import { type BaseOptions, type TopicWithPartitionAndOffset } from '../base/types.ts';
5
- import { type BeforeSerializationHook, type Serializers } from '../serde.ts';
4
+ import { type Serializers } from '../serde.ts';
6
5
  export interface ProducerInfo {
7
6
  producerId: bigint;
8
7
  producerEpoch: number;
@@ -26,8 +25,6 @@ export interface ProduceOptions<Key, Value, HeaderKey, HeaderValue> {
26
25
  export type ProducerOptions<Key, Value, HeaderKey, HeaderValue> = BaseOptions & ProduceOptions<Key, Value, HeaderKey, HeaderValue> & {
27
26
  transactionalId?: string;
28
27
  serializers?: Partial<Serializers<Key, Value, HeaderKey, HeaderValue>>;
29
- beforeSerialization?: BeforeSerializationHook<Key, Value, HeaderKey, HeaderValue>;
30
- registry?: SchemaRegistry<unknown, unknown, Key, Value, HeaderKey, HeaderValue>;
31
28
  };
32
29
  export type SendOptions<Key, Value, HeaderKey, HeaderValue> = {
33
30
  messages: MessageToProduce<Key, Value, HeaderKey, HeaderValue>[];
@@ -1,9 +1,7 @@
1
- import { type Callback } from '../apis/definitions.ts';
2
- import { type MessageToConsume, type MessageToProduce } from '../protocol/records.ts';
3
- export type Serializer<InputType = unknown> = (data?: InputType, metadata?: unknown) => Buffer | undefined;
4
- export type SerializerWithHeaders<InputType = unknown, HeaderKey = unknown, HeaderValue = unknown> = (data?: InputType, headers?: Map<HeaderKey, HeaderValue>, message?: MessageToProduce<unknown, unknown, unknown, unknown>) => Buffer | undefined;
5
- export type Deserializer<OutputType = unknown> = (data?: Buffer, message?: MessageToConsume) => OutputType | undefined;
6
- export type DeserializerWithHeaders<OutputType = unknown, HeaderKey = unknown, HeaderValue = unknown> = (data?: Buffer, headers?: Map<HeaderKey, HeaderValue>, message?: MessageToConsume) => OutputType | undefined;
1
+ export type Serializer<InputType = unknown> = (data?: InputType) => Buffer | undefined;
2
+ export type Deserializer<OutputType = unknown> = (data?: Buffer) => OutputType | undefined;
3
+ export type SerializerWithHeaders<InputType = unknown, HeaderKey = unknown, HeaderValue = unknown> = (data?: InputType, headers?: Map<HeaderKey, HeaderValue>) => Buffer | undefined;
4
+ export type DeserializerWithHeaders<OutputType = unknown, HeaderKey = unknown, HeaderValue = unknown> = (data?: Buffer, headers?: Map<HeaderKey, HeaderValue>) => OutputType | undefined;
7
5
  export interface Serializers<Key, Value, HeaderKey, HeaderValue> {
8
6
  key: SerializerWithHeaders<Key, HeaderKey, HeaderValue>;
9
7
  value: SerializerWithHeaders<Value, HeaderKey, HeaderValue>;
@@ -11,14 +9,11 @@ export interface Serializers<Key, Value, HeaderKey, HeaderValue> {
11
9
  headerValue: Serializer<HeaderValue>;
12
10
  }
13
11
  export interface Deserializers<Key, Value, HeaderKey, HeaderValue> {
14
- key: DeserializerWithHeaders<Key, HeaderKey, HeaderValue>;
15
- value: DeserializerWithHeaders<Value, HeaderKey, HeaderValue>;
12
+ key: DeserializerWithHeaders<Key>;
13
+ value: DeserializerWithHeaders<Value>;
16
14
  headerKey: Deserializer<HeaderKey>;
17
15
  headerValue: Deserializer<HeaderValue>;
18
16
  }
19
- export type BeforeHookPayloadType = 'key' | 'value' | 'headerKey' | 'headerValue';
20
- export type BeforeDeserializationHook = (payload: Buffer, type: BeforeHookPayloadType, message: MessageToConsume, callback: Callback<void>) => void | Promise<void>;
21
- export type BeforeSerializationHook<Key, Value, HeaderKey, HeaderValue> = (payload: unknown, type: BeforeHookPayloadType, message: MessageToProduce<Key, Value, HeaderKey, HeaderValue>, callback: Callback<void>) => void | Promise<void>;
22
17
  export declare function stringSerializer(data?: string): Buffer | undefined;
23
18
  export declare function stringDeserializer(data?: string | Buffer): string | undefined;
24
19
  export declare function jsonSerializer<T = Record<string, any>>(data?: T): Buffer | undefined;