@quanticjs/events-kafka 6.0.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/KafkaEvent.d.ts +13 -0
- package/dist/KafkaEvent.js +2 -0
- package/dist/KafkaEventConsumer.d.ts +24 -0
- package/dist/KafkaEventConsumer.js +175 -0
- package/dist/KafkaEventPublisher.d.ts +20 -0
- package/dist/KafkaEventPublisher.js +93 -0
- package/dist/QuanticEventsKafkaModule.d.ts +5 -0
- package/dist/QuanticEventsKafkaModule.js +45 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +9 -0
- package/package.json +34 -0
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export interface KafkaEvent {
|
|
2
|
+
id: string;
|
|
3
|
+
type: string;
|
|
4
|
+
version: number;
|
|
5
|
+
aggregateId: string;
|
|
6
|
+
organizationId?: string;
|
|
7
|
+
timestamp: string;
|
|
8
|
+
payload: Record<string, unknown>;
|
|
9
|
+
}
|
|
10
|
+
export interface KafkaEventsModuleOptions {
|
|
11
|
+
brokers: string[];
|
|
12
|
+
clientId: string;
|
|
13
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { Logger, OnModuleInit, OnModuleDestroy, OnApplicationBootstrap } from '@nestjs/common';
|
|
2
|
+
import { KafkaEvent } from './KafkaEvent';
|
|
3
|
+
export declare abstract class KafkaEventConsumer implements OnModuleInit, OnModuleDestroy, OnApplicationBootstrap {
|
|
4
|
+
private readonly brokers;
|
|
5
|
+
private readonly clientId;
|
|
6
|
+
protected readonly logger: Logger;
|
|
7
|
+
abstract readonly topic: string;
|
|
8
|
+
abstract readonly groupId: string;
|
|
9
|
+
abstract handleMessage(event: KafkaEvent): Promise<void>;
|
|
10
|
+
protected shouldHandle(_event: KafkaEvent): boolean;
|
|
11
|
+
private readonly tracer;
|
|
12
|
+
private consumer;
|
|
13
|
+
private producer;
|
|
14
|
+
private kafka;
|
|
15
|
+
private heartbeatFn;
|
|
16
|
+
constructor(brokers: string[], clientId: string);
|
|
17
|
+
protected heartbeat(): void;
|
|
18
|
+
onModuleInit(): Promise<void>;
|
|
19
|
+
onApplicationBootstrap(): Promise<void>;
|
|
20
|
+
onModuleDestroy(): Promise<void>;
|
|
21
|
+
private processMessage;
|
|
22
|
+
private processWithSpan;
|
|
23
|
+
private routeToDlq;
|
|
24
|
+
}
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.KafkaEventConsumer = void 0;
|
|
4
|
+
const common_1 = require("@nestjs/common");
|
|
5
|
+
const core_1 = require("@quanticjs/core");
|
|
6
|
+
const kafka_javascript_1 = require("@confluentinc/kafka-javascript");
|
|
7
|
+
const MAX_RETRIES = 5;
|
|
8
|
+
const MAX_BACKOFF_MS = 30000;
|
|
9
|
+
class KafkaEventConsumer {
|
|
10
|
+
brokers;
|
|
11
|
+
clientId;
|
|
12
|
+
logger = new common_1.Logger(this.constructor.name);
|
|
13
|
+
shouldHandle(_event) {
|
|
14
|
+
return true;
|
|
15
|
+
}
|
|
16
|
+
tracer = (0, core_1.getTracer)('@quanticjs/events-kafka');
|
|
17
|
+
consumer = null;
|
|
18
|
+
producer = null;
|
|
19
|
+
kafka = null;
|
|
20
|
+
heartbeatFn = null;
|
|
21
|
+
constructor(brokers, clientId) {
|
|
22
|
+
this.brokers = brokers;
|
|
23
|
+
this.clientId = clientId;
|
|
24
|
+
}
|
|
25
|
+
heartbeat() {
|
|
26
|
+
this.heartbeatFn?.();
|
|
27
|
+
}
|
|
28
|
+
async onModuleInit() {
|
|
29
|
+
this.kafka = new kafka_javascript_1.KafkaJS.Kafka({
|
|
30
|
+
'bootstrap.servers': this.brokers.join(','),
|
|
31
|
+
'client.id': this.clientId,
|
|
32
|
+
});
|
|
33
|
+
this.consumer = this.kafka.consumer({
|
|
34
|
+
'group.id': this.groupId,
|
|
35
|
+
'session.timeout.ms': 45000,
|
|
36
|
+
'heartbeat.interval.ms': 5000,
|
|
37
|
+
'fetch.wait.max.ms': 1000,
|
|
38
|
+
'partition.assignment.strategy': 'roundrobin',
|
|
39
|
+
'enable.auto.commit': false,
|
|
40
|
+
});
|
|
41
|
+
this.producer = this.kafka.producer({
|
|
42
|
+
'compression.type': 'lz4',
|
|
43
|
+
});
|
|
44
|
+
await this.consumer.connect();
|
|
45
|
+
await this.producer.connect();
|
|
46
|
+
await this.consumer.subscribe({ topic: this.topic });
|
|
47
|
+
this.logger.log(`Subscribed to ${this.topic} as ${this.groupId}`);
|
|
48
|
+
}
|
|
49
|
+
async onApplicationBootstrap() {
|
|
50
|
+
if (!this.consumer)
|
|
51
|
+
return;
|
|
52
|
+
void this.consumer.run({
|
|
53
|
+
eachMessage: async ({ topic, partition, message, heartbeat }) => {
|
|
54
|
+
this.heartbeatFn = heartbeat;
|
|
55
|
+
try {
|
|
56
|
+
await this.processMessage(topic, partition, message);
|
|
57
|
+
}
|
|
58
|
+
finally {
|
|
59
|
+
this.heartbeatFn = null;
|
|
60
|
+
}
|
|
61
|
+
},
|
|
62
|
+
});
|
|
63
|
+
this.logger.log(`Consumer started for ${this.topic}`);
|
|
64
|
+
}
|
|
65
|
+
async onModuleDestroy() {
|
|
66
|
+
if (this.consumer) {
|
|
67
|
+
await this.consumer.disconnect();
|
|
68
|
+
this.consumer = null;
|
|
69
|
+
}
|
|
70
|
+
if (this.producer) {
|
|
71
|
+
await this.producer.disconnect();
|
|
72
|
+
this.producer = null;
|
|
73
|
+
}
|
|
74
|
+
this.logger.log(`Consumer disconnected from ${this.topic}`);
|
|
75
|
+
}
|
|
76
|
+
async processMessage(topic, partition, message) {
|
|
77
|
+
const raw = message.value?.toString();
|
|
78
|
+
if (!raw)
|
|
79
|
+
return;
|
|
80
|
+
let parsed;
|
|
81
|
+
try {
|
|
82
|
+
parsed = JSON.parse(raw);
|
|
83
|
+
}
|
|
84
|
+
catch {
|
|
85
|
+
this.logger.error(`Invalid JSON in message on ${topic}:${partition}`);
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
const event = {
|
|
89
|
+
id: parsed.id || '',
|
|
90
|
+
type: parsed.type || '',
|
|
91
|
+
version: parsed.version ?? 1,
|
|
92
|
+
aggregateId: parsed.aggregateId || '',
|
|
93
|
+
organizationId: parsed.organizationId,
|
|
94
|
+
timestamp: parsed.timestamp || '',
|
|
95
|
+
payload: parsed.payload || {},
|
|
96
|
+
};
|
|
97
|
+
if (!this.shouldHandle(event))
|
|
98
|
+
return;
|
|
99
|
+
for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {
|
|
100
|
+
try {
|
|
101
|
+
await this.processWithSpan(topic, partition, message, event);
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
catch (error) {
|
|
105
|
+
const err = error instanceof Error ? error : new Error(String(error));
|
|
106
|
+
if (attempt < MAX_RETRIES) {
|
|
107
|
+
const delay = Math.min(1000 * Math.pow(2, attempt) + Math.floor(Math.random() * 1000), MAX_BACKOFF_MS);
|
|
108
|
+
this.logger.warn(`Retry ${attempt + 1}/${MAX_RETRIES} for ${event.type} on ${topic}:${partition} in ${delay}ms: ${err.message}`);
|
|
109
|
+
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
110
|
+
}
|
|
111
|
+
else {
|
|
112
|
+
this.logger.error(`Dead-lettering ${event.type} on ${topic}:${partition} after ${MAX_RETRIES} retries: ${err.message}`);
|
|
113
|
+
await this.routeToDlq(topic, partition, message, event, err);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
async processWithSpan(topic, partition, message, event) {
|
|
119
|
+
if (!this.tracer) {
|
|
120
|
+
return this.handleMessage(event);
|
|
121
|
+
}
|
|
122
|
+
const spanName = `${topic} process`;
|
|
123
|
+
return this.tracer.startActiveSpan(spanName, async (span) => {
|
|
124
|
+
span.setAttribute('messaging.system', 'kafka');
|
|
125
|
+
span.setAttribute('messaging.destination', topic);
|
|
126
|
+
span.setAttribute('messaging.kafka.partition', partition);
|
|
127
|
+
span.setAttribute('messaging.message_id', event.id);
|
|
128
|
+
if (event.type)
|
|
129
|
+
span.setAttribute('messaging.event_type', event.type);
|
|
130
|
+
try {
|
|
131
|
+
await this.handleMessage(event);
|
|
132
|
+
}
|
|
133
|
+
catch (error) {
|
|
134
|
+
const err = error instanceof Error ? error : new Error(String(error));
|
|
135
|
+
span.setStatus({ code: core_1.SPAN_STATUS_ERROR, message: err.message });
|
|
136
|
+
span.recordException(err);
|
|
137
|
+
throw error;
|
|
138
|
+
}
|
|
139
|
+
finally {
|
|
140
|
+
span.end();
|
|
141
|
+
}
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
async routeToDlq(topic, partition, message, event, error) {
|
|
145
|
+
if (!this.producer)
|
|
146
|
+
return;
|
|
147
|
+
const dlqTopic = `${topic}.dlq`;
|
|
148
|
+
const dlqPayload = {
|
|
149
|
+
originalTopic: topic,
|
|
150
|
+
originalPartition: partition,
|
|
151
|
+
originalOffset: message.offset,
|
|
152
|
+
type: event.type,
|
|
153
|
+
payload: event.payload,
|
|
154
|
+
attempts: MAX_RETRIES,
|
|
155
|
+
lastError: error.message,
|
|
156
|
+
deadLetteredAt: new Date().toISOString(),
|
|
157
|
+
};
|
|
158
|
+
try {
|
|
159
|
+
await this.producer.send({
|
|
160
|
+
topic: dlqTopic,
|
|
161
|
+
messages: [
|
|
162
|
+
{
|
|
163
|
+
key: event.aggregateId || null,
|
|
164
|
+
value: JSON.stringify(dlqPayload),
|
|
165
|
+
},
|
|
166
|
+
],
|
|
167
|
+
});
|
|
168
|
+
this.logger.log(`Routed ${event.type} to ${dlqTopic}`);
|
|
169
|
+
}
|
|
170
|
+
catch (dlqError) {
|
|
171
|
+
this.logger.error(`Failed to route to DLQ ${dlqTopic}: ${dlqError.message}`);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
exports.KafkaEventConsumer = KafkaEventConsumer;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { OnModuleInit, OnModuleDestroy } from '@nestjs/common';
|
|
2
|
+
import { IEventPublisher, DomainEvent } from '@quanticjs/events-core';
|
|
3
|
+
declare const KAFKA_OPTIONS: unique symbol;
|
|
4
|
+
export { KAFKA_OPTIONS };
|
|
5
|
+
export interface KafkaPublisherConfig {
|
|
6
|
+
brokers: string[];
|
|
7
|
+
clientId: string;
|
|
8
|
+
}
|
|
9
|
+
export declare class KafkaEventPublisher implements IEventPublisher, OnModuleInit, OnModuleDestroy {
|
|
10
|
+
private readonly config;
|
|
11
|
+
private readonly logger;
|
|
12
|
+
private producer;
|
|
13
|
+
private kafka;
|
|
14
|
+
constructor(config: KafkaPublisherConfig);
|
|
15
|
+
onModuleInit(): Promise<void>;
|
|
16
|
+
onModuleDestroy(): Promise<void>;
|
|
17
|
+
publish(event: DomainEvent): Promise<void>;
|
|
18
|
+
publishToDestination(destination: string, fields: Record<string, string>): Promise<string | null>;
|
|
19
|
+
private produceMessage;
|
|
20
|
+
}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
3
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
4
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
5
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
6
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
7
|
+
};
|
|
8
|
+
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
9
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
10
|
+
};
|
|
11
|
+
var KafkaEventPublisher_1;
|
|
12
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
13
|
+
exports.KafkaEventPublisher = exports.KAFKA_OPTIONS = void 0;
|
|
14
|
+
const common_1 = require("@nestjs/common");
|
|
15
|
+
const kafka_javascript_1 = require("@confluentinc/kafka-javascript");
|
|
16
|
+
const KAFKA_OPTIONS = Symbol('KAFKA_OPTIONS');
|
|
17
|
+
exports.KAFKA_OPTIONS = KAFKA_OPTIONS;
|
|
18
|
+
let KafkaEventPublisher = KafkaEventPublisher_1 = class KafkaEventPublisher {
|
|
19
|
+
config;
|
|
20
|
+
logger = new common_1.Logger(KafkaEventPublisher_1.name);
|
|
21
|
+
producer = null;
|
|
22
|
+
kafka = null;
|
|
23
|
+
constructor(config) {
|
|
24
|
+
this.config = config;
|
|
25
|
+
}
|
|
26
|
+
async onModuleInit() {
|
|
27
|
+
this.kafka = new kafka_javascript_1.KafkaJS.Kafka({
|
|
28
|
+
'bootstrap.servers': this.config.brokers.join(','),
|
|
29
|
+
'client.id': this.config.clientId,
|
|
30
|
+
});
|
|
31
|
+
this.producer = this.kafka.producer({
|
|
32
|
+
'compression.type': 'lz4',
|
|
33
|
+
});
|
|
34
|
+
await this.producer.connect();
|
|
35
|
+
this.logger.log('Kafka producer connected');
|
|
36
|
+
}
|
|
37
|
+
async onModuleDestroy() {
|
|
38
|
+
if (this.producer) {
|
|
39
|
+
await this.producer.disconnect();
|
|
40
|
+
this.producer = null;
|
|
41
|
+
this.logger.log('Kafka producer disconnected');
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
async publish(event) {
|
|
45
|
+
const envelope = {
|
|
46
|
+
id: event.eventId,
|
|
47
|
+
type: event.eventType,
|
|
48
|
+
version: 1,
|
|
49
|
+
aggregateId: event.aggregateId,
|
|
50
|
+
organizationId: event.organizationId,
|
|
51
|
+
timestamp: event.occurredAt.toISOString(),
|
|
52
|
+
payload: event.payload,
|
|
53
|
+
};
|
|
54
|
+
await this.produceMessage(event.topic, event.aggregateId, envelope);
|
|
55
|
+
}
|
|
56
|
+
async publishToDestination(destination, fields) {
|
|
57
|
+
if (!this.producer) {
|
|
58
|
+
this.logger.warn('Kafka producer not connected, skipping publish');
|
|
59
|
+
return null;
|
|
60
|
+
}
|
|
61
|
+
const result = await this.producer.send({
|
|
62
|
+
topic: destination,
|
|
63
|
+
messages: [
|
|
64
|
+
{
|
|
65
|
+
key: fields.aggregateId || null,
|
|
66
|
+
value: JSON.stringify(fields),
|
|
67
|
+
},
|
|
68
|
+
],
|
|
69
|
+
});
|
|
70
|
+
const offset = result?.[0]?.offset;
|
|
71
|
+
return offset != null ? String(offset) : null;
|
|
72
|
+
}
|
|
73
|
+
async produceMessage(topic, key, value) {
|
|
74
|
+
if (!this.producer) {
|
|
75
|
+
this.logger.warn('Kafka producer not connected, skipping publish');
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
await this.producer.send({
|
|
79
|
+
topic,
|
|
80
|
+
messages: [
|
|
81
|
+
{
|
|
82
|
+
key,
|
|
83
|
+
value: JSON.stringify(value),
|
|
84
|
+
},
|
|
85
|
+
],
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
};
|
|
89
|
+
exports.KafkaEventPublisher = KafkaEventPublisher;
|
|
90
|
+
exports.KafkaEventPublisher = KafkaEventPublisher = KafkaEventPublisher_1 = __decorate([
|
|
91
|
+
(0, common_1.Injectable)(),
|
|
92
|
+
__metadata("design:paramtypes", [Object])
|
|
93
|
+
], KafkaEventPublisher);
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
3
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
4
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
5
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
6
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
7
|
+
};
|
|
8
|
+
var QuanticEventsKafkaModule_1;
|
|
9
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
10
|
+
exports.QuanticEventsKafkaModule = void 0;
|
|
11
|
+
const common_1 = require("@nestjs/common");
|
|
12
|
+
const events_core_1 = require("@quanticjs/events-core");
|
|
13
|
+
const KafkaEventPublisher_1 = require("./KafkaEventPublisher");
|
|
14
|
+
let QuanticEventsKafkaModule = QuanticEventsKafkaModule_1 = class QuanticEventsKafkaModule {
|
|
15
|
+
static forRoot(options) {
|
|
16
|
+
return {
|
|
17
|
+
module: QuanticEventsKafkaModule_1,
|
|
18
|
+
imports: [events_core_1.QuanticEventsCoreModule.forRoot()],
|
|
19
|
+
providers: [
|
|
20
|
+
{
|
|
21
|
+
provide: KafkaEventPublisher_1.KAFKA_OPTIONS,
|
|
22
|
+
useValue: options,
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
provide: KafkaEventPublisher_1.KafkaEventPublisher,
|
|
26
|
+
useFactory: (config) => new KafkaEventPublisher_1.KafkaEventPublisher(config),
|
|
27
|
+
inject: [KafkaEventPublisher_1.KAFKA_OPTIONS],
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
provide: events_core_1.EVENT_PUBLISHER,
|
|
31
|
+
useExisting: KafkaEventPublisher_1.KafkaEventPublisher,
|
|
32
|
+
},
|
|
33
|
+
],
|
|
34
|
+
exports: [
|
|
35
|
+
KafkaEventPublisher_1.KafkaEventPublisher,
|
|
36
|
+
events_core_1.EVENT_PUBLISHER,
|
|
37
|
+
],
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
exports.QuanticEventsKafkaModule = QuanticEventsKafkaModule;
|
|
42
|
+
exports.QuanticEventsKafkaModule = QuanticEventsKafkaModule = QuanticEventsKafkaModule_1 = __decorate([
|
|
43
|
+
(0, common_1.Global)(),
|
|
44
|
+
(0, common_1.Module)({})
|
|
45
|
+
], QuanticEventsKafkaModule);
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.QuanticEventsKafkaModule = exports.KafkaEventConsumer = exports.KafkaEventPublisher = void 0;
|
|
4
|
+
var KafkaEventPublisher_1 = require("./KafkaEventPublisher");
|
|
5
|
+
Object.defineProperty(exports, "KafkaEventPublisher", { enumerable: true, get: function () { return KafkaEventPublisher_1.KafkaEventPublisher; } });
|
|
6
|
+
var KafkaEventConsumer_1 = require("./KafkaEventConsumer");
|
|
7
|
+
Object.defineProperty(exports, "KafkaEventConsumer", { enumerable: true, get: function () { return KafkaEventConsumer_1.KafkaEventConsumer; } });
|
|
8
|
+
var QuanticEventsKafkaModule_1 = require("./QuanticEventsKafkaModule");
|
|
9
|
+
Object.defineProperty(exports, "QuanticEventsKafkaModule", { enumerable: true, get: function () { return QuanticEventsKafkaModule_1.QuanticEventsKafkaModule; } });
|
package/package.json
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@quanticjs/events-kafka",
|
|
3
|
+
"version": "6.0.0",
|
|
4
|
+
"description": "Kafka transport for @quanticjs/events-core — KafkaEventPublisher, KafkaEventConsumer with ADR-008 compliance",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"types": "dist/index.d.ts",
|
|
7
|
+
"files": [
|
|
8
|
+
"dist"
|
|
9
|
+
],
|
|
10
|
+
"publishConfig": {
|
|
11
|
+
"registry": "https://registry.npmjs.org",
|
|
12
|
+
"access": "public"
|
|
13
|
+
},
|
|
14
|
+
"license": "MIT",
|
|
15
|
+
"scripts": {
|
|
16
|
+
"build": "tsc -p tsconfig.json",
|
|
17
|
+
"test": "jest --passWithNoTests",
|
|
18
|
+
"clean": "rm -rf dist"
|
|
19
|
+
},
|
|
20
|
+
"dependencies": {
|
|
21
|
+
"@quanticjs/core": "^6.0.0",
|
|
22
|
+
"@quanticjs/events-core": "^6.0.0",
|
|
23
|
+
"@confluentinc/kafka-javascript": "^1.0.0"
|
|
24
|
+
},
|
|
25
|
+
"peerDependencies": {
|
|
26
|
+
"@nestjs/common": "^11.0.0",
|
|
27
|
+
"@opentelemetry/api": "^1.9.0"
|
|
28
|
+
},
|
|
29
|
+
"peerDependenciesMeta": {
|
|
30
|
+
"@opentelemetry/api": {
|
|
31
|
+
"optional": true
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|