@platformatic/kafka 1.29.0 → 1.31.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 (70) hide show
  1. package/README.md +1 -1
  2. package/dist/apis/admin/alter-partition-reassignments-v0.d.ts +2 -2
  3. package/dist/apis/admin/alter-partition-reassignments-v0.js +1 -1
  4. package/dist/apis/admin/alter-partition-reassignments-v1.d.ts +30 -0
  5. package/dist/apis/admin/alter-partition-reassignments-v1.js +71 -0
  6. package/dist/apis/admin/index.d.ts +2 -0
  7. package/dist/apis/admin/index.js +2 -0
  8. package/dist/apis/admin/list-transactions-v0.d.ts +3 -2
  9. package/dist/apis/admin/list-transactions-v0.js +1 -1
  10. package/dist/apis/admin/list-transactions-v1.d.ts +3 -2
  11. package/dist/apis/admin/list-transactions-v1.js +1 -1
  12. package/dist/apis/admin/list-transactions-v2.d.ts +20 -0
  13. package/dist/apis/admin/list-transactions-v2.js +49 -0
  14. package/dist/apis/callbacks.js +1 -1
  15. package/dist/apis/consumer/consumer-group-heartbeat-v0.d.ts +2 -2
  16. package/dist/apis/consumer/consumer-group-heartbeat-v0.js +1 -1
  17. package/dist/apis/consumer/consumer-group-heartbeat-v1.d.ts +27 -0
  18. package/dist/apis/consumer/consumer-group-heartbeat-v1.js +70 -0
  19. package/dist/apis/consumer/index.d.ts +1 -0
  20. package/dist/apis/consumer/index.js +1 -0
  21. package/dist/clients/admin/admin.d.ts +3 -1
  22. package/dist/clients/admin/admin.js +63 -35
  23. package/dist/clients/admin/options.d.ts +22 -0
  24. package/dist/clients/admin/options.js +15 -1
  25. package/dist/clients/admin/types.d.ts +11 -1
  26. package/dist/clients/base/base.d.ts +4 -1
  27. package/dist/clients/base/base.js +17 -2
  28. package/dist/clients/base/index.d.ts +1 -1
  29. package/dist/clients/base/index.js +1 -1
  30. package/dist/clients/base/options.d.ts +1 -0
  31. package/dist/clients/base/options.js +1 -0
  32. package/dist/clients/base/types.d.ts +1 -0
  33. package/dist/clients/consumer/consumer.d.ts +4 -1
  34. package/dist/clients/consumer/consumer.js +46 -5
  35. package/dist/clients/consumer/messages-stream.d.ts +1 -0
  36. package/dist/clients/consumer/messages-stream.js +65 -15
  37. package/dist/clients/consumer/options.d.ts +3 -0
  38. package/dist/clients/consumer/options.js +3 -0
  39. package/dist/clients/consumer/types.d.ts +3 -0
  40. package/dist/clients/producer/producer.js +13 -3
  41. package/dist/errors.js +9 -1
  42. package/dist/network/connection-pool.d.ts +2 -0
  43. package/dist/network/connection-pool.js +11 -2
  44. package/dist/network/connection.d.ts +3 -0
  45. package/dist/network/connection.js +34 -4
  46. package/dist/registries/abstract.js +4 -0
  47. package/dist/typescript-4/dist/apis/admin/alter-partition-reassignments-v0.d.ts +2 -2
  48. package/dist/typescript-4/dist/apis/admin/alter-partition-reassignments-v1.d.ts +30 -0
  49. package/dist/typescript-4/dist/apis/admin/index.d.ts +2 -0
  50. package/dist/typescript-4/dist/apis/admin/list-transactions-v0.d.ts +3 -2
  51. package/dist/typescript-4/dist/apis/admin/list-transactions-v1.d.ts +3 -2
  52. package/dist/typescript-4/dist/apis/admin/list-transactions-v2.d.ts +20 -0
  53. package/dist/typescript-4/dist/apis/consumer/consumer-group-heartbeat-v0.d.ts +2 -2
  54. package/dist/typescript-4/dist/apis/consumer/consumer-group-heartbeat-v1.d.ts +27 -0
  55. package/dist/typescript-4/dist/apis/consumer/index.d.ts +1 -0
  56. package/dist/typescript-4/dist/clients/admin/admin.d.ts +3 -1
  57. package/dist/typescript-4/dist/clients/admin/options.d.ts +22 -0
  58. package/dist/typescript-4/dist/clients/admin/types.d.ts +11 -1
  59. package/dist/typescript-4/dist/clients/base/base.d.ts +4 -1
  60. package/dist/typescript-4/dist/clients/base/index.d.ts +1 -1
  61. package/dist/typescript-4/dist/clients/base/options.d.ts +1 -0
  62. package/dist/typescript-4/dist/clients/base/types.d.ts +1 -0
  63. package/dist/typescript-4/dist/clients/consumer/consumer.d.ts +4 -1
  64. package/dist/typescript-4/dist/clients/consumer/messages-stream.d.ts +1 -0
  65. package/dist/typescript-4/dist/clients/consumer/options.d.ts +3 -0
  66. package/dist/typescript-4/dist/clients/consumer/types.d.ts +3 -0
  67. package/dist/typescript-4/dist/network/connection-pool.d.ts +2 -0
  68. package/dist/typescript-4/dist/network/connection.d.ts +3 -0
  69. package/dist/version.js +1 -1
  70. package/package.json +2 -1
@@ -60,6 +60,7 @@ export class MessagesStream extends Readable {
60
60
  #closeCallbacks;
61
61
  #metricsConsumedMessages;
62
62
  #corruptedMessageHandler;
63
+ #context;
63
64
  #pushRecordsOperation;
64
65
  [kInstance];
65
66
  /*
@@ -79,7 +80,7 @@ export class MessagesStream extends Readable {
79
80
  */
80
81
  [kConnections];
81
82
  constructor(consumer, options) {
82
- const { autocommit, mode, fallbackMode, maxFetches, offsets, deserializers, onCorruptedMessage, registry, beforeDeserialization,
83
+ const { autocommit, mode, fallbackMode, maxFetches, offsets, context, deserializers, onCorruptedMessage, registry, beforeDeserialization,
83
84
  // The options below are only destructured to avoid being part of structuredClone below
84
85
  partitionAssigner: _partitionAssigner, ...otherOptions } = options;
85
86
  if (offsets && mode !== MessagesStreamModes.MANUAL) {
@@ -94,7 +95,7 @@ export class MessagesStream extends Readable {
94
95
  highWaterMark: maxFetches ?? options.highWaterMark ?? defaultConsumerOptions.highWaterMark
95
96
  });
96
97
  this[kInstance] = currentInstance++;
97
- this[kConnections] = consumer[kCreateConnectionPool]();
98
+ this[kConnections] = consumer[kCreateConnectionPool](context);
98
99
  this.#consumer = consumer;
99
100
  this.#mode = mode ?? MessagesStreamModes.LATEST;
100
101
  this.#fallbackMode = fallbackMode ?? MessagesStreamFallbackModes.LATEST;
@@ -108,7 +109,7 @@ export class MessagesStream extends Readable {
108
109
  this.#fetches = 0;
109
110
  this.#maxFetches = maxFetches ?? 0;
110
111
  this.#topics = structuredClone(options.topics);
111
- this.#inflightNodes = new Set();
112
+ this.#inflightNodes = new Map();
112
113
  this.#keyDeserializer =
113
114
  deserializers?.key ?? noopDeserializer;
114
115
  this.#valueDeserializer =
@@ -120,6 +121,7 @@ export class MessagesStream extends Readable {
120
121
  this.#closed = false;
121
122
  this.#closeCallbacks = [];
122
123
  this.#corruptedMessageHandler = onCorruptedMessage ?? defaultCorruptedMessageHandler;
124
+ this.#context = context;
123
125
  if (registry) {
124
126
  this.#pushRecordsOperation = this.#beforeDeserialization.bind(this, registry.getBeforeDeserializationHook());
125
127
  }
@@ -150,6 +152,7 @@ export class MessagesStream extends Readable {
150
152
  // having some.
151
153
  this.#consumer.on('consumer:group:join', () => {
152
154
  this.#offsetsCommitted.clear();
155
+ this.#partitionsEpochs.clear();
153
156
  this.#scheduleRefreshOffsetsAndFetch();
154
157
  });
155
158
  if (consumer[kPrometheus]) {
@@ -169,6 +172,9 @@ export class MessagesStream extends Readable {
169
172
  get offsetsToFetch() {
170
173
  return this.#offsetsToFetch;
171
174
  }
175
+ get context() {
176
+ return this.#context;
177
+ }
172
178
  /* c8 ignore next 3 - Simple getter */
173
179
  get offsetsToCommit() {
174
180
  return this.#offsetsToCommit;
@@ -239,8 +245,28 @@ export class MessagesStream extends Readable {
239
245
  return this.#consumer.isConnected();
240
246
  }
241
247
  resume() {
248
+ const wasPaused = this.#paused;
242
249
  this.#paused = false;
243
- return super.resume();
250
+ const result = super.resume();
251
+ // Restart the fetch loop when transitioning from paused → unpaused.
252
+ //
253
+ // When a downstream Duplex (e.g. a batching stream) signals backpressure,
254
+ // pipeline()/pipe() calls pause() on this stream, setting #paused = true.
255
+ // If a previously scheduled process.nextTick(#fetch) fires while #paused is
256
+ // true, #fetch() returns early without scheduling another iteration — the
257
+ // loop is now dead. Later, pipe() calls resume() when the downstream drains,
258
+ // but super.resume() does not reliably trigger _read() when the readable
259
+ // buffer is already empty and the stream is in flowing mode (Node.js
260
+ // considers it "already flowing" and skips the _read() → #fetch() path).
261
+ //
262
+ // The wasPaused guard prevents a premature fetch during initial pipeline()
263
+ // setup, where resume() is called before _construct() completes.
264
+ if (wasPaused) {
265
+ process.nextTick(() => {
266
+ this.#fetch();
267
+ });
268
+ }
269
+ return result;
244
270
  }
245
271
  // We want to track if the stream is paused explicitly by the user, while isPaused from Node.js can also
246
272
  // be true if the stream is paused because there is no consumer.
@@ -278,7 +304,9 @@ export class MessagesStream extends Readable {
278
304
  if (this.#autocommitInterval) {
279
305
  clearInterval(this.#autocommitInterval);
280
306
  }
281
- callback(error);
307
+ this[kConnections].close(closeError => {
308
+ callback(closeError ?? error);
309
+ });
282
310
  }
283
311
  _read() {
284
312
  this.#fetch();
@@ -311,6 +339,14 @@ export class MessagesStream extends Readable {
311
339
  this.push(null);
312
340
  return;
313
341
  }
342
+ // Remove stale inflight entries that have been pending for too long.
343
+ // This prevents permanent partition starvation if a fetch callback is never invoked.
344
+ const now = Date.now();
345
+ for (const [node, timestamp] of this.#inflightNodes) {
346
+ if (now - timestamp > 120_000) {
347
+ this.#inflightNodes.delete(node);
348
+ }
349
+ }
314
350
  const requests = new Map();
315
351
  const topicIds = new Map();
316
352
  // Group topic-partitions by the destination broker
@@ -351,9 +387,12 @@ export class MessagesStream extends Readable {
351
387
  });
352
388
  }
353
389
  }
390
+ if (requests.size === 0) {
391
+ return;
392
+ }
354
393
  for (const [leader, leaderRequests] of requests) {
355
- this.#inflightNodes.add(leader);
356
- this.#consumer.fetch({ ...this.#options, node: leader, topics: leaderRequests }, (error, response) => {
394
+ this.#inflightNodes.set(leader, Date.now());
395
+ this.#consumer.fetch({ ...this.#options, node: leader, topics: leaderRequests, connectionPool: this[kConnections] }, (error, response) => {
357
396
  this.#inflightNodes.delete(leader);
358
397
  this.emit('fetch');
359
398
  if (error) {
@@ -381,7 +420,6 @@ export class MessagesStream extends Readable {
381
420
  }
382
421
  #pushRecords(metadata, topicIds, response, requestedOffsets) {
383
422
  const autocommit = this.#autocommitEnabled;
384
- let canPush = true;
385
423
  const keyDeserializer = this.#keyDeserializer;
386
424
  const valueDeserializer = this.#valueDeserializer;
387
425
  const headerKeyDeserializer = this.#headerKeyDeserializer;
@@ -406,7 +444,7 @@ export class MessagesStream extends Readable {
406
444
  const firstTimestamp = batch.firstTimestamp;
407
445
  const firstOffset = batch.firstOffset;
408
446
  const leaderEpoch = metadata.topics.get(topic).partitions[partition].leaderEpoch;
409
- this.#partitionsEpochs.set(`${topic}:${partition}`, batch.partitionLeaderEpoch);
447
+ this.#partitionsEpochs.set(`${topic}:${partition}`, leaderEpoch);
410
448
  // Track offsets
411
449
  if (batch === recordsBatches[recordsBatches.length - 1]) {
412
450
  // Track the last read offset
@@ -469,7 +507,7 @@ export class MessagesStream extends Readable {
469
507
  };
470
508
  diagnosticContext.result = message;
471
509
  consumerReceivesChannel.asyncStart.publish(diagnosticContext);
472
- canPush = this.push(message);
510
+ this.push(message);
473
511
  consumerReceivesChannel.asyncEnd.publish(diagnosticContext);
474
512
  }
475
513
  catch (error) {
@@ -491,11 +529,19 @@ export class MessagesStream extends Readable {
491
529
  if (this.#autocommitEnabled && !this.#autocommitInterval) {
492
530
  this[kAutocommit]();
493
531
  }
494
- if (canPush) {
495
- process.nextTick(() => {
496
- this.#fetch();
497
- });
498
- }
532
+ // Always schedule the next fetch, even when push() returned false.
533
+ //
534
+ // In pull mode, _read() would restart the loop once the buffer drains.
535
+ // In flowing mode with pipeline(), however, _read() is not reliably called
536
+ // again when the buffer is already empty — Node.js considers the stream
537
+ // "already flowing" and does not re-invoke _read(). The fetch loop dies
538
+ // and unconsumed messages remain in Kafka.
539
+ //
540
+ // Unconditionally scheduling is safe because #fetch() checks #paused,
541
+ // #closed, and other guards before issuing a Kafka fetch request.
542
+ process.nextTick(() => {
543
+ this.#fetch();
544
+ });
499
545
  if (this.#maxFetches > 0 && ++this.#fetches >= this.#maxFetches) {
500
546
  this.push(null);
501
547
  }
@@ -708,6 +754,10 @@ export class MessagesStream extends Readable {
708
754
  }
709
755
  }
710
756
  }
757
+ if (requests.length === 0) {
758
+ this.#pushRecords(metadata, topicIds, response, requestedOffsets);
759
+ return;
760
+ }
711
761
  runAsyncSeries((request, cb) => {
712
762
  const [data, type, message] = request;
713
763
  const result = hook(data, type, message, cb);
@@ -317,6 +317,7 @@ export declare const consumeOptionsSchema: {
317
317
  pattern: string;
318
318
  };
319
319
  };
320
+ context: boolean;
320
321
  mode: {
321
322
  type: string;
322
323
  enum: import("./types.ts").MessagesStreamModeValue[];
@@ -475,6 +476,8 @@ export declare const consumerOptionsSchema: {
475
476
  type: string;
476
477
  pattern: string;
477
478
  };
479
+ context: boolean;
480
+ streamContext: boolean;
478
481
  };
479
482
  required: string[];
480
483
  additionalProperties: boolean;
@@ -74,6 +74,7 @@ export const consumeOptionsSchema = {
74
74
  type: 'object',
75
75
  properties: {
76
76
  topics: { type: 'array', items: idProperty },
77
+ context: true,
77
78
  mode: { type: 'string', enum: allowedMessagesStreamModes },
78
79
  fallbackMode: { type: 'string', enum: allowedMessagesStreamFallbackModes },
79
80
  maxFetches: { type: 'number', minimum: 0, default: 0 },
@@ -97,6 +98,8 @@ export const consumerOptionsSchema = {
97
98
  type: 'object',
98
99
  properties: {
99
100
  groupId: idProperty,
101
+ context: true,
102
+ streamContext: true,
100
103
  ...groupOptionsProperties,
101
104
  ...consumeOptionsProperties
102
105
  },
@@ -74,6 +74,7 @@ export interface ConsumeBaseOptions<Key, Value, HeaderKey, HeaderValue> {
74
74
  }
75
75
  export interface StreamOptions {
76
76
  topics: string[];
77
+ context?: unknown;
77
78
  mode?: MessagesStreamModeValue;
78
79
  fallbackMode?: MessagesStreamFallbackModeValue;
79
80
  maxFetches?: number;
@@ -83,6 +84,8 @@ export interface StreamOptions {
83
84
  export type ConsumeOptions<Key, Value, HeaderKey, HeaderValue> = StreamOptions & ConsumeBaseOptions<Key, Value, HeaderKey, HeaderValue> & GroupOptions;
84
85
  export type ConsumerOptions<Key, Value, HeaderKey, HeaderValue> = BaseOptions & {
85
86
  groupId: string;
87
+ context?: unknown;
88
+ streamContext?: unknown;
86
89
  } & (GroupOptions | ConsumerGroupOptions) & ConsumeBaseOptions<Key, Value, HeaderKey, HeaderValue>;
87
90
  export type FetchOptions<Key, Value, HeaderKey, HeaderValue> = Pick<ConsumeBaseOptions<Key, Value, HeaderKey, HeaderValue>, 'minBytes' | 'maxBytes' | 'maxWaitTime' | 'isolationLevel'> & {
88
91
  node: number;
@@ -2,7 +2,7 @@ import { randomUUID } from 'crypto';
2
2
  import { createPromisifiedCallback, kCallbackPromise, runConcurrentCallbacks } from "../../apis/callbacks.js";
3
3
  import { FindCoordinatorKeyTypes, ProduceAcks } from "../../apis/enumerations.js";
4
4
  import { createDiagnosticContext, producerInitIdempotentChannel, producerSendsChannel, producerTransactionsChannel } from "../../diagnostic.js";
5
- import { UserError } from "../../errors.js";
5
+ import { GenericError, UserError } from "../../errors.js";
6
6
  import { murmur2 } from "../../protocol/murmur2.js";
7
7
  import { runAsyncSeries } from "../../registries/abstract.js";
8
8
  import { kInstance, kTransaction, kTransactionAddOffsets, kTransactionAddPartitions, kTransactionCancel, kTransactionCommitOffset, kTransactionEnd, kTransactionFindCoordinator, kTransactionPrepare } from "../../symbols.js";
@@ -671,7 +671,10 @@ export class Producer extends Base {
671
671
  if (error) {
672
672
  // If the last error was due to stale metadata, we retry the operation with this set of messages
673
673
  // since the partition is already set, it should attempt on the new destination
674
- const hasStaleMetadata = error.findBy('hasStaleMetadata', true);
674
+ const kafkaError = GenericError.isGenericError(error)
675
+ ? error
676
+ : null;
677
+ const hasStaleMetadata = kafkaError?.findBy('hasStaleMetadata', true);
675
678
  if (hasStaleMetadata && repeatOnStaleMetadata) {
676
679
  this.clearMetadata();
677
680
  this.#performSingleDestinationSend(topics, messages, timeout, acks, autocreateTopics, false, produceOptions, callback);
@@ -682,7 +685,10 @@ export class Producer extends Base {
682
685
  }
683
686
  callback(error, results);
684
687
  }, 0, [], error => {
685
- return repeatOnStaleMetadata && !!error.findBy('hasStaleMetadata', true);
688
+ if (!repeatOnStaleMetadata || !GenericError.isGenericError(error)) {
689
+ return false;
690
+ }
691
+ return !!error.findBy('hasStaleMetadata', true);
686
692
  });
687
693
  });
688
694
  }
@@ -726,6 +732,10 @@ export class Producer extends Base {
726
732
  }
727
733
  }
728
734
  }
735
+ if (requests.length === 0) {
736
+ this.#send(options, callback);
737
+ return;
738
+ }
729
739
  runAsyncSeries((request, cb) => {
730
740
  const [data, type, message] = request;
731
741
  const result = hook(data, type, message, cb);
package/dist/errors.js CHANGED
@@ -78,6 +78,9 @@ export class MultipleErrors extends AggregateError {
78
78
  return this;
79
79
  }
80
80
  for (const error of this.errors) {
81
+ if (!error) {
82
+ continue;
83
+ }
81
84
  if (error[property] === value) {
82
85
  return error;
83
86
  }
@@ -111,7 +114,12 @@ export class ProtocolError extends GenericError {
111
114
  apiCode: code,
112
115
  serverErrorMessage,
113
116
  canRetry,
114
- hasStaleMetadata: ['UNKNOWN_TOPIC_OR_PARTITION', 'LEADER_NOT_AVAILABLE', 'NOT_LEADER_OR_FOLLOWER'].includes(id),
117
+ hasStaleMetadata: [
118
+ 'UNKNOWN_TOPIC_OR_PARTITION',
119
+ 'LEADER_NOT_AVAILABLE',
120
+ 'NOT_LEADER_OR_FOLLOWER',
121
+ 'FENCED_LEADER_EPOCH'
122
+ ].includes(id),
115
123
  needsRejoin: ['MEMBER_ID_REQUIRED', 'UNKNOWN_MEMBER_ID', 'REBALANCE_IN_PROGRESS'].includes(id),
116
124
  producerFenced: id === 'INVALID_PRODUCER_EPOCH',
117
125
  rebalanceInProgress: id === 'REBALANCE_IN_PROGRESS',
@@ -25,6 +25,8 @@ export declare class ConnectionPool extends TypedEventEmitter<ConnectionPoolEven
25
25
  #private;
26
26
  constructor(clientId: string, connectionOptions?: ConnectionOptions);
27
27
  get instanceId(): number;
28
+ get ownerId(): number | undefined;
29
+ get context(): unknown;
28
30
  get(broker: Broker, callback: CallbackWithPromise<Connection>): void;
29
31
  get(broker: Broker): Promise<Connection>;
30
32
  getFirstAvailable(brokers: Broker[], callback: CallbackWithPromise<Connection>): void;
@@ -1,14 +1,14 @@
1
1
  import { createPromisifiedCallback, kCallbackPromise, runConcurrentCallbacks } from "../apis/callbacks.js";
2
2
  import { connectionsPoolGetsChannel, createDiagnosticContext, notifyCreation } from "../diagnostic.js";
3
- import { TypedEventEmitter } from "../events.js";
4
3
  import { MultipleErrors } from "../errors.js";
4
+ import { TypedEventEmitter } from "../events.js";
5
5
  import { Connection, ConnectionStatuses } from "./connection.js";
6
6
  let currentInstance = 0;
7
7
  export class ConnectionPool extends TypedEventEmitter {
8
8
  #instanceId;
9
9
  #clientId;
10
10
  #closed;
11
- // @ts-ignore This is used just for debugging
11
+ #context;
12
12
  #ownerId;
13
13
  #connections;
14
14
  #connectionOptions;
@@ -17,6 +17,7 @@ export class ConnectionPool extends TypedEventEmitter {
17
17
  this.#closed = false;
18
18
  this.#instanceId = currentInstance++;
19
19
  this.#clientId = clientId;
20
+ this.#context = connectionOptions.context;
20
21
  this.#ownerId = connectionOptions.ownerId;
21
22
  this.#connections = new Map();
22
23
  this.#connectionOptions = connectionOptions;
@@ -25,6 +26,14 @@ export class ConnectionPool extends TypedEventEmitter {
25
26
  get instanceId() {
26
27
  return this.#instanceId;
27
28
  }
29
+ /* c8 ignore next 3 - Simple getter */
30
+ get ownerId() {
31
+ return this.#ownerId;
32
+ }
33
+ /* c8 ignore next 3 - Simple getter */
34
+ get context() {
35
+ return this.#context;
36
+ }
28
37
  get(broker, callback) {
29
38
  if (!callback) {
30
39
  callback = createPromisifiedCallback();
@@ -46,6 +46,7 @@ export interface ConnectionOptions {
46
46
  sasl?: SASLOptions;
47
47
  ownerId?: number;
48
48
  handleBackPressure?: boolean;
49
+ context?: unknown;
49
50
  }
50
51
  export interface Request {
51
52
  correlationId: number;
@@ -83,7 +84,9 @@ export declare class Connection extends TypedEventEmitter<ConnectionEvents> {
83
84
  get host(): string | undefined;
84
85
  get port(): number | undefined;
85
86
  get instanceId(): number;
87
+ get ownerId(): number | undefined;
86
88
  get status(): ConnectionStatusValue;
89
+ get context(): unknown;
87
90
  get socket(): Socket;
88
91
  isConnected(): boolean;
89
92
  connect(host: string, port: number, callback?: CallbackWithPromise<void>): void | Promise<void>;
@@ -38,7 +38,7 @@ export class Connection extends TypedEventEmitter {
38
38
  #status;
39
39
  #instanceId;
40
40
  #clientId;
41
- // @ts-ignore This is used just for debugging
41
+ #context;
42
42
  #ownerId;
43
43
  #handleBackPressure;
44
44
  #correlationId;
@@ -59,6 +59,7 @@ export class Connection extends TypedEventEmitter {
59
59
  this.#options.tls ??= this.#options.ssl;
60
60
  this.#status = ConnectionStatuses.NONE;
61
61
  this.#clientId = clientId;
62
+ this.#context = options.context;
62
63
  this.#ownerId = options.ownerId;
63
64
  this.#handleBackPressure = options.handleBackPressure ?? false;
64
65
  this.#correlationId = 0;
@@ -82,9 +83,16 @@ export class Connection extends TypedEventEmitter {
82
83
  get instanceId() {
83
84
  return this.#instanceId;
84
85
  }
86
+ /* c8 ignore next 3 - Simple getter */
87
+ get ownerId() {
88
+ return this.#ownerId;
89
+ }
85
90
  get status() {
86
91
  return this.#status;
87
92
  }
93
+ get context() {
94
+ return this.#context;
95
+ }
88
96
  get socket() {
89
97
  return this.#socket;
90
98
  }
@@ -178,16 +186,38 @@ export class Connection extends TypedEventEmitter {
178
186
  if (!callback) {
179
187
  callback = createPromisifiedCallback();
180
188
  }
181
- const onConnect = () => {
189
+ const cleanup = () => {
190
+ clearTimeout(timeout);
191
+ this.removeListener('connect', onConnect);
182
192
  this.removeListener('error', onError);
193
+ this.removeListener('close', onClose);
194
+ };
195
+ const onConnect = () => {
196
+ cleanup();
183
197
  callback(null);
184
198
  };
185
199
  const onError = (error) => {
186
- this.removeListener('connect', onConnect);
200
+ cleanup();
187
201
  callback(error);
188
202
  };
203
+ const onClose = () => {
204
+ cleanup();
205
+ callback(new NetworkError('Connection closed while waiting for ready.'));
206
+ };
207
+ const timeout = setTimeout(() => {
208
+ cleanup();
209
+ // If the connection already failed (e.g. invalid port), don't double-invoke the callback
210
+ if (this.#status === ConnectionStatuses.ERROR || this.#status === ConnectionStatuses.CLOSED) {
211
+ return;
212
+ }
213
+ this.#socket?.destroy();
214
+ callback(new TimeoutError(this.#host
215
+ ? `Connection to ${this.#host}:${this.#port} timed out.`
216
+ : `Connection ready timed out after ${this.#options.connectTimeout}ms.`));
217
+ }, this.#options.connectTimeout);
189
218
  this.once('connect', onConnect);
190
219
  this.once('error', onError);
220
+ this.once('close', onClose);
191
221
  this.emit('ready');
192
222
  return callback[kCallbackPromise];
193
223
  }
@@ -218,7 +248,7 @@ export class Connection extends TypedEventEmitter {
218
248
  });
219
249
  this.#status = ConnectionStatuses.CLOSING;
220
250
  this.emit('closing');
221
- this.#socket.end();
251
+ this.#socket.destroySoon();
222
252
  return callback[kCallbackPromise];
223
253
  }
224
254
  send(apiKey, apiVersion, createPayload, responseParser, hasRequestHeaderTaggedFields, hasResponseHeaderTaggedFields, callback) {
@@ -1,4 +1,8 @@
1
1
  export function runAsyncSeries(operation, collection, index, callback) {
2
+ if (collection.length === 0 || index >= collection.length) {
3
+ callback(null);
4
+ return;
5
+ }
2
6
  operation(collection[index], error => {
3
7
  if (error) {
4
8
  callback(error);
@@ -25,6 +25,6 @@ export interface AlterPartitionReassignmentsResponse {
25
25
  errorMessage: NullableString;
26
26
  responses: AlterPartitionReassignmentsResponseResponse[];
27
27
  }
28
- export declare function createRequest(timeoutMs: number, topics: AlterPartitionReassignmentsRequestTopic[]): Writer;
28
+ export declare function createRequest(timeoutMs: number, _allowReplicationFactorChange: boolean, topics: AlterPartitionReassignmentsRequestTopic[]): Writer;
29
29
  export declare function parseResponse(_correlationId: number, apiKey: number, apiVersion: number, reader: Reader): AlterPartitionReassignmentsResponse;
30
- export declare const api: import("../definitions").API<[timeoutMs: number, topics: AlterPartitionReassignmentsRequestTopic[]], AlterPartitionReassignmentsResponse>;
30
+ export declare const api: import("../definitions").API<[timeoutMs: number, _allowReplicationFactorChange: boolean, topics: AlterPartitionReassignmentsRequestTopic[]], AlterPartitionReassignmentsResponse>;
@@ -0,0 +1,30 @@
1
+ import { type NullableString } from "../../protocol/definitions";
2
+ import { type Reader } from "../../protocol/reader";
3
+ import { Writer } from "../../protocol/writer";
4
+ export interface AlterPartitionReassignmentsRequestPartition {
5
+ partitionIndex: number;
6
+ replicas: number[];
7
+ }
8
+ export interface AlterPartitionReassignmentsRequestTopic {
9
+ name: string;
10
+ partitions: AlterPartitionReassignmentsRequestPartition[];
11
+ }
12
+ export type AlterPartitionReassignmentsRequest = Parameters<typeof createRequest>;
13
+ export interface AlterPartitionReassignmentsResponsePartition {
14
+ partitionIndex: number;
15
+ errorCode: number;
16
+ errorMessage: NullableString;
17
+ }
18
+ export interface AlterPartitionReassignmentsResponseResponse {
19
+ name: string;
20
+ partitions: AlterPartitionReassignmentsResponsePartition[];
21
+ }
22
+ export interface AlterPartitionReassignmentsResponse {
23
+ throttleTimeMs: number;
24
+ errorCode: number;
25
+ errorMessage: NullableString;
26
+ responses: AlterPartitionReassignmentsResponseResponse[];
27
+ }
28
+ export declare function createRequest(timeoutMs: number, allowReplicationFactorChange: boolean, topics: AlterPartitionReassignmentsRequestTopic[]): Writer;
29
+ export declare function parseResponse(_correlationId: number, apiKey: number, apiVersion: number, reader: Reader): AlterPartitionReassignmentsResponse;
30
+ export declare const api: import("../definitions").API<[timeoutMs: number, allowReplicationFactorChange: boolean, topics: AlterPartitionReassignmentsRequestTopic[]], AlterPartitionReassignmentsResponse>;
@@ -1,6 +1,7 @@
1
1
  export * as alterClientQuotasV1 from "./alter-client-quotas-v1";
2
2
  export * as alterConfigsV2 from "./alter-configs-v2";
3
3
  export * as alterPartitionReassignmentsV0 from "./alter-partition-reassignments-v0";
4
+ export * as alterPartitionReassignmentsV1 from "./alter-partition-reassignments-v1";
4
5
  export * as alterPartitionV3 from "./alter-partition-v3";
5
6
  export * as alterReplicaLogDirsV2 from "./alter-replica-log-dirs-v2";
6
7
  export * as alterUserScramCredentialsV0 from "./alter-user-scram-credentials-v0";
@@ -43,6 +44,7 @@ export * as listGroupsV5 from "./list-groups-v5";
43
44
  export * as listPartitionReassignmentsV0 from "./list-partition-reassignments-v0";
44
45
  export * as listTransactionsV0 from "./list-transactions-v0";
45
46
  export * as listTransactionsV1 from "./list-transactions-v1";
47
+ export * as listTransactionsV2 from "./list-transactions-v2";
46
48
  export * as offsetDeleteV0 from "./offset-delete-v0";
47
49
  export * as renewDelegationTokenV2 from "./renew-delegation-token-v2";
48
50
  export * as unregisterBrokerV0 from "./unregister-broker-v0";
@@ -1,3 +1,4 @@
1
+ import { type NullableString } from "../../protocol/definitions";
1
2
  import { type Reader } from "../../protocol/reader";
2
3
  import { Writer } from "../../protocol/writer";
3
4
  import { type TransactionState } from "../enumerations";
@@ -13,6 +14,6 @@ export interface ListTransactionsResponse {
13
14
  unknownStateFilters: string[];
14
15
  transactionStates: ListTransactionsResponseTransactionState[];
15
16
  }
16
- export declare function createRequest(stateFilters: TransactionState[], producerIdFilters: bigint[], _durationFilter: bigint): Writer;
17
+ export declare function createRequest(stateFilters: TransactionState[], producerIdFilters: bigint[], _durationFilter: bigint, _transactionalIdPattern: NullableString): Writer;
17
18
  export declare function parseResponse(_correlationId: number, apiKey: number, apiVersion: number, reader: Reader): ListTransactionsResponse;
18
- export declare const api: import("../definitions").API<[stateFilters: ("EMPTY" | "ONGOING" | "PREPARE_ABORT" | "COMMITTING" | "ABORTING" | "COMPLETE_COMMIT" | "COMPLETE_ABORT")[], producerIdFilters: bigint[], _durationFilter: bigint], ListTransactionsResponse>;
19
+ export declare const api: import("../definitions").API<[stateFilters: ("EMPTY" | "ONGOING" | "PREPARE_ABORT" | "COMMITTING" | "ABORTING" | "COMPLETE_COMMIT" | "COMPLETE_ABORT")[], producerIdFilters: bigint[], _durationFilter: bigint, _transactionalIdPattern: NullableString], ListTransactionsResponse>;
@@ -1,3 +1,4 @@
1
+ import { type NullableString } from "../../protocol/definitions";
1
2
  import { type Reader } from "../../protocol/reader";
2
3
  import { Writer } from "../../protocol/writer";
3
4
  import { type TransactionState } from "../enumerations";
@@ -13,6 +14,6 @@ export interface ListTransactionsResponse {
13
14
  unknownStateFilters: string[];
14
15
  transactionStates: ListTransactionsResponseTransactionState[];
15
16
  }
16
- export declare function createRequest(stateFilters: TransactionState[], producerIdFilters: bigint[], durationFilter: bigint): Writer;
17
+ export declare function createRequest(stateFilters: TransactionState[], producerIdFilters: bigint[], durationFilter: bigint, _transactionalIdPattern: NullableString): Writer;
17
18
  export declare function parseResponse(_correlationId: number, apiKey: number, apiVersion: number, reader: Reader): ListTransactionsResponse;
18
- export declare const api: import("../definitions").API<[stateFilters: ("EMPTY" | "ONGOING" | "PREPARE_ABORT" | "COMMITTING" | "ABORTING" | "COMPLETE_COMMIT" | "COMPLETE_ABORT")[], producerIdFilters: bigint[], durationFilter: bigint], ListTransactionsResponse>;
19
+ export declare const api: import("../definitions").API<[stateFilters: ("EMPTY" | "ONGOING" | "PREPARE_ABORT" | "COMMITTING" | "ABORTING" | "COMPLETE_COMMIT" | "COMPLETE_ABORT")[], producerIdFilters: bigint[], durationFilter: bigint, _transactionalIdPattern: NullableString], ListTransactionsResponse>;
@@ -0,0 +1,20 @@
1
+ import { type NullableString } from "../../protocol/definitions";
2
+ import { type Reader } from "../../protocol/reader";
3
+ import { Writer } from "../../protocol/writer";
4
+ import { type TransactionState } from "../enumerations";
5
+ export type ListTransactionsRequest = Parameters<typeof createRequest>;
6
+ export interface ListTransactionsResponseTransactionState {
7
+ transactionalId: string;
8
+ producerId: bigint;
9
+ transactionState: string;
10
+ }
11
+ export interface ListTransactionsResponse {
12
+ throttleTimeMs: number;
13
+ errorCode: number;
14
+ errorMessage: NullableString;
15
+ unknownStateFilters: string[];
16
+ transactionStates: ListTransactionsResponseTransactionState[];
17
+ }
18
+ export declare function createRequest(stateFilters: TransactionState[], producerIdFilters: bigint[], durationFilter: bigint, transactionalIdPattern: NullableString): Writer;
19
+ export declare function parseResponse(_correlationId: number, apiKey: number, apiVersion: number, reader: Reader): ListTransactionsResponse;
20
+ export declare const api: import("../definitions").API<[stateFilters: ("EMPTY" | "ONGOING" | "PREPARE_ABORT" | "COMMITTING" | "ABORTING" | "COMPLETE_COMMIT" | "COMPLETE_ABORT")[], producerIdFilters: bigint[], durationFilter: bigint, transactionalIdPattern: NullableString], ListTransactionsResponse>;
@@ -22,6 +22,6 @@ export interface ConsumerGroupHeartbeatResponse {
22
22
  heartbeatIntervalMs: number;
23
23
  assignment: ConsumerGroupHeartbeatResponseAssignment | null;
24
24
  }
25
- export declare function createRequest(groupId: string, memberId: string, memberEpoch: number, instanceId: NullableString, rackId: NullableString, rebalanceTimeoutMs: number, subscribedTopicNames: string[] | null, serverAssignor: NullableString, topicPartitions: ConsumerGroupHeartbeatRequestTopicPartition[]): Writer;
25
+ export declare function createRequest(groupId: string, memberId: string, memberEpoch: number, instanceId: NullableString, rackId: NullableString, rebalanceTimeoutMs: number, subscribedTopicNames: string[] | null, _subscribedTopicRegex: NullableString, serverAssignor: NullableString, topicPartitions: ConsumerGroupHeartbeatRequestTopicPartition[]): Writer;
26
26
  export declare function parseResponse(_correlationId: number, apiKey: number, apiVersion: number, reader: Reader): ConsumerGroupHeartbeatResponse;
27
- export declare const api: import("../definitions").API<[groupId: string, memberId: string, memberEpoch: number, instanceId: NullableString, rackId: NullableString, rebalanceTimeoutMs: number, subscribedTopicNames: string[] | null, serverAssignor: NullableString, topicPartitions: ConsumerGroupHeartbeatRequestTopicPartition[]], ConsumerGroupHeartbeatResponse>;
27
+ export declare const api: import("../definitions").API<[groupId: string, memberId: string, memberEpoch: number, instanceId: NullableString, rackId: NullableString, rebalanceTimeoutMs: number, subscribedTopicNames: string[] | null, _subscribedTopicRegex: NullableString, serverAssignor: NullableString, topicPartitions: ConsumerGroupHeartbeatRequestTopicPartition[]], ConsumerGroupHeartbeatResponse>;
@@ -0,0 +1,27 @@
1
+ import { type NullableString } from "../../protocol/definitions";
2
+ import { type Reader } from "../../protocol/reader";
3
+ import { Writer } from "../../protocol/writer";
4
+ export interface ConsumerGroupHeartbeatRequestTopicPartition {
5
+ topicId: string;
6
+ partitions: number[];
7
+ }
8
+ export type ConsumerGroupHeartbeatRequest = Parameters<typeof createRequest>;
9
+ export interface ConsumerGroupHeartbeatResponseAssignmentTopicPartition {
10
+ topicId: string;
11
+ partitions: number[];
12
+ }
13
+ export interface ConsumerGroupHeartbeatResponseAssignment {
14
+ topicPartitions: ConsumerGroupHeartbeatResponseAssignmentTopicPartition[];
15
+ }
16
+ export interface ConsumerGroupHeartbeatResponse {
17
+ throttleTimeMs: number;
18
+ errorCode: number;
19
+ errorMessage: NullableString;
20
+ memberId: NullableString;
21
+ memberEpoch: number;
22
+ heartbeatIntervalMs: number;
23
+ assignment: ConsumerGroupHeartbeatResponseAssignment | null;
24
+ }
25
+ export declare function createRequest(groupId: string, memberId: string, memberEpoch: number, instanceId: NullableString, rackId: NullableString, rebalanceTimeoutMs: number, subscribedTopicNames: string[] | null, subscribedTopicRegex: NullableString, serverAssignor: NullableString, topicPartitions: ConsumerGroupHeartbeatRequestTopicPartition[]): Writer;
26
+ export declare function parseResponse(_correlationId: number, apiKey: number, apiVersion: number, reader: Reader): ConsumerGroupHeartbeatResponse;
27
+ export declare const api: import("../definitions").API<[groupId: string, memberId: string, memberEpoch: number, instanceId: NullableString, rackId: NullableString, rebalanceTimeoutMs: number, subscribedTopicNames: string[] | null, subscribedTopicRegex: NullableString, serverAssignor: NullableString, topicPartitions: ConsumerGroupHeartbeatRequestTopicPartition[]], ConsumerGroupHeartbeatResponse>;
@@ -1,4 +1,5 @@
1
1
  export * as consumerGroupHeartbeatV0 from "./consumer-group-heartbeat-v0";
2
+ export * as consumerGroupHeartbeatV1 from "./consumer-group-heartbeat-v1";
2
3
  export * as fetchV12 from "./fetch-v12";
3
4
  export * as fetchV13 from "./fetch-v13";
4
5
  export * as fetchV14 from "./fetch-v14";