@platformatic/kafka 1.6.0 → 1.8.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.
- package/README.md +4 -0
- package/dist/clients/admin/admin.d.ts +3 -1
- package/dist/clients/admin/admin.js +53 -1
- package/dist/clients/admin/options.d.ts +15 -2
- package/dist/clients/admin/options.js +12 -2
- package/dist/clients/admin/types.d.ts +3 -0
- package/dist/clients/base/base.js +42 -16
- package/dist/clients/base/index.d.ts +1 -1
- package/dist/clients/base/index.js +1 -1
- package/dist/clients/base/options.d.ts +5 -0
- package/dist/clients/base/options.js +1 -0
- package/dist/clients/base/types.d.ts +1 -0
- package/dist/clients/consumer/consumer.d.ts +3 -1
- package/dist/clients/consumer/consumer.js +58 -40
- package/dist/clients/consumer/options.d.ts +25 -0
- package/dist/clients/consumer/options.js +9 -1
- package/dist/clients/consumer/partitions-assigners.d.ts +3 -0
- package/dist/clients/consumer/partitions-assigners.js +23 -0
- package/dist/clients/consumer/types.d.ts +12 -1
- package/dist/clients/producer/options.d.ts +24 -12
- package/dist/clients/producer/options.js +8 -4
- package/dist/clients/producer/producer.d.ts +1 -0
- package/dist/clients/producer/producer.js +1 -1
- package/dist/network/connection.d.ts +1 -0
- package/dist/network/connection.js +6 -2
- package/dist/protocol/compression.js +1 -1
- package/dist/protocol/reader.js +4 -1
- package/dist/utils.d.ts +7 -0
- package/dist/utils.js +11 -3
- package/package.json +3 -2
package/README.md
CHANGED
|
@@ -224,6 +224,10 @@ await consumer.close()
|
|
|
224
224
|
await producer.close()
|
|
225
225
|
```
|
|
226
226
|
|
|
227
|
+
### Avro
|
|
228
|
+
|
|
229
|
+
An example on how to use [Avro]() for (de)serialisation can be found in the [examples](./examples) folder.
|
|
230
|
+
|
|
227
231
|
### Error Handling
|
|
228
232
|
|
|
229
233
|
`@platformatic/kafka` defines its hierarchy of errors.
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import { type CallbackWithPromise } from '../../apis/callbacks.ts';
|
|
2
2
|
import { type Callback } from '../../apis/definitions.ts';
|
|
3
3
|
import { Base } from '../base/base.ts';
|
|
4
|
-
import { type AdminOptions, type CreatedTopic, type CreateTopicsOptions, type DeleteGroupsOptions, type DeleteTopicsOptions, type DescribeGroupsOptions, type Group, type GroupBase, type ListGroupsOptions } from './types.ts';
|
|
4
|
+
import { type AdminOptions, type CreatedTopic, type CreateTopicsOptions, type DeleteGroupsOptions, type DeleteTopicsOptions, type DescribeGroupsOptions, type Group, type GroupBase, type ListGroupsOptions, type ListTopicsOptions } from './types.ts';
|
|
5
5
|
export declare class Admin extends Base<AdminOptions> {
|
|
6
6
|
#private;
|
|
7
7
|
constructor(options: AdminOptions);
|
|
8
|
+
listTopics(options: ListTopicsOptions, callback: Callback<string[]>): void;
|
|
9
|
+
listTopics(options?: ListTopicsOptions): Promise<string[]>;
|
|
8
10
|
createTopics(options: CreateTopicsOptions, callback: Callback<CreatedTopic[]>): void;
|
|
9
11
|
createTopics(options: CreateTopicsOptions): Promise<CreatedTopic[]>;
|
|
10
12
|
deleteTopics(options: DeleteTopicsOptions, callback: CallbackWithPromise<void>): void;
|
|
@@ -3,12 +3,30 @@ import { FindCoordinatorKeyTypes } from "../../apis/enumerations.js";
|
|
|
3
3
|
import { adminGroupsChannel, adminTopicsChannel, createDiagnosticContext } from "../../diagnostic.js";
|
|
4
4
|
import { Reader } from "../../protocol/reader.js";
|
|
5
5
|
import { Base, kAfterCreate, kCheckNotClosed, kGetApi, kGetBootstrapConnection, kGetConnection, kMetadata, kOptions, kPerformDeduplicated, kPerformWithRetry, kValidateOptions } from "../base/base.js";
|
|
6
|
-
import { createTopicsOptionsValidator, deleteGroupsOptionsValidator, deleteTopicsOptionsValidator, describeGroupsOptionsValidator, listGroupsOptionsValidator } from "./options.js";
|
|
6
|
+
import { createTopicsOptionsValidator, deleteGroupsOptionsValidator, deleteTopicsOptionsValidator, describeGroupsOptionsValidator, listGroupsOptionsValidator, listTopicsOptionsValidator } from "./options.js";
|
|
7
7
|
export class Admin extends Base {
|
|
8
8
|
constructor(options) {
|
|
9
9
|
super(options);
|
|
10
10
|
this[kAfterCreate]('admin');
|
|
11
11
|
}
|
|
12
|
+
listTopics(options, callback) {
|
|
13
|
+
if (!callback) {
|
|
14
|
+
callback = createPromisifiedCallback();
|
|
15
|
+
}
|
|
16
|
+
if (this[kCheckNotClosed](callback)) {
|
|
17
|
+
return callback[kCallbackPromise];
|
|
18
|
+
}
|
|
19
|
+
if (!options) {
|
|
20
|
+
options = {};
|
|
21
|
+
}
|
|
22
|
+
const validationError = this[kValidateOptions](options, listTopicsOptionsValidator, '/options', false);
|
|
23
|
+
if (validationError) {
|
|
24
|
+
callback(validationError, undefined);
|
|
25
|
+
return callback[kCallbackPromise];
|
|
26
|
+
}
|
|
27
|
+
adminTopicsChannel.traceCallback(this.#listTopics, 1, createDiagnosticContext({ client: this, operation: 'listTopics', options }), this, options, callback);
|
|
28
|
+
return callback[kCallbackPromise];
|
|
29
|
+
}
|
|
12
30
|
createTopics(options, callback) {
|
|
13
31
|
if (!callback) {
|
|
14
32
|
callback = createPromisifiedCallback();
|
|
@@ -88,6 +106,40 @@ export class Admin extends Base {
|
|
|
88
106
|
adminGroupsChannel.traceCallback(this.#deleteGroups, 1, createDiagnosticContext({ client: this, operation: 'deleteGroups', options }), this, options, callback);
|
|
89
107
|
return callback[kCallbackPromise];
|
|
90
108
|
}
|
|
109
|
+
#listTopics(options, callback) {
|
|
110
|
+
const includeInternals = options.includeInternals ?? false;
|
|
111
|
+
this[kPerformDeduplicated]('metadata', deduplicateCallback => {
|
|
112
|
+
this[kPerformWithRetry]('metadata', retryCallback => {
|
|
113
|
+
this[kGetBootstrapConnection]((error, connection) => {
|
|
114
|
+
if (error) {
|
|
115
|
+
retryCallback(error, undefined);
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
this[kGetApi]('Metadata', (error, api) => {
|
|
119
|
+
if (error) {
|
|
120
|
+
retryCallback(error, undefined);
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
api(connection, null, false, false, retryCallback);
|
|
124
|
+
});
|
|
125
|
+
});
|
|
126
|
+
}, (error, metadata) => {
|
|
127
|
+
if (error) {
|
|
128
|
+
deduplicateCallback(error, undefined);
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
const topics = new Set();
|
|
132
|
+
for (const { name, isInternal } of metadata.topics) {
|
|
133
|
+
/* c8 ignore next 3 - Sometimes internal topics might be returned by Kafka */
|
|
134
|
+
if (isInternal && !includeInternals) {
|
|
135
|
+
continue;
|
|
136
|
+
}
|
|
137
|
+
topics.add(name);
|
|
138
|
+
}
|
|
139
|
+
deduplicateCallback(null, Array.from(topics).sort());
|
|
140
|
+
}, 0);
|
|
141
|
+
}, callback);
|
|
142
|
+
}
|
|
91
143
|
#createTopics(options, callback) {
|
|
92
144
|
const numPartitions = options.partitions ?? 1;
|
|
93
145
|
const replicationFactor = options.replicas ?? 1;
|
|
@@ -50,6 +50,16 @@ export declare const createTopicOptionsSchema: {
|
|
|
50
50
|
required: string[];
|
|
51
51
|
additionalProperties: boolean;
|
|
52
52
|
};
|
|
53
|
+
export declare const listTopicOptionsSchema: {
|
|
54
|
+
type: string;
|
|
55
|
+
properties: {
|
|
56
|
+
includeInternals: {
|
|
57
|
+
type: string;
|
|
58
|
+
default: boolean;
|
|
59
|
+
};
|
|
60
|
+
};
|
|
61
|
+
additionalProperties: boolean;
|
|
62
|
+
};
|
|
53
63
|
export declare const deleteTopicOptionsSchema: {
|
|
54
64
|
type: string;
|
|
55
65
|
properties: {
|
|
@@ -71,8 +81,10 @@ export declare const listGroupsOptionsSchema: {
|
|
|
71
81
|
type: string;
|
|
72
82
|
items: {
|
|
73
83
|
type: string;
|
|
74
|
-
|
|
75
|
-
|
|
84
|
+
enumeration: {
|
|
85
|
+
allowed: readonly ["PREPARING_REBALANCE", "COMPLETING_REBALANCE", "STABLE", "DEAD", "EMPTY"];
|
|
86
|
+
errorMessage: string;
|
|
87
|
+
};
|
|
76
88
|
};
|
|
77
89
|
minItems: number;
|
|
78
90
|
};
|
|
@@ -123,6 +135,7 @@ export declare const deleteGroupsOptionsSchema: {
|
|
|
123
135
|
export declare const createTopicsOptionsValidator: import("ajv").ValidateFunction<{
|
|
124
136
|
[x: string]: {};
|
|
125
137
|
}>;
|
|
138
|
+
export declare const listTopicsOptionsValidator: import("ajv").ValidateFunction<unknown>;
|
|
126
139
|
export declare const deleteTopicsOptionsValidator: import("ajv").ValidateFunction<{
|
|
127
140
|
[x: string]: {};
|
|
128
141
|
}>;
|
|
@@ -31,6 +31,13 @@ export const createTopicOptionsSchema = {
|
|
|
31
31
|
required: ['topics'],
|
|
32
32
|
additionalProperties: false
|
|
33
33
|
};
|
|
34
|
+
export const listTopicOptionsSchema = {
|
|
35
|
+
type: 'object',
|
|
36
|
+
properties: {
|
|
37
|
+
includeInternals: { type: 'boolean', default: false }
|
|
38
|
+
},
|
|
39
|
+
additionalProperties: false
|
|
40
|
+
};
|
|
34
41
|
export const deleteTopicOptionsSchema = {
|
|
35
42
|
type: 'object',
|
|
36
43
|
properties: {
|
|
@@ -46,8 +53,10 @@ export const listGroupsOptionsSchema = {
|
|
|
46
53
|
type: 'array',
|
|
47
54
|
items: {
|
|
48
55
|
type: 'string',
|
|
49
|
-
|
|
50
|
-
|
|
56
|
+
enumeration: {
|
|
57
|
+
allowed: ConsumerGroupStates,
|
|
58
|
+
errorMessage: listErrorMessage(ConsumerGroupStates)
|
|
59
|
+
}
|
|
51
60
|
},
|
|
52
61
|
minItems: 0
|
|
53
62
|
},
|
|
@@ -75,6 +84,7 @@ export const deleteGroupsOptionsSchema = {
|
|
|
75
84
|
additionalProperties: false
|
|
76
85
|
};
|
|
77
86
|
export const createTopicsOptionsValidator = ajv.compile(createTopicOptionsSchema);
|
|
87
|
+
export const listTopicsOptionsValidator = ajv.compile(listTopicOptionsSchema);
|
|
78
88
|
export const deleteTopicsOptionsValidator = ajv.compile(deleteTopicOptionsSchema);
|
|
79
89
|
export const listGroupsOptionsValidator = ajv.compile(listGroupsOptionsSchema);
|
|
80
90
|
export const describeGroupsOptionsValidator = ajv.compile(describeGroupsOptionsSchema);
|
|
@@ -148,13 +148,26 @@ export class Base extends EventEmitter {
|
|
|
148
148
|
}, callback);
|
|
149
149
|
}
|
|
150
150
|
[kMetadata](options, callback) {
|
|
151
|
-
const
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
options.topics
|
|
156
|
-
|
|
157
|
-
|
|
151
|
+
const expiralDate = Date.now() - (options.metadataMaxAge ?? this[kOptions].metadataMaxAge);
|
|
152
|
+
let topicsToFetch = [];
|
|
153
|
+
// Determine which topics we need to fetch
|
|
154
|
+
if (!this.#metadata || options.forceUpdate) {
|
|
155
|
+
topicsToFetch = options.topics;
|
|
156
|
+
}
|
|
157
|
+
else {
|
|
158
|
+
for (const topic of options.topics) {
|
|
159
|
+
const existingTopic = this.#metadata.topics.get(topic);
|
|
160
|
+
if (!existingTopic || existingTopic.lastUpdate < expiralDate) {
|
|
161
|
+
topicsToFetch.push(topic);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
// All topics are already up-to-date, simply return them
|
|
166
|
+
if (this.#metadata && !topicsToFetch.length) {
|
|
167
|
+
callback(null, {
|
|
168
|
+
...this.#metadata,
|
|
169
|
+
topics: new Map(options.topics.map(topic => [topic, this.#metadata.topics.get(topic)]))
|
|
170
|
+
});
|
|
158
171
|
return;
|
|
159
172
|
}
|
|
160
173
|
const autocreateTopics = options.autocreateTopics ?? this[kOptions].autocreateTopics;
|
|
@@ -178,12 +191,26 @@ export class Base extends EventEmitter {
|
|
|
178
191
|
deduplicateCallback(error, undefined);
|
|
179
192
|
return;
|
|
180
193
|
}
|
|
194
|
+
const lastUpdate = Date.now();
|
|
195
|
+
if (!this.#metadata) {
|
|
196
|
+
this.#metadata = {
|
|
197
|
+
id: metadata.clusterId,
|
|
198
|
+
brokers: new Map(),
|
|
199
|
+
topics: new Map(),
|
|
200
|
+
lastUpdate
|
|
201
|
+
};
|
|
202
|
+
}
|
|
203
|
+
else {
|
|
204
|
+
this.#metadata.lastUpdate = lastUpdate;
|
|
205
|
+
}
|
|
181
206
|
const brokers = new Map();
|
|
182
|
-
|
|
207
|
+
// This should never change, but we act defensively here
|
|
183
208
|
for (const broker of metadata.brokers) {
|
|
184
209
|
const { host, port } = broker;
|
|
185
210
|
brokers.set(broker.nodeId, { host, port });
|
|
186
211
|
}
|
|
212
|
+
this.#metadata.brokers = brokers;
|
|
213
|
+
// Update all the topics in the cache
|
|
187
214
|
for (const { name, topicId: id, partitions: rawPartitions, isInternal } of metadata.topics) {
|
|
188
215
|
/* c8 ignore next 3 - Sometimes internal topics might be returned by Kafka */
|
|
189
216
|
if (isInternal) {
|
|
@@ -197,16 +224,15 @@ export class Base extends EventEmitter {
|
|
|
197
224
|
replicas: rawPartition.replicaNodes
|
|
198
225
|
};
|
|
199
226
|
}
|
|
200
|
-
topics.set(name, { id, partitions, partitionsCount: rawPartitions.length });
|
|
227
|
+
this.#metadata.topics.set(name, { id, partitions, partitionsCount: rawPartitions.length, lastUpdate });
|
|
201
228
|
}
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
topics,
|
|
206
|
-
lastUpdate: Date.now()
|
|
229
|
+
// Now build the object to return
|
|
230
|
+
const updatedMetadata = {
|
|
231
|
+
...this.#metadata,
|
|
232
|
+
topics: new Map(options.topics.map(topic => [topic, this.#metadata.topics.get(topic)]))
|
|
207
233
|
};
|
|
208
|
-
this.emitWithDebug('client', 'metadata',
|
|
209
|
-
deduplicateCallback(null,
|
|
234
|
+
this.emitWithDebug('client', 'metadata', updatedMetadata);
|
|
235
|
+
deduplicateCallback(null, updatedMetadata);
|
|
210
236
|
}, 0);
|
|
211
237
|
}, callback);
|
|
212
238
|
}
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
export { Base } from './base.ts';
|
|
1
|
+
export { Base, kCheckNotClosed, kClearMetadata, kGetApi, kGetBootstrapConnection, kGetConnection, kListApis, kMetadata, kOptions, kParseBroker, 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 } from "./base.js";
|
|
1
|
+
export { Base, kCheckNotClosed, kClearMetadata, kGetApi, kGetBootstrapConnection, kGetConnection, kListApis, kMetadata, kOptions, kParseBroker, kPerformDeduplicated, kPerformWithRetry, kValidateOptions } from "./base.js";
|
|
2
2
|
export * from "./options.js";
|
|
3
3
|
export * from "./types.js";
|
|
@@ -33,6 +33,7 @@ export const baseOptionsSchema = {
|
|
|
33
33
|
retryDelay: { type: 'number', minimum: 0 },
|
|
34
34
|
maxInflights: { type: 'number', minimum: 0 },
|
|
35
35
|
tls: { type: 'object', additionalProperties: true }, // No validation as they come from Node.js
|
|
36
|
+
tlsServerName: { oneOf: [{ type: 'boolean' }, { type: 'string' }] },
|
|
36
37
|
sasl: {
|
|
37
38
|
type: 'object',
|
|
38
39
|
properties: {
|
|
@@ -4,7 +4,7 @@ import { type ConnectionPool } from '../../network/connection-pool.ts';
|
|
|
4
4
|
import { Base, kFetchConnections } from '../base/base.ts';
|
|
5
5
|
import { MessagesStream } from './messages-stream.ts';
|
|
6
6
|
import { TopicsMap } from './topics-map.ts';
|
|
7
|
-
import { type CommitOptions, type ConsumeOptions, type ConsumerOptions, type FetchOptions, type GroupAssignment, type GroupOptions, type ListCommitsOptions, type ListOffsetsOptions, type Offsets } from './types.ts';
|
|
7
|
+
import { type CommitOptions, type ConsumeOptions, type ConsumerOptions, type FetchOptions, type GroupAssignment, type GroupOptions, type ListCommitsOptions, type ListOffsetsOptions, type Offsets, type OffsetsWithTimestamps } from './types.ts';
|
|
8
8
|
export declare class Consumer<Key = Buffer, Value = Buffer, HeaderKey = Buffer, HeaderValue = Buffer> extends Base<ConsumerOptions<Key, Value, HeaderKey, HeaderValue>> {
|
|
9
9
|
#private;
|
|
10
10
|
groupId: string;
|
|
@@ -25,6 +25,8 @@ export declare class Consumer<Key = Buffer, Value = Buffer, HeaderKey = Buffer,
|
|
|
25
25
|
commit(options: CommitOptions): Promise<void>;
|
|
26
26
|
listOffsets(options: ListOffsetsOptions, callback: CallbackWithPromise<Offsets>): void;
|
|
27
27
|
listOffsets(options: ListOffsetsOptions): Promise<Offsets>;
|
|
28
|
+
listOffsetsWithTimestamps(options: ListOffsetsOptions, callback: CallbackWithPromise<OffsetsWithTimestamps>): void;
|
|
29
|
+
listOffsetsWithTimestamps(options: ListOffsetsOptions): Promise<OffsetsWithTimestamps>;
|
|
28
30
|
listCommittedOffsets(options: ListCommitsOptions, callback: CallbackWithPromise<Offsets>): void;
|
|
29
31
|
listCommittedOffsets(options: ListCommitsOptions): Promise<Offsets>;
|
|
30
32
|
findGroupCoordinator(callback: CallbackWithPromise<number>): void;
|
|
@@ -9,6 +9,7 @@ import { defaultBaseOptions } from "../base/options.js";
|
|
|
9
9
|
import { ensureMetric } from "../metrics.js";
|
|
10
10
|
import { MessagesStream } from "./messages-stream.js";
|
|
11
11
|
import { commitOptionsValidator, consumeOptionsValidator, consumerOptionsValidator, defaultConsumerOptions, fetchOptionsValidator, groupIdAndOptionsValidator, groupOptionsValidator, listCommitsOptionsValidator, listOffsetsOptionsValidator } from "./options.js";
|
|
12
|
+
import { roundRobinAssigner } from "./partitions-assigners.js";
|
|
12
13
|
import { TopicsMap } from "./topics-map.js";
|
|
13
14
|
export class Consumer extends Base {
|
|
14
15
|
groupId;
|
|
@@ -23,6 +24,7 @@ export class Consumer extends Base {
|
|
|
23
24
|
#coordinatorId;
|
|
24
25
|
#heartbeatInterval;
|
|
25
26
|
#streams;
|
|
27
|
+
#partitionsAssigner;
|
|
26
28
|
/*
|
|
27
29
|
The following requests are blocking in Kafka:
|
|
28
30
|
|
|
@@ -56,6 +58,7 @@ export class Consumer extends Base {
|
|
|
56
58
|
this.#coordinatorId = null;
|
|
57
59
|
this.#heartbeatInterval = null;
|
|
58
60
|
this.#streams = new Set();
|
|
61
|
+
this.#partitionsAssigner = this[kOptions].partitionAssigner ?? roundRobinAssigner;
|
|
59
62
|
this.#validateGroupOptions(this[kOptions], groupIdAndOptionsValidator);
|
|
60
63
|
// Initialize connection pool
|
|
61
64
|
this[kFetchConnections] = this[kCreateConnectionPool]();
|
|
@@ -176,7 +179,22 @@ export class Consumer extends Base {
|
|
|
176
179
|
callback(validationError, undefined);
|
|
177
180
|
return callback[kCallbackPromise];
|
|
178
181
|
}
|
|
179
|
-
consumerOffsetsChannel.traceCallback(this.#listOffsets,
|
|
182
|
+
consumerOffsetsChannel.traceCallback(this.#listOffsets, 2, createDiagnosticContext({ client: this, operation: 'listOffsets', options }), this, false, options, callback);
|
|
183
|
+
return callback[kCallbackPromise];
|
|
184
|
+
}
|
|
185
|
+
listOffsetsWithTimestamps(options, callback) {
|
|
186
|
+
if (!callback) {
|
|
187
|
+
callback = createPromisifiedCallback();
|
|
188
|
+
}
|
|
189
|
+
if (this[kCheckNotClosed](callback)) {
|
|
190
|
+
return callback[kCallbackPromise];
|
|
191
|
+
}
|
|
192
|
+
const validationError = this[kValidateOptions](options, listOffsetsOptionsValidator, '/options', false);
|
|
193
|
+
if (validationError) {
|
|
194
|
+
callback(validationError, undefined);
|
|
195
|
+
return callback[kCallbackPromise];
|
|
196
|
+
}
|
|
197
|
+
consumerOffsetsChannel.traceCallback(this.#listOffsets, 2, createDiagnosticContext({ client: this, operation: 'listOffsets', options }), this, true, options, callback);
|
|
180
198
|
return callback[kCallbackPromise];
|
|
181
199
|
}
|
|
182
200
|
listCommittedOffsets(options, callback) {
|
|
@@ -309,7 +327,7 @@ export class Consumer extends Base {
|
|
|
309
327
|
callback(error);
|
|
310
328
|
});
|
|
311
329
|
}
|
|
312
|
-
#listOffsets(options, callback) {
|
|
330
|
+
#listOffsets(withTimestamps, options, callback) {
|
|
313
331
|
this[kMetadata]({ topics: options.topics }, (error, metadata) => {
|
|
314
332
|
if (error) {
|
|
315
333
|
callback(error, undefined);
|
|
@@ -318,7 +336,12 @@ export class Consumer extends Base {
|
|
|
318
336
|
const requests = new Map();
|
|
319
337
|
for (const name of options.topics) {
|
|
320
338
|
const topic = metadata.topics.get(name);
|
|
339
|
+
const toInclude = options.partitions?.[name] ?? [];
|
|
340
|
+
const hasPartitionsFilter = toInclude.length > 0;
|
|
321
341
|
for (let i = 0; i < topic.partitionsCount; i++) {
|
|
342
|
+
if (hasPartitionsFilter && !toInclude.includes(i)) {
|
|
343
|
+
continue;
|
|
344
|
+
}
|
|
322
345
|
const partition = topic.partitions[i];
|
|
323
346
|
const { leader, leaderEpoch } = partition;
|
|
324
347
|
let leaderRequests = requests.get(leader);
|
|
@@ -359,16 +382,34 @@ export class Consumer extends Base {
|
|
|
359
382
|
callback(error, undefined);
|
|
360
383
|
return;
|
|
361
384
|
}
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
topicOffsets =
|
|
368
|
-
|
|
385
|
+
let offsets = new Map();
|
|
386
|
+
if (withTimestamps) {
|
|
387
|
+
offsets = new Map();
|
|
388
|
+
for (const response of responses) {
|
|
389
|
+
for (const { name: topic, partitions } of response.topics) {
|
|
390
|
+
let topicOffsets = offsets.get(topic);
|
|
391
|
+
if (!topicOffsets) {
|
|
392
|
+
topicOffsets = new Map();
|
|
393
|
+
offsets.set(topic, topicOffsets);
|
|
394
|
+
}
|
|
395
|
+
for (const { partitionIndex: index, offset, timestamp } of partitions) {
|
|
396
|
+
topicOffsets.set(index, { offset, timestamp });
|
|
397
|
+
}
|
|
369
398
|
}
|
|
370
|
-
|
|
371
|
-
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
else {
|
|
402
|
+
offsets = new Map();
|
|
403
|
+
for (const response of responses) {
|
|
404
|
+
for (const { name: topic, partitions } of response.topics) {
|
|
405
|
+
let topicOffsets = offsets.get(topic);
|
|
406
|
+
if (!topicOffsets) {
|
|
407
|
+
topicOffsets = Array(metadata.topics.get(topic).partitionsCount);
|
|
408
|
+
offsets.set(topic, topicOffsets);
|
|
409
|
+
}
|
|
410
|
+
for (const { partitionIndex: index, offset } of partitions) {
|
|
411
|
+
topicOffsets[index] = offset;
|
|
412
|
+
}
|
|
372
413
|
}
|
|
373
414
|
}
|
|
374
415
|
}
|
|
@@ -684,7 +725,7 @@ export class Consumer extends Base {
|
|
|
684
725
|
callback(error, undefined);
|
|
685
726
|
return;
|
|
686
727
|
}
|
|
687
|
-
this.#performSyncGroup(this.#
|
|
728
|
+
this.#performSyncGroup(this.#createAssignments(metadata), callback);
|
|
688
729
|
});
|
|
689
730
|
return;
|
|
690
731
|
}
|
|
@@ -785,7 +826,7 @@ export class Consumer extends Base {
|
|
|
785
826
|
w.appendString(topic).appendArray(partitions, (w, a) => w.appendInt32(a), true, false);
|
|
786
827
|
}, true, false).buffer;
|
|
787
828
|
}
|
|
788
|
-
#
|
|
829
|
+
#createAssignments(metadata) {
|
|
789
830
|
const partitionTracker = new Map();
|
|
790
831
|
// First of all, layout topics-partitions in a list
|
|
791
832
|
for (const [topic, partitions] of metadata.topics) {
|
|
@@ -805,37 +846,14 @@ export class Consumer extends Base {
|
|
|
805
846
|
}
|
|
806
847
|
return [{ memberId: this.memberId, assignment: this.#encodeProtocolAssignment(assignments) }];
|
|
807
848
|
}
|
|
808
|
-
|
|
809
|
-
const members
|
|
810
|
-
|
|
811
|
-
for (const [memberId, subscription] of this.#members) {
|
|
812
|
-
members.push({ memberId, assignments: new Map() });
|
|
813
|
-
for (const topic of subscription.topics) {
|
|
814
|
-
subscribedTopics.add(topic);
|
|
815
|
-
}
|
|
816
|
-
}
|
|
817
|
-
// Assign topic-partitions in round robin
|
|
818
|
-
let currentMember = 0;
|
|
819
|
-
for (const topic of subscribedTopics) {
|
|
820
|
-
const partitionsCount = metadata.topics.get(topic).partitionsCount;
|
|
821
|
-
for (let i = 0; i < partitionsCount; i++) {
|
|
822
|
-
const member = members[currentMember++ % membersSize];
|
|
823
|
-
let topicAssignments = member.assignments.get(topic);
|
|
824
|
-
if (!topicAssignments) {
|
|
825
|
-
topicAssignments = { topic, partitions: [] };
|
|
826
|
-
member.assignments.set(topic, topicAssignments);
|
|
827
|
-
}
|
|
828
|
-
topicAssignments?.partitions.push(i);
|
|
829
|
-
}
|
|
830
|
-
}
|
|
831
|
-
const assignments = [];
|
|
832
|
-
for (const member of members) {
|
|
833
|
-
assignments.push({
|
|
849
|
+
const encodedAssignments = [];
|
|
850
|
+
for (const member of this.#partitionsAssigner(this.memberId, this.#members, new Set(this.topics.current), metadata)) {
|
|
851
|
+
encodedAssignments.push({
|
|
834
852
|
memberId: member.memberId,
|
|
835
853
|
assignment: this.#encodeProtocolAssignment(Array.from(member.assignments.values()))
|
|
836
854
|
});
|
|
837
855
|
}
|
|
838
|
-
return
|
|
856
|
+
return encodedAssignments;
|
|
839
857
|
}
|
|
840
858
|
#getRejoinError(error) {
|
|
841
859
|
const protocolError = error.findBy?.('needsRejoin', true);
|
|
@@ -43,6 +43,9 @@ export declare const groupOptionsProperties: {
|
|
|
43
43
|
};
|
|
44
44
|
};
|
|
45
45
|
};
|
|
46
|
+
partitionAssigner: {
|
|
47
|
+
function: boolean;
|
|
48
|
+
};
|
|
46
49
|
};
|
|
47
50
|
export declare const groupOptionsAdditionalValidations: {
|
|
48
51
|
rebalanceTimeout: {
|
|
@@ -161,6 +164,9 @@ export declare const groupOptionsSchema: {
|
|
|
161
164
|
};
|
|
162
165
|
};
|
|
163
166
|
};
|
|
167
|
+
partitionAssigner: {
|
|
168
|
+
function: boolean;
|
|
169
|
+
};
|
|
164
170
|
};
|
|
165
171
|
additionalProperties: boolean;
|
|
166
172
|
};
|
|
@@ -257,6 +263,9 @@ export declare const consumeOptionsSchema: {
|
|
|
257
263
|
};
|
|
258
264
|
};
|
|
259
265
|
};
|
|
266
|
+
partitionAssigner: {
|
|
267
|
+
function: boolean;
|
|
268
|
+
};
|
|
260
269
|
topics: {
|
|
261
270
|
type: string;
|
|
262
271
|
items: {
|
|
@@ -393,6 +402,9 @@ export declare const consumerOptionsSchema: {
|
|
|
393
402
|
};
|
|
394
403
|
};
|
|
395
404
|
};
|
|
405
|
+
partitionAssigner: {
|
|
406
|
+
function: boolean;
|
|
407
|
+
};
|
|
396
408
|
groupId: {
|
|
397
409
|
type: string;
|
|
398
410
|
pattern: string;
|
|
@@ -494,6 +506,9 @@ export declare const fetchOptionsSchema: {
|
|
|
494
506
|
};
|
|
495
507
|
};
|
|
496
508
|
};
|
|
509
|
+
partitionAssigner: {
|
|
510
|
+
function: boolean;
|
|
511
|
+
};
|
|
497
512
|
node: {
|
|
498
513
|
type: string;
|
|
499
514
|
minimum: number;
|
|
@@ -607,6 +622,16 @@ export declare const listOffsetsOptionsSchema: {
|
|
|
607
622
|
pattern: string;
|
|
608
623
|
};
|
|
609
624
|
};
|
|
625
|
+
partitions: {
|
|
626
|
+
type: string;
|
|
627
|
+
additionalProperties: {
|
|
628
|
+
type: string;
|
|
629
|
+
items: {
|
|
630
|
+
type: string;
|
|
631
|
+
minimum: number;
|
|
632
|
+
};
|
|
633
|
+
};
|
|
634
|
+
};
|
|
610
635
|
isolationLevel: {
|
|
611
636
|
type: string;
|
|
612
637
|
enum: string[];
|
|
@@ -21,7 +21,8 @@ export const groupOptionsProperties = {
|
|
|
21
21
|
metadata: { oneOf: [{ type: 'string' }, { buffer: true }] }
|
|
22
22
|
}
|
|
23
23
|
}
|
|
24
|
-
}
|
|
24
|
+
},
|
|
25
|
+
partitionAssigner: { function: true }
|
|
25
26
|
};
|
|
26
27
|
export const groupOptionsAdditionalValidations = {
|
|
27
28
|
rebalanceTimeout: {
|
|
@@ -178,6 +179,13 @@ export const listOffsetsOptionsSchema = {
|
|
|
178
179
|
type: 'object',
|
|
179
180
|
properties: {
|
|
180
181
|
topics: { type: 'array', items: idProperty },
|
|
182
|
+
partitions: {
|
|
183
|
+
type: 'object',
|
|
184
|
+
additionalProperties: {
|
|
185
|
+
type: 'array',
|
|
186
|
+
items: { type: 'number', minimum: 0 }
|
|
187
|
+
}
|
|
188
|
+
},
|
|
181
189
|
isolationLevel: { type: 'string', enum: Object.keys(FetchIsolationLevels) },
|
|
182
190
|
timestamp: { bigint: true }
|
|
183
191
|
},
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import { type ClusterMetadata } from '../base/types.ts';
|
|
2
|
+
import { type ExtendedGroupProtocolSubscription, type GroupPartitionsAssignments } from './types.ts';
|
|
3
|
+
export declare function roundRobinAssigner(_current: string, members: Map<string, ExtendedGroupProtocolSubscription>, topics: Set<string>, metadata: ClusterMetadata): GroupPartitionsAssignments[];
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
export function roundRobinAssigner(_current, members, topics, metadata) {
|
|
2
|
+
const membersSize = members.size;
|
|
3
|
+
const assignments = [];
|
|
4
|
+
// Flat the list of members and subscribed topics
|
|
5
|
+
for (const memberId of members.keys()) {
|
|
6
|
+
assignments.push({ memberId, assignments: new Map() });
|
|
7
|
+
}
|
|
8
|
+
// Assign topic-partitions in round robin
|
|
9
|
+
let currentMember = 0;
|
|
10
|
+
for (const topic of topics) {
|
|
11
|
+
const partitionsCount = metadata.topics.get(topic).partitionsCount;
|
|
12
|
+
for (let i = 0; i < partitionsCount; i++) {
|
|
13
|
+
const member = assignments[currentMember++ % membersSize];
|
|
14
|
+
let topicAssignments = member.assignments.get(topic);
|
|
15
|
+
if (!topicAssignments) {
|
|
16
|
+
topicAssignments = { topic, partitions: [] };
|
|
17
|
+
member.assignments.set(topic, topicAssignments);
|
|
18
|
+
}
|
|
19
|
+
topicAssignments?.partitions.push(i);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
return assignments;
|
|
23
|
+
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { type FetchRequestTopic } from '../../apis/consumer/fetch-v17.ts';
|
|
2
2
|
import { type FetchIsolationLevel } from '../../apis/enumerations.ts';
|
|
3
3
|
import { type KafkaRecord, type Message } from '../../protocol/records.ts';
|
|
4
|
-
import { type BaseOptions, type TopicWithPartitionAndOffset } from '../base/types.ts';
|
|
4
|
+
import { type BaseOptions, type ClusterMetadata, type TopicWithPartitionAndOffset } from '../base/types.ts';
|
|
5
5
|
import { type Deserializers } from '../serde.ts';
|
|
6
6
|
export interface GroupProtocolSubscription {
|
|
7
7
|
name: string;
|
|
@@ -12,12 +12,21 @@ export interface GroupAssignment {
|
|
|
12
12
|
topic: string;
|
|
13
13
|
partitions: number[];
|
|
14
14
|
}
|
|
15
|
+
export interface GroupPartitionsAssignments {
|
|
16
|
+
memberId: string;
|
|
17
|
+
assignments: Map<string, GroupAssignment>;
|
|
18
|
+
}
|
|
15
19
|
export interface ExtendedGroupProtocolSubscription extends Omit<GroupProtocolSubscription, 'name'> {
|
|
16
20
|
topics?: string[];
|
|
17
21
|
memberId: string;
|
|
18
22
|
}
|
|
19
23
|
export type Offsets = Map<string, bigint[]>;
|
|
24
|
+
export type OffsetsWithTimestamps = Map<string, Map<number, {
|
|
25
|
+
offset: bigint;
|
|
26
|
+
timestamp: bigint;
|
|
27
|
+
}>>;
|
|
20
28
|
export type CorruptedMessageHandler = (record: KafkaRecord, topic: string, partition: number, firstTimestamp: bigint, firstOffset: bigint, commit: Message['commit']) => boolean;
|
|
29
|
+
export type GroupPartitionsAssigner = (current: string, members: Map<string, ExtendedGroupProtocolSubscription>, topics: Set<string>, metadata: ClusterMetadata) => GroupPartitionsAssignments[];
|
|
21
30
|
export declare const MessagesStreamModes: {
|
|
22
31
|
readonly LATEST: "latest";
|
|
23
32
|
readonly EARLIEST: "earliest";
|
|
@@ -38,6 +47,7 @@ export interface GroupOptions {
|
|
|
38
47
|
rebalanceTimeout?: number;
|
|
39
48
|
heartbeatInterval?: number;
|
|
40
49
|
protocols?: GroupProtocolSubscription[];
|
|
50
|
+
partitionAssigner?: GroupPartitionsAssigner;
|
|
41
51
|
}
|
|
42
52
|
export interface ConsumeBaseOptions<Key, Value, HeaderKey, HeaderValue> {
|
|
43
53
|
autocommit?: boolean | number;
|
|
@@ -74,6 +84,7 @@ export interface ListCommitsOptions {
|
|
|
74
84
|
}
|
|
75
85
|
export interface ListOffsetsOptions {
|
|
76
86
|
topics: string[];
|
|
87
|
+
partitions?: Record<string, number[]>;
|
|
77
88
|
timestamp?: bigint;
|
|
78
89
|
isolationLevel?: FetchIsolationLevel;
|
|
79
90
|
}
|
|
@@ -10,13 +10,17 @@ export declare const produceOptionsProperties: {
|
|
|
10
10
|
};
|
|
11
11
|
acks: {
|
|
12
12
|
type: string;
|
|
13
|
-
|
|
14
|
-
|
|
13
|
+
enumeration: {
|
|
14
|
+
allowed: (0 | 1 | -1)[];
|
|
15
|
+
errorMessage: string;
|
|
16
|
+
};
|
|
15
17
|
};
|
|
16
18
|
compression: {
|
|
17
19
|
type: string;
|
|
18
|
-
|
|
19
|
-
|
|
20
|
+
enumeration: {
|
|
21
|
+
allowed: string[];
|
|
22
|
+
errorMessage: string;
|
|
23
|
+
};
|
|
20
24
|
};
|
|
21
25
|
partitioner: {
|
|
22
26
|
function: boolean;
|
|
@@ -42,13 +46,17 @@ export declare const produceOptionsSchema: {
|
|
|
42
46
|
};
|
|
43
47
|
acks: {
|
|
44
48
|
type: string;
|
|
45
|
-
|
|
46
|
-
|
|
49
|
+
enumeration: {
|
|
50
|
+
allowed: (0 | 1 | -1)[];
|
|
51
|
+
errorMessage: string;
|
|
52
|
+
};
|
|
47
53
|
};
|
|
48
54
|
compression: {
|
|
49
55
|
type: string;
|
|
50
|
-
|
|
51
|
-
|
|
56
|
+
enumeration: {
|
|
57
|
+
allowed: string[];
|
|
58
|
+
errorMessage: string;
|
|
59
|
+
};
|
|
52
60
|
};
|
|
53
61
|
partitioner: {
|
|
54
62
|
function: boolean;
|
|
@@ -78,13 +86,17 @@ export declare const sendOptionsSchema: {
|
|
|
78
86
|
};
|
|
79
87
|
acks: {
|
|
80
88
|
type: string;
|
|
81
|
-
|
|
82
|
-
|
|
89
|
+
enumeration: {
|
|
90
|
+
allowed: (0 | 1 | -1)[];
|
|
91
|
+
errorMessage: string;
|
|
92
|
+
};
|
|
83
93
|
};
|
|
84
94
|
compression: {
|
|
85
95
|
type: string;
|
|
86
|
-
|
|
87
|
-
|
|
96
|
+
enumeration: {
|
|
97
|
+
allowed: string[];
|
|
98
|
+
errorMessage: string;
|
|
99
|
+
};
|
|
88
100
|
};
|
|
89
101
|
partitioner: {
|
|
90
102
|
function: boolean;
|
|
@@ -9,13 +9,17 @@ export const produceOptionsProperties = {
|
|
|
9
9
|
idempotent: { type: 'boolean' },
|
|
10
10
|
acks: {
|
|
11
11
|
type: 'number',
|
|
12
|
-
|
|
13
|
-
|
|
12
|
+
enumeration: {
|
|
13
|
+
allowed: Object.values(ProduceAcks),
|
|
14
|
+
errorMessage: enumErrorMessage(ProduceAcks)
|
|
15
|
+
}
|
|
14
16
|
},
|
|
15
17
|
compression: {
|
|
16
18
|
type: 'string',
|
|
17
|
-
|
|
18
|
-
|
|
19
|
+
enumeration: {
|
|
20
|
+
allowed: Object.keys(compressionsAlgorithms),
|
|
21
|
+
errorMessage: enumErrorMessage(compressionsAlgorithms, true)
|
|
22
|
+
}
|
|
19
23
|
},
|
|
20
24
|
partitioner: { function: true },
|
|
21
25
|
autocreateTopics: { type: 'boolean' },
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { type CallbackWithPromise } from '../../apis/callbacks.ts';
|
|
2
2
|
import { Base } from '../base/base.ts';
|
|
3
3
|
import { type ProduceOptions, type ProduceResult, type ProducerInfo, type ProducerOptions, type SendOptions } from './types.ts';
|
|
4
|
+
export declare function noopSerializer(data?: Buffer): Buffer | undefined;
|
|
4
5
|
export declare class Producer<Key = Buffer, Value = Buffer, HeaderKey = Buffer, HeaderValue = Buffer> extends Base<ProducerOptions<Key, Value, HeaderKey, HeaderValue>> {
|
|
5
6
|
#private;
|
|
6
7
|
constructor(options: ProducerOptions<Key, Value, HeaderKey, HeaderValue>);
|
|
@@ -9,7 +9,7 @@ import { ensureMetric } from "../metrics.js";
|
|
|
9
9
|
import { produceOptionsValidator, producerOptionsValidator, sendOptionsValidator } from "./options.js";
|
|
10
10
|
// Don't move this function as being in the same file will enable V8 to remove.
|
|
11
11
|
// For futher info, ask Matteo.
|
|
12
|
-
function noopSerializer(data) {
|
|
12
|
+
export function noopSerializer(data) {
|
|
13
13
|
return data;
|
|
14
14
|
}
|
|
15
15
|
export class Producer extends Base {
|
|
@@ -99,6 +99,10 @@ export class Connection extends EventEmitter {
|
|
|
99
99
|
const connectionOptions = {
|
|
100
100
|
timeout: this.#options.connectTimeout
|
|
101
101
|
};
|
|
102
|
+
if (this.#options.tlsServerName) {
|
|
103
|
+
connectionOptions.servername =
|
|
104
|
+
typeof this.#options.tlsServerName === 'string' ? this.#options.tlsServerName : host;
|
|
105
|
+
}
|
|
102
106
|
const connectionTimeoutHandler = () => {
|
|
103
107
|
const error = new TimeoutError(`Connection to ${host}:${port} timed out.`);
|
|
104
108
|
diagnosticContext.error = error;
|
|
@@ -114,14 +118,14 @@ export class Connection extends EventEmitter {
|
|
|
114
118
|
this.#onConnectionError(host, port, diagnosticContext, error);
|
|
115
119
|
};
|
|
116
120
|
this.emit('connecting');
|
|
117
|
-
/* c8 ignore next 3 - TLS connection is not tested but we rely on Node.js tests */
|
|
118
121
|
this.#host = host;
|
|
119
122
|
this.#port = port;
|
|
123
|
+
/* c8 ignore next 3 - TLS connection is not tested but we rely on Node.js tests */
|
|
120
124
|
this.#socket = this.#options.tls
|
|
121
125
|
? createTLSConnection(port, host, { ...this.#options.tls, ...connectionOptions })
|
|
122
126
|
: createConnection({ ...connectionOptions, port, host });
|
|
123
127
|
this.#socket.setNoDelay(true);
|
|
124
|
-
this.#socket.once('connect', () => {
|
|
128
|
+
this.#socket.once(this.#options.tls ? 'secureConnect' : 'connect', () => {
|
|
125
129
|
this.#socket.removeListener('timeout', connectionTimeoutHandler);
|
|
126
130
|
this.#socket.removeListener('error', connectionErrorHandler);
|
|
127
131
|
this.#socket.on('error', this.#onError.bind(this));
|
|
@@ -3,7 +3,7 @@ import zlib from 'node:zlib';
|
|
|
3
3
|
import { UnsupportedCompressionError } from "../errors.js";
|
|
4
4
|
import { DynamicBuffer } from "./dynamic-buffer.js";
|
|
5
5
|
const require = createRequire(import.meta.url);
|
|
6
|
-
// @ts-ignore
|
|
6
|
+
// @ts-ignore - Added in Node.js 22.15.0
|
|
7
7
|
const { zstdCompressSync, zstdDecompressSync, gzipSync, gunzipSync } = zlib;
|
|
8
8
|
function ensureBuffer(data) {
|
|
9
9
|
return DynamicBuffer.isDynamicBuffer(data) ? data.slice() : data;
|
package/dist/protocol/reader.js
CHANGED
|
@@ -208,7 +208,10 @@ export class Reader {
|
|
|
208
208
|
return this.readNullableBytes(compact) || EMPTY_BUFFER;
|
|
209
209
|
}
|
|
210
210
|
readVarIntBytes() {
|
|
211
|
-
|
|
211
|
+
let length = this.readVarInt();
|
|
212
|
+
if (length === -1) {
|
|
213
|
+
length = 0;
|
|
214
|
+
}
|
|
212
215
|
const value = this.buffer.slice(this.position, this.position + length);
|
|
213
216
|
this.position += length;
|
|
214
217
|
return value;
|
package/dist/utils.d.ts
CHANGED
|
@@ -1,6 +1,13 @@
|
|
|
1
1
|
import { Ajv2020 } from 'ajv/dist/2020.js';
|
|
2
2
|
import debug from 'debug';
|
|
3
3
|
import { type DynamicBuffer } from './protocol/dynamic-buffer.ts';
|
|
4
|
+
export interface EnumerationDefinition<T> {
|
|
5
|
+
allowed: T[];
|
|
6
|
+
errorMessage?: string;
|
|
7
|
+
}
|
|
8
|
+
export type KeywordSchema<T> = {
|
|
9
|
+
schema: T;
|
|
10
|
+
};
|
|
4
11
|
export interface DataValidationContext {
|
|
5
12
|
parentData: {
|
|
6
13
|
[k: string | number]: any;
|
package/dist/utils.js
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import ajvErrors from 'ajv-errors';
|
|
2
1
|
import { Ajv2020 } from 'ajv/dist/2020.js';
|
|
3
2
|
import debug from 'debug';
|
|
4
3
|
import { inspect } from 'node:util';
|
|
@@ -12,8 +11,6 @@ export const loggers = {
|
|
|
12
11
|
'consumer:heartbeat': debug('plt:kafka:consumer:heartbeat'),
|
|
13
12
|
admin: debug('plt:kafka:admin')
|
|
14
13
|
};
|
|
15
|
-
// @ts-ignore
|
|
16
|
-
ajvErrors(ajv);
|
|
17
14
|
let debugDumpLogger = console.error;
|
|
18
15
|
ajv.addKeyword({
|
|
19
16
|
keyword: 'bigint',
|
|
@@ -75,6 +72,17 @@ ajv.addKeyword({
|
|
|
75
72
|
}
|
|
76
73
|
}
|
|
77
74
|
});
|
|
75
|
+
ajv.addKeyword({
|
|
76
|
+
keyword: 'enumeration', // This mimics the enum keyword but defines a custom error message
|
|
77
|
+
validate(property, current) {
|
|
78
|
+
return property.allowed.includes(current);
|
|
79
|
+
},
|
|
80
|
+
error: {
|
|
81
|
+
message({ schema }) {
|
|
82
|
+
return schema.errorMessage;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
});
|
|
78
86
|
export class NumericMap extends Map {
|
|
79
87
|
getWithDefault(key, fallback) {
|
|
80
88
|
return this.get(key) ?? fallback;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@platformatic/kafka",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.8.0",
|
|
4
4
|
"description": "Modern and performant client for Apache Kafka",
|
|
5
5
|
"homepage": "https://github.com/platformatic/kafka",
|
|
6
6
|
"author": "Platformatic Inc. <oss@platformatic.dev> (https://platformatic.dev)",
|
|
@@ -26,7 +26,6 @@
|
|
|
26
26
|
"types": "./dist/index.d.ts",
|
|
27
27
|
"dependencies": {
|
|
28
28
|
"ajv": "^8.17.1",
|
|
29
|
-
"ajv-errors": "^3.0.0",
|
|
30
29
|
"debug": "^4.4.0",
|
|
31
30
|
"fastq": "^1.19.1",
|
|
32
31
|
"mnemonist": "^0.40.3",
|
|
@@ -42,6 +41,7 @@
|
|
|
42
41
|
"@types/node": "^22.13.5",
|
|
43
42
|
"@types/semver": "^7.7.0",
|
|
44
43
|
"@watchable/unpromise": "^1.0.2",
|
|
44
|
+
"avsc": "^5.7.7",
|
|
45
45
|
"c8": "^10.1.3",
|
|
46
46
|
"cleaner-spec-reporter": "^0.5.0",
|
|
47
47
|
"cronometro": "^5.3.0",
|
|
@@ -53,6 +53,7 @@
|
|
|
53
53
|
"node-rdkafka": "^3.3.1",
|
|
54
54
|
"parse5": "^7.2.1",
|
|
55
55
|
"prettier": "^3.5.3",
|
|
56
|
+
"prettier-plugin-space-before-function-paren": "^0.0.8",
|
|
56
57
|
"prom-client": "^15.1.3",
|
|
57
58
|
"semver": "^7.7.1",
|
|
58
59
|
"table": "^6.9.0",
|