kafka-ts 1.1.6 → 1.1.8

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 (63) hide show
  1. package/dist/api/index.d.ts +122 -121
  2. package/dist/api/index.js +24 -1
  3. package/dist/broker.d.ts +0 -1
  4. package/dist/broker.js +5 -8
  5. package/dist/cluster.d.ts +2 -2
  6. package/dist/cluster.js +48 -23
  7. package/dist/connection.js +9 -13
  8. package/dist/consumer/consumer-group.d.ts +2 -1
  9. package/dist/consumer/consumer-group.js +75 -36
  10. package/dist/consumer/consumer.d.ts +3 -0
  11. package/dist/consumer/consumer.js +47 -13
  12. package/dist/consumer/metadata.d.ts +24 -0
  13. package/dist/consumer/metadata.js +64 -0
  14. package/dist/examples/src/replicator.js +34 -0
  15. package/dist/examples/src/utils/json.js +5 -0
  16. package/dist/producer/producer.d.ts +3 -1
  17. package/dist/producer/producer.js +85 -86
  18. package/dist/request-handler.d.ts +16 -0
  19. package/dist/request-handler.js +67 -0
  20. package/dist/request-handler.test.d.ts +1 -0
  21. package/dist/request-handler.test.js +340 -0
  22. package/dist/src/api/api-versions.js +18 -0
  23. package/dist/src/api/create-topics.js +46 -0
  24. package/dist/src/api/delete-topics.js +26 -0
  25. package/dist/src/api/fetch.js +95 -0
  26. package/dist/src/api/find-coordinator.js +34 -0
  27. package/dist/src/api/heartbeat.js +22 -0
  28. package/dist/src/api/index.js +38 -0
  29. package/dist/src/api/init-producer-id.js +24 -0
  30. package/dist/src/api/join-group.js +48 -0
  31. package/dist/src/api/leave-group.js +30 -0
  32. package/dist/src/api/list-offsets.js +39 -0
  33. package/dist/src/api/metadata.js +47 -0
  34. package/dist/src/api/offset-commit.js +39 -0
  35. package/dist/src/api/offset-fetch.js +44 -0
  36. package/dist/src/api/produce.js +119 -0
  37. package/dist/src/api/sync-group.js +31 -0
  38. package/dist/src/broker.js +35 -0
  39. package/dist/src/connection.js +21 -0
  40. package/dist/src/consumer/consumer-group.js +131 -0
  41. package/dist/src/consumer/consumer.js +103 -0
  42. package/dist/src/consumer/metadata.js +52 -0
  43. package/dist/src/consumer/offset-manager.js +23 -0
  44. package/dist/src/index.js +19 -0
  45. package/dist/src/producer/producer.js +84 -0
  46. package/dist/src/request-handler.js +57 -0
  47. package/dist/src/request-handler.test.js +321 -0
  48. package/dist/src/types.js +2 -0
  49. package/dist/src/utils/api.js +5 -0
  50. package/dist/src/utils/decoder.js +161 -0
  51. package/dist/src/utils/encoder.js +137 -0
  52. package/dist/src/utils/error.js +10 -0
  53. package/dist/utils/debug.d.ts +2 -0
  54. package/dist/utils/debug.js +11 -0
  55. package/dist/utils/error.d.ts +1 -4
  56. package/dist/utils/error.js +5 -9
  57. package/dist/utils/logger.d.ts +7 -0
  58. package/dist/utils/logger.js +20 -5
  59. package/dist/utils/memo.d.ts +1 -0
  60. package/dist/utils/memo.js +16 -0
  61. package/dist/utils/mutex.d.ts +3 -0
  62. package/dist/utils/mutex.js +32 -0
  63. package/package.json +1 -1
@@ -451,125 +451,126 @@ export declare const API: {
451
451
  };
452
452
  export declare const getApiName: <Request, Response>(api: Api<Request, Response>) => string;
453
453
  export declare const API_ERROR: {
454
- UNKNOWN_SERVER_ERROR: number;
455
- OFFSET_OUT_OF_RANGE: number;
456
- CORRUPT_MESSAGE: number;
457
- UNKNOWN_TOPIC_OR_PARTITION: number;
458
- INVALID_FETCH_SIZE: number;
459
- LEADER_NOT_AVAILABLE: number;
460
- NOT_LEADER_OR_FOLLOWER: number;
461
- REQUEST_TIMED_OUT: number;
462
- BROKER_NOT_AVAILABLE: number;
463
- REPLICA_NOT_AVAILABLE: number;
464
- MESSAGE_TOO_LARGE: number;
465
- STALE_CONTROLLER_EPOCH: number;
466
- OFFSET_METADATA_TOO_LARGE: number;
467
- NETWORK_EXCEPTION: number;
468
- COORDINATOR_LOAD_IN_PROGRESS: number;
469
- COORDINATOR_NOT_AVAILABLE: number;
470
- NOT_COORDINATOR: number;
471
- INVALID_TOPIC_EXCEPTION: number;
472
- RECORD_LIST_TOO_LARGE: number;
473
- NOT_ENOUGH_REPLICAS: number;
474
- NOT_ENOUGH_REPLICAS_AFTER_APPEND: number;
475
- INVALID_REQUIRED_ACKS: number;
476
- ILLEGAL_GENERATION: number;
477
- INCONSISTENT_GROUP_PROTOCOL: number;
478
- INVALID_GROUP_ID: number;
479
- UNKNOWN_MEMBER_ID: number;
480
- INVALID_SESSION_TIMEOUT: number;
481
- REBALANCE_IN_PROGRESS: number;
482
- INVALID_COMMIT_OFFSET_SIZE: number;
483
- TOPIC_AUTHORIZATION_FAILED: number;
484
- GROUP_AUTHORIZATION_FAILED: number;
485
- CLUSTER_AUTHORIZATION_FAILED: number;
486
- INVALID_TIMESTAMP: number;
487
- UNSUPPORTED_SASL_MECHANISM: number;
488
- ILLEGAL_SASL_STATE: number;
489
- UNSUPPORTED_VERSION: number;
490
- TOPIC_ALREADY_EXISTS: number;
491
- INVALID_PARTITIONS: number;
492
- INVALID_REPLICATION_FACTOR: number;
493
- INVALID_REPLICA_ASSIGNMENT: number;
494
- INVALID_CONFIG: number;
495
- NOT_CONTROLLER: number;
496
- INVALID_REQUEST: number;
497
- UNSUPPORTED_FOR_MESSAGE_FORMAT: number;
498
- POLICY_VIOLATION: number;
499
- OUT_OF_ORDER_SEQUENCE_NUMBER: number;
500
- DUPLICATE_SEQUENCE_NUMBER: number;
501
- INVALID_PRODUCER_EPOCH: number;
502
- INVALID_TXN_STATE: number;
503
- INVALID_PRODUCER_ID_MAPPING: number;
504
- INVALID_TRANSACTION_TIMEOUT: number;
505
- CONCURRENT_TRANSACTIONS: number;
506
- TRANSACTION_COORDINATOR_FENCED: number;
507
- TRANSACTIONAL_ID_AUTHORIZATION_FAILED: number;
508
- SECURITY_DISABLED: number;
509
- OPERATION_NOT_ATTEMPTED: number;
510
- KAFKA_STORAGE_ERROR: number;
511
- LOG_DIR_NOT_FOUND: number;
512
- SASL_AUTHENTICATION_FAILED: number;
513
- UNKNOWN_PRODUCER_ID: number;
514
- REASSIGNMENT_IN_PROGRESS: number;
515
- DELEGATION_TOKEN_AUTH_DISABLED: number;
516
- DELEGATION_TOKEN_NOT_FOUND: number;
517
- DELEGATION_TOKEN_OWNER_MISMATCH: number;
518
- DELEGATION_TOKEN_REQUEST_NOT_ALLOWED: number;
519
- DELEGATION_TOKEN_AUTHORIZATION_FAILED: number;
520
- DELEGATION_TOKEN_EXPIRED: number;
521
- INVALID_PRINCIPAL_TYPE: number;
522
- NON_EMPTY_GROUP: number;
523
- GROUP_ID_NOT_FOUND: number;
524
- FETCH_SESSION_ID_NOT_FOUND: number;
525
- INVALID_FETCH_SESSION_EPOCH: number;
526
- LISTENER_NOT_FOUND: number;
527
- TOPIC_DELETION_DISABLED: number;
528
- FENCED_LEADER_EPOCH: number;
529
- UNKNOWN_LEADER_EPOCH: number;
530
- UNSUPPORTED_COMPRESSION_TYPE: number;
531
- STALE_BROKER_EPOCH: number;
532
- OFFSET_NOT_AVAILABLE: number;
533
- MEMBER_ID_REQUIRED: number;
534
- PREFERRED_LEADER_NOT_AVAILABLE: number;
535
- GROUP_MAX_SIZE_REACHED: number;
536
- FENCED_INSTANCE_ID: number;
537
- ELIGIBLE_LEADERS_NOT_AVAILABLE: number;
538
- ELECTION_NOT_NEEDED: number;
539
- NO_REASSIGNMENT_IN_PROGRESS: number;
540
- GROUP_SUBSCRIBED_TO_TOPIC: number;
541
- INVALID_RECORD: number;
542
- UNSTABLE_OFFSET_COMMIT: number;
543
- THROTTLING_QUOTA_EXCEEDED: number;
544
- PRODUCER_FENCED: number;
545
- RESOURCE_NOT_FOUND: number;
546
- DUPLICATE_RESOURCE: number;
547
- UNACCEPTABLE_CREDENTIAL: number;
548
- INCONSISTENT_VOTER_SET: number;
549
- INVALID_UPDATE_VERSION: number;
550
- FEATURE_UPDATE_FAILED: number;
551
- PRINCIPAL_DESERIALIZATION_FAILURE: number;
552
- SNAPSHOT_NOT_FOUND: number;
553
- POSITION_OUT_OF_RANGE: number;
554
- UNKNOWN_TOPIC_ID: number;
555
- DUPLICATE_BROKER_REGISTRATION: number;
556
- BROKER_ID_NOT_REGISTERED: number;
557
- INCONSISTENT_TOPIC_ID: number;
558
- INCONSISTENT_CLUSTER_ID: number;
559
- TRANSACTIONAL_ID_NOT_FOUND: number;
560
- FETCH_SESSION_TOPIC_ID_ERROR: number;
561
- INELIGIBLE_REPLICA: number;
562
- NEW_LEADER_ELECTED: number;
563
- OFFSET_MOVED_TO_TIERED_STORAGE: number;
564
- FENCED_MEMBER_EPOCH: number;
565
- UNRELEASED_INSTANCE_ID: number;
566
- UNSUPPORTED_ASSIGNOR: number;
567
- STALE_MEMBER_EPOCH: number;
568
- MISMATCHED_ENDPOINT_TYPE: number;
569
- UNSUPPORTED_ENDPOINT_TYPE: number;
570
- UNKNOWN_CONTROLLER_ID: number;
571
- UNKNOWN_SUBSCRIPTION_ID: number;
572
- TELEMETRY_TOO_LARGE: number;
573
- INVALID_REGISTRATION: number;
574
- TRANSACTION_ABORTABLE: number;
454
+ readonly UNKNOWN_SERVER_ERROR: -1;
455
+ readonly OFFSET_OUT_OF_RANGE: 1;
456
+ readonly CORRUPT_MESSAGE: 2;
457
+ readonly UNKNOWN_TOPIC_OR_PARTITION: 3;
458
+ readonly INVALID_FETCH_SIZE: 4;
459
+ readonly LEADER_NOT_AVAILABLE: 5;
460
+ readonly NOT_LEADER_OR_FOLLOWER: 6;
461
+ readonly REQUEST_TIMED_OUT: 7;
462
+ readonly BROKER_NOT_AVAILABLE: 8;
463
+ readonly REPLICA_NOT_AVAILABLE: 9;
464
+ readonly MESSAGE_TOO_LARGE: 10;
465
+ readonly STALE_CONTROLLER_EPOCH: 11;
466
+ readonly OFFSET_METADATA_TOO_LARGE: 12;
467
+ readonly NETWORK_EXCEPTION: 13;
468
+ readonly COORDINATOR_LOAD_IN_PROGRESS: 14;
469
+ readonly COORDINATOR_NOT_AVAILABLE: 15;
470
+ readonly NOT_COORDINATOR: 16;
471
+ readonly INVALID_TOPIC_EXCEPTION: 17;
472
+ readonly RECORD_LIST_TOO_LARGE: 18;
473
+ readonly NOT_ENOUGH_REPLICAS: 19;
474
+ readonly NOT_ENOUGH_REPLICAS_AFTER_APPEND: 20;
475
+ readonly INVALID_REQUIRED_ACKS: 21;
476
+ readonly ILLEGAL_GENERATION: 22;
477
+ readonly INCONSISTENT_GROUP_PROTOCOL: 23;
478
+ readonly INVALID_GROUP_ID: 24;
479
+ readonly UNKNOWN_MEMBER_ID: 25;
480
+ readonly INVALID_SESSION_TIMEOUT: 26;
481
+ readonly REBALANCE_IN_PROGRESS: 27;
482
+ readonly INVALID_COMMIT_OFFSET_SIZE: 28;
483
+ readonly TOPIC_AUTHORIZATION_FAILED: 29;
484
+ readonly GROUP_AUTHORIZATION_FAILED: 30;
485
+ readonly CLUSTER_AUTHORIZATION_FAILED: 31;
486
+ readonly INVALID_TIMESTAMP: 32;
487
+ readonly UNSUPPORTED_SASL_MECHANISM: 33;
488
+ readonly ILLEGAL_SASL_STATE: 34;
489
+ readonly UNSUPPORTED_VERSION: 35;
490
+ readonly TOPIC_ALREADY_EXISTS: 36;
491
+ readonly INVALID_PARTITIONS: 37;
492
+ readonly INVALID_REPLICATION_FACTOR: 38;
493
+ readonly INVALID_REPLICA_ASSIGNMENT: 39;
494
+ readonly INVALID_CONFIG: 40;
495
+ readonly NOT_CONTROLLER: 41;
496
+ readonly INVALID_REQUEST: 42;
497
+ readonly UNSUPPORTED_FOR_MESSAGE_FORMAT: 43;
498
+ readonly POLICY_VIOLATION: 44;
499
+ readonly OUT_OF_ORDER_SEQUENCE_NUMBER: 45;
500
+ readonly DUPLICATE_SEQUENCE_NUMBER: 46;
501
+ readonly INVALID_PRODUCER_EPOCH: 47;
502
+ readonly INVALID_TXN_STATE: 48;
503
+ readonly INVALID_PRODUCER_ID_MAPPING: 49;
504
+ readonly INVALID_TRANSACTION_TIMEOUT: 50;
505
+ readonly CONCURRENT_TRANSACTIONS: 51;
506
+ readonly TRANSACTION_COORDINATOR_FENCED: 52;
507
+ readonly TRANSACTIONAL_ID_AUTHORIZATION_FAILED: 53;
508
+ readonly SECURITY_DISABLED: 54;
509
+ readonly OPERATION_NOT_ATTEMPTED: 55;
510
+ readonly KAFKA_STORAGE_ERROR: 56;
511
+ readonly LOG_DIR_NOT_FOUND: 57;
512
+ readonly SASL_AUTHENTICATION_FAILED: 58;
513
+ readonly UNKNOWN_PRODUCER_ID: 59;
514
+ readonly REASSIGNMENT_IN_PROGRESS: 60;
515
+ readonly DELEGATION_TOKEN_AUTH_DISABLED: 61;
516
+ readonly DELEGATION_TOKEN_NOT_FOUND: 62;
517
+ readonly DELEGATION_TOKEN_OWNER_MISMATCH: 63;
518
+ readonly DELEGATION_TOKEN_REQUEST_NOT_ALLOWED: 64;
519
+ readonly DELEGATION_TOKEN_AUTHORIZATION_FAILED: 65;
520
+ readonly DELEGATION_TOKEN_EXPIRED: 66;
521
+ readonly INVALID_PRINCIPAL_TYPE: 67;
522
+ readonly NON_EMPTY_GROUP: 68;
523
+ readonly GROUP_ID_NOT_FOUND: 69;
524
+ readonly FETCH_SESSION_ID_NOT_FOUND: 70;
525
+ readonly INVALID_FETCH_SESSION_EPOCH: 71;
526
+ readonly LISTENER_NOT_FOUND: 72;
527
+ readonly TOPIC_DELETION_DISABLED: 73;
528
+ readonly FENCED_LEADER_EPOCH: 74;
529
+ readonly UNKNOWN_LEADER_EPOCH: 75;
530
+ readonly UNSUPPORTED_COMPRESSION_TYPE: 76;
531
+ readonly STALE_BROKER_EPOCH: 77;
532
+ readonly OFFSET_NOT_AVAILABLE: 78;
533
+ readonly MEMBER_ID_REQUIRED: 79;
534
+ readonly PREFERRED_LEADER_NOT_AVAILABLE: 80;
535
+ readonly GROUP_MAX_SIZE_REACHED: 81;
536
+ readonly FENCED_INSTANCE_ID: 82;
537
+ readonly ELIGIBLE_LEADERS_NOT_AVAILABLE: 83;
538
+ readonly ELECTION_NOT_NEEDED: 84;
539
+ readonly NO_REASSIGNMENT_IN_PROGRESS: 85;
540
+ readonly GROUP_SUBSCRIBED_TO_TOPIC: 86;
541
+ readonly INVALID_RECORD: 87;
542
+ readonly UNSTABLE_OFFSET_COMMIT: 88;
543
+ readonly THROTTLING_QUOTA_EXCEEDED: 89;
544
+ readonly PRODUCER_FENCED: 90;
545
+ readonly RESOURCE_NOT_FOUND: 91;
546
+ readonly DUPLICATE_RESOURCE: 92;
547
+ readonly UNACCEPTABLE_CREDENTIAL: 93;
548
+ readonly INCONSISTENT_VOTER_SET: 94;
549
+ readonly INVALID_UPDATE_VERSION: 95;
550
+ readonly FEATURE_UPDATE_FAILED: 96;
551
+ readonly PRINCIPAL_DESERIALIZATION_FAILURE: 97;
552
+ readonly SNAPSHOT_NOT_FOUND: 98;
553
+ readonly POSITION_OUT_OF_RANGE: 99;
554
+ readonly UNKNOWN_TOPIC_ID: 100;
555
+ readonly DUPLICATE_BROKER_REGISTRATION: 101;
556
+ readonly BROKER_ID_NOT_REGISTERED: 102;
557
+ readonly INCONSISTENT_TOPIC_ID: 103;
558
+ readonly INCONSISTENT_CLUSTER_ID: 104;
559
+ readonly TRANSACTIONAL_ID_NOT_FOUND: 105;
560
+ readonly FETCH_SESSION_TOPIC_ID_ERROR: 106;
561
+ readonly INELIGIBLE_REPLICA: 107;
562
+ readonly NEW_LEADER_ELECTED: 108;
563
+ readonly OFFSET_MOVED_TO_TIERED_STORAGE: 109;
564
+ readonly FENCED_MEMBER_EPOCH: 110;
565
+ readonly UNRELEASED_INSTANCE_ID: 111;
566
+ readonly UNSUPPORTED_ASSIGNOR: 112;
567
+ readonly STALE_MEMBER_EPOCH: 113;
568
+ readonly MISMATCHED_ENDPOINT_TYPE: 114;
569
+ readonly UNSUPPORTED_ENDPOINT_TYPE: 115;
570
+ readonly UNKNOWN_CONTROLLER_ID: 116;
571
+ readonly UNKNOWN_SUBSCRIPTION_ID: 117;
572
+ readonly TELEMETRY_TOO_LARGE: 118;
573
+ readonly INVALID_REGISTRATION: 119;
574
+ readonly TRANSACTION_ABORTABLE: 120;
575
575
  };
576
+ export declare const handleApiError: (error: unknown) => Promise<void>;
package/dist/api/index.js CHANGED
@@ -1,6 +1,9 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.API_ERROR = exports.getApiName = exports.API = void 0;
3
+ exports.handleApiError = exports.API_ERROR = exports.getApiName = exports.API = void 0;
4
+ const delay_1 = require("../utils/delay");
5
+ const error_1 = require("../utils/error");
6
+ const logger_1 = require("../utils/logger");
4
7
  const api_versions_1 = require("./api-versions");
5
8
  const create_topics_1 = require("./create-topics");
6
9
  const delete_topics_1 = require("./delete-topics");
@@ -163,3 +166,23 @@ exports.API_ERROR = {
163
166
  INVALID_REGISTRATION: 119,
164
167
  TRANSACTION_ABORTABLE: 120,
165
168
  };
169
+ const handleApiError = async (error) => {
170
+ if (error instanceof error_1.KafkaTSApiError) {
171
+ switch (error.errorCode) {
172
+ case exports.API_ERROR.LEADER_NOT_AVAILABLE:
173
+ logger_1.log.debug('Leader not available yet. Retrying...');
174
+ return (0, delay_1.delay)(500);
175
+ case exports.API_ERROR.COORDINATOR_LOAD_IN_PROGRESS:
176
+ logger_1.log.debug('Coordinator load in progress. Retrying...');
177
+ return (0, delay_1.delay)(100);
178
+ case exports.API_ERROR.COORDINATOR_NOT_AVAILABLE:
179
+ logger_1.log.debug('Coordinator not available yet. Retrying...');
180
+ return (0, delay_1.delay)(100);
181
+ case exports.API_ERROR.OFFSET_NOT_AVAILABLE:
182
+ logger_1.log.debug('Offset not available yet. Retrying...');
183
+ return (0, delay_1.delay)(100);
184
+ }
185
+ }
186
+ throw error;
187
+ };
188
+ exports.handleApiError = handleApiError;
package/dist/broker.d.ts CHANGED
@@ -20,7 +20,6 @@ export declare class Broker {
20
20
  sendRequest: SendRequest;
21
21
  constructor(options: BrokerOptions);
22
22
  connect(): Promise<this>;
23
- ensureConnected(): Promise<void>;
24
23
  disconnect(): Promise<void>;
25
24
  private validateApiVersions;
26
25
  private saslHandshake;
package/dist/broker.js CHANGED
@@ -19,16 +19,13 @@ class Broker {
19
19
  this.sendRequest = this.connection.sendRequest.bind(this.connection);
20
20
  }
21
21
  async connect() {
22
- await this.connection.connect();
23
- await this.validateApiVersions();
24
- await this.saslHandshake();
25
- await this.saslAuthenticate();
26
- return this;
27
- }
28
- async ensureConnected() {
29
22
  if (!this.connection.isConnected()) {
30
- await this.connect();
23
+ await this.connection.connect();
24
+ await this.validateApiVersions();
25
+ await this.saslHandshake();
26
+ await this.saslAuthenticate();
31
27
  }
28
+ return this;
32
29
  }
33
30
  async disconnect() {
34
31
  await this.connection.disconnect();
package/dist/cluster.d.ts CHANGED
@@ -16,13 +16,13 @@ export declare class Cluster {
16
16
  private brokerMetadata;
17
17
  constructor(options: ClusterOptions);
18
18
  connect(): Promise<void>;
19
- refreshBrokerMetadata(): Promise<void>;
20
- ensureConnected(): Promise<void>;
21
19
  disconnect(): Promise<void>;
20
+ ensureConnected: () => Promise<void>;
22
21
  setSeedBroker: (nodeId: number) => Promise<void>;
23
22
  sendRequest: SendRequest;
24
23
  sendRequestToNode: (nodeId: number) => SendRequest;
25
24
  acquireBroker(nodeId: number): Promise<Broker>;
26
25
  private findSeedBroker;
26
+ private refreshBrokerMetadata;
27
27
  }
28
28
  export {};
package/dist/cluster.js CHANGED
@@ -14,6 +14,7 @@ const api_1 = require("./api");
14
14
  const broker_1 = require("./broker");
15
15
  const error_1 = require("./utils/error");
16
16
  const logger_1 = require("./utils/logger");
17
+ const shared_1 = require("./utils/shared");
17
18
  const tracer_1 = require("./utils/tracer");
18
19
  const trace = (0, tracer_1.createTracer)('Cluster');
19
20
  class Cluster {
@@ -29,34 +30,51 @@ class Cluster {
29
30
  this.brokerById = {};
30
31
  await this.refreshBrokerMetadata();
31
32
  }
32
- async refreshBrokerMetadata() {
33
- const metadata = await this.sendRequest(api_1.API.METADATA, { topics: [] });
34
- this.brokerMetadata = Object.fromEntries(metadata.brokers.map((options) => [options.nodeId, options]));
35
- }
36
- async ensureConnected() {
37
- if (!this.seedBroker) {
38
- return this.connect();
39
- }
40
- try {
41
- await Promise.all([this.seedBroker, ...Object.values(this.brokerById)].map((x) => x.ensureConnected()));
42
- }
43
- catch {
44
- logger_1.log.warn('Failed to connect to known brokers, reconnecting...');
45
- await this.disconnect();
46
- return this.connect();
47
- }
48
- }
49
33
  async disconnect() {
50
34
  await Promise.all([
51
35
  this.seedBroker?.disconnect(),
52
36
  ...Object.values(this.brokerById).map((x) => x.disconnect()),
53
37
  ]);
54
38
  }
39
+ ensureConnected = (0, shared_1.shared)(async () => {
40
+ if (!this.seedBroker) {
41
+ return this.connect();
42
+ }
43
+ const brokers = [
44
+ {
45
+ broker: this.seedBroker,
46
+ handleError: async (error) => {
47
+ logger_1.log.debug(`Failed to connect to seed broker. Reconnecting...`, { reason: error.message });
48
+ await this.seedBroker?.disconnect();
49
+ this.seedBroker = await this.findSeedBroker();
50
+ },
51
+ },
52
+ ...Object.entries(this.brokerById).map(([nodeId, broker]) => ({
53
+ broker,
54
+ handleError: async (error) => {
55
+ logger_1.log.debug(`Failed to connect to broker ${nodeId}. Disconnecting...`, { reason: error.message });
56
+ await broker.disconnect();
57
+ delete this.brokerById[parseInt(nodeId)];
58
+ },
59
+ })),
60
+ ];
61
+ await Promise.all(brokers.map(async ({ broker, handleError }) => {
62
+ try {
63
+ await broker.connect();
64
+ }
65
+ catch (error) {
66
+ await handleError(error);
67
+ }
68
+ }));
69
+ });
55
70
  setSeedBroker = async (nodeId) => {
71
+ const broker = await this.acquireBroker(nodeId);
56
72
  await this.seedBroker?.disconnect();
57
- this.seedBroker = await this.acquireBroker(nodeId);
73
+ this.seedBroker = broker;
74
+ };
75
+ sendRequest = async (...args) => {
76
+ return this.seedBroker.sendRequest(...args);
58
77
  };
59
- sendRequest = (...args) => this.seedBroker.sendRequest(...args);
60
78
  sendRequestToNode = (nodeId) => async (...args) => {
61
79
  if (!this.brokerById[nodeId]) {
62
80
  this.brokerById[nodeId] = await this.acquireBroker(nodeId);
@@ -64,9 +82,10 @@ class Cluster {
64
82
  return this.brokerById[nodeId].sendRequest(...args);
65
83
  };
66
84
  async acquireBroker(nodeId) {
67
- if (!(nodeId in this.brokerMetadata)) {
68
- throw new error_1.BrokerNotAvailableError(nodeId);
69
- }
85
+ if (!(nodeId in this.brokerMetadata))
86
+ await this.refreshBrokerMetadata();
87
+ if (!(nodeId in this.brokerMetadata))
88
+ throw new error_1.ConnectionError(`Broker ${nodeId} is not available`);
70
89
  const broker = new broker_1.Broker({
71
90
  clientId: this.options.clientId,
72
91
  sasl: this.options.sasl,
@@ -92,11 +111,17 @@ class Cluster {
92
111
  return broker;
93
112
  }
94
113
  catch (error) {
95
- logger_1.log.warn(`Failed to connect to seed broker ${options.host}:${options.port}`, error);
114
+ logger_1.log.debug(`Failed to connect to seed broker ${options.host}:${options.port}`, {
115
+ reason: error.message,
116
+ });
96
117
  }
97
118
  }
98
119
  throw new error_1.KafkaTSError('No seed brokers found');
99
120
  }
121
+ async refreshBrokerMetadata() {
122
+ const metadata = await this.sendRequest(api_1.API.METADATA, { topics: [] });
123
+ this.brokerMetadata = Object.fromEntries(metadata.brokers.map((options) => [options.nodeId, options]));
124
+ }
100
125
  }
101
126
  exports.Cluster = Cluster;
102
127
  __decorate([
@@ -71,6 +71,7 @@ class Connection {
71
71
  async connect() {
72
72
  this.queue = {};
73
73
  this.chunks = [];
74
+ const { stack } = new Error();
74
75
  await new Promise((resolve, reject) => {
75
76
  const { ssl, connection } = this.options;
76
77
  this.socket = ssl
@@ -81,14 +82,16 @@ class Connection {
81
82
  }, resolve)
82
83
  : net_1.default.connect(connection, resolve);
83
84
  this.socket.setKeepAlive(true, 30_000);
84
- this.socket.once('error', reject);
85
+ this.socket.once('error', (error) => {
86
+ reject(new error_1.ConnectionError(error.message, stack));
87
+ });
85
88
  });
86
89
  this.socket.removeAllListeners('error');
87
90
  this.socket.on('error', (error) => logger_1.log.debug('Socket error', { error }));
88
91
  this.socket.on('data', (data) => this.handleData(data));
89
92
  this.socket.once('close', async () => {
90
93
  Object.values(this.queue).forEach(({ reject }) => {
91
- reject(new error_1.ConnectionError('Socket closed unexpectedly'));
94
+ reject(new error_1.ConnectionError('Socket closed unexpectedly', stack));
92
95
  });
93
96
  this.queue = {};
94
97
  });
@@ -112,18 +115,19 @@ class Connection {
112
115
  .writeString(this.options.clientId);
113
116
  const request = api.request(encoder, body);
114
117
  const requestEncoder = new encoder_1.Encoder().writeInt32(request.getBufferLength()).writeEncoder(request);
118
+ const { stack } = new Error();
115
119
  let timeout;
116
120
  const { responseDecoder, responseSize } = await new Promise(async (resolve, reject) => {
117
121
  timeout = setTimeout(() => {
118
122
  delete this.queue[correlationId];
119
- reject(new error_1.ConnectionError(`${apiName} timed out`));
123
+ reject(new error_1.ConnectionError(`${apiName} timed out`, stack));
120
124
  }, this.options.requestTimeout);
121
125
  try {
122
126
  this.queue[correlationId] = { resolve, reject };
123
127
  await this.write(requestEncoder.value());
124
128
  }
125
129
  catch (error) {
126
- reject(error);
130
+ reject(new error_1.ConnectionError(error.message, stack));
127
131
  }
128
132
  });
129
133
  clearTimeout(timeout);
@@ -142,15 +146,7 @@ class Connection {
142
146
  }
143
147
  write(buffer) {
144
148
  return new Promise((resolve, reject) => {
145
- const { stack } = new Error('Write error');
146
- this.socket.write(buffer, 'binary', (error) => {
147
- if (error) {
148
- const err = new error_1.ConnectionError(error.message);
149
- err.stack += `\n${stack}`;
150
- return reject(err);
151
- }
152
- resolve();
153
- });
149
+ this.socket.write(buffer, 'binary', (error) => (error ? reject(error) : resolve()));
154
150
  });
155
151
  }
156
152
  handleData(buffer) {
@@ -28,12 +28,13 @@ export declare class ConsumerGroup {
28
28
  private startHeartbeater;
29
29
  private stopHeartbeater;
30
30
  handleLastHeartbeat(): void;
31
- private findCoordinator;
31
+ findCoordinator(): Promise<void>;
32
32
  private joinGroup;
33
33
  private syncGroup;
34
34
  private offsetFetch;
35
35
  offsetCommit(topicPartitions: Record<string, Set<number>>): Promise<void>;
36
36
  heartbeat(): Promise<void>;
37
37
  leaveGroup(): Promise<void>;
38
+ private handleError;
38
39
  }
39
40
  export {};