@aichatwar/shared 1.0.168 → 1.0.170
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/build/events/kafka/baseListener.d.ts +18 -0
- package/build/events/kafka/baseListener.js +49 -3
- package/build/events/types/subscriptionTier.d.ts +7 -0
- package/build/events/types/subscriptionTier.js +16 -0
- package/build/events/userEvents.d.ts +3 -0
- package/build/index.d.ts +1 -0
- package/build/index.js +1 -0
- package/package.json +1 -1
|
@@ -5,6 +5,20 @@ export declare abstract class Listener<T extends BaseEvent> {
|
|
|
5
5
|
abstract topic: T['subject'];
|
|
6
6
|
abstract groupId: string;
|
|
7
7
|
abstract onMessage(data: T['data'], payload: EachMessagePayload): Promise<void>;
|
|
8
|
+
private static registry;
|
|
9
|
+
/**
|
|
10
|
+
* Returns true only if every registered listener's consumer is connected.
|
|
11
|
+
* Wire this into your /ready endpoint alongside the MongoDB check.
|
|
12
|
+
*/
|
|
13
|
+
static allHealthy(): boolean;
|
|
14
|
+
/**
|
|
15
|
+
* Returns per-listener health details for diagnostics.
|
|
16
|
+
*/
|
|
17
|
+
static healthDetails(): Array<{
|
|
18
|
+
topic: string;
|
|
19
|
+
groupId: string;
|
|
20
|
+
healthy: boolean;
|
|
21
|
+
}>;
|
|
8
22
|
protected consumer: Consumer;
|
|
9
23
|
protected ackDeadline: number;
|
|
10
24
|
protected fromBeginning: boolean;
|
|
@@ -16,7 +30,11 @@ export declare abstract class Listener<T extends BaseEvent> {
|
|
|
16
30
|
private isListening;
|
|
17
31
|
private crashHandlerSetup;
|
|
18
32
|
private crashRestartCount;
|
|
33
|
+
private readonly maxCrashRestarts;
|
|
19
34
|
private readonly maxCrashRestartDelay;
|
|
35
|
+
private lastSuccessfulMessageAt;
|
|
36
|
+
/** Expose consumer health for readiness probes */
|
|
37
|
+
get healthy(): boolean;
|
|
20
38
|
constructor(consumer: Consumer);
|
|
21
39
|
private setupCrashHandler;
|
|
22
40
|
ack(payload?: EachMessagePayload): Promise<void>;
|
|
@@ -11,6 +11,29 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
11
11
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
12
|
exports.Listener = void 0;
|
|
13
13
|
class Listener {
|
|
14
|
+
/**
|
|
15
|
+
* Returns true only if every registered listener's consumer is connected.
|
|
16
|
+
* Wire this into your /ready endpoint alongside the MongoDB check.
|
|
17
|
+
*/
|
|
18
|
+
static allHealthy() {
|
|
19
|
+
if (Listener.registry.length === 0)
|
|
20
|
+
return true;
|
|
21
|
+
return Listener.registry.every(l => l.healthy);
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Returns per-listener health details for diagnostics.
|
|
25
|
+
*/
|
|
26
|
+
static healthDetails() {
|
|
27
|
+
return Listener.registry.map(l => ({
|
|
28
|
+
topic: l.topic,
|
|
29
|
+
groupId: l.groupId,
|
|
30
|
+
healthy: l.healthy,
|
|
31
|
+
}));
|
|
32
|
+
}
|
|
33
|
+
/** Expose consumer health for readiness probes */
|
|
34
|
+
get healthy() {
|
|
35
|
+
return this.isListening;
|
|
36
|
+
}
|
|
14
37
|
constructor(consumer) {
|
|
15
38
|
this.ackDeadline = 5 * 1000; // 5 seconds
|
|
16
39
|
this.fromBeginning = false; // Override in subclasses to read from beginning
|
|
@@ -21,8 +44,11 @@ class Listener {
|
|
|
21
44
|
this.isListening = false; // Track if listener is active
|
|
22
45
|
this.crashHandlerSetup = false; // Track if crash handler has been set up
|
|
23
46
|
this.crashRestartCount = 0;
|
|
47
|
+
this.maxCrashRestarts = 5; // Exit process after this many consecutive crashes
|
|
24
48
|
this.maxCrashRestartDelay = 120000; // 2 minutes cap
|
|
49
|
+
this.lastSuccessfulMessageAt = 0; // Timestamp of last successfully processed message
|
|
25
50
|
this.consumer = consumer;
|
|
51
|
+
Listener.registry.push(this);
|
|
26
52
|
this.setupCrashHandler();
|
|
27
53
|
}
|
|
28
54
|
setupCrashHandler() {
|
|
@@ -37,21 +63,34 @@ class Listener {
|
|
|
37
63
|
return;
|
|
38
64
|
}
|
|
39
65
|
this.crashRestartCount++;
|
|
66
|
+
this.isListening = false;
|
|
67
|
+
if (this.crashRestartCount >= this.maxCrashRestarts) {
|
|
68
|
+
console.error(`💀 [${this.topic}] Consumer crashed ${this.crashRestartCount} times without recovery. ` +
|
|
69
|
+
`Exiting process to let Kubernetes restart the pod with a fresh state.`);
|
|
70
|
+
process.exit(1);
|
|
71
|
+
}
|
|
40
72
|
const delay = Math.min(5000 * Math.pow(2, Math.min(this.crashRestartCount - 1, 5)), this.maxCrashRestartDelay);
|
|
41
|
-
console.error(`[${this.topic}] Consumer crashed (restart #${this.crashRestartCount}, ` +
|
|
73
|
+
console.error(`[${this.topic}] Consumer crashed (restart #${this.crashRestartCount}/${this.maxCrashRestarts}, ` +
|
|
42
74
|
`retrying in ${delay}ms):`, error);
|
|
43
|
-
this.isListening = false;
|
|
44
75
|
try {
|
|
45
76
|
yield this.consumer.disconnect();
|
|
46
77
|
}
|
|
47
78
|
catch (_) { /* best effort */ }
|
|
48
79
|
setTimeout(() => {
|
|
49
|
-
console.log(`[${this.topic}] Auto-restarting consumer after crash (attempt #${this.crashRestartCount})...`);
|
|
80
|
+
console.log(`[${this.topic}] Auto-restarting consumer after crash (attempt #${this.crashRestartCount}/${this.maxCrashRestarts})...`);
|
|
50
81
|
this.listen().catch((err) => {
|
|
51
82
|
console.error(`[${this.topic}] Auto-restart failed:`, err);
|
|
83
|
+
console.error(`💀 [${this.topic}] Auto-restart listen() threw. Exiting process to let Kubernetes restart the pod.`);
|
|
84
|
+
process.exit(1);
|
|
52
85
|
});
|
|
53
86
|
}, delay);
|
|
54
87
|
}));
|
|
88
|
+
this.consumer.on('consumer.disconnect', () => {
|
|
89
|
+
if (this.isListening) {
|
|
90
|
+
console.warn(`⚠️ [${this.topic}] Consumer disconnected unexpectedly`);
|
|
91
|
+
this.isListening = false;
|
|
92
|
+
}
|
|
93
|
+
});
|
|
55
94
|
this.crashHandlerSetup = true;
|
|
56
95
|
}
|
|
57
96
|
// Manual acknowledgment method
|
|
@@ -67,6 +106,12 @@ class Listener {
|
|
|
67
106
|
partition: targetPayload.partition,
|
|
68
107
|
offset: (BigInt(targetPayload.message.offset) + BigInt(1)).toString()
|
|
69
108
|
}]);
|
|
109
|
+
// Reset crash counter on successful processing — proves the consumer is healthy
|
|
110
|
+
if (this.crashRestartCount > 0) {
|
|
111
|
+
console.log(`[${this.topic}] Crash counter reset (was ${this.crashRestartCount}) after successful message processing`);
|
|
112
|
+
this.crashRestartCount = 0;
|
|
113
|
+
}
|
|
114
|
+
this.lastSuccessfulMessageAt = Date.now();
|
|
70
115
|
console.log(`Message manually acknowledged for topic: ${this.topic}`);
|
|
71
116
|
});
|
|
72
117
|
}
|
|
@@ -198,3 +243,4 @@ class Listener {
|
|
|
198
243
|
}
|
|
199
244
|
}
|
|
200
245
|
exports.Listener = Listener;
|
|
246
|
+
Listener.registry = [];
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.AGENT_LIMITS_BY_TIER = exports.SubscriptionTier = void 0;
|
|
4
|
+
var SubscriptionTier;
|
|
5
|
+
(function (SubscriptionTier) {
|
|
6
|
+
SubscriptionTier["Free"] = "free";
|
|
7
|
+
SubscriptionTier["Basic"] = "basic";
|
|
8
|
+
SubscriptionTier["Pro"] = "pro";
|
|
9
|
+
SubscriptionTier["Enterprise"] = "enterprise";
|
|
10
|
+
})(SubscriptionTier || (exports.SubscriptionTier = SubscriptionTier = {}));
|
|
11
|
+
exports.AGENT_LIMITS_BY_TIER = {
|
|
12
|
+
[SubscriptionTier.Free]: 2,
|
|
13
|
+
[SubscriptionTier.Basic]: 3,
|
|
14
|
+
[SubscriptionTier.Pro]: 5,
|
|
15
|
+
[SubscriptionTier.Enterprise]: 50,
|
|
16
|
+
};
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { Subjects } from "./subjects";
|
|
2
2
|
import { BaseEvent } from "./baseEvent";
|
|
3
3
|
import { UserStatus } from "./types/userStatus";
|
|
4
|
+
import { SubscriptionTier } from "./types/subscriptionTier";
|
|
4
5
|
interface UserCreatedEvent extends BaseEvent {
|
|
5
6
|
subject: Subjects.UserCreated;
|
|
6
7
|
data: {
|
|
@@ -11,6 +12,7 @@ interface UserCreatedEvent extends BaseEvent {
|
|
|
11
12
|
isAgent?: boolean;
|
|
12
13
|
ownerUserId?: string;
|
|
13
14
|
role?: 'user' | 'admin';
|
|
15
|
+
subscriptionTier?: SubscriptionTier;
|
|
14
16
|
};
|
|
15
17
|
}
|
|
16
18
|
interface UserUpdatedEvent extends BaseEvent {
|
|
@@ -21,6 +23,7 @@ interface UserUpdatedEvent extends BaseEvent {
|
|
|
21
23
|
status: UserStatus;
|
|
22
24
|
version: number;
|
|
23
25
|
role?: 'user' | 'admin';
|
|
26
|
+
subscriptionTier?: SubscriptionTier;
|
|
24
27
|
};
|
|
25
28
|
}
|
|
26
29
|
interface UserSingedInEvent extends BaseEvent {
|
package/build/index.d.ts
CHANGED
|
@@ -36,6 +36,7 @@ export * from "./events/types/friendshipStatus";
|
|
|
36
36
|
export * from "./events/types/userStatus";
|
|
37
37
|
export * from "./events/types/visibility";
|
|
38
38
|
export * from "./events/types/postStatus";
|
|
39
|
+
export * from "./events/types/subscriptionTier";
|
|
39
40
|
export * from "./events/userEvents";
|
|
40
41
|
export * from "./events/mediaEvents";
|
|
41
42
|
export * from "./events/agentManagerEvents";
|
package/build/index.js
CHANGED
|
@@ -52,6 +52,7 @@ __exportStar(require("./events/types/friendshipStatus"), exports);
|
|
|
52
52
|
__exportStar(require("./events/types/userStatus"), exports);
|
|
53
53
|
__exportStar(require("./events/types/visibility"), exports);
|
|
54
54
|
__exportStar(require("./events/types/postStatus"), exports);
|
|
55
|
+
__exportStar(require("./events/types/subscriptionTier"), exports);
|
|
55
56
|
__exportStar(require("./events/userEvents"), exports);
|
|
56
57
|
__exportStar(require("./events/mediaEvents"), exports);
|
|
57
58
|
__exportStar(require("./events/agentManagerEvents"), exports);
|