@platformatic/kafka 1.33.2 → 1.35.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.
Files changed (29) hide show
  1. package/dist/apis/admin/alter-partition-reassignments-v1.js +1 -0
  2. package/dist/clients/admin/admin.d.ts +1 -1
  3. package/dist/clients/admin/admin.js +2 -1
  4. package/dist/clients/base/base.d.ts +2 -0
  5. package/dist/clients/base/base.js +17 -4
  6. package/dist/clients/consumer/consumer.js +25 -2
  7. package/dist/clients/consumer/messages-stream.d.ts +1 -0
  8. package/dist/clients/consumer/messages-stream.js +41 -14
  9. package/dist/clients/consumer/options.d.ts +16 -0
  10. package/dist/clients/consumer/options.js +1 -0
  11. package/dist/clients/consumer/types.d.ts +1 -0
  12. package/dist/clients/producer/partitioners.js +2 -0
  13. package/dist/clients/producer/producer.d.ts +6 -0
  14. package/dist/clients/producer/producer.js +125 -24
  15. package/dist/clients/producer/types.d.ts +5 -2
  16. package/dist/errors.js +1 -1
  17. package/dist/network/connection-pool.d.ts +3 -0
  18. package/dist/network/connection-pool.js +9 -0
  19. package/dist/network/connection.js +1 -0
  20. package/dist/typescript-4/dist/clients/admin/admin.d.ts +1 -1
  21. package/dist/typescript-4/dist/clients/base/base.d.ts +2 -0
  22. package/dist/typescript-4/dist/clients/consumer/messages-stream.d.ts +1 -0
  23. package/dist/typescript-4/dist/clients/consumer/options.d.ts +16 -0
  24. package/dist/typescript-4/dist/clients/consumer/types.d.ts +1 -0
  25. package/dist/typescript-4/dist/clients/producer/producer.d.ts +6 -0
  26. package/dist/typescript-4/dist/clients/producer/types.d.ts +5 -2
  27. package/dist/typescript-4/dist/network/connection-pool.d.ts +3 -0
  28. package/dist/version.js +1 -1
  29. package/package.json +19 -17
@@ -39,6 +39,7 @@ export function parseResponse(_correlationId, apiKey, apiVersion, reader) {
39
39
  const throttleTimeMs = reader.readInt32();
40
40
  const errorCode = reader.readInt16();
41
41
  const errorMessage = reader.readNullableString();
42
+ /* c8 ignore next 3 - Hard to test */
42
43
  if (errorCode !== 0) {
43
44
  errors.push(['', [errorCode, errorMessage]]);
44
45
  }
@@ -5,7 +5,7 @@ import { type CallbackWithPromise } from '../../apis/callbacks.ts';
5
5
  import { type Callback } from '../../apis/definitions.ts';
6
6
  import { type Acl } from '../../apis/types.ts';
7
7
  import { Base } from '../base/base.ts';
8
- import { type AdminListOffsetsOptions, type AdminOptions, type AlterClientQuotasOptions, type AlterConfigsOptions, type AlterConsumerGroupOffsetsOptions, type BrokerLogDirDescription, type ConfigDescription, type CreateAclsOptions, type CreatedTopic, type CreatePartitionsOptions, type CreateTopicsOptions, type DeleteAclsOptions, type DeleteConsumerGroupOffsetsOptions, type DeleteRecordsOptions, type DeleteGroupsOptions, type DeleteTopicsOptions, type DeletedRecordsTopic, type DescribeAclsOptions, type DescribeClientQuotasOptions, type DescribeConfigsOptions, type DescribeGroupsOptions, type DescribeLogDirsOptions, type FindCoordinatorOptions, type FindCoordinatorResult, type Group, type GroupBase, type IncrementalAlterConfigsOptions, type ListConsumerGroupOffsetsGroup, type ListConsumerGroupOffsetsOptions, type ListedOffsetsTopic, type ListGroupsOptions, type ListTopicsOptions, type RemoveMembersFromConsumerGroupOptions } from './types.ts';
8
+ import { type AdminListOffsetsOptions, type AdminOptions, type AlterClientQuotasOptions, type AlterConfigsOptions, type AlterConsumerGroupOffsetsOptions, type BrokerLogDirDescription, type ConfigDescription, type CreateAclsOptions, type CreatedTopic, type CreatePartitionsOptions, type CreateTopicsOptions, type DeleteAclsOptions, type DeleteConsumerGroupOffsetsOptions, type DeletedRecordsTopic, type DeleteGroupsOptions, type DeleteRecordsOptions, type DeleteTopicsOptions, type DescribeAclsOptions, type DescribeClientQuotasOptions, type DescribeConfigsOptions, type DescribeGroupsOptions, type DescribeLogDirsOptions, type FindCoordinatorOptions, type FindCoordinatorResult, type Group, type GroupBase, type IncrementalAlterConfigsOptions, type ListConsumerGroupOffsetsGroup, type ListConsumerGroupOffsetsOptions, type ListedOffsetsTopic, type ListGroupsOptions, type ListTopicsOptions, type RemoveMembersFromConsumerGroupOptions } from './types.ts';
9
9
  export declare class Admin extends Base<AdminOptions> {
10
10
  #private;
11
11
  constructor(options: AdminOptions);
@@ -4,7 +4,7 @@ import { adminAclsChannel, adminClientQuotasChannel, adminConfigsChannel, adminC
4
4
  import { MultipleErrors, UserError } from "../../errors.js";
5
5
  import { Reader } from "../../protocol/reader.js";
6
6
  import { Base, kAfterCreate, kCheckNotClosed, kConnections, kGetApi, kGetBootstrapConnection, kGetConnection, kMetadata, kOptions, kPerformDeduplicated, kPerformWithRetry, kValidateOptions } from "../base/base.js";
7
- import { adminListOffsetsOptionsValidator, alterClientQuotasOptionsValidator, alterConfigsOptionsValidator, alterConsumerGroupOffsetsOptionsValidator, createAclsOptionsValidator, createPartitionsOptionsValidator, createTopicsOptionsValidator, deleteAclsOptionsValidator, deleteConsumerGroupOffsetsOptionsValidator, deleteRecordsOptionsValidator, deleteGroupsOptionsValidator, deleteTopicsOptionsValidator, describeAclsOptionsValidator, describeClientQuotasOptionsValidator, describeConfigsOptionsValidator, describeGroupsOptionsValidator, describeLogDirsOptionsValidator, findCoordinatorOptionsValidator, incrementalAlterConfigsOptionsValidator, listConsumerGroupOffsetsOptionsValidator, listGroupsOptionsValidator, listTopicsOptionsValidator, removeMembersFromConsumerGroupOptionsValidator } from "./options.js";
7
+ import { adminListOffsetsOptionsValidator, alterClientQuotasOptionsValidator, alterConfigsOptionsValidator, alterConsumerGroupOffsetsOptionsValidator, createAclsOptionsValidator, createPartitionsOptionsValidator, createTopicsOptionsValidator, deleteAclsOptionsValidator, deleteConsumerGroupOffsetsOptionsValidator, deleteGroupsOptionsValidator, deleteRecordsOptionsValidator, deleteTopicsOptionsValidator, describeAclsOptionsValidator, describeClientQuotasOptionsValidator, describeConfigsOptionsValidator, describeGroupsOptionsValidator, describeLogDirsOptionsValidator, findCoordinatorOptionsValidator, incrementalAlterConfigsOptionsValidator, listConsumerGroupOffsetsOptionsValidator, listGroupsOptionsValidator, listTopicsOptionsValidator, removeMembersFromConsumerGroupOptionsValidator } from "./options.js";
8
8
  export class Admin extends Base {
9
9
  #controller = null;
10
10
  constructor(options) {
@@ -1391,6 +1391,7 @@ export class Admin extends Base {
1391
1391
  // topics.get(topic.name) must be defined as the metadata request was successful
1392
1392
  const topicData = topics.get(topic.name);
1393
1393
  const targetPartitionData = topicData.partitions[partition.partitionIndex];
1394
+ /* c8 ignore next 4 - Hard to test */
1394
1395
  if (!targetPartitionData) {
1395
1396
  callback(new UserError(`Unknown partition ${partition.partitionIndex} for topic ${topic.name}.`));
1396
1397
  return;
@@ -66,6 +66,8 @@ export declare class Base<OptionsType extends BaseOptions = BaseOptions, EventsT
66
66
  get closed(): boolean;
67
67
  get type(): ClientType;
68
68
  get context(): unknown;
69
+ get currentMetadata(): ClusterMetadata | undefined;
70
+ get connections(): ConnectionPool;
69
71
  emitWithDebug(section: string | null, name: string, ...args: any[]): boolean;
70
72
  close(callback: CallbackWithPromise<void>): void;
71
73
  close(): Promise<void>;
@@ -90,6 +90,12 @@ export class Base extends TypedEventEmitter {
90
90
  get context() {
91
91
  return this[kContext];
92
92
  }
93
+ get currentMetadata() {
94
+ return this.#metadata;
95
+ }
96
+ get connections() {
97
+ return this[kConnections];
98
+ }
93
99
  emitWithDebug(section, name, ...args) {
94
100
  if (!section) {
95
101
  return this.emit(name, ...args);
@@ -254,14 +260,19 @@ export class Base extends TypedEventEmitter {
254
260
  this.once('client:close', onClose);
255
261
  }
256
262
  else {
257
- if (attempt === 0) {
258
- callback(error);
259
- return;
263
+ if (attempt > 0) {
264
+ error = new MultipleErrors(`${operationId} failed ${attempt + 1} times.`, errors);
260
265
  }
261
- callback(new MultipleErrors(`${operationId} failed ${attempt + 1} times.`, errors));
266
+ this.emitWithDebug('client', 'performWithRetry:failed', operationId, retries, errors);
267
+ callback(error);
268
+ return;
262
269
  }
263
270
  return;
264
271
  }
272
+ // Make sure all previous errors are deleted
273
+ if (errors.length > 0) {
274
+ errors.splice(0, errors.length);
275
+ }
265
276
  callback(null, result);
266
277
  });
267
278
  return callback[kCallbackPromise];
@@ -398,6 +409,7 @@ export class Base extends TypedEventEmitter {
398
409
  const unknownTopicError = error.findBy('apiCode', 3);
399
410
  if (unknownTopicError) {
400
411
  const topicIndexMatch = unknownTopicError.path?.match(/\/topics\/(\d+)/);
412
+ /* c8 ignore next 3 - Hard to test */
401
413
  const topicIndex = topicIndexMatch ? parseInt(topicIndexMatch[1]) : -1;
402
414
  const topicName = topicIndex >= 0 && topicIndex < topicsToFetch.length ? topicsToFetch[topicIndex] : 'unknown';
403
415
  deduplicateCallback(new UserError(`Unknown topic ${topicName}.`));
@@ -405,6 +417,7 @@ export class Base extends TypedEventEmitter {
405
417
  }
406
418
  const hasStaleMetadata = error.findBy('hasStaleMetadata', true);
407
419
  // Stale metadata, we need to fetch everything again
420
+ /* c8 ignore next 4 - Hard to test */
408
421
  if (hasStaleMetadata) {
409
422
  this.clearMetadata();
410
423
  topicsToFetch = topics;
@@ -172,6 +172,7 @@ export class Consumer extends Base {
172
172
  }
173
173
  options.autocommit ??= this[kOptions].autocommit ?? true;
174
174
  options.maxBytes ??= this[kOptions].maxBytes;
175
+ options.maxBytesPerPartition ??= this[kOptions].maxBytesPerPartition ?? options.maxBytes;
175
176
  options.highWaterMark ??= this[kOptions].highWaterMark;
176
177
  options.registry ??= this[kOptions].registry;
177
178
  options.beforeDeserialization ??= this[kOptions].beforeDeserialization;
@@ -483,7 +484,7 @@ export class Consumer extends Base {
483
484
  callback(error, response);
484
485
  }, 0);
485
486
  }
486
- #commit(options, callback) {
487
+ #commit(options, callback, rejoinAttempts = 0) {
487
488
  this.#performGroupOperation('commit', (connection, groupCallback) => {
488
489
  const topics = new Map();
489
490
  for (const { topic, partition, offset, leaderEpoch } of options.offsets) {
@@ -507,6 +508,16 @@ export class Consumer extends Base {
507
508
  api(connection, this.groupId, this.#useConsumerGroupProtocol ? this.#memberEpoch : this.generationId, this.memberId, null, Array.from(topics.values()), groupCallback);
508
509
  });
509
510
  }, error => {
511
+ if (error && rejoinAttempts < this[kOptions].retries && this.#getRejoinError(error)) {
512
+ this.joinGroup({}, joinError => {
513
+ if (joinError) {
514
+ callback(joinError);
515
+ return;
516
+ }
517
+ this.#commit(options, callback, rejoinAttempts + 1);
518
+ });
519
+ return;
520
+ }
510
521
  callback(error);
511
522
  });
512
523
  }
@@ -608,7 +619,7 @@ export class Consumer extends Base {
608
619
  });
609
620
  });
610
621
  }
611
- #listCommittedOffsets(options, callback) {
622
+ #listCommittedOffsets(options, callback, staleMemberEpochRetries = 0) {
612
623
  const topics = [];
613
624
  for (const { topic: name, partitions } of options.topics) {
614
625
  topics.push({ name, partitionIndexes: partitions });
@@ -630,6 +641,18 @@ export class Consumer extends Base {
630
641
  });
631
642
  }, (error, response) => {
632
643
  if (error) {
644
+ if (this.#useConsumerGroupProtocol &&
645
+ staleMemberEpochRetries < this[kOptions].retries &&
646
+ error.findBy?.('apiId', 'STALE_MEMBER_EPOCH')) {
647
+ this.#consumerGroupHeartbeat(this[kOptions], heartbeatError => {
648
+ if (heartbeatError) {
649
+ callback(this.#handleMetadataError(heartbeatError));
650
+ return;
651
+ }
652
+ this.#listCommittedOffsets(options, callback, staleMemberEpochRetries + 1);
653
+ });
654
+ return;
655
+ }
633
656
  callback(this.#handleMetadataError(error));
634
657
  return;
635
658
  }
@@ -19,6 +19,7 @@ export declare class MessagesStream<Key, Value, HeaderKey, HeaderValue> extends
19
19
  get offsetsToCommit(): Map<string, CommitOptionsPartition>;
20
20
  get offsetsCommitted(): Map<string, bigint>;
21
21
  get committedOffsets(): Map<string, bigint>;
22
+ get connections(): ConnectionPool;
22
23
  close(callback: CallbackWithPromise<void>): void;
23
24
  close(): Promise<void>;
24
25
  isActive(): boolean;
@@ -61,6 +61,8 @@ export class MessagesStream extends Readable {
61
61
  #metricsConsumedMessages;
62
62
  #corruptedMessageHandler;
63
63
  #context;
64
+ #onConsumerGroupJoin;
65
+ #onBrokerDisconnect;
64
66
  #pushRecordsOperation;
65
67
  [kInstance];
66
68
  /*
@@ -122,6 +124,14 @@ export class MessagesStream extends Readable {
122
124
  this.#closeCallbacks = [];
123
125
  this.#corruptedMessageHandler = onCorruptedMessage ?? defaultCorruptedMessageHandler;
124
126
  this.#context = context;
127
+ this.#onConsumerGroupJoin = () => {
128
+ this.#offsetsCommitted.clear();
129
+ this.#partitionsEpochs.clear();
130
+ this.#scheduleRefreshOffsetsAndFetch();
131
+ };
132
+ this.#onBrokerDisconnect = () => {
133
+ this.#partitionsEpochs.clear();
134
+ };
125
135
  if (registry) {
126
136
  this.#pushRecordsOperation = this.#beforeDeserialization.bind(this, registry.getBeforeDeserializationHook());
127
137
  }
@@ -150,18 +160,12 @@ export class MessagesStream extends Readable {
150
160
  // When the consumer joins a group, we need to fetch again as the assignments
151
161
  // will have changed so we may have gone from last with no assignments to
152
162
  // having some.
153
- this.#consumer.on('consumer:group:join', () => {
154
- this.#offsetsCommitted.clear();
155
- this.#partitionsEpochs.clear();
156
- this.#scheduleRefreshOffsetsAndFetch();
157
- });
163
+ this.#consumer.on('consumer:group:join', this.#onConsumerGroupJoin);
158
164
  if (consumer[kPrometheus]) {
159
165
  this.#metricsConsumedMessages = ensureMetric(consumer[kPrometheus], 'Counter', 'kafka_consumed_messages', 'Number of consumed Kafka messages');
160
166
  }
161
167
  // Whenever the consumer loses a connection, reset all the partitions epochs
162
- consumer.on('client:broker:disconnect', () => {
163
- this.#partitionsEpochs.clear();
164
- });
168
+ consumer.on('client:broker:disconnect', this.#onBrokerDisconnect);
165
169
  notifyCreation('messages-stream', this);
166
170
  }
167
171
  /* c8 ignore next 3 - Simple getter */
@@ -188,6 +192,10 @@ export class MessagesStream extends Readable {
188
192
  get committedOffsets() {
189
193
  return this.#offsetsCommitted;
190
194
  }
195
+ /* c8 ignore next 3 - Simple getter */
196
+ get connections() {
197
+ return this[kConnections];
198
+ }
191
199
  close(callback) {
192
200
  if (!callback) {
193
201
  callback = createPromisifiedCallback();
@@ -298,12 +306,23 @@ export class MessagesStream extends Readable {
298
306
  return super[Symbol.asyncIterator]();
299
307
  }
300
308
  _construct(callback) {
301
- this.#refreshOffsets(callback);
309
+ this.#refreshOffsetsInflight = true;
310
+ this.#refreshOffsets(error => {
311
+ const wasPending = this.#refreshOffsetsPending;
312
+ this.#refreshOffsetsInflight = false;
313
+ this.#refreshOffsetsPending = false;
314
+ if (!error && wasPending) {
315
+ this.#scheduleRefreshOffsetsAndFetch();
316
+ }
317
+ callback(error ?? undefined);
318
+ });
302
319
  }
303
320
  _destroy(error, callback) {
304
321
  if (this.#autocommitInterval) {
305
322
  clearInterval(this.#autocommitInterval);
306
323
  }
324
+ this.#consumer.removeListener('consumer:group:join', this.#onConsumerGroupJoin);
325
+ this.#consumer.removeListener('client:broker:disconnect', this.#onBrokerDisconnect);
307
326
  this[kConnections].close(closeError => {
308
327
  callback(closeError ?? error);
309
328
  });
@@ -379,7 +398,7 @@ export class MessagesStream extends Readable {
379
398
  {
380
399
  partition,
381
400
  fetchOffset,
382
- partitionMaxBytes: this.#options.maxBytes,
401
+ partitionMaxBytes: this.#options.maxBytesPerPartition,
383
402
  currentLeaderEpoch: leaderEpoch,
384
403
  lastFetchedEpoch: leaderEpoch
385
404
  }
@@ -589,7 +608,12 @@ export class MessagesStream extends Readable {
589
608
  this.#autocommitInflight = false;
590
609
  if (error) {
591
610
  this.emit('autocommit', error);
592
- this.destroy(error);
611
+ // Only destroy the stream when the broker requires a group rejoin
612
+ // (ILLEGAL_GENERATION, UNKNOWN_MEMBER_ID, REBALANCE_IN_PROGRESS).
613
+ // Transient coordinator errors must not tear down the consumption loop.
614
+ if (error.findBy?.('needsRejoin', true)) {
615
+ this.destroy(error);
616
+ }
593
617
  return;
594
618
  }
595
619
  for (const { topic, partition, offset } of offsets) {
@@ -635,7 +659,10 @@ export class MessagesStream extends Readable {
635
659
  topics.push(assignment);
636
660
  }
637
661
  if (!topics.length) {
638
- this.#assignOffsets(offsets, new Map(), callback);
662
+ // No assignment yet: do not seed fallback offsets for the whole topic.
663
+ // consumer:group:join will refresh once partitions are assigned.
664
+ this.emit('offsets');
665
+ callback(null);
639
666
  return;
640
667
  }
641
668
  this.#consumer.listCommittedOffsets({ topics }, (error, commits) => {
@@ -662,6 +689,7 @@ export class MessagesStream extends Readable {
662
689
  this.#refreshOffsetsInflight = true;
663
690
  this.#refreshOffsets(error => {
664
691
  const shouldDestroyOnError = this.#refreshOffsetsDestroyOnError;
692
+ const wasPending = this.#refreshOffsetsPending;
665
693
  this.#refreshOffsetsInflight = false;
666
694
  this.#refreshOffsetsDestroyOnError = false;
667
695
  this.#refreshOffsetsPending = false;
@@ -673,8 +701,7 @@ export class MessagesStream extends Readable {
673
701
  return;
674
702
  }
675
703
  // A new one was scheduled while the previous one was inflight, we need to run it immediately
676
- /* c8 ignore next 4 - Hard to test */
677
- if (this.#refreshOffsetsPending) {
704
+ if (wasPending) {
678
705
  this.#scheduleRefreshOffsetsAndFetch(shouldDestroyOnError);
679
706
  return;
680
707
  }
@@ -97,6 +97,10 @@ export declare const consumeOptionsProperties: {
97
97
  type: string;
98
98
  minimum: number;
99
99
  };
100
+ maxBytesPerPartition: {
101
+ type: string;
102
+ minimum: number;
103
+ };
100
104
  maxWaitTime: {
101
105
  type: string;
102
106
  minimum: number;
@@ -217,6 +221,10 @@ export declare const consumeOptionsSchema: {
217
221
  type: string;
218
222
  minimum: number;
219
223
  };
224
+ maxBytesPerPartition: {
225
+ type: string;
226
+ minimum: number;
227
+ };
220
228
  maxWaitTime: {
221
229
  type: string;
222
230
  minimum: number;
@@ -379,6 +387,10 @@ export declare const consumerOptionsSchema: {
379
387
  type: string;
380
388
  minimum: number;
381
389
  };
390
+ maxBytesPerPartition: {
391
+ type: string;
392
+ minimum: number;
393
+ };
382
394
  maxWaitTime: {
383
395
  type: string;
384
396
  minimum: number;
@@ -502,6 +514,10 @@ export declare const fetchOptionsSchema: {
502
514
  type: string;
503
515
  minimum: number;
504
516
  };
517
+ maxBytesPerPartition: {
518
+ type: string;
519
+ minimum: number;
520
+ };
505
521
  maxWaitTime: {
506
522
  type: string;
507
523
  minimum: number;
@@ -58,6 +58,7 @@ export const consumeOptionsProperties = {
58
58
  autocommit: { oneOf: [{ type: 'boolean' }, { type: 'number', minimum: 100 }] },
59
59
  minBytes: { type: 'number', minimum: 0 },
60
60
  maxBytes: { type: 'number', minimum: 0 },
61
+ maxBytesPerPartition: { type: 'number', minimum: 0 },
61
62
  maxWaitTime: { type: 'number', minimum: 0 },
62
63
  isolationLevel: { type: 'number', enum: allowedFetchIsolationLevels },
63
64
  deserializers: serdeProperties,
@@ -65,6 +65,7 @@ export interface ConsumeBaseOptions<Key, Value, HeaderKey, HeaderValue> {
65
65
  autocommit?: boolean | number;
66
66
  minBytes?: number;
67
67
  maxBytes?: number;
68
+ maxBytesPerPartition?: number;
68
69
  maxWaitTime?: number;
69
70
  isolationLevel?: number;
70
71
  deserializers?: Partial<Deserializers<Key, Value, HeaderKey, HeaderValue>>;
@@ -1,8 +1,10 @@
1
1
  import { murmur2 } from "../../protocol/murmur2.js";
2
2
  const compatibilityMurmur2Mask = 0x7fffffff;
3
3
  export function defaultPartitioner(_, key) {
4
+ /* c8 ignore next - Hard to test */
4
5
  return Buffer.isBuffer(key) ? murmur2(key) >>> 0 : 0;
5
6
  }
6
7
  export function compatibilityPartitioner(_, key) {
8
+ /* c8 ignore next - Hard to test */
7
9
  return Buffer.isBuffer(key) ? murmur2(key) & compatibilityMurmur2Mask : 0;
8
10
  }
@@ -1,4 +1,5 @@
1
1
  import { type CallbackWithPromise } from '../../apis/callbacks.ts';
2
+ import { type Broker, type Connection } from '../../network/connection.ts';
2
3
  import { type Message } from '../../protocol/records.ts';
3
4
  import { kTransaction, kTransactionAddOffsets, kTransactionAddPartitions, kTransactionCancel, kTransactionCommitOffset, kTransactionEnd, kTransactionFindCoordinator } from '../../symbols.ts';
4
5
  import { Base } from '../base/base.ts';
@@ -27,6 +28,11 @@ export declare class Producer<Key = Buffer, Value = Buffer, HeaderKey = Buffer,
27
28
  asStream(options?: ProducerStreamOptions<Key, Value, HeaderKey, HeaderValue>): ProducerStream<Key, Value, HeaderKey, HeaderValue>;
28
29
  beginTransaction(options: ProduceOptions<Key, Value, HeaderKey, HeaderValue>, callback: CallbackWithPromise<Transaction<Key, Value, HeaderKey, HeaderValue>>): void;
29
30
  beginTransaction(options?: ProduceOptions<Key, Value, HeaderKey, HeaderValue>): Promise<Transaction<Key, Value, HeaderKey, HeaderValue>>;
31
+ getSendTopicPartitions(options: SendOptions<Key, Value, HeaderKey, HeaderValue>): Map<string, Set<number>>;
32
+ getSendBrokers(options: SendOptions<Key, Value, HeaderKey, HeaderValue>, callback: CallbackWithPromise<Record<string, Broker>>): void;
33
+ getSendBrokers(options: SendOptions<Key, Value, HeaderKey, HeaderValue>): Promise<Record<string, Broker>>;
34
+ getSendConnections(options: SendOptions<Key, Value, HeaderKey, HeaderValue>, callback: CallbackWithPromise<Record<string, Connection>>): void;
35
+ getSendConnections(options: SendOptions<Key, Value, HeaderKey, HeaderValue>): Promise<Record<string, Connection>>;
30
36
  [kTransactionFindCoordinator](callback: CallbackWithPromise<void>): void;
31
37
  [kTransactionAddPartitions](transactionId: number, topicsPartitions: Map<string, Set<number>>, callback: CallbackWithPromise<void>): void;
32
38
  [kTransactionAddOffsets](transactionId: number, groupId: string, callback: CallbackWithPromise<void>): void;
@@ -243,6 +243,98 @@ export class Producer extends Base {
243
243
  producerTransactionsChannel.traceCallback(this.#transaction[kTransactionPrepare], 2, createDiagnosticContext({ client: this, operation: 'begin' }), this.#transaction, this.#producerInfo?.transactionalId, options, callback);
244
244
  return callback[kCallbackPromise];
245
245
  }
246
+ getSendTopicPartitions(options) {
247
+ const topicsPartitions = new Map();
248
+ const partitioner = options.partitioner ?? this[kOptions].partitioner;
249
+ for (const message of options.messages) {
250
+ const topic = message.topic;
251
+ let key;
252
+ let headers = new Map();
253
+ try {
254
+ if (message.headers) {
255
+ headers =
256
+ message.headers instanceof Map
257
+ ? message.headers
258
+ : new Map(Object.entries(message.headers));
259
+ }
260
+ key = this.#keySerializer(message.key, headers, message);
261
+ }
262
+ catch (error) {
263
+ throw new UserError('Failed to serialize a message.', { cause: error });
264
+ }
265
+ const partition = this.#assignPartition(message, partitioner, key, topic);
266
+ let partitions = topicsPartitions.get(topic);
267
+ if (!partitions) {
268
+ partitions = new Set();
269
+ topicsPartitions.set(topic, partitions);
270
+ }
271
+ partitions.add(partition);
272
+ }
273
+ return topicsPartitions;
274
+ }
275
+ getSendBrokers(options, callback) {
276
+ if (!callback) {
277
+ callback = createPromisifiedCallback();
278
+ }
279
+ if (this[kCheckNotClosed](callback)) {
280
+ return callback[kCallbackPromise];
281
+ }
282
+ let topicsPartitions;
283
+ try {
284
+ topicsPartitions = this.getSendTopicPartitions(options);
285
+ }
286
+ catch (error) {
287
+ callback(error);
288
+ return callback[kCallbackPromise];
289
+ }
290
+ this[kMetadata]({ topics: Array.from(topicsPartitions.keys()), autocreateTopics: options.autocreateTopics }, (error, metadata) => {
291
+ if (error) {
292
+ callback(error);
293
+ return;
294
+ }
295
+ const brokers = {};
296
+ for (const [topic, partitions] of topicsPartitions) {
297
+ for (const rawPartition of partitions) {
298
+ const partition = rawPartition & metadata.topics.get(topic).partitionsCount;
299
+ const leader = metadata.topics.get(topic).partitions[partition].leader;
300
+ const broker = metadata.brokers.get(leader);
301
+ brokers[`${topic}:${partition}`] = broker;
302
+ }
303
+ }
304
+ callback(null, brokers);
305
+ });
306
+ return callback[kCallbackPromise];
307
+ }
308
+ getSendConnections(options, callback) {
309
+ if (!callback) {
310
+ callback = createPromisifiedCallback();
311
+ }
312
+ if (this[kCheckNotClosed](callback)) {
313
+ return callback[kCallbackPromise];
314
+ }
315
+ this.getSendBrokers(options, (error, brokers) => {
316
+ if (error) {
317
+ callback(error);
318
+ return;
319
+ }
320
+ runConcurrentCallbacks('Preparing producer connections failed.', Object.entries(brokers), ([topicPartition, broker], concurrentCallback) => {
321
+ this[kGetConnection](broker, (error, connection) => {
322
+ if (error) {
323
+ concurrentCallback(error);
324
+ return;
325
+ }
326
+ concurrentCallback(null, [topicPartition, connection]);
327
+ });
328
+ }, (error, results) => {
329
+ if (error) {
330
+ callback(error);
331
+ return;
332
+ }
333
+ callback(null, Object.fromEntries(results));
334
+ });
335
+ });
336
+ return callback[kCallbackPromise];
337
+ }
246
338
  [kTransactionFindCoordinator](callback) {
247
339
  if (this.#coordinatorId !== undefined) {
248
340
  callback(null);
@@ -533,22 +625,7 @@ export class Producer extends Base {
533
625
  callback(new UserError('Failed to serialize a message.', { cause: error }));
534
626
  return;
535
627
  }
536
- let partition = 0;
537
- if (typeof message.partition !== 'number') {
538
- if (partitioner) {
539
- partition = partitioner(message, key);
540
- }
541
- else if (key) {
542
- partition = defaultPartitioner(message, key);
543
- }
544
- else {
545
- // Use the roundrobin
546
- partition = this.#partitionsRoundRobin.postIncrement(topic, 1, 0);
547
- }
548
- }
549
- else {
550
- partition = message.partition;
551
- }
628
+ const partition = this.#assignPartition(message, partitioner, key, topic);
552
629
  topics.add(topic);
553
630
  messages.push({
554
631
  key,
@@ -610,16 +687,12 @@ export class Producer extends Base {
610
687
  nodes.push(destination);
611
688
  this.#performSingleDestinationSend(topics, destinationMessages, this[kOptions].timeout, sendOptions.acks, sendOptions.autocreateTopics, sendOptions.repeatOnStaleMetadata, produceOptions, concurrentCallback);
612
689
  }, (error, apiResults) => {
613
- if (error) {
614
- callback(error);
615
- return;
616
- }
617
690
  this.#metricsProducedMessages?.inc(messages.length);
618
691
  const results = {};
619
692
  if (sendOptions.acks === ProduceAcks.NO_RESPONSE) {
620
693
  const unwritableNodes = [];
621
694
  for (let i = 0; i < apiResults.length; i++) {
622
- if (apiResults[i] === false) {
695
+ if (typeof apiResults[i] === 'undefined' || apiResults[i] === false) {
623
696
  unwritableNodes.push(nodes[i]);
624
697
  }
625
698
  }
@@ -628,7 +701,7 @@ export class Producer extends Base {
628
701
  else {
629
702
  const topics = [];
630
703
  for (const result of apiResults) {
631
- for (const { name, partitionResponses } of result.responses) {
704
+ for (const { name, partitionResponses } of result?.responses ?? []) {
632
705
  for (const partitionResponse of partitionResponses) {
633
706
  topics.push({
634
707
  topic: name,
@@ -642,7 +715,14 @@ export class Producer extends Base {
642
715
  results.offsets = topics;
643
716
  }
644
717
  }
645
- callback(null, results);
718
+ if (error) {
719
+ ;
720
+ error.produced = results;
721
+ callback(error);
722
+ }
723
+ else {
724
+ callback(null, results);
725
+ }
646
726
  });
647
727
  });
648
728
  }
@@ -673,6 +753,7 @@ export class Producer extends Base {
673
753
  if (error) {
674
754
  // If the last error was due to stale metadata, we retry the operation with this set of messages
675
755
  // since the partition is already set, it should attempt on the new destination
756
+ /* c8 ignore next 3 - Hard to test */
676
757
  const kafkaError = GenericError.isGenericError(error)
677
758
  ? error
678
759
  : null;
@@ -682,11 +763,12 @@ export class Producer extends Base {
682
763
  this.#performSingleDestinationSend(topics, messages, timeout, acks, autocreateTopics, false, produceOptions, callback);
683
764
  return;
684
765
  }
685
- callback(error);
766
+ callback(error, results);
686
767
  return;
687
768
  }
688
769
  callback(error, results);
689
770
  }, 0, [], error => {
771
+ /* c8 ignore next 3 - Hard to test */
690
772
  if (!repeatOnStaleMetadata || !GenericError.isGenericError(error)) {
691
773
  return false;
692
774
  }
@@ -752,4 +834,23 @@ export class Producer extends Base {
752
834
  this.#send(options, callback);
753
835
  });
754
836
  }
837
+ #assignPartition(message, partitioner, key, topic) {
838
+ let partition = 0;
839
+ if (typeof message.partition !== 'number') {
840
+ if (partitioner) {
841
+ partition = partitioner(message, key, { metadata: this.currentMetadata });
842
+ }
843
+ else if (key) {
844
+ partition = defaultPartitioner(message, key);
845
+ }
846
+ else {
847
+ // Use the roundrobin
848
+ partition = this.#partitionsRoundRobin.postIncrement(topic, 1, 0);
849
+ }
850
+ }
851
+ else {
852
+ partition = message.partition;
853
+ }
854
+ return partition;
855
+ }
755
856
  }
@@ -1,7 +1,7 @@
1
1
  import { type CompressionAlgorithmValue } from '../../protocol/compression.ts';
2
2
  import { type MessageToProduce } from '../../protocol/records.ts';
3
3
  import { type SchemaRegistry } from '../../registries/abstract.ts';
4
- import { type BaseOptions, type TopicWithPartitionAndOffset } from '../base/types.ts';
4
+ import { type BaseOptions, type ClusterMetadata, type TopicWithPartitionAndOffset } from '../base/types.ts';
5
5
  import { type BeforeSerializationHook, type Serializers } from '../serde.ts';
6
6
  export interface ProducerInfo {
7
7
  producerId: bigint;
@@ -12,7 +12,10 @@ export interface ProduceResult {
12
12
  offsets?: TopicWithPartitionAndOffset[];
13
13
  unwritableNodes?: number[];
14
14
  }
15
- export type Partitioner<Key, Value, HeaderKey, HeaderValue> = (message: MessageToProduce<Key, Value, HeaderKey, HeaderValue>, key?: Buffer | undefined) => number;
15
+ export interface PartitionerContext {
16
+ metadata: ClusterMetadata | undefined;
17
+ }
18
+ export type Partitioner<Key, Value, HeaderKey, HeaderValue> = (message: MessageToProduce<Key, Value, HeaderKey, HeaderValue>, key: Buffer | undefined, context: PartitionerContext) => number;
16
19
  export interface ProducerStreamReport {
17
20
  batchId: number;
18
21
  count: number;
package/dist/errors.js CHANGED
@@ -120,7 +120,7 @@ export class ProtocolError extends GenericError {
120
120
  'NOT_LEADER_OR_FOLLOWER',
121
121
  'FENCED_LEADER_EPOCH'
122
122
  ].includes(id),
123
- needsRejoin: ['MEMBER_ID_REQUIRED', 'UNKNOWN_MEMBER_ID', 'REBALANCE_IN_PROGRESS'].includes(id),
123
+ needsRejoin: ['MEMBER_ID_REQUIRED', 'UNKNOWN_MEMBER_ID', 'REBALANCE_IN_PROGRESS', 'ILLEGAL_GENERATION'].includes(id),
124
124
  producerFenced: id === 'INVALID_PRODUCER_EPOCH',
125
125
  rebalanceInProgress: id === 'REBALANCE_IN_PROGRESS',
126
126
  unknownMemberId: id === 'UNKNOWN_MEMBER_ID',
@@ -27,12 +27,15 @@ export declare class ConnectionPool extends TypedEventEmitter<ConnectionPoolEven
27
27
  get instanceId(): number;
28
28
  get ownerId(): number | undefined;
29
29
  get context(): unknown;
30
+ [Symbol.iterator](): MapIterator<[string, Connection]>;
30
31
  get(broker: Broker, callback: CallbackWithPromise<Connection>): void;
31
32
  get(broker: Broker): Promise<Connection>;
32
33
  getFirstAvailable(brokers: Broker[], callback: CallbackWithPromise<Connection>): void;
33
34
  getFirstAvailable(brokers: Broker[]): Promise<Connection>;
35
+ getEstablishedConnection(broker: Broker): Connection | undefined;
34
36
  close(callback: CallbackWithPromise<void>): void;
35
37
  close(): Promise<void>;
36
38
  isActive(): boolean;
37
39
  isConnected(): boolean;
40
+ has(broker: Broker): boolean;
38
41
  }
@@ -34,6 +34,9 @@ export class ConnectionPool extends TypedEventEmitter {
34
34
  get context() {
35
35
  return this.#context;
36
36
  }
37
+ [Symbol.iterator]() {
38
+ return this.#connections[Symbol.iterator]();
39
+ }
37
40
  get(broker, callback) {
38
41
  if (!callback) {
39
42
  callback = createPromisifiedCallback();
@@ -48,6 +51,9 @@ export class ConnectionPool extends TypedEventEmitter {
48
51
  connectionsPoolGetsChannel.traceCallback(this.#getFirstAvailable, 3, createDiagnosticContext({ connectionPool: this, brokers, operation: 'getFirstAvailable' }), this, brokers, 0, [], callback);
49
52
  return callback[kCallbackPromise];
50
53
  }
54
+ getEstablishedConnection(broker) {
55
+ return this.#connections.get(`${broker.host}:${broker.port}`);
56
+ }
51
57
  close(callback) {
52
58
  if (!callback) {
53
59
  callback = createPromisifiedCallback();
@@ -81,6 +87,9 @@ export class ConnectionPool extends TypedEventEmitter {
81
87
  }
82
88
  return true;
83
89
  }
90
+ has(broker) {
91
+ return this.#connections.has(`${broker.host}:${broker.port}`);
92
+ }
84
93
  #get(broker, callback) {
85
94
  if (this.#closed) {
86
95
  callback(new Error('Connection pool is closed.'));
@@ -122,6 +122,7 @@ export class Connection extends TypedEventEmitter {
122
122
  connectionOptions.servername =
123
123
  typeof this.#options.tlsServerName === 'string' ? this.#options.tlsServerName : host;
124
124
  }
125
+ /* c8 ignore next 13 - Hard to test */
125
126
  const connectingSocketTimeoutHandler = () => {
126
127
  const error = new TimeoutError(`Connection to ${host}:${port} timed out.`);
127
128
  diagnosticContext.error = error;
@@ -5,7 +5,7 @@ import { type CallbackWithPromise } from "../../apis/callbacks";
5
5
  import { type Callback } from "../../apis/definitions";
6
6
  import { type Acl } from "../../apis/types";
7
7
  import { Base } from "../base/base";
8
- import { type AdminListOffsetsOptions, type AdminOptions, type AlterClientQuotasOptions, type AlterConfigsOptions, type AlterConsumerGroupOffsetsOptions, type BrokerLogDirDescription, type ConfigDescription, type CreateAclsOptions, type CreatedTopic, type CreatePartitionsOptions, type CreateTopicsOptions, type DeleteAclsOptions, type DeleteConsumerGroupOffsetsOptions, type DeleteRecordsOptions, type DeleteGroupsOptions, type DeleteTopicsOptions, type DeletedRecordsTopic, type DescribeAclsOptions, type DescribeClientQuotasOptions, type DescribeConfigsOptions, type DescribeGroupsOptions, type DescribeLogDirsOptions, type FindCoordinatorOptions, type FindCoordinatorResult, type Group, type GroupBase, type IncrementalAlterConfigsOptions, type ListConsumerGroupOffsetsGroup, type ListConsumerGroupOffsetsOptions, type ListedOffsetsTopic, type ListGroupsOptions, type ListTopicsOptions, type RemoveMembersFromConsumerGroupOptions } from "./types";
8
+ import { type AdminListOffsetsOptions, type AdminOptions, type AlterClientQuotasOptions, type AlterConfigsOptions, type AlterConsumerGroupOffsetsOptions, type BrokerLogDirDescription, type ConfigDescription, type CreateAclsOptions, type CreatedTopic, type CreatePartitionsOptions, type CreateTopicsOptions, type DeleteAclsOptions, type DeleteConsumerGroupOffsetsOptions, type DeletedRecordsTopic, type DeleteGroupsOptions, type DeleteRecordsOptions, type DeleteTopicsOptions, type DescribeAclsOptions, type DescribeClientQuotasOptions, type DescribeConfigsOptions, type DescribeGroupsOptions, type DescribeLogDirsOptions, type FindCoordinatorOptions, type FindCoordinatorResult, type Group, type GroupBase, type IncrementalAlterConfigsOptions, type ListConsumerGroupOffsetsGroup, type ListConsumerGroupOffsetsOptions, type ListedOffsetsTopic, type ListGroupsOptions, type ListTopicsOptions, type RemoveMembersFromConsumerGroupOptions } from "./types";
9
9
  export declare class Admin extends Base<AdminOptions> {
10
10
  #private;
11
11
  constructor(options: AdminOptions);
@@ -66,6 +66,8 @@ export declare class Base<OptionsType extends BaseOptions = BaseOptions, EventsT
66
66
  get closed(): boolean;
67
67
  get type(): ClientType;
68
68
  get context(): unknown;
69
+ get currentMetadata(): ClusterMetadata | undefined;
70
+ get connections(): ConnectionPool;
69
71
  emitWithDebug(section: string | null, name: string, ...args: any[]): boolean;
70
72
  close(callback: CallbackWithPromise<void>): void;
71
73
  close(): Promise<void>;
@@ -19,6 +19,7 @@ export declare class MessagesStream<Key, Value, HeaderKey, HeaderValue> extends
19
19
  get offsetsToCommit(): Map<string, CommitOptionsPartition>;
20
20
  get offsetsCommitted(): Map<string, bigint>;
21
21
  get committedOffsets(): Map<string, bigint>;
22
+ get connections(): ConnectionPool;
22
23
  close(callback: CallbackWithPromise<void>): void;
23
24
  close(): Promise<void>;
24
25
  isActive(): boolean;
@@ -97,6 +97,10 @@ export declare const consumeOptionsProperties: {
97
97
  type: string;
98
98
  minimum: number;
99
99
  };
100
+ maxBytesPerPartition: {
101
+ type: string;
102
+ minimum: number;
103
+ };
100
104
  maxWaitTime: {
101
105
  type: string;
102
106
  minimum: number;
@@ -217,6 +221,10 @@ export declare const consumeOptionsSchema: {
217
221
  type: string;
218
222
  minimum: number;
219
223
  };
224
+ maxBytesPerPartition: {
225
+ type: string;
226
+ minimum: number;
227
+ };
220
228
  maxWaitTime: {
221
229
  type: string;
222
230
  minimum: number;
@@ -379,6 +387,10 @@ export declare const consumerOptionsSchema: {
379
387
  type: string;
380
388
  minimum: number;
381
389
  };
390
+ maxBytesPerPartition: {
391
+ type: string;
392
+ minimum: number;
393
+ };
382
394
  maxWaitTime: {
383
395
  type: string;
384
396
  minimum: number;
@@ -502,6 +514,10 @@ export declare const fetchOptionsSchema: {
502
514
  type: string;
503
515
  minimum: number;
504
516
  };
517
+ maxBytesPerPartition: {
518
+ type: string;
519
+ minimum: number;
520
+ };
505
521
  maxWaitTime: {
506
522
  type: string;
507
523
  minimum: number;
@@ -65,6 +65,7 @@ export interface ConsumeBaseOptions<Key, Value, HeaderKey, HeaderValue> {
65
65
  autocommit?: boolean | number;
66
66
  minBytes?: number;
67
67
  maxBytes?: number;
68
+ maxBytesPerPartition?: number;
68
69
  maxWaitTime?: number;
69
70
  isolationLevel?: number;
70
71
  deserializers?: Partial<Deserializers<Key, Value, HeaderKey, HeaderValue>>;
@@ -1,4 +1,5 @@
1
1
  import { type CallbackWithPromise } from "../../apis/callbacks";
2
+ import { type Broker, type Connection } from "../../network/connection";
2
3
  import { type Message } from "../../protocol/records";
3
4
  import { kTransaction, kTransactionAddOffsets, kTransactionAddPartitions, kTransactionCancel, kTransactionCommitOffset, kTransactionEnd, kTransactionFindCoordinator } from "../../symbols";
4
5
  import { Base } from "../base/base";
@@ -27,6 +28,11 @@ export declare class Producer<Key = Buffer, Value = Buffer, HeaderKey = Buffer,
27
28
  asStream(options?: ProducerStreamOptions<Key, Value, HeaderKey, HeaderValue>): ProducerStream<Key, Value, HeaderKey, HeaderValue>;
28
29
  beginTransaction(options: ProduceOptions<Key, Value, HeaderKey, HeaderValue>, callback: CallbackWithPromise<Transaction<Key, Value, HeaderKey, HeaderValue>>): void;
29
30
  beginTransaction(options?: ProduceOptions<Key, Value, HeaderKey, HeaderValue>): Promise<Transaction<Key, Value, HeaderKey, HeaderValue>>;
31
+ getSendTopicPartitions(options: SendOptions<Key, Value, HeaderKey, HeaderValue>): Map<string, Set<number>>;
32
+ getSendBrokers(options: SendOptions<Key, Value, HeaderKey, HeaderValue>, callback: CallbackWithPromise<Record<string, Broker>>): void;
33
+ getSendBrokers(options: SendOptions<Key, Value, HeaderKey, HeaderValue>): Promise<Record<string, Broker>>;
34
+ getSendConnections(options: SendOptions<Key, Value, HeaderKey, HeaderValue>, callback: CallbackWithPromise<Record<string, Connection>>): void;
35
+ getSendConnections(options: SendOptions<Key, Value, HeaderKey, HeaderValue>): Promise<Record<string, Connection>>;
30
36
  [kTransactionFindCoordinator](callback: CallbackWithPromise<void>): void;
31
37
  [kTransactionAddPartitions](transactionId: number, topicsPartitions: Map<string, Set<number>>, callback: CallbackWithPromise<void>): void;
32
38
  [kTransactionAddOffsets](transactionId: number, groupId: string, callback: CallbackWithPromise<void>): void;
@@ -1,7 +1,7 @@
1
1
  import { type CompressionAlgorithmValue } from "../../protocol/compression";
2
2
  import { type MessageToProduce } from "../../protocol/records";
3
3
  import { type SchemaRegistry } from "../../registries/abstract";
4
- import { type BaseOptions, type TopicWithPartitionAndOffset } from "../base/types";
4
+ import { type BaseOptions, type ClusterMetadata, type TopicWithPartitionAndOffset } from "../base/types";
5
5
  import { type BeforeSerializationHook, type Serializers } from "../serde";
6
6
  export interface ProducerInfo {
7
7
  producerId: bigint;
@@ -12,7 +12,10 @@ export interface ProduceResult {
12
12
  offsets?: TopicWithPartitionAndOffset[];
13
13
  unwritableNodes?: number[];
14
14
  }
15
- export type Partitioner<Key, Value, HeaderKey, HeaderValue> = (message: MessageToProduce<Key, Value, HeaderKey, HeaderValue>, key?: Buffer | undefined) => number;
15
+ export interface PartitionerContext {
16
+ metadata: ClusterMetadata | undefined;
17
+ }
18
+ export type Partitioner<Key, Value, HeaderKey, HeaderValue> = (message: MessageToProduce<Key, Value, HeaderKey, HeaderValue>, key: Buffer | undefined, context: PartitionerContext) => number;
16
19
  export interface ProducerStreamReport {
17
20
  batchId: number;
18
21
  count: number;
@@ -27,12 +27,15 @@ export declare class ConnectionPool extends TypedEventEmitter<ConnectionPoolEven
27
27
  get instanceId(): number;
28
28
  get ownerId(): number | undefined;
29
29
  get context(): unknown;
30
+ [Symbol.iterator](): MapIterator<[string, Connection]>;
30
31
  get(broker: Broker, callback: CallbackWithPromise<Connection>): void;
31
32
  get(broker: Broker): Promise<Connection>;
32
33
  getFirstAvailable(brokers: Broker[], callback: CallbackWithPromise<Connection>): void;
33
34
  getFirstAvailable(brokers: Broker[]): Promise<Connection>;
35
+ getEstablishedConnection(broker: Broker): Connection | undefined;
34
36
  close(callback: CallbackWithPromise<void>): void;
35
37
  close(): Promise<void>;
36
38
  isActive(): boolean;
37
39
  isConnected(): boolean;
40
+ has(broker: Broker): boolean;
38
41
  }
package/dist/version.js CHANGED
@@ -1,2 +1,2 @@
1
1
  export const name = "@platformatic/kafka";
2
- export const version = "1.33.2";
2
+ export const version = "1.35.0";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@platformatic/kafka",
3
- "version": "1.33.2",
3
+ "version": "1.35.0",
4
4
  "description": "Modern and performant client for Apache Kafka",
5
5
  "homepage": "https://github.com/platformatic/kafka",
6
6
  "author": "Platformatic Inc. <oss@platformatic.dev> (https://platformatic.dev)",
@@ -24,6 +24,24 @@
24
24
  "type": "module",
25
25
  "exports": "./dist/index.js",
26
26
  "types": "./dist/index.d.ts",
27
+ "scripts": {
28
+ "build": "rm -rf dist && tsc -p tsconfig.base.json",
29
+ "postbuild": "./scripts/node scripts/postbuild.ts",
30
+ "lint": "eslint --cache",
31
+ "typecheck": "tsc -p . --noEmit",
32
+ "format": "prettier -w benchmarks playground src test",
33
+ "test": "c8 -c test/config/c8-local.json ./scripts/node --test --test-reporter=cleaner-spec-reporter 'test/*.test.ts' 'test/*/*.test.ts' 'test/*/*/*.test.ts'",
34
+ "test:ci": "c8 -c test/config/c8-ci.json ./scripts/node --test --test-reporter=cleaner-spec-reporter 'test/*.test.ts' 'test/*/*.test.ts' 'test/*/*/*.test.ts'",
35
+ "test:memory": "NODE_OPTIONS='--expose-gc' ./scripts/node --test --test-reporter=cleaner-spec-reporter 'test/**/*.memory-test.ts'",
36
+ "test:docker:up": "docker-compose up -d --wait",
37
+ "test:docker:down": "docker-compose down",
38
+ "ci": "npm run build && npm run lint && npm run test:ci",
39
+ "prepublishOnly": "npm run build && npm run lint",
40
+ "postpublish": "git push origin && git push origin -f --tags",
41
+ "generate:apis": "node scripts/generate-apis.ts",
42
+ "generate:errors": "node scripts/generate-errors.ts",
43
+ "create:api": "node scripts/create-api.ts"
44
+ },
27
45
  "dependencies": {
28
46
  "@platformatic/dynamic-buffer": "^0.3.1",
29
47
  "@platformatic/wasm-utils": "^0.2.1",
@@ -84,21 +102,5 @@
84
102
  "dist/typescript-4/*"
85
103
  ]
86
104
  }
87
- },
88
- "scripts": {
89
- "build": "rm -rf dist && tsc -p tsconfig.base.json",
90
- "postbuild": "./scripts/node scripts/postbuild.ts",
91
- "lint": "eslint --cache",
92
- "typecheck": "tsc -p . --noEmit",
93
- "format": "prettier -w benchmarks playground src test",
94
- "test": "c8 -c test/config/c8-local.json ./scripts/node --test --test-reporter=cleaner-spec-reporter 'test/*.test.ts' 'test/*/*.test.ts' 'test/*/*/*.test.ts'",
95
- "test:ci": "c8 -c test/config/c8-ci.json ./scripts/node --test --test-reporter=cleaner-spec-reporter 'test/*.test.ts' 'test/*/*.test.ts' 'test/*/*/*.test.ts'",
96
- "test:memory": "NODE_OPTIONS='--expose-gc' ./scripts/node --test --test-reporter=cleaner-spec-reporter 'test/**/*.memory-test.ts'",
97
- "test:docker:up": "docker-compose up -d --wait",
98
- "test:docker:down": "docker-compose down",
99
- "ci": "npm run build && npm run lint && npm run test:ci",
100
- "generate:apis": "node scripts/generate-apis.ts",
101
- "generate:errors": "node scripts/generate-errors.ts",
102
- "create:api": "node scripts/create-api.ts"
103
105
  }
104
106
  }