@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.
@@ -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
- for (const [headerKey, headerValue] of record.headers) {
609
- headers.set(
610
- /* c8 ignore next 2 - Hard to test */
611
- headerKeyDeserializer(headerKey ?? undefined, messageToConsume), headerValueDeserializer(headerValue ?? undefined, messageToConsume));
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
- const shouldDestroy = this.#corruptedMessageHandler(record, topic, partition, firstTimestamp, firstOffset, commit, error);
636
- if (shouldDestroy) {
637
- diagnosticContext.error = error;
638
- consumerReceivesChannel.error.publish(diagnosticContext);
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);
@@ -372,6 +372,9 @@ export declare const consumeOptionsSchema: {
372
372
  onCorruptedMessage: {
373
373
  function: boolean;
374
374
  };
375
+ onDeserializationError: {
376
+ function: boolean;
377
+ };
375
378
  };
376
379
  required: string[];
377
380
  additionalProperties: boolean;
@@ -90,6 +90,7 @@ export const consumeOptionsSchema = {
90
90
  }
91
91
  },
92
92
  onCorruptedMessage: { function: true },
93
+ onDeserializationError: { function: true },
93
94
  ...groupOptionsProperties,
94
95
  ...consumeOptionsProperties
95
96
  },
@@ -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,3 +1,7 @@
1
+ export const DeserializationErrorActions = {
2
+ FAIL: 'fail',
3
+ SKIP: 'skip'
4
+ };
1
5
  export const MessagesStreamModes = {
2
6
  LATEST: 'latest',
3
7
  EARLIEST: 'earliest',
@@ -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
- this.#jsonAjv = new Ajv2020({ allErrors: true, coerceTypes: false, strict: true, ...options.jsonAjvOptions });
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
- this.#schemas.set(id, { id, type: 'json', schema: this.#jsonAjv.compile(JSON.parse(schema)) });
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 UserError(`JSON Schema validation failed before serialization: ${this.#jsonAjv.errorsText(validate.errors)}`, { type, data, headers, validationErrors: validate.errors });
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 UserError(`JSON Schema validation failed before deserialization: ${this.#jsonAjv.errorsText(validate.errors)}`, { type, data: parsed, headers, validationErrors: validate.errors });
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
  }
@@ -372,6 +372,9 @@ export declare const consumeOptionsSchema: {
372
372
  onCorruptedMessage: {
373
373
  function: boolean;
374
374
  };
375
+ onDeserializationError: {
376
+ function: boolean;
377
+ };
375
378
  };
376
379
  required: string[];
377
380
  additionalProperties: boolean;
@@ -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.7.0";
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.7.0",
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",