@aichatwar/shared 1.0.101 → 1.0.103

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.
@@ -7,6 +7,8 @@ export declare abstract class Listener<T extends BaseEvent> {
7
7
  protected consumer: Consumer;
8
8
  protected ackDeadline: number;
9
9
  private currentPayload?;
10
+ private retryCount;
11
+ private readonly maxRetries;
10
12
  constructor(consumer: Consumer);
11
13
  ack(): Promise<void>;
12
14
  listen(): Promise<void>;
@@ -13,6 +13,8 @@ exports.Listener = void 0;
13
13
  class Listener {
14
14
  constructor(consumer) {
15
15
  this.ackDeadline = 5 * 1000; // 5 seconds
16
+ this.retryCount = 0;
17
+ this.maxRetries = 5;
16
18
  this.consumer = consumer;
17
19
  }
18
20
  // Manual acknowledgment method
@@ -31,36 +33,74 @@ class Listener {
31
33
  }
32
34
  listen() {
33
35
  return __awaiter(this, void 0, void 0, function* () {
34
- yield this.consumer.connect();
35
- yield this.consumer.subscribe({
36
- topic: this.topic,
37
- fromBeginning: false,
38
- });
39
- console.log(`Listening to topic: ${this.topic} with groupId: ${this.groupId}`);
40
- yield this.consumer.run({
41
- eachMessage: (payload) => __awaiter(this, void 0, void 0, function* () {
42
- if (!payload.message.value)
36
+ var _a;
37
+ try {
38
+ yield this.consumer.connect();
39
+ // Handle consumer errors (including partition errors)
40
+ this.consumer.on('consumer.crash', (event) => {
41
+ var _a;
42
+ const error = event.payload.error;
43
+ if ((_a = error === null || error === void 0 ? void 0 : error.message) === null || _a === void 0 ? void 0 : _a.includes('does not host this topic-partition')) {
44
+ console.warn(`[${this.topic}] Consumer partition error (non-fatal):`, error.message);
45
+ // Don't crash - this is often a transient error
43
46
  return;
44
- console.log(`Message received -> topic: ${this.topic}, groupId: ${this.groupId}`);
45
- // Store current payload for manual ack
46
- this.currentPayload = payload;
47
- try {
48
- const data = JSON.parse(payload.message.value.toString());
49
- yield this.onMessage(data, payload);
50
- // Note: Child listeners must call this.ack() manually after successful processing
51
- // If they don't call ack(), the message will be redelivered
52
47
  }
53
- catch (error) {
54
- console.error(`Error processing message for topic: ${this.topic}`, error);
55
- // In case of error, we don't commit the offset, so the message will be redelivered
56
- throw error;
48
+ console.error(`[${this.topic}] Consumer crashed:`, error);
49
+ });
50
+ yield this.consumer.subscribe({
51
+ topic: this.topic,
52
+ fromBeginning: false,
53
+ });
54
+ // Reset retry count on successful connection
55
+ this.retryCount = 0;
56
+ console.log(`Listening to topic: ${this.topic} with groupId: ${this.groupId}`);
57
+ yield this.consumer.run({
58
+ eachMessage: (payload) => __awaiter(this, void 0, void 0, function* () {
59
+ if (!payload.message.value)
60
+ return;
61
+ console.log(`Message received -> topic: ${this.topic}, groupId: ${this.groupId}`);
62
+ // Store current payload for manual ack
63
+ this.currentPayload = payload;
64
+ try {
65
+ const data = JSON.parse(payload.message.value.toString());
66
+ yield this.onMessage(data, payload);
67
+ // Note: Child listeners must call this.ack() manually after successful processing
68
+ // If they don't call ack(), the message will be redelivered
69
+ }
70
+ catch (error) {
71
+ console.error(`Error processing message for topic: ${this.topic}`, error);
72
+ // In case of error, we don't commit the offset, so the message will be redelivered
73
+ throw error;
74
+ }
75
+ finally {
76
+ // Clear the current payload
77
+ this.currentPayload = undefined;
78
+ }
79
+ })
80
+ });
81
+ }
82
+ catch (error) {
83
+ // Handle connection/subscription errors gracefully
84
+ // The "This server does not host this topic-partition" error is often harmless
85
+ // and occurs when KafkaJS tries to list offsets for a topic that doesn't exist yet or has no messages
86
+ if ((_a = error === null || error === void 0 ? void 0 : error.message) === null || _a === void 0 ? void 0 : _a.includes('does not host this topic-partition')) {
87
+ this.retryCount++;
88
+ if (this.retryCount <= this.maxRetries) {
89
+ console.warn(`Topic partition error for ${this.topic} (attempt ${this.retryCount}/${this.maxRetries}):`, error.message);
90
+ // Retry subscription after a delay (exponential backoff)
91
+ const delay = Math.min(5000 * Math.pow(2, this.retryCount - 1), 30000);
92
+ setTimeout(() => this.listen(), delay);
93
+ return;
57
94
  }
58
- finally {
59
- // Clear the current payload
60
- this.currentPayload = undefined;
95
+ else {
96
+ console.error(`Max retries reached for topic ${this.topic}. Error:`, error.message);
97
+ // Don't throw - just log and continue (service will still work for other topics)
98
+ return;
61
99
  }
62
- })
63
- });
100
+ }
101
+ console.error(`Error setting up listener for topic: ${this.topic}`, error);
102
+ throw error;
103
+ }
64
104
  });
65
105
  }
66
106
  }
@@ -0,0 +1,11 @@
1
+ import { Subjects } from './subjects';
2
+ export interface MessageIngestEvent {
3
+ subject: Subjects.MessageIngest;
4
+ data: {
5
+ roomId: string;
6
+ content: string;
7
+ senderId: string;
8
+ senderType: string;
9
+ tempId?: string;
10
+ };
11
+ }
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,10 @@
1
+ import { Subjects } from './subjects';
2
+ export interface PresenceUpdatedEvent {
3
+ subject: Subjects.PresenceUpdated;
4
+ data: {
5
+ userId: string;
6
+ roomId: string;
7
+ status: 'online' | 'offline' | 'typing' | 'idle';
8
+ timestamp?: string;
9
+ };
10
+ }
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -31,5 +31,7 @@ export declare enum Subjects {
31
31
  MessageCreated = "message.created",
32
32
  MessageUpdated = "message.updated",
33
33
  MessageRead = "message.read",
34
- MessageDeleted = "message.deleted"
34
+ MessageDeleted = "message.deleted",
35
+ MessageIngest = "message.ingest",
36
+ PresenceUpdated = "presence.updated"
35
37
  }
@@ -36,4 +36,6 @@ var Subjects;
36
36
  Subjects["MessageUpdated"] = "message.updated";
37
37
  Subjects["MessageRead"] = "message.read";
38
38
  Subjects["MessageDeleted"] = "message.deleted";
39
+ Subjects["MessageIngest"] = "message.ingest";
40
+ Subjects["PresenceUpdated"] = "presence.updated";
39
41
  })(Subjects || (exports.Subjects = Subjects = {}));
package/build/index.d.ts CHANGED
@@ -21,6 +21,8 @@ export * from "./events/agentEvents";
21
21
  export * from "./events/commentEvents";
22
22
  export * from "./events/roomEvents";
23
23
  export * from "./events/messageEvents";
24
+ export * from "./events/messageIngestEvents";
25
+ export * from "./events/presenceEvents";
24
26
  export * from "./events/types/orderStatus";
25
27
  export * from "./events/types/friendshipStatus";
26
28
  export * from "./events/types/userStatus";
package/build/index.js CHANGED
@@ -37,6 +37,8 @@ __exportStar(require("./events/agentEvents"), exports);
37
37
  __exportStar(require("./events/commentEvents"), exports);
38
38
  __exportStar(require("./events/roomEvents"), exports);
39
39
  __exportStar(require("./events/messageEvents"), exports);
40
+ __exportStar(require("./events/messageIngestEvents"), exports);
41
+ __exportStar(require("./events/presenceEvents"), exports);
40
42
  __exportStar(require("./events/types/orderStatus"), exports);
41
43
  __exportStar(require("./events/types/friendshipStatus"), exports);
42
44
  __exportStar(require("./events/types/userStatus"), exports);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aichatwar/shared",
3
- "version": "1.0.101",
3
+ "version": "1.0.103",
4
4
  "main": "./build/index.js",
5
5
  "typs": "./build/index.d.ts",
6
6
  "files": [