@uber-clone/common 1.0.3 → 1.0.5

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.
@@ -0,0 +1,20 @@
1
+ export declare class KafkaClient {
2
+ private kafka;
3
+ private producer;
4
+ private consumer;
5
+ private admin;
6
+ private isConnected;
7
+ private config;
8
+ constructor();
9
+ private getKafkaConfig;
10
+ connect(): Promise<void>;
11
+ private ensureTopics;
12
+ publish(topic: string, event: any): Promise<void>;
13
+ subscribe(topic: string, handler: (event: any) => Promise<void>, options?: {
14
+ fromBeginning?: boolean;
15
+ }): Promise<void>;
16
+ private ensureConnection;
17
+ disconnect(): Promise<void>;
18
+ healthCheck(): Promise<boolean>;
19
+ getConnectionStatus(): boolean;
20
+ }
@@ -0,0 +1,287 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.KafkaClient = void 0;
13
+ const kafkajs_1 = require("kafkajs");
14
+ const subjects_1 = require("./subjects");
15
+ class KafkaClient {
16
+ constructor() {
17
+ this.isConnected = false;
18
+ this.config = this.getKafkaConfig();
19
+ this.kafka = new kafkajs_1.Kafka({
20
+ clientId: this.config.clientId,
21
+ brokers: this.config.brokers,
22
+ logLevel: kafkajs_1.logLevel.ERROR,
23
+ retry: {
24
+ initialRetryTime: this.config.initialRetryTime,
25
+ retries: this.config.retries,
26
+ },
27
+ connectionTimeout: 30000,
28
+ requestTimeout: 30000,
29
+ });
30
+ this.producer = this.kafka.producer({
31
+ allowAutoTopicCreation: this.config.enableAutoTopicCreation,
32
+ transactionTimeout: this.config.transactionTimeout,
33
+ maxInFlightRequests: 1,
34
+ idempotent: true,
35
+ });
36
+ this.consumer = this.kafka.consumer({
37
+ groupId: this.config.consumerGroup,
38
+ sessionTimeout: this.config.sessionTimeout,
39
+ heartbeatInterval: this.config.heartbeatInterval,
40
+ allowAutoTopicCreation: this.config.enableAutoTopicCreation,
41
+ });
42
+ this.admin = this.kafka.admin();
43
+ }
44
+ getKafkaConfig() {
45
+ return {
46
+ clientId: process.env.KAFKA_CLIENT_ID || "uber-service",
47
+ brokers: (process.env.KAFKA_BROKERS || "localhost:9092")
48
+ .split(",")
49
+ .map((b) => b.trim()),
50
+ consumerGroup: process.env.KAFKA_CONSUMER_GROUP || "uber-group",
51
+ enableAutoTopicCreation: process.env.KAFKA_AUTO_TOPIC_CREATION === "true",
52
+ sessionTimeout: parseInt(process.env.KAFKA_SESSION_TIMEOUT || "30000"),
53
+ heartbeatInterval: parseInt(process.env.KAFKA_HEARTBEAT_INTERVAL || "10000"),
54
+ transactionTimeout: parseInt(process.env.KAFKA_TRANSACTION_TIMEOUT || "30000"),
55
+ retries: parseInt(process.env.KAFKA_RETRIES || "8"),
56
+ initialRetryTime: parseInt(process.env.KAFKA_INITIAL_RETRY_TIME || "100"),
57
+ };
58
+ }
59
+ connect() {
60
+ return __awaiter(this, void 0, void 0, function* () {
61
+ if (this.isConnected)
62
+ return;
63
+ console.log(`Connecting to Kafka with brokers: ${this.config.brokers.join(", ")}`);
64
+ try {
65
+ yield this.producer.connect();
66
+ yield this.consumer.connect();
67
+ yield this.admin.connect();
68
+ this.isConnected = true;
69
+ console.log("Successfully connected to Kafka");
70
+ }
71
+ catch (error) {
72
+ console.error("Failed to connect to Kafka:", error);
73
+ throw new Error(`Kafka connection failed: ${error instanceof Error ? error.message : "Unknown error"}`);
74
+ }
75
+ yield this.ensureTopics();
76
+ });
77
+ }
78
+ ensureTopics() {
79
+ return __awaiter(this, void 0, void 0, function* () {
80
+ const topics = [
81
+ // User events
82
+ { topic: subjects_1.Subjects.UserCreated, partitions: 3, replicationFactor: 3 },
83
+ { topic: subjects_1.Subjects.UserUpdated, partitions: 3, replicationFactor: 3 },
84
+ { topic: subjects_1.Subjects.UserDeleted, partitions: 3, replicationFactor: 3 },
85
+ {
86
+ topic: subjects_1.Subjects.UserProfileUpdated,
87
+ partitions: 3,
88
+ replicationFactor: 3,
89
+ },
90
+ // Auth events
91
+ { topic: subjects_1.Subjects.UserSignedIn, partitions: 3, replicationFactor: 3 },
92
+ { topic: subjects_1.Subjects.UserSignedOut, partitions: 3, replicationFactor: 3 },
93
+ {
94
+ topic: subjects_1.Subjects.UserPasswordChanged,
95
+ partitions: 3,
96
+ replicationFactor: 3,
97
+ },
98
+ {
99
+ topic: subjects_1.Subjects.UserAccountLocked,
100
+ partitions: 3,
101
+ replicationFactor: 3,
102
+ },
103
+ // // Ride events
104
+ // { topic: Subjects.RideRequested, partitions: 6, replicationFactor: 3 },
105
+ // { topic: Subjects.RideAccepted, partitions: 6, replicationFactor: 3 },
106
+ // { topic: Subjects.RideStarted, partitions: 6, replicationFactor: 3 },
107
+ // { topic: Subjects.RideCompleted, partitions: 6, replicationFactor: 3 },
108
+ // { topic: Subjects.RideCancelled, partitions: 6, replicationFactor: 3 },
109
+ // // Driver events
110
+ // { topic: Subjects.DriverOnline, partitions: 6, replicationFactor: 3 },
111
+ // { topic: Subjects.DriverOffline, partitions: 6, replicationFactor: 3 },
112
+ // {
113
+ // topic: Subjects.DriverLocationUpdated,
114
+ // partitions: 12,
115
+ // replicationFactor: 3,
116
+ // },
117
+ // {
118
+ // topic: Subjects.DriverRideAccepted,
119
+ // partitions: 6,
120
+ // replicationFactor: 3,
121
+ // },
122
+ // {
123
+ // topic: Subjects.DriverRideCompleted,
124
+ // partitions: 6,
125
+ // replicationFactor: 3,
126
+ // },
127
+ // // Payment events
128
+ // { topic: Subjects.PaymentInitiated, partitions: 4, replicationFactor: 3 },
129
+ // { topic: Subjects.PaymentCompleted, partitions: 4, replicationFactor: 3 },
130
+ // { topic: Subjects.PaymentFailed, partitions: 4, replicationFactor: 3 },
131
+ // { topic: Subjects.PaymentRefunded, partitions: 4, replicationFactor: 3 },
132
+ // // Notification events
133
+ // {
134
+ // topic: Subjects.NotificationEmail,
135
+ // partitions: 4,
136
+ // replicationFactor: 3,
137
+ // },
138
+ // { topic: Subjects.NotificationSMS, partitions: 4, replicationFactor: 3 },
139
+ // { topic: Subjects.NotificationPush, partitions: 4, replicationFactor: 3 },
140
+ // {
141
+ // topic: Subjects.NotificationInApp,
142
+ // partitions: 4,
143
+ // replicationFactor: 3,
144
+ // },
145
+ ];
146
+ try {
147
+ const existingTopics = yield this.admin.listTopics();
148
+ const topicsToCreate = topics.filter((t) => !existingTopics.includes(t.topic));
149
+ if (topicsToCreate.length > 0) {
150
+ console.log(`Creating Kafka topics: ${topicsToCreate
151
+ .map((t) => t.topic)
152
+ .join(", ")}`);
153
+ yield this.admin.createTopics({
154
+ topics: topicsToCreate,
155
+ waitForLeaders: true,
156
+ });
157
+ console.log(`Successfully created topics: ${topicsToCreate
158
+ .map((t) => t.topic)
159
+ .join(", ")}`);
160
+ }
161
+ else {
162
+ console.log("All required Kafka topics already exist");
163
+ }
164
+ }
165
+ catch (error) {
166
+ console.error("Error creating topics:", error);
167
+ throw new Error(`Failed to create Kafka topics: ${error instanceof Error ? error.message : "Unknown error"}`);
168
+ }
169
+ });
170
+ }
171
+ publish(topic, event) {
172
+ return __awaiter(this, void 0, void 0, function* () {
173
+ yield this.ensureConnection();
174
+ if (!event || typeof event !== "object") {
175
+ throw new Error("Event must be a valid object");
176
+ }
177
+ try {
178
+ const message = {
179
+ topic,
180
+ messages: [
181
+ {
182
+ key: event.correlationId || event.eventId || event.id || "default-key",
183
+ value: JSON.stringify(event),
184
+ headers: {
185
+ "event-type": event.type || "unknown",
186
+ source: event.source || "unknown-service",
187
+ timestamp: event.timestamp
188
+ ? event.timestamp.toISOString()
189
+ : new Date().toISOString(),
190
+ version: event.version ? event.version.toString() : "1.0",
191
+ },
192
+ },
193
+ ],
194
+ };
195
+ const result = yield this.producer.send(message);
196
+ console.log(`Published event to topic ${topic}:`, {
197
+ partition: result[0].partition,
198
+ offset: result[0].baseOffset,
199
+ eventType: event.type,
200
+ });
201
+ }
202
+ catch (error) {
203
+ console.error(`Failed to publish event to ${topic}:`, error);
204
+ throw new Error(`Failed to publish event to ${topic}: ${error instanceof Error ? error.message : "Unknown error"}`);
205
+ }
206
+ });
207
+ }
208
+ subscribe(topic, handler, options) {
209
+ return __awaiter(this, void 0, void 0, function* () {
210
+ yield this.ensureConnection();
211
+ try {
212
+ yield this.consumer.subscribe({
213
+ topic,
214
+ fromBeginning: (options === null || options === void 0 ? void 0 : options.fromBeginning) || false,
215
+ });
216
+ console.log(`Subscribed to topic: ${topic}`);
217
+ yield this.consumer.run({
218
+ eachMessage: (_a) => __awaiter(this, [_a], void 0, function* ({ topic, partition, message }) {
219
+ try {
220
+ if (!message.value) {
221
+ console.warn(`Received message with no value from ${topic}:${partition}`);
222
+ return;
223
+ }
224
+ const event = JSON.parse(message.value.toString());
225
+ console.log(`Received event from ${topic}:${partition}`, {
226
+ offset: message.offset,
227
+ eventType: event.type || "unknown",
228
+ });
229
+ yield handler(event);
230
+ }
231
+ catch (error) {
232
+ console.error(`Error processing message from ${topic}:${partition}:`, error);
233
+ // In production, you might want to implement dead letter queue or retry logic here
234
+ }
235
+ }),
236
+ });
237
+ }
238
+ catch (error) {
239
+ console.error(`Failed to subscribe to topic ${topic}:`, error);
240
+ throw new Error(`Failed to subscribe to topic ${topic}: ${error instanceof Error ? error.message : "Unknown error"}`);
241
+ }
242
+ });
243
+ }
244
+ ensureConnection() {
245
+ return __awaiter(this, void 0, void 0, function* () {
246
+ if (!this.isConnected) {
247
+ yield this.connect();
248
+ }
249
+ });
250
+ }
251
+ disconnect() {
252
+ return __awaiter(this, void 0, void 0, function* () {
253
+ try {
254
+ if (this.isConnected) {
255
+ yield Promise.allSettled([
256
+ this.producer.disconnect(),
257
+ this.consumer.disconnect(),
258
+ this.admin.disconnect(),
259
+ ]);
260
+ this.isConnected = false;
261
+ console.log("Disconnected from Kafka");
262
+ }
263
+ }
264
+ catch (error) {
265
+ console.error("Error disconnecting from Kafka:", error);
266
+ }
267
+ });
268
+ }
269
+ // Health check method
270
+ healthCheck() {
271
+ return __awaiter(this, void 0, void 0, function* () {
272
+ try {
273
+ yield this.admin.describeCluster();
274
+ return this.isConnected;
275
+ }
276
+ catch (error) {
277
+ console.error("Kafka health check failed:", error);
278
+ return false;
279
+ }
280
+ });
281
+ }
282
+ // Get connection status
283
+ getConnectionStatus() {
284
+ return this.isConnected;
285
+ }
286
+ }
287
+ exports.KafkaClient = KafkaClient;
@@ -1,3 +1,28 @@
1
1
  export declare enum Subjects {
2
- UserCreated = "user-created"
2
+ UserCreated = "user.created",
3
+ UserUpdated = "user.updated",
4
+ UserDeleted = "user.deleted",
5
+ UserProfileUpdated = "user.profile-updated",
6
+ UserSignedIn = "user.signed-in",
7
+ UserSignedOut = "user.signed-out",
8
+ UserPasswordChanged = "user.password-changed",
9
+ UserAccountLocked = "user.account-locked",
10
+ RideRequested = "ride.requested",
11
+ RideAccepted = "ride.accepted",
12
+ RideStarted = "ride.started",
13
+ RideCompleted = "ride.completed",
14
+ RideCancelled = "ride.cancelled",
15
+ DriverOnline = "driver.online",
16
+ DriverOffline = "driver.offline",
17
+ DriverLocationUpdated = "driver.location-updated",
18
+ DriverRideAccepted = "driver.ride-accepted",
19
+ DriverRideCompleted = "driver.ride-completed",
20
+ PaymentInitiated = "payment.initiated",
21
+ PaymentCompleted = "payment.completed",
22
+ PaymentFailed = "payment.failed",
23
+ PaymentRefunded = "payment.refunded",
24
+ NotificationEmail = "notification.email",
25
+ NotificationSMS = "notification.sms",
26
+ NotificationPush = "notification.push",
27
+ NotificationInApp = "notification.in-app"
3
28
  }
@@ -1,7 +1,39 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.Subjects = void 0;
4
+ // Subjects enum
4
5
  var Subjects;
5
6
  (function (Subjects) {
6
- Subjects["UserCreated"] = "user-created";
7
+ // User events
8
+ Subjects["UserCreated"] = "user.created";
9
+ Subjects["UserUpdated"] = "user.updated";
10
+ Subjects["UserDeleted"] = "user.deleted";
11
+ Subjects["UserProfileUpdated"] = "user.profile-updated";
12
+ // Auth events
13
+ Subjects["UserSignedIn"] = "user.signed-in";
14
+ Subjects["UserSignedOut"] = "user.signed-out";
15
+ Subjects["UserPasswordChanged"] = "user.password-changed";
16
+ Subjects["UserAccountLocked"] = "user.account-locked";
17
+ // Ride events
18
+ Subjects["RideRequested"] = "ride.requested";
19
+ Subjects["RideAccepted"] = "ride.accepted";
20
+ Subjects["RideStarted"] = "ride.started";
21
+ Subjects["RideCompleted"] = "ride.completed";
22
+ Subjects["RideCancelled"] = "ride.cancelled";
23
+ // Driver events
24
+ Subjects["DriverOnline"] = "driver.online";
25
+ Subjects["DriverOffline"] = "driver.offline";
26
+ Subjects["DriverLocationUpdated"] = "driver.location-updated";
27
+ Subjects["DriverRideAccepted"] = "driver.ride-accepted";
28
+ Subjects["DriverRideCompleted"] = "driver.ride-completed";
29
+ // Payment events
30
+ Subjects["PaymentInitiated"] = "payment.initiated";
31
+ Subjects["PaymentCompleted"] = "payment.completed";
32
+ Subjects["PaymentFailed"] = "payment.failed";
33
+ Subjects["PaymentRefunded"] = "payment.refunded";
34
+ // Notification events
35
+ Subjects["NotificationEmail"] = "notification.email";
36
+ Subjects["NotificationSMS"] = "notification.sms";
37
+ Subjects["NotificationPush"] = "notification.push";
38
+ Subjects["NotificationInApp"] = "notification.in-app";
7
39
  })(Subjects || (exports.Subjects = Subjects = {}));
@@ -0,0 +1,88 @@
1
+ import { Subjects } from "./subjects";
2
+ export interface BaseEvent {
3
+ type: Subjects;
4
+ source: string;
5
+ timestamp: Date;
6
+ version: string;
7
+ data: any;
8
+ correlationId?: string;
9
+ eventId?: string;
10
+ id?: string;
11
+ }
12
+ export interface UserEvent extends BaseEvent {
13
+ type: Subjects.UserCreated | Subjects.UserUpdated | Subjects.UserDeleted | Subjects.UserProfileUpdated;
14
+ data: {
15
+ userId: string;
16
+ email: string;
17
+ name?: string;
18
+ phone?: string;
19
+ [key: string]: any;
20
+ };
21
+ }
22
+ export interface AuthEvent extends BaseEvent {
23
+ type: Subjects.UserSignedIn | Subjects.UserSignedOut | Subjects.UserPasswordChanged | Subjects.UserAccountLocked;
24
+ data: {
25
+ userId: string;
26
+ email: string;
27
+ ipAddress?: string;
28
+ userAgent?: string;
29
+ [key: string]: any;
30
+ };
31
+ }
32
+ export interface RideEvent extends BaseEvent {
33
+ type: Subjects.RideRequested | Subjects.RideAccepted | Subjects.RideStarted | Subjects.RideCompleted | Subjects.RideCancelled;
34
+ data: {
35
+ rideId: string;
36
+ passengerId: string;
37
+ driverId?: string;
38
+ pickupLocation: {
39
+ lat: number;
40
+ lng: number;
41
+ address?: string;
42
+ };
43
+ destinationLocation: {
44
+ lat: number;
45
+ lng: number;
46
+ address?: string;
47
+ };
48
+ estimatedFare?: number;
49
+ [key: string]: any;
50
+ };
51
+ }
52
+ export interface DriverEvent extends BaseEvent {
53
+ type: Subjects.DriverOnline | Subjects.DriverOffline | Subjects.DriverLocationUpdated | Subjects.DriverRideAccepted | Subjects.DriverRideCompleted;
54
+ data: {
55
+ driverId: string;
56
+ location?: {
57
+ lat: number;
58
+ lng: number;
59
+ };
60
+ rideId?: string;
61
+ [key: string]: any;
62
+ };
63
+ }
64
+ export interface PaymentEvent extends BaseEvent {
65
+ type: Subjects.PaymentInitiated | Subjects.PaymentCompleted | Subjects.PaymentFailed | Subjects.PaymentRefunded;
66
+ data: {
67
+ paymentId: string;
68
+ userId: string;
69
+ rideId?: string;
70
+ amount: number;
71
+ currency: string;
72
+ paymentMethod: string;
73
+ status: string;
74
+ [key: string]: any;
75
+ };
76
+ }
77
+ export interface NotificationEvent extends BaseEvent {
78
+ type: Subjects.NotificationEmail | Subjects.NotificationSMS | Subjects.NotificationPush | Subjects.NotificationInApp;
79
+ data: {
80
+ userId: string;
81
+ notificationType: string;
82
+ title: string;
83
+ message: string;
84
+ metadata?: any;
85
+ [key: string]: any;
86
+ };
87
+ }
88
+ export type UberEvent = UserEvent | AuthEvent | RideEvent | DriverEvent | PaymentEvent | NotificationEvent;
package/build/index.d.ts CHANGED
@@ -8,7 +8,6 @@ export * from "./middlewares/current-user";
8
8
  export * from "./middlewares/error-handler";
9
9
  export * from "./middlewares/require-auth";
10
10
  export * from "./middlewares/validate-request";
11
- export * from "./events/kafka-listener";
12
- export * from "./events/kafka-publisher";
11
+ export * from "./events/kafka-client";
12
+ export * from "./events/types";
13
13
  export * from "./events/subjects";
14
- export * from "./events/event-types/user-created-event";
package/build/index.js CHANGED
@@ -24,7 +24,8 @@ __exportStar(require("./middlewares/current-user"), exports);
24
24
  __exportStar(require("./middlewares/error-handler"), exports);
25
25
  __exportStar(require("./middlewares/require-auth"), exports);
26
26
  __exportStar(require("./middlewares/validate-request"), exports);
27
- __exportStar(require("./events/kafka-listener"), exports);
28
- __exportStar(require("./events/kafka-publisher"), exports);
27
+ // export * from "./events/kafka-listener";
28
+ // export * from "./events/kafka-publisher";
29
+ __exportStar(require("./events/kafka-client"), exports);
30
+ __exportStar(require("./events/types"), exports);
29
31
  __exportStar(require("./events/subjects"), exports);
30
- __exportStar(require("./events/event-types/user-created-event"), exports);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@uber-clone/common",
3
- "version": "1.0.3",
3
+ "version": "1.0.5",
4
4
  "main": "./build/index.js",
5
5
  "types": "./build/index.d.ts",
6
6
  "files": [
@@ -1,9 +0,0 @@
1
- import { Subjects } from "../subjects";
2
- export interface UserCreatedEvent {
3
- subject: Subjects.UserCreated;
4
- data: {
5
- id: string;
6
- name: string;
7
- email: number;
8
- };
9
- }