@platformatic/kafka 2.7.0 → 2.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/dist/clients/consumer/consumer.js +4 -0
- package/dist/clients/consumer/messages-stream.js +53 -18
- package/dist/clients/consumer/options.d.ts +3 -0
- package/dist/clients/consumer/options.js +1 -0
- package/dist/clients/consumer/types.d.ts +24 -1
- package/dist/clients/consumer/types.js +4 -0
- package/dist/registries/confluent-schema-registry.d.ts +24 -2
- package/dist/registries/confluent-schema-registry.js +52 -5
- package/dist/typescript-4/dist/clients/consumer/options.d.ts +3 -0
- package/dist/typescript-4/dist/clients/consumer/types.d.ts +24 -1
- package/dist/typescript-4/dist/registries/confluent-schema-registry.d.ts +24 -2
- package/dist/version.js +1 -1
- package/package.json +2 -1
|
@@ -178,6 +178,10 @@ export class Consumer extends Base {
|
|
|
178
178
|
callback(validationError);
|
|
179
179
|
return callback[kCallbackPromise];
|
|
180
180
|
}
|
|
181
|
+
if (options.onCorruptedMessage && options.onDeserializationError) {
|
|
182
|
+
callback(new UserError('Cannot specify both onCorruptedMessage and onDeserializationError.'));
|
|
183
|
+
return callback[kCallbackPromise];
|
|
184
|
+
}
|
|
181
185
|
options.autocommit ??= this[kOptions].autocommit ?? true;
|
|
182
186
|
options.maxBytes ??= this[kOptions].maxBytes;
|
|
183
187
|
options.maxBytesPerPartition ??= this[kOptions].maxBytesPerPartition ?? options.maxBytes;
|
|
@@ -9,7 +9,7 @@ import { kAutocommit, kGetFetchNode, kInstance, kRefreshOffsetsAndFetch } from "
|
|
|
9
9
|
import { kConnections, kCreateConnectionPool, kInspect, kPrometheus } from "../base/base.js";
|
|
10
10
|
import { ensureMetric } from "../metrics.js";
|
|
11
11
|
import { defaultConsumerOptions } from "./options.js";
|
|
12
|
-
import { MessagesStreamFallbackModes, MessagesStreamModes } from "./types.js";
|
|
12
|
+
import { DeserializationErrorActions, MessagesStreamFallbackModes, MessagesStreamModes } from "./types.js";
|
|
13
13
|
import { partitionKey } from "./utils.js";
|
|
14
14
|
// Don't move this function as being in the same file will enable V8 to remove.
|
|
15
15
|
// For futher info, ask Matteo.
|
|
@@ -61,6 +61,7 @@ export class MessagesStream extends Readable {
|
|
|
61
61
|
#closeCallbacks;
|
|
62
62
|
#metricsConsumedMessages;
|
|
63
63
|
#corruptedMessageHandler;
|
|
64
|
+
#deserializationErrorHandler;
|
|
64
65
|
#context;
|
|
65
66
|
#onConsumerGroupJoin;
|
|
66
67
|
#onBrokerDisconnect;
|
|
@@ -83,9 +84,12 @@ export class MessagesStream extends Readable {
|
|
|
83
84
|
*/
|
|
84
85
|
[kConnections];
|
|
85
86
|
constructor(consumer, options) {
|
|
86
|
-
const { autocommit, mode, fallbackMode, maxFetches, offsets, context, deserializers, onCorruptedMessage, registry, beforeDeserialization,
|
|
87
|
+
const { autocommit, mode, fallbackMode, maxFetches, offsets, context, deserializers, onCorruptedMessage, onDeserializationError, registry, beforeDeserialization,
|
|
87
88
|
// The options below are only destructured to avoid being part of structuredClone below
|
|
88
89
|
partitionAssigner: _partitionAssigner, protocolsMetadata: _protocolsMetadata, ...otherOptions } = options;
|
|
90
|
+
if (onCorruptedMessage && onDeserializationError) {
|
|
91
|
+
throw new UserError('Cannot specify both onCorruptedMessage and onDeserializationError.');
|
|
92
|
+
}
|
|
89
93
|
if (offsets && mode !== MessagesStreamModes.MANUAL) {
|
|
90
94
|
throw new UserError('Cannot specify offsets when the stream mode is not MANUAL.');
|
|
91
95
|
}
|
|
@@ -124,6 +128,7 @@ export class MessagesStream extends Readable {
|
|
|
124
128
|
this.#closed = false;
|
|
125
129
|
this.#closeCallbacks = [];
|
|
126
130
|
this.#corruptedMessageHandler = onCorruptedMessage ?? defaultCorruptedMessageHandler;
|
|
131
|
+
this.#deserializationErrorHandler = onDeserializationError;
|
|
127
132
|
this.#context = context;
|
|
128
133
|
this.#onConsumerGroupJoin = () => {
|
|
129
134
|
this.#offsetsCommitted.clear();
|
|
@@ -605,18 +610,51 @@ export class MessagesStream extends Readable {
|
|
|
605
610
|
: this.#commit.bind(this, topic, partition, offset + 1n, leaderEpoch);
|
|
606
611
|
try {
|
|
607
612
|
const headers = new Map();
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
613
|
+
let payloadType = 'headerKey';
|
|
614
|
+
let deserializedKey;
|
|
615
|
+
let deserializedValue;
|
|
616
|
+
try {
|
|
617
|
+
for (const [headerKey, headerValue] of record.headers) {
|
|
618
|
+
payloadType = 'headerKey';
|
|
619
|
+
/* c8 ignore next - Hard to test */
|
|
620
|
+
const deserializedHeaderKey = headerKeyDeserializer(headerKey ?? undefined, messageToConsume);
|
|
621
|
+
payloadType = 'headerValue';
|
|
622
|
+
/* c8 ignore next - Hard to test */
|
|
623
|
+
const deserializedHeaderValue = headerValueDeserializer(headerValue ?? undefined, messageToConsume);
|
|
624
|
+
headers.set(deserializedHeaderKey, deserializedHeaderValue);
|
|
625
|
+
}
|
|
626
|
+
payloadType = 'key';
|
|
627
|
+
/* c8 ignore next - Hard to test */
|
|
628
|
+
deserializedKey = keyDeserializer(record.key ?? undefined, headers, messageToConsume);
|
|
629
|
+
payloadType = 'value';
|
|
630
|
+
deserializedValue = valueDeserializer(record.value ?? undefined, headers, messageToConsume);
|
|
631
|
+
}
|
|
632
|
+
catch (error) {
|
|
633
|
+
const context = {
|
|
634
|
+
error,
|
|
635
|
+
payloadType,
|
|
636
|
+
record,
|
|
637
|
+
topic,
|
|
638
|
+
partition,
|
|
639
|
+
offset,
|
|
640
|
+
timestamp: firstTimestamp + record.timestampDelta,
|
|
641
|
+
commit: commit
|
|
642
|
+
};
|
|
643
|
+
const shouldDestroy = this.#deserializationErrorHandler
|
|
644
|
+
? this.#deserializationErrorHandler(context) !== DeserializationErrorActions.SKIP
|
|
645
|
+
: this.#corruptedMessageHandler(record, topic, partition, firstTimestamp, firstOffset, commit, error);
|
|
646
|
+
if (shouldDestroy) {
|
|
647
|
+
diagnosticContext.error = error;
|
|
648
|
+
consumerReceivesChannel.error.publish(diagnosticContext);
|
|
649
|
+
this.destroy(new UserError('Failed to deserialize a message.', { cause: error }));
|
|
650
|
+
return;
|
|
651
|
+
}
|
|
652
|
+
continue;
|
|
612
653
|
}
|
|
613
|
-
/* c8 ignore next 2 - Hard to test */
|
|
614
|
-
const key = keyDeserializer(record.key ?? undefined, headers, messageToConsume);
|
|
615
|
-
const value = valueDeserializer(record.value ?? undefined, headers, messageToConsume);
|
|
616
654
|
this.#metricsConsumedMessages?.inc();
|
|
617
655
|
const message = {
|
|
618
|
-
key,
|
|
619
|
-
value,
|
|
656
|
+
key: deserializedKey,
|
|
657
|
+
value: deserializedValue,
|
|
620
658
|
headers,
|
|
621
659
|
topic,
|
|
622
660
|
partition,
|
|
@@ -632,13 +670,10 @@ export class MessagesStream extends Readable {
|
|
|
632
670
|
consumerReceivesChannel.asyncEnd.publish(diagnosticContext);
|
|
633
671
|
}
|
|
634
672
|
catch (error) {
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
this.destroy(new UserError('Failed to deserialize a message.', { cause: error }));
|
|
640
|
-
return;
|
|
641
|
-
}
|
|
673
|
+
diagnosticContext.error = error;
|
|
674
|
+
consumerReceivesChannel.error.publish(diagnosticContext);
|
|
675
|
+
this.destroy(new UserError('Failed to process a message.', { cause: error }));
|
|
676
|
+
return;
|
|
642
677
|
}
|
|
643
678
|
finally {
|
|
644
679
|
consumerReceivesChannel.end.publish(diagnosticContext);
|
|
@@ -5,7 +5,7 @@ import { type ConnectionPool } from '../../network/connection-pool.ts';
|
|
|
5
5
|
import { type KafkaRecord, type Message } from '../../protocol/records.ts';
|
|
6
6
|
import { type SchemaRegistry } from '../../registries/abstract.ts';
|
|
7
7
|
import { type BaseOptions, type ClusterMetadata, type TopicWithPartitionAndOffset } from '../base/types.ts';
|
|
8
|
-
import { type BeforeDeserializationHook, type Deserializers } from '../serde.ts';
|
|
8
|
+
import { type BeforeDeserializationHook, type BeforeHookPayloadType, type Deserializers } from '../serde.ts';
|
|
9
9
|
export interface GroupProtocolSubscription {
|
|
10
10
|
name: string;
|
|
11
11
|
version: number;
|
|
@@ -32,6 +32,25 @@ export interface PreferredReadReplica {
|
|
|
32
32
|
node: number;
|
|
33
33
|
expiresAt: number;
|
|
34
34
|
}
|
|
35
|
+
export declare const DeserializationErrorActions: {
|
|
36
|
+
readonly FAIL: "fail";
|
|
37
|
+
readonly SKIP: "skip";
|
|
38
|
+
};
|
|
39
|
+
export type DeserializationErrorAction = (typeof DeserializationErrorActions)[keyof typeof DeserializationErrorActions];
|
|
40
|
+
export interface DeserializationErrorContext {
|
|
41
|
+
error: unknown;
|
|
42
|
+
payloadType: BeforeHookPayloadType;
|
|
43
|
+
record: KafkaRecord;
|
|
44
|
+
topic: string;
|
|
45
|
+
partition: number;
|
|
46
|
+
offset: bigint;
|
|
47
|
+
timestamp: bigint;
|
|
48
|
+
commit: Message['commit'];
|
|
49
|
+
}
|
|
50
|
+
export type DeserializationErrorHandler = (context: DeserializationErrorContext) => DeserializationErrorAction;
|
|
51
|
+
/**
|
|
52
|
+
* @deprecated Use `DeserializationErrorHandler` instead.
|
|
53
|
+
*/
|
|
35
54
|
export type CorruptedMessageHandler = (record: KafkaRecord, topic: string, partition: number, firstTimestamp: bigint, firstOffset: bigint, commit: Message['commit'], error: unknown) => boolean;
|
|
36
55
|
export type GroupPartitionsAssigner = (current: string, members: Map<string, ExtendedGroupProtocolSubscription>, topics: Set<string>, metadata: ClusterMetadata) => GroupPartitionsAssignments[];
|
|
37
56
|
export type GroupProtocolsMetadataCallback = (protocols: GroupProtocolSubscription[], topics: string[], metadata: ClusterMetadata, callback: Callback<Buffer>) => Buffer | Promise<Buffer> | undefined;
|
|
@@ -89,6 +108,10 @@ export interface StreamOptions {
|
|
|
89
108
|
fallbackMode?: MessagesStreamFallbackModeValue;
|
|
90
109
|
maxFetches?: number;
|
|
91
110
|
offsets?: TopicWithPartitionAndOffset[];
|
|
111
|
+
onDeserializationError?: DeserializationErrorHandler;
|
|
112
|
+
/**
|
|
113
|
+
* @deprecated Use `onDeserializationError` instead.
|
|
114
|
+
*/
|
|
92
115
|
onCorruptedMessage?: CorruptedMessageHandler;
|
|
93
116
|
}
|
|
94
117
|
export type ConsumeOptions<Key, Value, HeaderKey, HeaderValue> = StreamOptions & ConsumeBaseOptions<Key, Value, HeaderKey, HeaderValue> & GroupOptions;
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { type Options, type ValidateFunction } from 'ajv';
|
|
1
|
+
import { type ErrorObject, type Options, type ValidateFunction } from 'ajv';
|
|
2
2
|
import { type Type } from 'avsc';
|
|
3
3
|
import { type Root } from 'protobufjs';
|
|
4
4
|
import { type Callback } from '../apis/definitions.ts';
|
|
5
5
|
import { type BeforeDeserializationHook, type BeforeHookPayloadType, type BeforeSerializationHook, type Deserializers, type Serializers } from '../clients/serde.ts';
|
|
6
|
-
import { type CredentialProvider } from '../index.ts';
|
|
6
|
+
import { type CredentialProvider, UserError } from '../index.ts';
|
|
7
7
|
import { type MessageToConsume, type MessageToProduce } from '../protocol/records.ts';
|
|
8
8
|
import { AbstractSchemaRegistry } from './abstract.ts';
|
|
9
9
|
type ConfluentSchemaRegistryMessageToProduce = MessageToProduce<unknown, unknown, unknown, unknown>;
|
|
@@ -22,6 +22,28 @@ export interface ConfluentSchemaRegistryOptions {
|
|
|
22
22
|
jsonValidateSend?: boolean;
|
|
23
23
|
jsonAjvOptions?: Options;
|
|
24
24
|
}
|
|
25
|
+
export type SchemaValidationPhase = 'serialization' | 'deserialization';
|
|
26
|
+
export interface SchemaValidationErrorProperties {
|
|
27
|
+
schemaId: number;
|
|
28
|
+
schemaType: 'json';
|
|
29
|
+
phase: SchemaValidationPhase;
|
|
30
|
+
payloadType: BeforeHookPayloadType;
|
|
31
|
+
type: BeforeHookPayloadType;
|
|
32
|
+
data: unknown;
|
|
33
|
+
headers?: ReadonlyMap<unknown, unknown>;
|
|
34
|
+
validationErrors: ErrorObject[] | null | undefined;
|
|
35
|
+
}
|
|
36
|
+
export declare class SchemaValidationError extends UserError {
|
|
37
|
+
readonly schemaId: number;
|
|
38
|
+
readonly schemaType: 'json';
|
|
39
|
+
readonly phase: SchemaValidationPhase;
|
|
40
|
+
readonly payloadType: BeforeHookPayloadType;
|
|
41
|
+
readonly type: BeforeHookPayloadType;
|
|
42
|
+
readonly data: unknown;
|
|
43
|
+
readonly headers?: ReadonlyMap<unknown, unknown>;
|
|
44
|
+
readonly validationErrors: ErrorObject[] | null | undefined;
|
|
45
|
+
constructor(message: string, properties: SchemaValidationErrorProperties);
|
|
46
|
+
}
|
|
25
47
|
export interface Schema {
|
|
26
48
|
id: number;
|
|
27
49
|
type: 'avro' | 'protobuf' | 'json';
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { Ajv } from 'ajv';
|
|
1
2
|
import { Ajv2020 } from 'ajv/dist/2020.js';
|
|
2
3
|
import avro from 'avsc';
|
|
3
4
|
import { createRequire } from 'node:module';
|
|
@@ -6,6 +7,14 @@ import { EMPTY_BUFFER, UnsupportedFormatError, UserError } from "../index.js";
|
|
|
6
7
|
import { getCredential } from "../protocol/sasl/utils.js";
|
|
7
8
|
import { AbstractSchemaRegistry } from "./abstract.js";
|
|
8
9
|
const require = createRequire(import.meta.url);
|
|
10
|
+
const AjvDraft04 = require('ajv-draft-04');
|
|
11
|
+
const draft06MetaSchema = require('ajv/dist/refs/json-schema-draft-06.json');
|
|
12
|
+
export class SchemaValidationError extends UserError {
|
|
13
|
+
constructor(message, properties) {
|
|
14
|
+
super(message, properties);
|
|
15
|
+
this.name = 'SchemaValidationError';
|
|
16
|
+
}
|
|
17
|
+
}
|
|
9
18
|
/* c8 ignore next 8 */
|
|
10
19
|
export function defaultProtobufTypeMapper(_, type, context) {
|
|
11
20
|
// Confluent Schema Registry convention
|
|
@@ -18,6 +27,8 @@ export class ConfluentSchemaRegistry extends AbstractSchemaRegistry {
|
|
|
18
27
|
#protobufTypeMapper;
|
|
19
28
|
#jsonValidateSend;
|
|
20
29
|
#jsonAjv;
|
|
30
|
+
#jsonAjvDraft7;
|
|
31
|
+
#jsonAjvDraft04;
|
|
21
32
|
#pendingFetches;
|
|
22
33
|
#auth;
|
|
23
34
|
constructor(options) {
|
|
@@ -26,7 +37,11 @@ export class ConfluentSchemaRegistry extends AbstractSchemaRegistry {
|
|
|
26
37
|
this.#schemas = new Map();
|
|
27
38
|
this.#protobufTypeMapper = options.protobufTypeMapper ?? defaultProtobufTypeMapper;
|
|
28
39
|
this.#jsonValidateSend = options.jsonValidateSend ?? false;
|
|
29
|
-
|
|
40
|
+
const jsonAjvOptions = { allErrors: true, coerceTypes: false, strict: true, ...options.jsonAjvOptions };
|
|
41
|
+
this.#jsonAjv = new Ajv2020(jsonAjvOptions);
|
|
42
|
+
this.#jsonAjvDraft7 = new Ajv(jsonAjvOptions);
|
|
43
|
+
this.#jsonAjvDraft7.addMetaSchema(draft06MetaSchema);
|
|
44
|
+
this.#jsonAjvDraft04 = new AjvDraft04(jsonAjvOptions);
|
|
30
45
|
this.#auth = options.auth;
|
|
31
46
|
this.#pendingFetches = new Map();
|
|
32
47
|
}
|
|
@@ -145,10 +160,24 @@ export class ConfluentSchemaRegistry extends AbstractSchemaRegistry {
|
|
|
145
160
|
this.#protobufParse ??= this.#loadProtobuf();
|
|
146
161
|
this.#schemas.set(id, { id, type: 'protobuf', schema: this.#protobufParse(schema).root });
|
|
147
162
|
break;
|
|
148
|
-
case 'JSON':
|
|
149
|
-
|
|
163
|
+
case 'JSON': {
|
|
164
|
+
const jsonSchema = JSON.parse(schema);
|
|
165
|
+
this.#schemas.set(id, { id, type: 'json', schema: this.#getJsonAjv(jsonSchema).compile(jsonSchema) });
|
|
150
166
|
break;
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
#getJsonAjv(schema) {
|
|
171
|
+
if (typeof schema !== 'object' || schema === null || typeof schema.$schema !== 'string') {
|
|
172
|
+
return this.#jsonAjv;
|
|
173
|
+
}
|
|
174
|
+
if (schema.$schema.includes('draft-04')) {
|
|
175
|
+
return this.#jsonAjvDraft04;
|
|
176
|
+
}
|
|
177
|
+
if (schema.$schema.includes('draft-06') || schema.$schema.includes('draft-07')) {
|
|
178
|
+
return this.#jsonAjvDraft7;
|
|
151
179
|
}
|
|
180
|
+
return this.#jsonAjv;
|
|
152
181
|
}
|
|
153
182
|
#schemaSerializer(type, fallbackSerializer, data, headers, message) {
|
|
154
183
|
/* c8 ignore next 3 - Hard to test */
|
|
@@ -183,7 +212,16 @@ export class ConfluentSchemaRegistry extends AbstractSchemaRegistry {
|
|
|
183
212
|
const validate = schema.schema;
|
|
184
213
|
const valid = validate(data);
|
|
185
214
|
if (!valid) {
|
|
186
|
-
throw new
|
|
215
|
+
throw new SchemaValidationError(`JSON Schema validation failed before serialization: ${this.#jsonAjv.errorsText(validate.errors)}`, {
|
|
216
|
+
schemaId,
|
|
217
|
+
schemaType: 'json',
|
|
218
|
+
phase: 'serialization',
|
|
219
|
+
payloadType: type,
|
|
220
|
+
type,
|
|
221
|
+
data,
|
|
222
|
+
headers,
|
|
223
|
+
validationErrors: validate.errors
|
|
224
|
+
});
|
|
187
225
|
}
|
|
188
226
|
}
|
|
189
227
|
encodedMessage = Buffer.from(JSON.stringify(data));
|
|
@@ -222,7 +260,16 @@ export class ConfluentSchemaRegistry extends AbstractSchemaRegistry {
|
|
|
222
260
|
const validate = schema.schema;
|
|
223
261
|
const valid = validate(parsed);
|
|
224
262
|
if (!valid) {
|
|
225
|
-
throw new
|
|
263
|
+
throw new SchemaValidationError(`JSON Schema validation failed before deserialization: ${this.#jsonAjv.errorsText(validate.errors)}`, {
|
|
264
|
+
schemaId,
|
|
265
|
+
schemaType: 'json',
|
|
266
|
+
phase: 'deserialization',
|
|
267
|
+
payloadType: type,
|
|
268
|
+
type,
|
|
269
|
+
data: parsed,
|
|
270
|
+
headers,
|
|
271
|
+
validationErrors: validate.errors
|
|
272
|
+
});
|
|
226
273
|
}
|
|
227
274
|
return parsed;
|
|
228
275
|
}
|
|
@@ -5,7 +5,7 @@ import { type ConnectionPool } from "../../network/connection-pool";
|
|
|
5
5
|
import { type KafkaRecord, type Message } from "../../protocol/records";
|
|
6
6
|
import { type SchemaRegistry } from "../../registries/abstract";
|
|
7
7
|
import { type BaseOptions, type ClusterMetadata, type TopicWithPartitionAndOffset } from "../base/types";
|
|
8
|
-
import { type BeforeDeserializationHook, type Deserializers } from "../serde";
|
|
8
|
+
import { type BeforeDeserializationHook, type BeforeHookPayloadType, type Deserializers } from "../serde";
|
|
9
9
|
export interface GroupProtocolSubscription {
|
|
10
10
|
name: string;
|
|
11
11
|
version: number;
|
|
@@ -32,6 +32,25 @@ export interface PreferredReadReplica {
|
|
|
32
32
|
node: number;
|
|
33
33
|
expiresAt: number;
|
|
34
34
|
}
|
|
35
|
+
export declare const DeserializationErrorActions: {
|
|
36
|
+
readonly FAIL: "fail";
|
|
37
|
+
readonly SKIP: "skip";
|
|
38
|
+
};
|
|
39
|
+
export type DeserializationErrorAction = (typeof DeserializationErrorActions)[keyof typeof DeserializationErrorActions];
|
|
40
|
+
export interface DeserializationErrorContext {
|
|
41
|
+
error: unknown;
|
|
42
|
+
payloadType: BeforeHookPayloadType;
|
|
43
|
+
record: KafkaRecord;
|
|
44
|
+
topic: string;
|
|
45
|
+
partition: number;
|
|
46
|
+
offset: bigint;
|
|
47
|
+
timestamp: bigint;
|
|
48
|
+
commit: Message['commit'];
|
|
49
|
+
}
|
|
50
|
+
export type DeserializationErrorHandler = (context: DeserializationErrorContext) => DeserializationErrorAction;
|
|
51
|
+
/**
|
|
52
|
+
* @deprecated Use `DeserializationErrorHandler` instead.
|
|
53
|
+
*/
|
|
35
54
|
export type CorruptedMessageHandler = (record: KafkaRecord, topic: string, partition: number, firstTimestamp: bigint, firstOffset: bigint, commit: Message['commit'], error: unknown) => boolean;
|
|
36
55
|
export type GroupPartitionsAssigner = (current: string, members: Map<string, ExtendedGroupProtocolSubscription>, topics: Set<string>, metadata: ClusterMetadata) => GroupPartitionsAssignments[];
|
|
37
56
|
export type GroupProtocolsMetadataCallback = (protocols: GroupProtocolSubscription[], topics: string[], metadata: ClusterMetadata, callback: Callback<Buffer>) => Buffer | Promise<Buffer> | undefined;
|
|
@@ -89,6 +108,10 @@ export interface StreamOptions {
|
|
|
89
108
|
fallbackMode?: MessagesStreamFallbackModeValue;
|
|
90
109
|
maxFetches?: number;
|
|
91
110
|
offsets?: TopicWithPartitionAndOffset[];
|
|
111
|
+
onDeserializationError?: DeserializationErrorHandler;
|
|
112
|
+
/**
|
|
113
|
+
* @deprecated Use `onDeserializationError` instead.
|
|
114
|
+
*/
|
|
92
115
|
onCorruptedMessage?: CorruptedMessageHandler;
|
|
93
116
|
}
|
|
94
117
|
export type ConsumeOptions<Key, Value, HeaderKey, HeaderValue> = StreamOptions & ConsumeBaseOptions<Key, Value, HeaderKey, HeaderValue> & GroupOptions;
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { type Options, type ValidateFunction } from 'ajv';
|
|
1
|
+
import { type ErrorObject, type Options, type ValidateFunction } from 'ajv';
|
|
2
2
|
import { type Type } from 'avsc';
|
|
3
3
|
import { type Root } from 'protobufjs';
|
|
4
4
|
import { type Callback } from "../apis/definitions";
|
|
5
5
|
import { type BeforeDeserializationHook, type BeforeHookPayloadType, type BeforeSerializationHook, type Deserializers, type Serializers } from "../clients/serde";
|
|
6
|
-
import { type CredentialProvider } from "../index";
|
|
6
|
+
import { type CredentialProvider, UserError } from "../index";
|
|
7
7
|
import { type MessageToConsume, type MessageToProduce } from "../protocol/records";
|
|
8
8
|
import { AbstractSchemaRegistry } from "./abstract";
|
|
9
9
|
type ConfluentSchemaRegistryMessageToProduce = MessageToProduce<unknown, unknown, unknown, unknown>;
|
|
@@ -22,6 +22,28 @@ export interface ConfluentSchemaRegistryOptions {
|
|
|
22
22
|
jsonValidateSend?: boolean;
|
|
23
23
|
jsonAjvOptions?: Options;
|
|
24
24
|
}
|
|
25
|
+
export type SchemaValidationPhase = 'serialization' | 'deserialization';
|
|
26
|
+
export interface SchemaValidationErrorProperties {
|
|
27
|
+
schemaId: number;
|
|
28
|
+
schemaType: 'json';
|
|
29
|
+
phase: SchemaValidationPhase;
|
|
30
|
+
payloadType: BeforeHookPayloadType;
|
|
31
|
+
type: BeforeHookPayloadType;
|
|
32
|
+
data: unknown;
|
|
33
|
+
headers?: ReadonlyMap<unknown, unknown>;
|
|
34
|
+
validationErrors: ErrorObject[] | null | undefined;
|
|
35
|
+
}
|
|
36
|
+
export declare class SchemaValidationError extends UserError {
|
|
37
|
+
readonly schemaId: number;
|
|
38
|
+
readonly schemaType: 'json';
|
|
39
|
+
readonly phase: SchemaValidationPhase;
|
|
40
|
+
readonly payloadType: BeforeHookPayloadType;
|
|
41
|
+
readonly type: BeforeHookPayloadType;
|
|
42
|
+
readonly data: unknown;
|
|
43
|
+
readonly headers?: ReadonlyMap<unknown, unknown>;
|
|
44
|
+
readonly validationErrors: ErrorObject[] | null | undefined;
|
|
45
|
+
constructor(message: string, properties: SchemaValidationErrorProperties);
|
|
46
|
+
}
|
|
25
47
|
export interface Schema {
|
|
26
48
|
id: number;
|
|
27
49
|
type: 'avro' | 'protobuf' | 'json';
|
package/dist/version.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
export const name = "@platformatic/kafka";
|
|
2
|
-
export const version = "2.
|
|
2
|
+
export const version = "2.8.0";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@platformatic/kafka",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.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)",
|
|
@@ -28,6 +28,7 @@
|
|
|
28
28
|
"@platformatic/dynamic-buffer": "^0.3.1",
|
|
29
29
|
"@platformatic/wasm-utils": "^0.2.1",
|
|
30
30
|
"ajv": "^8.17.1",
|
|
31
|
+
"ajv-draft-04": "^1.0.0",
|
|
31
32
|
"avsc": "^5.7.9",
|
|
32
33
|
"debug": "^4.4.3",
|
|
33
34
|
"fastq": "^1.19.1",
|