@platformatic/kafka 1.30.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 (41) hide show
  1. package/dist/apis/callbacks.js +1 -1
  2. package/dist/clients/admin/admin.d.ts +3 -1
  3. package/dist/clients/admin/admin.js +63 -35
  4. package/dist/clients/admin/options.d.ts +22 -0
  5. package/dist/clients/admin/options.js +15 -1
  6. package/dist/clients/admin/types.d.ts +11 -1
  7. package/dist/clients/base/base.d.ts +4 -1
  8. package/dist/clients/base/base.js +17 -2
  9. package/dist/clients/base/index.d.ts +1 -1
  10. package/dist/clients/base/index.js +1 -1
  11. package/dist/clients/base/options.d.ts +1 -0
  12. package/dist/clients/base/options.js +1 -0
  13. package/dist/clients/base/types.d.ts +1 -0
  14. package/dist/clients/consumer/consumer.d.ts +4 -1
  15. package/dist/clients/consumer/consumer.js +29 -2
  16. package/dist/clients/consumer/messages-stream.d.ts +1 -0
  17. package/dist/clients/consumer/messages-stream.js +61 -15
  18. package/dist/clients/consumer/options.d.ts +3 -0
  19. package/dist/clients/consumer/options.js +3 -0
  20. package/dist/clients/consumer/types.d.ts +3 -0
  21. package/dist/clients/producer/producer.js +9 -3
  22. package/dist/errors.js +9 -1
  23. package/dist/network/connection-pool.d.ts +2 -0
  24. package/dist/network/connection-pool.js +11 -2
  25. package/dist/network/connection.d.ts +3 -0
  26. package/dist/network/connection.js +34 -4
  27. package/dist/typescript-4/dist/clients/admin/admin.d.ts +3 -1
  28. package/dist/typescript-4/dist/clients/admin/options.d.ts +22 -0
  29. package/dist/typescript-4/dist/clients/admin/types.d.ts +11 -1
  30. package/dist/typescript-4/dist/clients/base/base.d.ts +4 -1
  31. package/dist/typescript-4/dist/clients/base/index.d.ts +1 -1
  32. package/dist/typescript-4/dist/clients/base/options.d.ts +1 -0
  33. package/dist/typescript-4/dist/clients/base/types.d.ts +1 -0
  34. package/dist/typescript-4/dist/clients/consumer/consumer.d.ts +4 -1
  35. package/dist/typescript-4/dist/clients/consumer/messages-stream.d.ts +1 -0
  36. package/dist/typescript-4/dist/clients/consumer/options.d.ts +3 -0
  37. package/dist/typescript-4/dist/clients/consumer/types.d.ts +3 -0
  38. package/dist/typescript-4/dist/network/connection-pool.d.ts +2 -0
  39. package/dist/typescript-4/dist/network/connection.d.ts +3 -0
  40. package/dist/version.js +1 -1
  41. package/package.json +2 -1
@@ -35,7 +35,7 @@ export function runConcurrentCallbacks(errorMessage, collection, operation, call
35
35
  }
36
36
  remaining--;
37
37
  if (remaining === 0) {
38
- callback(hasErrors ? new MultipleErrors(errorMessage, errors) : null, results);
38
+ callback(hasErrors ? new MultipleErrors(errorMessage, errors.filter(Boolean)) : null, results);
39
39
  }
40
40
  }
41
41
  if (remaining === 0) {
@@ -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 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 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';
9
9
  export declare class Admin extends Base<AdminOptions> {
10
10
  #private;
11
11
  constructor(options: AdminOptions);
@@ -21,6 +21,8 @@ export declare class Admin extends Base<AdminOptions> {
21
21
  listGroups(options?: ListGroupsOptions): Promise<Map<string, GroupBase>>;
22
22
  describeGroups(options: DescribeGroupsOptions, callback: CallbackWithPromise<Map<string, Group>>): void;
23
23
  describeGroups(options: DescribeGroupsOptions): Promise<Map<string, Group>>;
24
+ findCoordinator(options: FindCoordinatorOptions, callback: CallbackWithPromise<FindCoordinatorResult[]>): void;
25
+ findCoordinator(options: FindCoordinatorOptions): Promise<FindCoordinatorResult[]>;
24
26
  deleteGroups(options: DeleteGroupsOptions, callback: CallbackWithPromise<void>): void;
25
27
  deleteGroups(options: DeleteGroupsOptions): Promise<void>;
26
28
  removeMembersFromConsumerGroup(options: RemoveMembersFromConsumerGroupOptions, callback: CallbackWithPromise<void>): void;
@@ -1,10 +1,10 @@
1
1
  import { createPromisifiedCallback, kCallbackPromise, runConcurrentCallbacks } from "../../apis/callbacks.js";
2
2
  import { ConfigResourceTypes, FetchIsolationLevels, FindCoordinatorKeyTypes } from "../../apis/enumerations.js";
3
3
  import { adminAclsChannel, adminClientQuotasChannel, adminConfigsChannel, adminConsumerGroupOffsetsChannel, adminGroupsChannel, adminLogDirsChannel, adminOffsetsChannel, adminTopicsChannel, createDiagnosticContext } from "../../diagnostic.js";
4
- import { MultipleErrors } from "../../errors.js";
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, incrementalAlterConfigsOptionsValidator, listConsumerGroupOffsetsOptionsValidator, listGroupsOptionsValidator, listTopicsOptionsValidator, removeMembersFromConsumerGroupOptionsValidator } from "./options.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";
8
8
  export class Admin extends Base {
9
9
  #controller = null;
10
10
  constructor(options) {
@@ -108,6 +108,21 @@ export class Admin extends Base {
108
108
  adminGroupsChannel.traceCallback(this.#describeGroups, 1, createDiagnosticContext({ client: this, operation: 'describeGroups', options }), this, options, callback);
109
109
  return callback[kCallbackPromise];
110
110
  }
111
+ findCoordinator(options, callback) {
112
+ if (!callback) {
113
+ callback = createPromisifiedCallback();
114
+ }
115
+ if (this[kCheckNotClosed](callback)) {
116
+ return callback[kCallbackPromise];
117
+ }
118
+ const validationError = this[kValidateOptions](options, findCoordinatorOptionsValidator, '/options', false);
119
+ if (validationError) {
120
+ callback(validationError);
121
+ return callback[kCallbackPromise];
122
+ }
123
+ adminGroupsChannel.traceCallback(this.#findCoordinator, 1, createDiagnosticContext({ client: this, operation: 'findCoordinator', options }), this, options, callback);
124
+ return callback[kCallbackPromise];
125
+ }
111
126
  deleteGroups(options, callback) {
112
127
  if (!callback) {
113
128
  callback = createPromisifiedCallback();
@@ -563,22 +578,22 @@ export class Admin extends Base {
563
578
  callback(error);
564
579
  return;
565
580
  }
566
- this.#findGroupCoordinator(options.groups, (error, response) => {
581
+ this.#findCoordinator({ keyType: FindCoordinatorKeyTypes.GROUP, keys: options.groups }, (error, coordinators) => {
567
582
  if (error) {
568
583
  callback(error);
569
584
  return;
570
585
  }
571
586
  // Group the groups by coordinator
572
- const coordinators = new Map();
573
- for (const { key: group, nodeId: node } of response.coordinators) {
574
- let coordinator = coordinators.get(node);
587
+ const coordinatorsMap = new Map();
588
+ for (const { key: group, nodeId: node } of coordinators) {
589
+ let coordinator = coordinatorsMap.get(node);
575
590
  if (!coordinator) {
576
591
  coordinator = [];
577
- coordinators.set(node, coordinator);
592
+ coordinatorsMap.set(node, coordinator);
578
593
  }
579
594
  coordinator.push(group);
580
595
  }
581
- runConcurrentCallbacks('Describing groups failed.', coordinators, ([node, groups], concurrentCallback) => {
596
+ runConcurrentCallbacks('Describing groups failed.', coordinatorsMap, ([node, groups], concurrentCallback) => {
582
597
  this[kGetConnection](metadata.brokers.get(node), (error, connection) => {
583
598
  if (error) {
584
599
  concurrentCallback(error);
@@ -651,22 +666,22 @@ export class Admin extends Base {
651
666
  callback(error);
652
667
  return;
653
668
  }
654
- this.#findGroupCoordinator(options.groups, (error, response) => {
669
+ this.#findCoordinator({ keyType: FindCoordinatorKeyTypes.GROUP, keys: options.groups }, (error, coordinators) => {
655
670
  if (error) {
656
671
  callback(error);
657
672
  return;
658
673
  }
659
674
  // Group the groups by coordinator
660
- const coordinators = new Map();
661
- for (const { key: group, nodeId: node } of response.coordinators) {
662
- let coordinator = coordinators.get(node);
675
+ const coordinatorsMap = new Map();
676
+ for (const { key: group, nodeId: node } of coordinators) {
677
+ let coordinator = coordinatorsMap.get(node);
663
678
  if (!coordinator) {
664
679
  coordinator = [];
665
- coordinators.set(node, coordinator);
680
+ coordinatorsMap.set(node, coordinator);
666
681
  }
667
682
  coordinator.push(group);
668
683
  }
669
- runConcurrentCallbacks('Deleting groups failed.', coordinators, ([node, groups], concurrentCallback) => {
684
+ runConcurrentCallbacks('Deleting groups failed.', coordinatorsMap, ([node, groups], concurrentCallback) => {
670
685
  this[kGetConnection](metadata.brokers.get(node), (error, connection) => {
671
686
  if (error) {
672
687
  concurrentCallback(error);
@@ -714,12 +729,12 @@ export class Admin extends Base {
714
729
  callback(error, undefined);
715
730
  return;
716
731
  }
717
- this.#findGroupCoordinator([options.groupId], (error, response) => {
732
+ this.#findCoordinator({ keyType: FindCoordinatorKeyTypes.GROUP, keys: [options.groupId] }, (error, coordinators) => {
718
733
  if (error) {
719
734
  callback(new MultipleErrors('Removing members from consumer group failed.', [error]));
720
735
  return;
721
736
  }
722
- const coordinator = response.coordinators.find(c => c.key === options.groupId);
737
+ const coordinator = coordinators.find(c => c.key === options.groupId);
723
738
  /* c8 ignore next 8 - Hard to test */
724
739
  if (!coordinator) {
725
740
  callback(new MultipleErrors('Removing members from consumer group failed.', [
@@ -765,8 +780,8 @@ export class Admin extends Base {
765
780
  });
766
781
  });
767
782
  }
768
- #findGroupCoordinator(groups, callback) {
769
- this[kPerformWithRetry]('findGroupCoordinator', retryCallback => {
783
+ #findCoordinator(options, callback) {
784
+ this[kPerformWithRetry]('findCoordinator', retryCallback => {
770
785
  this[kGetBootstrapConnection]((error, connection) => {
771
786
  if (error) {
772
787
  retryCallback(error);
@@ -777,7 +792,7 @@ export class Admin extends Base {
777
792
  retryCallback(error);
778
793
  return;
779
794
  }
780
- api(connection, FindCoordinatorKeyTypes.GROUP, groups, retryCallback);
795
+ api(connection, options.keyType, options.keys, retryCallback);
781
796
  });
782
797
  });
783
798
  }, (error, response) => {
@@ -785,7 +800,12 @@ export class Admin extends Base {
785
800
  callback(error);
786
801
  return;
787
802
  }
788
- callback(null, response);
803
+ callback(null, response.coordinators.map(coordinator => ({
804
+ key: coordinator.key,
805
+ nodeId: coordinator.nodeId,
806
+ host: coordinator.host,
807
+ port: coordinator.port
808
+ })));
789
809
  }, 0);
790
810
  }
791
811
  #describeClientQuotas(options, callback) {
@@ -883,13 +903,13 @@ export class Admin extends Base {
883
903
  }
884
904
  /* c8 ignore next - Hard to test */
885
905
  const groupIds = options.groups.map(group => (typeof group === 'string' ? group : group.groupId));
886
- this.#findGroupCoordinator(groupIds, (error, response) => {
906
+ this.#findCoordinator({ keyType: FindCoordinatorKeyTypes.GROUP, keys: groupIds }, (error, coordinators) => {
887
907
  if (error) {
888
908
  callback(new MultipleErrors('Listing consumer group offsets failed.', [error]));
889
909
  return;
890
910
  }
891
- const coordinators = new Map();
892
- for (const { key: groupId, nodeId: node } of response.coordinators) {
911
+ const coordinatorsMap = new Map();
912
+ for (const { key: groupId, nodeId: node } of coordinators) {
893
913
  const groupRequest = {
894
914
  groupId,
895
915
  memberId: null,
@@ -901,14 +921,14 @@ export class Admin extends Base {
901
921
  break;
902
922
  }
903
923
  }
904
- let coordinator = coordinators.get(node);
924
+ let coordinator = coordinatorsMap.get(node);
905
925
  if (!coordinator) {
906
926
  coordinator = [];
907
- coordinators.set(node, coordinator);
927
+ coordinatorsMap.set(node, coordinator);
908
928
  }
909
929
  coordinator.push(groupRequest);
910
930
  }
911
- runConcurrentCallbacks('Listing consumer group offsets failed.', coordinators, ([node, groups], concurrentCallback) => {
931
+ runConcurrentCallbacks('Listing consumer group offsets failed.', coordinatorsMap, ([node, groups], concurrentCallback) => {
912
932
  this[kGetConnection](metadata.brokers.get(node), (error, connection) => {
913
933
  if (error) {
914
934
  concurrentCallback(error);
@@ -952,12 +972,12 @@ export class Admin extends Base {
952
972
  callback(error, undefined);
953
973
  return;
954
974
  }
955
- this.#findGroupCoordinator([options.groupId], (error, response) => {
975
+ this.#findCoordinator({ keyType: FindCoordinatorKeyTypes.GROUP, keys: [options.groupId] }, (error, coordinators) => {
956
976
  if (error) {
957
977
  callback(new MultipleErrors('Altering consumer group offsets failed.', [error]));
958
978
  return;
959
979
  }
960
- const coordinator = response.coordinators.find(c => c.key === options.groupId);
980
+ const coordinator = coordinators.find(c => c.key === options.groupId);
961
981
  /* c8 ignore next 9 - Hard to test */
962
982
  if (!coordinator) {
963
983
  callback(new MultipleErrors('Altering consumer group offsets failed.', [
@@ -1013,12 +1033,12 @@ export class Admin extends Base {
1013
1033
  callback(error);
1014
1034
  return;
1015
1035
  }
1016
- this.#findGroupCoordinator([options.groupId], (error, response) => {
1036
+ this.#findCoordinator({ keyType: FindCoordinatorKeyTypes.GROUP, keys: [options.groupId] }, (error, coordinators) => {
1017
1037
  if (error) {
1018
1038
  callback(new MultipleErrors('Deleting consumer group offsets failed.', [error]));
1019
1039
  return;
1020
1040
  }
1021
- const coordinator = response.coordinators.find(c => c.key === options.groupId);
1041
+ const coordinator = coordinators.find(c => c.key === options.groupId);
1022
1042
  /* c8 ignore next 9 - Hard to test */
1023
1043
  if (!coordinator) {
1024
1044
  callback(new MultipleErrors('Deleting consumer group offsets failed.', [
@@ -1363,14 +1383,22 @@ export class Admin extends Base {
1363
1383
  callback(error);
1364
1384
  return;
1365
1385
  }
1386
+ // metadata must be defined at this point
1387
+ const { topics, brokers } = metadata;
1366
1388
  const requests = new Map();
1367
1389
  for (const topic of options.topics) {
1368
1390
  for (const partition of topic.partitions) {
1369
- const { leader, leaderEpoch } = metadata.topics.get(topic.name).partitions[partition.partitionIndex];
1370
- let leaderRequests = requests.get(leader);
1391
+ // topics.get(topic.name) must be defined as the metadata request was successful
1392
+ const topicData = topics.get(topic.name);
1393
+ const targetPartitionData = topicData.partitions[partition.partitionIndex];
1394
+ if (!targetPartitionData) {
1395
+ callback(new UserError(`Unknown partition ${partition.partitionIndex} for topic ${topic.name}.`));
1396
+ return;
1397
+ }
1398
+ let leaderRequests = requests.get(targetPartitionData.leader);
1371
1399
  if (!leaderRequests) {
1372
1400
  leaderRequests = [];
1373
- requests.set(leader, leaderRequests);
1401
+ requests.set(targetPartitionData.leader, leaderRequests);
1374
1402
  }
1375
1403
  let topicRequest = leaderRequests.find(t => t.name === topic.name);
1376
1404
  if (!topicRequest) {
@@ -1379,14 +1407,14 @@ export class Admin extends Base {
1379
1407
  }
1380
1408
  topicRequest.partitions.push({
1381
1409
  partitionIndex: partition.partitionIndex,
1382
- currentLeaderEpoch: leaderEpoch,
1410
+ currentLeaderEpoch: targetPartitionData.leaderEpoch,
1383
1411
  /* c8 ignore next - Hard to test */
1384
1412
  timestamp: partition.timestamp ?? -1n
1385
1413
  });
1386
1414
  }
1387
1415
  }
1388
1416
  runConcurrentCallbacks('Listing offsets failed.', requests, ([leader, requests], concurrentCallback) => {
1389
- this[kGetConnection](metadata.brokers.get(leader), (error, connection) => {
1417
+ this[kGetConnection](brokers.get(leader), (error, connection) => {
1390
1418
  if (error) {
1391
1419
  concurrentCallback(error);
1392
1420
  return;
@@ -886,6 +886,25 @@ export declare const adminListOffsetsOptionsSchema: {
886
886
  required: string[];
887
887
  additionalProperties: boolean;
888
888
  };
889
+ export declare const findCoordinatorOptionsSchema: {
890
+ type: string;
891
+ properties: {
892
+ keyType: {
893
+ type: string;
894
+ enum: (0 | 1 | 2)[];
895
+ };
896
+ keys: {
897
+ type: string;
898
+ items: {
899
+ type: string;
900
+ pattern: string;
901
+ };
902
+ minItems: number;
903
+ };
904
+ };
905
+ required: string[];
906
+ additionalProperties: boolean;
907
+ };
889
908
  export declare const createTopicsOptionsValidator: import("ajv").ValidateFunction<{
890
909
  [x: string]: {};
891
910
  }>;
@@ -948,3 +967,6 @@ export declare const deleteAclsOptionsValidator: import("ajv").ValidateFunction<
948
967
  export declare const adminListOffsetsOptionsValidator: import("ajv").ValidateFunction<{
949
968
  [x: string]: {};
950
969
  }>;
970
+ export declare const findCoordinatorOptionsValidator: import("ajv").ValidateFunction<{
971
+ [x: string]: {};
972
+ }>;
@@ -1,4 +1,4 @@
1
- import { allowedAclOperations, allowedAclPermissionTypes, allowedClientQuotaMatchTypes, allowedFetchIsolationLevels, allowedIncrementalAlterConfigOperationTypes, allowedResourcePatternTypes, allowedResourceTypes, ConsumerGroupStates, IncrementalAlterConfigOperationTypes } from "../../apis/enumerations.js";
1
+ import { allowedAclOperations, allowedAclPermissionTypes, allowedClientQuotaMatchTypes, allowedFetchIsolationLevels, allowedFindCoordinatorKeyTypes, allowedIncrementalAlterConfigOperationTypes, allowedResourcePatternTypes, allowedResourceTypes, ConsumerGroupStates, IncrementalAlterConfigOperationTypes } from "../../apis/enumerations.js";
2
2
  import { ajv, listErrorMessage } from "../../utils.js";
3
3
  import { idProperty } from "../base/options.js";
4
4
  export const groupsProperties = {
@@ -587,6 +587,19 @@ export const adminListOffsetsOptionsSchema = {
587
587
  required: ['topics'],
588
588
  additionalProperties: false
589
589
  };
590
+ export const findCoordinatorOptionsSchema = {
591
+ type: 'object',
592
+ properties: {
593
+ keyType: { type: 'number', enum: [...allowedFindCoordinatorKeyTypes] },
594
+ keys: {
595
+ type: 'array',
596
+ items: idProperty,
597
+ minItems: 1
598
+ }
599
+ },
600
+ required: ['keyType', 'keys'],
601
+ additionalProperties: false
602
+ };
590
603
  export const createTopicsOptionsValidator = ajv.compile(createTopicOptionsSchema);
591
604
  export const createPartitionsOptionsValidator = ajv.compile(createPartitionsOptionsSchema);
592
605
  export const listTopicsOptionsValidator = ajv.compile(listTopicOptionsSchema);
@@ -609,3 +622,4 @@ export const createAclsOptionsValidator = ajv.compile(createAclsOptionsSchema);
609
622
  export const describeAclsOptionsValidator = ajv.compile(describeAclsOptionsSchema);
610
623
  export const deleteAclsOptionsValidator = ajv.compile(deleteAclsOptionsSchema);
611
624
  export const adminListOffsetsOptionsValidator = ajv.compile(adminListOffsetsOptionsSchema);
625
+ export const findCoordinatorOptionsValidator = ajv.compile(findCoordinatorOptionsSchema);
@@ -6,7 +6,7 @@ import { type DescribeClientQuotasRequestComponent } from '../../apis/admin/desc
6
6
  import { type DescribeConfigsRequestResource, type DescribeConfigsResponseConfig } from '../../apis/admin/describe-configs-v4.ts';
7
7
  import { type DescribeLogDirsRequestTopic, type DescribeLogDirsResponse, type DescribeLogDirsResponseResult } from '../../apis/admin/describe-log-dirs-v4.ts';
8
8
  import { type IncrementalAlterConfigsRequestResource } from '../../apis/admin/incremental-alter-configs-v1.ts';
9
- import { type ConfigResourceTypeValue, type ConsumerGroupStateValue, type FetchIsolationLevelValue } from '../../apis/enumerations.ts';
9
+ import { type ConfigResourceTypeValue, type ConsumerGroupStateValue, type FetchIsolationLevelValue, type FindCoordinatorKeyTypeValue } from '../../apis/enumerations.ts';
10
10
  import { type Acl, type AclFilter } from '../../apis/types.ts';
11
11
  import { type Nullable, type NullableString } from '../../protocol/definitions.ts';
12
12
  import { type BaseOptions } from '../base/types.ts';
@@ -68,6 +68,16 @@ export interface DescribeGroupsOptions {
68
68
  groups: string[];
69
69
  includeAuthorizedOperations?: boolean;
70
70
  }
71
+ export interface FindCoordinatorOptions {
72
+ keyType: FindCoordinatorKeyTypeValue;
73
+ keys: string[];
74
+ }
75
+ export interface FindCoordinatorResult {
76
+ key: string;
77
+ nodeId: number;
78
+ host: string;
79
+ port: number;
80
+ }
71
81
  export interface DeleteGroupsOptions {
72
82
  groups: string[];
73
83
  }
@@ -30,6 +30,7 @@ export declare const kFormatValidationErrors: unique symbol;
30
30
  export declare const kPrometheus: unique symbol;
31
31
  export declare const kClientType: unique symbol;
32
32
  export declare const kAfterCreate: unique symbol;
33
+ export declare const kContext: unique symbol;
33
34
  export interface BaseEvents extends TypedEvents {
34
35
  'client:broker:connect': (payload: ConnectionPoolEventPayload) => void;
35
36
  'client:broker:disconnect': (payload: ConnectionPoolEventPayload) => void;
@@ -52,6 +53,7 @@ export declare class Base<OptionsType extends BaseOptions = BaseOptions, EventsT
52
53
  [kInstance]: number;
53
54
  [kClientId]: string;
54
55
  [kClientType]: ClientType;
56
+ [kContext]: unknown;
55
57
  [kBootstrapBrokers]: Broker[];
56
58
  [kApis]: ApiVersionsResponseApi[];
57
59
  [kOptions]: OptionsType;
@@ -63,6 +65,7 @@ export declare class Base<OptionsType extends BaseOptions = BaseOptions, EventsT
63
65
  get clientId(): string;
64
66
  get closed(): boolean;
65
67
  get type(): ClientType;
68
+ get context(): unknown;
66
69
  emitWithDebug(section: string | null, name: string, ...args: any[]): boolean;
67
70
  close(callback: CallbackWithPromise<void>): void;
68
71
  close(): Promise<void>;
@@ -74,7 +77,7 @@ export declare class Base<OptionsType extends BaseOptions = BaseOptions, EventsT
74
77
  connectToBrokers(nodeIds?: number[] | null): Promise<Map<number, Connection>>;
75
78
  isActive(): boolean;
76
79
  isConnected(): boolean;
77
- [kCreateConnectionPool](): ConnectionPool;
80
+ [kCreateConnectionPool](context?: unknown): ConnectionPool;
78
81
  [kListApis](callback: CallbackWithPromise<ApiVersionsResponseApi[]>): void;
79
82
  [kMetadata](options: MetadataOptions, callback: CallbackWithPromise<ClusterMetadata>): void;
80
83
  [kCheckNotClosed](callback: CallbackWithPromise<any>): boolean;
@@ -30,6 +30,7 @@ export const kFormatValidationErrors = Symbol('plt.kafka.base.formatValidationEr
30
30
  export const kPrometheus = Symbol('plt.kafka.base.prometheus');
31
31
  export const kClientType = Symbol('plt.kafka.base.clientType');
32
32
  export const kAfterCreate = Symbol('plt.kafka.base.afterCreate');
33
+ export const kContext = Symbol('plt.kafka.base.context');
33
34
  let currentInstance = 0;
34
35
  export class Base extends TypedEventEmitter {
35
36
  // This is declared using a symbol (a.k.a protected/friend) to make it available in ConnectionPool and MessagesStream
@@ -37,6 +38,7 @@ export class Base extends TypedEventEmitter {
37
38
  // General status - Use symbols rather than JS private property to make them "protected" as in C++
38
39
  [kClientId];
39
40
  [kClientType];
41
+ [kContext];
40
42
  [kBootstrapBrokers];
41
43
  [kApis];
42
44
  [kOptions];
@@ -51,6 +53,7 @@ export class Base extends TypedEventEmitter {
51
53
  this[kClientType] = 'base';
52
54
  this[kInstance] = currentInstance++;
53
55
  this[kApis] = [];
56
+ this[kContext] = options.context;
54
57
  // Validate options
55
58
  this[kOptions] = Object.assign({}, defaultBaseOptions, options);
56
59
  this[kValidateOptions](this[kOptions], baseOptionsValidator, '/options');
@@ -84,6 +87,9 @@ export class Base extends TypedEventEmitter {
84
87
  get type() {
85
88
  return this[kClientType];
86
89
  }
90
+ get context() {
91
+ return this[kContext];
92
+ }
87
93
  emitWithDebug(section, name, ...args) {
88
94
  if (!section) {
89
95
  return this.emit(name, ...args);
@@ -170,10 +176,11 @@ export class Base extends TypedEventEmitter {
170
176
  }
171
177
  return this[kConnections].isConnected();
172
178
  }
173
- [kCreateConnectionPool]() {
179
+ [kCreateConnectionPool](context) {
174
180
  const pool = new ConnectionPool(this[kClientId], {
181
+ ...this[kOptions],
175
182
  ownerId: this[kInstance],
176
- ...this[kOptions]
183
+ context: context ?? this[kContext]
177
184
  });
178
185
  this.#forwardEvents(pool, [
179
186
  'connect',
@@ -388,6 +395,14 @@ export class Base extends TypedEventEmitter {
388
395
  });
389
396
  }, (error, metadata) => {
390
397
  if (error) {
398
+ const unknownTopicError = error.findBy('apiCode', 3);
399
+ if (unknownTopicError) {
400
+ const topicIndexMatch = unknownTopicError.path?.match(/\/topics\/(\d+)/);
401
+ const topicIndex = topicIndexMatch ? parseInt(topicIndexMatch[1]) : -1;
402
+ const topicName = topicIndex >= 0 && topicIndex < topicsToFetch.length ? topicsToFetch[topicIndex] : 'unknown';
403
+ deduplicateCallback(new UserError(`Unknown topic ${topicName}.`));
404
+ return;
405
+ }
391
406
  const hasStaleMetadata = error.findBy('hasStaleMetadata', true);
392
407
  // Stale metadata, we need to fetch everything again
393
408
  if (hasStaleMetadata) {
@@ -1,3 +1,3 @@
1
- export { Base, kCheckNotClosed, kGetApi, kGetBootstrapConnection, kGetConnection, kListApis, kMetadata, kOptions, kPerformDeduplicated, kPerformWithRetry, kValidateOptions } from './base.ts';
1
+ export { Base, kContext, kCheckNotClosed, kGetApi, kGetBootstrapConnection, kGetConnection, kListApis, kMetadata, kOptions, kPerformDeduplicated, kPerformWithRetry, kValidateOptions } from './base.ts';
2
2
  export * from './options.ts';
3
3
  export * from './types.ts';
@@ -1,3 +1,3 @@
1
- export { Base, kCheckNotClosed, kGetApi, kGetBootstrapConnection, kGetConnection, kListApis, kMetadata, kOptions, kPerformDeduplicated, kPerformWithRetry, kValidateOptions } from "./base.js";
1
+ export { Base, kContext, kCheckNotClosed, kGetApi, kGetBootstrapConnection, kGetConnection, kListApis, kMetadata, kOptions, kPerformDeduplicated, kPerformWithRetry, kValidateOptions } from "./base.js";
2
2
  export * from "./options.js";
3
3
  export * from "./types.js";
@@ -157,6 +157,7 @@ export declare const baseOptionsSchema: {
157
157
  required: string[];
158
158
  additionalProperties: boolean;
159
159
  };
160
+ context: boolean;
160
161
  metadataMaxAge: {
161
162
  type: string;
162
163
  minimum: number;
@@ -51,6 +51,7 @@ export const baseOptionsSchema = {
51
51
  required: ['mechanism'],
52
52
  additionalProperties: false
53
53
  },
54
+ context: true,
54
55
  metadataMaxAge: { type: 'number', minimum: 0 },
55
56
  autocreateTopics: { type: 'boolean' },
56
57
  strict: { type: 'boolean' },
@@ -33,6 +33,7 @@ export type RetryDelayGetter<Owner = object> = (client: Owner, operationId: stri
33
33
  export interface BaseOptions extends ConnectionOptions {
34
34
  clientId: string;
35
35
  bootstrapBrokers: Broker[] | string[];
36
+ context?: unknown;
36
37
  timeout?: number;
37
38
  retries?: number | boolean;
38
39
  retryDelay?: number | RetryDelayGetter;
@@ -1,6 +1,7 @@
1
1
  import { type CallbackWithPromise } from '../../apis/callbacks.ts';
2
2
  import { type FetchResponse } from '../../apis/consumer/fetch-v17.ts';
3
- import { Base, type BaseEvents } from '../base/base.ts';
3
+ import { type ConnectionPool } from '../../network/connection-pool.ts';
4
+ import { Base, type BaseEvents, kCreateConnectionPool } from '../base/base.ts';
4
5
  import { MessagesStream } from './messages-stream.ts';
5
6
  import { TopicsMap } from './topics-map.ts';
6
7
  import { type CommitOptions, type ConsumeOptions, type ConsumerGroupJoinPayload, type ConsumerGroupLeavePayload, type ConsumerGroupRebalancePayload, type ConsumerHeartbeatErrorPayload, type ConsumerHeartbeatPayload, type ConsumerOptions, type FetchOptions, type GetLagOptions, type GroupAssignment, type GroupOptions, type ListCommitsOptions, type ListOffsetsOptions, type Offsets, type OffsetsWithTimestamps } from './types.ts';
@@ -28,6 +29,7 @@ export declare class Consumer<Key = Buffer, Value = Buffer, HeaderKey = Buffer,
28
29
  get streamsCount(): number;
29
30
  get lastHeartbeat(): Date | null;
30
31
  get coordinatorId(): number | null;
32
+ get streamContext(): unknown;
31
33
  close(force: boolean | CallbackWithPromise<void>, callback?: CallbackWithPromise<void>): void;
32
34
  close(force?: boolean): Promise<void>;
33
35
  isActive(): boolean;
@@ -49,6 +51,7 @@ export declare class Consumer<Key = Buffer, Value = Buffer, HeaderKey = Buffer,
49
51
  stopLagMonitoring(): void;
50
52
  findGroupCoordinator(callback: CallbackWithPromise<number>): void;
51
53
  findGroupCoordinator(): Promise<number>;
54
+ [kCreateConnectionPool](context?: unknown): ConnectionPool;
52
55
  joinGroup(options: GroupOptions, callback: CallbackWithPromise<string>): void;
53
56
  joinGroup(options?: GroupOptions): Promise<string>;
54
57
  leaveGroup(force?: boolean | CallbackWithPromise<void>, callback?: CallbackWithPromise<void>): void;
@@ -9,7 +9,7 @@ import { IS_CONTROL } from "../../protocol/records.js";
9
9
  import { Writer } from "../../protocol/writer.js";
10
10
  import { kAutocommit, kRefreshOffsetsAndFetch } from "../../symbols.js";
11
11
  import { emitExperimentalApiWarning } from "../../utils.js";
12
- import { Base, kAfterCreate, kCheckNotClosed, kClosed, kConnections, kFormatValidationErrors, kGetApi, kGetBootstrapConnection, kGetConnection, kMetadata, kOptions, kPerformDeduplicated, kPerformWithRetry, kPrometheus, kValidateOptions } from "../base/base.js";
12
+ import { Base, kAfterCreate, kCheckNotClosed, kClosed, kConnections, kCreateConnectionPool, kFormatValidationErrors, kGetApi, kGetBootstrapConnection, kGetConnection, kMetadata, kOptions, kPerformDeduplicated, kPerformWithRetry, kPrometheus, kValidateOptions } from "../base/base.js";
13
13
  import { ensureMetric } from "../metrics.js";
14
14
  import { MessagesStream } from "./messages-stream.js";
15
15
  import { commitOptionsValidator, consumeOptionsValidator, consumerOptionsValidator, defaultConsumerOptions, fetchOptionsValidator, getLagOptionsValidator, groupIdAndOptionsValidator, groupOptionsValidator, listCommitsOptionsValidator, listOffsetsOptionsValidator } from "./options.js";
@@ -36,6 +36,7 @@ export class Consumer extends Base {
36
36
  #groupRemoteAssignor;
37
37
  #streams;
38
38
  #lagMonitoring;
39
+ #streamContext;
39
40
  // Metrics
40
41
  #metricActiveStreams;
41
42
  #metricLags;
@@ -77,6 +78,7 @@ export class Consumer extends Base {
77
78
  this.#memberEpoch = 0;
78
79
  this.#useConsumerGroupProtocol = this[kOptions].groupProtocol === 'consumer';
79
80
  this.#groupRemoteAssignor = this[kOptions].groupRemoteAssignor ?? null;
81
+ this.#streamContext = options.streamContext ?? options.context;
80
82
  this.#validateGroupOptions(this[kOptions], groupIdAndOptionsValidator);
81
83
  if (this[kPrometheus]) {
82
84
  ensureMetric(this[kPrometheus], 'Gauge', 'kafka_consumers', 'Number of active Kafka consumers').inc();
@@ -95,6 +97,9 @@ export class Consumer extends Base {
95
97
  get coordinatorId() {
96
98
  return this.#coordinatorId;
97
99
  }
100
+ get streamContext() {
101
+ return this.#streamContext;
102
+ }
98
103
  close(force, callback) {
99
104
  if (typeof force === 'function') {
100
105
  callback = force;
@@ -170,6 +175,7 @@ export class Consumer extends Base {
170
175
  options.highWaterMark ??= this[kOptions].highWaterMark;
171
176
  options.registry ??= this[kOptions].registry;
172
177
  options.beforeDeserialization ??= this[kOptions].beforeDeserialization;
178
+ options.context ??= this.#streamContext;
173
179
  if (options.beforeDeserialization) {
174
180
  emitExperimentalApiWarning('beforeDeserialization');
175
181
  }
@@ -354,6 +360,9 @@ export class Consumer extends Base {
354
360
  this.#findGroupCoordinator(callback);
355
361
  return callback[kCallbackPromise];
356
362
  }
363
+ [kCreateConnectionPool](context) {
364
+ return super[kCreateConnectionPool](context);
365
+ }
357
366
  joinGroup(options, callback) {
358
367
  if (!callback) {
359
368
  callback = createPromisifiedCallback();
@@ -444,7 +453,21 @@ export class Consumer extends Base {
444
453
  retryCallback(error);
445
454
  return;
446
455
  }
447
- api(connection, options.maxWaitTime ?? this[kOptions].maxWaitTime, options.minBytes ?? this[kOptions].minBytes, options.maxBytes ?? this[kOptions].maxBytes, isolationLevel, 0, 0, options.topics, [], '', retryCallback);
456
+ api(connection, options.maxWaitTime ?? this[kOptions].maxWaitTime, options.minBytes ?? this[kOptions].minBytes, options.maxBytes ?? this[kOptions].maxBytes, isolationLevel, 0, 0, options.topics, [], '', (error, result) => {
457
+ if (error) {
458
+ const genericError = error;
459
+ if (genericError.findBy?.('apiId', 'FENCED_LEADER_EPOCH')) {
460
+ this.clearMetadata();
461
+ for (const topic of options.topics) {
462
+ for (const partition of topic.partitions) {
463
+ partition.currentLeaderEpoch = -1;
464
+ partition.lastFetchedEpoch = -1;
465
+ }
466
+ }
467
+ }
468
+ }
469
+ retryCallback(error, result);
470
+ });
448
471
  });
449
472
  });
450
473
  });
@@ -814,6 +837,10 @@ export class Consumer extends Base {
814
837
  }
815
838
  #updateAssignments(newAssignments, callback) {
816
839
  this[kMetadata]({ topics: this.topics.current }, (error, metadata) => {
840
+ if (!this.#membershipActive) {
841
+ callback(null);
842
+ return;
843
+ }
817
844
  if (error) {
818
845
  callback(error);
819
846
  return;
@@ -15,6 +15,7 @@ export declare class MessagesStream<Key, Value, HeaderKey, HeaderValue> extends
15
15
  constructor(consumer: Consumer<Key, Value, HeaderKey, HeaderValue>, options: ConsumeOptions<Key, Value, HeaderKey, HeaderValue>);
16
16
  get consumer(): Consumer<Key, Value, HeaderKey, HeaderValue>;
17
17
  get offsetsToFetch(): Map<string, bigint>;
18
+ get context(): unknown;
18
19
  get offsetsToCommit(): Map<string, CommitOptionsPartition>;
19
20
  get offsetsCommitted(): Map<string, bigint>;
20
21
  get committedOffsets(): Map<string, bigint>;