@aerostack/sdk-web 0.6.6 → 0.6.7
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/commonjs/sdk/realtime.d.ts +43 -13
- package/dist/commonjs/sdk/realtime.d.ts.map +1 -1
- package/dist/commonjs/sdk/realtime.js +103 -18
- package/dist/commonjs/sdk/realtime.js.map +1 -1
- package/dist/esm/sdk/realtime.d.ts +43 -13
- package/dist/esm/sdk/realtime.d.ts.map +1 -1
- package/dist/esm/sdk/realtime.js +103 -18
- package/dist/esm/sdk/realtime.js.map +1 -1
- package/package.json +2 -2
- package/src/sdk/realtime.ts +145 -30
|
@@ -8,19 +8,37 @@ export interface RealtimeSubscriptionOptions {
|
|
|
8
8
|
event?: RealtimeEvent;
|
|
9
9
|
filter?: Record<string, any>;
|
|
10
10
|
}
|
|
11
|
-
export type RealtimeCallback = (payload:
|
|
12
|
-
export
|
|
11
|
+
export type RealtimeCallback<T = any> = (payload: RealtimePayload<T>) => void;
|
|
12
|
+
export interface RealtimePayload<T = any> {
|
|
13
|
+
type: 'db_change' | 'chat_message';
|
|
14
|
+
topic: string;
|
|
15
|
+
operation: RealtimeEvent;
|
|
16
|
+
data: T;
|
|
17
|
+
old?: T;
|
|
18
|
+
timestamp?: string;
|
|
19
|
+
[key: string]: any;
|
|
20
|
+
}
|
|
21
|
+
export declare class RealtimeSubscription<T = any> {
|
|
13
22
|
private client;
|
|
14
23
|
private topic;
|
|
15
24
|
private options;
|
|
16
25
|
private callbacks;
|
|
17
26
|
private isSubscribed;
|
|
18
27
|
constructor(client: RealtimeClient, topic: string, options?: RealtimeSubscriptionOptions);
|
|
19
|
-
on(event: RealtimeEvent, callback: RealtimeCallback): this;
|
|
28
|
+
on(event: RealtimeEvent, callback: RealtimeCallback<T>): this;
|
|
20
29
|
subscribe(): this;
|
|
21
30
|
unsubscribe(): void;
|
|
22
31
|
/** @internal */
|
|
23
|
-
_emit(payload:
|
|
32
|
+
_emit(payload: RealtimePayload<T>): void;
|
|
33
|
+
}
|
|
34
|
+
export type RealtimeStatus = 'idle' | 'connecting' | 'connected' | 'reconnecting' | 'disconnected';
|
|
35
|
+
export interface RealtimeClientOptions {
|
|
36
|
+
baseUrl: string;
|
|
37
|
+
projectId: string;
|
|
38
|
+
token?: string;
|
|
39
|
+
userId?: string;
|
|
40
|
+
apiKey?: string;
|
|
41
|
+
maxReconnectAttempts?: number;
|
|
24
42
|
}
|
|
25
43
|
export declare class RealtimeClient {
|
|
26
44
|
private baseUrl;
|
|
@@ -33,23 +51,35 @@ export declare class RealtimeClient {
|
|
|
33
51
|
private reconnectTimer;
|
|
34
52
|
private heartbeatTimer;
|
|
35
53
|
private reconnectAttempts;
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
54
|
+
private _sendQueue;
|
|
55
|
+
private _connectingPromise;
|
|
56
|
+
private _status;
|
|
57
|
+
private _statusListeners;
|
|
58
|
+
private _lastPong;
|
|
59
|
+
private _maxReconnectAttempts;
|
|
60
|
+
private _maxRetriesListeners;
|
|
61
|
+
constructor(options: RealtimeClientOptions);
|
|
62
|
+
get status(): RealtimeStatus;
|
|
63
|
+
onStatusChange(cb: (status: RealtimeStatus) => void): () => void;
|
|
64
|
+
onMaxRetriesExceeded(cb: () => void): () => void;
|
|
65
|
+
private _setStatus;
|
|
66
|
+
setToken(newToken: string): void;
|
|
43
67
|
connect(): Promise<void>;
|
|
68
|
+
private _doConnect;
|
|
44
69
|
disconnect(): void;
|
|
45
|
-
channel(topic: string, options?: RealtimeSubscriptionOptions): RealtimeSubscription
|
|
70
|
+
channel<T = any>(topic: string, options?: RealtimeSubscriptionOptions): RealtimeSubscription<T>;
|
|
71
|
+
sendChat(roomId: string, text: string): void;
|
|
72
|
+
chatRoom(roomId: string): RealtimeSubscription;
|
|
46
73
|
/** @internal */
|
|
47
74
|
_send(data: any): void;
|
|
48
75
|
private handleMessage;
|
|
49
76
|
private startHeartbeat;
|
|
50
77
|
private stopHeartbeat;
|
|
51
|
-
/** Exponential backoff with jitter: 1s → 2s → 4s → ... → 30s */
|
|
52
78
|
private scheduleReconnect;
|
|
53
79
|
private stopReconnect;
|
|
80
|
+
private _handleOnline;
|
|
81
|
+
private _handleOffline;
|
|
82
|
+
private _setupOfflineDetection;
|
|
83
|
+
private _teardownOfflineDetection;
|
|
54
84
|
}
|
|
55
85
|
//# sourceMappingURL=realtime.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"realtime.d.ts","sourceRoot":"","sources":["../../../src/sdk/realtime.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,aAAa,GAAG,QAAQ,GAAG,QAAQ,GAAG,QAAQ,GAAG,GAAG,CAAC;AAEjE,MAAM,WAAW,eAAe;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAC;CACtB;AAED,MAAM,WAAW,2BAA2B;IACxC,KAAK,CAAC,EAAE,aAAa,CAAC;IACtB,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;CAChC;AAED,MAAM,MAAM,gBAAgB,GAAG,CAAC,OAAO,EAAE,GAAG,KAAK,IAAI,CAAC;
|
|
1
|
+
{"version":3,"file":"realtime.d.ts","sourceRoot":"","sources":["../../../src/sdk/realtime.ts"],"names":[],"mappings":"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"}
|
|
@@ -40,13 +40,8 @@ class RealtimeSubscription {
|
|
|
40
40
|
/** @internal */
|
|
41
41
|
_emit(payload) {
|
|
42
42
|
const event = payload.operation;
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
// Emit to specific event listeners
|
|
46
|
-
this.callbacks.get(event)?.forEach(cb => cb(payload));
|
|
47
|
-
// Emit to catch-all listeners
|
|
48
|
-
this.callbacks.get('*')?.forEach(cb => cb(payload));
|
|
49
|
-
}
|
|
43
|
+
this.callbacks.get(event)?.forEach(cb => cb(payload));
|
|
44
|
+
this.callbacks.get('*')?.forEach(cb => cb(payload));
|
|
50
45
|
}
|
|
51
46
|
}
|
|
52
47
|
exports.RealtimeSubscription = RealtimeSubscription;
|
|
@@ -57,7 +52,24 @@ class RealtimeClient {
|
|
|
57
52
|
this.reconnectTimer = null;
|
|
58
53
|
this.heartbeatTimer = null;
|
|
59
54
|
this.reconnectAttempts = 0;
|
|
60
|
-
this.
|
|
55
|
+
this._sendQueue = [];
|
|
56
|
+
this._connectingPromise = null;
|
|
57
|
+
this._status = 'idle';
|
|
58
|
+
this._statusListeners = new Set();
|
|
59
|
+
this._lastPong = 0;
|
|
60
|
+
this._maxRetriesListeners = new Set();
|
|
61
|
+
this._handleOnline = () => {
|
|
62
|
+
if (this._status !== 'connected') {
|
|
63
|
+
this.reconnectAttempts = 0;
|
|
64
|
+
this.connect().catch(() => { });
|
|
65
|
+
}
|
|
66
|
+
};
|
|
67
|
+
this._handleOffline = () => {
|
|
68
|
+
this.stopReconnect();
|
|
69
|
+
this._setStatus('disconnected');
|
|
70
|
+
};
|
|
71
|
+
const wsBase = options.baseUrl.replace(/\/v1\/?$/, '').replace(/^http/, 'ws');
|
|
72
|
+
this.baseUrl = `${wsBase}/api/realtime`;
|
|
61
73
|
this.projectId = options.projectId || '';
|
|
62
74
|
if (options.token)
|
|
63
75
|
this.token = options.token;
|
|
@@ -65,10 +77,37 @@ class RealtimeClient {
|
|
|
65
77
|
this.userId = options.userId;
|
|
66
78
|
if (options.apiKey)
|
|
67
79
|
this.apiKey = options.apiKey;
|
|
80
|
+
this._maxReconnectAttempts = options.maxReconnectAttempts ?? Infinity;
|
|
81
|
+
}
|
|
82
|
+
get status() { return this._status; }
|
|
83
|
+
onStatusChange(cb) {
|
|
84
|
+
this._statusListeners.add(cb);
|
|
85
|
+
return () => this._statusListeners.delete(cb);
|
|
86
|
+
}
|
|
87
|
+
onMaxRetriesExceeded(cb) {
|
|
88
|
+
this._maxRetriesListeners.add(cb);
|
|
89
|
+
return () => this._maxRetriesListeners.delete(cb);
|
|
90
|
+
}
|
|
91
|
+
_setStatus(s) {
|
|
92
|
+
this._status = s;
|
|
93
|
+
this._statusListeners.forEach(cb => cb(s));
|
|
94
|
+
}
|
|
95
|
+
setToken(newToken) {
|
|
96
|
+
this.token = newToken;
|
|
97
|
+
this._send({ type: 'auth', token: newToken });
|
|
68
98
|
}
|
|
69
99
|
connect() {
|
|
70
|
-
if (this.ws)
|
|
100
|
+
if (this.ws && this.ws.readyState === WebSocket.OPEN)
|
|
71
101
|
return Promise.resolve();
|
|
102
|
+
if (this._connectingPromise)
|
|
103
|
+
return this._connectingPromise;
|
|
104
|
+
this._connectingPromise = this._doConnect().finally(() => {
|
|
105
|
+
this._connectingPromise = null;
|
|
106
|
+
});
|
|
107
|
+
return this._connectingPromise;
|
|
108
|
+
}
|
|
109
|
+
_doConnect() {
|
|
110
|
+
this._setStatus('connecting');
|
|
72
111
|
return new Promise((resolve, reject) => {
|
|
73
112
|
const url = new URL(this.baseUrl);
|
|
74
113
|
if (this.apiKey) {
|
|
@@ -83,10 +122,14 @@ class RealtimeClient {
|
|
|
83
122
|
url.searchParams.set('token', this.token);
|
|
84
123
|
this.ws = new WebSocket(url.toString());
|
|
85
124
|
this.ws.onopen = () => {
|
|
86
|
-
|
|
125
|
+
this._setStatus('connected');
|
|
87
126
|
this.reconnectAttempts = 0;
|
|
127
|
+
this._lastPong = Date.now();
|
|
88
128
|
this.startHeartbeat();
|
|
89
|
-
|
|
129
|
+
this._setupOfflineDetection();
|
|
130
|
+
while (this._sendQueue.length > 0) {
|
|
131
|
+
this.ws.send(JSON.stringify(this._sendQueue.shift()));
|
|
132
|
+
}
|
|
90
133
|
for (const sub of this.subscriptions.values()) {
|
|
91
134
|
sub.subscribe();
|
|
92
135
|
}
|
|
@@ -102,22 +145,28 @@ class RealtimeClient {
|
|
|
102
145
|
}
|
|
103
146
|
};
|
|
104
147
|
this.ws.onclose = () => {
|
|
105
|
-
|
|
148
|
+
this._setStatus('reconnecting');
|
|
106
149
|
this.stopHeartbeat();
|
|
107
150
|
this.ws = null;
|
|
108
151
|
this.scheduleReconnect();
|
|
109
152
|
};
|
|
110
153
|
this.ws.onerror = (err) => {
|
|
111
154
|
console.error('Realtime connection error:', err);
|
|
155
|
+
this._setStatus('disconnected');
|
|
112
156
|
reject(err);
|
|
113
157
|
};
|
|
114
158
|
});
|
|
115
159
|
}
|
|
116
160
|
disconnect() {
|
|
161
|
+
this._setStatus('disconnected');
|
|
162
|
+
this.stopReconnect();
|
|
163
|
+
this.stopHeartbeat();
|
|
164
|
+
this._teardownOfflineDetection();
|
|
117
165
|
if (this.ws) {
|
|
118
166
|
this.ws.close();
|
|
167
|
+
this.ws = null;
|
|
119
168
|
}
|
|
120
|
-
this.
|
|
169
|
+
this._sendQueue = [];
|
|
121
170
|
}
|
|
122
171
|
channel(topic, options = {}) {
|
|
123
172
|
const fullTopic = topic.includes('/') ? topic : `table/${topic}/${this.projectId}`;
|
|
@@ -128,32 +177,54 @@ class RealtimeClient {
|
|
|
128
177
|
}
|
|
129
178
|
return sub;
|
|
130
179
|
}
|
|
180
|
+
sendChat(roomId, text) {
|
|
181
|
+
this._send({ type: 'chat', roomId, text });
|
|
182
|
+
}
|
|
183
|
+
chatRoom(roomId) {
|
|
184
|
+
return this.channel(`chat/${roomId}/${this.projectId}`);
|
|
185
|
+
}
|
|
131
186
|
/** @internal */
|
|
132
187
|
_send(data) {
|
|
133
188
|
if (this.ws && this.ws.readyState === WebSocket.OPEN) {
|
|
134
189
|
this.ws.send(JSON.stringify(data));
|
|
135
190
|
}
|
|
191
|
+
else {
|
|
192
|
+
this._sendQueue.push(data);
|
|
193
|
+
}
|
|
136
194
|
}
|
|
137
195
|
handleMessage(data) {
|
|
196
|
+
if (data.type === 'pong') {
|
|
197
|
+
this._lastPong = Date.now();
|
|
198
|
+
return;
|
|
199
|
+
}
|
|
138
200
|
if (data.type === 'db_change' || data.type === 'chat_message') {
|
|
139
201
|
const sub = this.subscriptions.get(data.topic);
|
|
140
|
-
if (sub)
|
|
202
|
+
if (sub)
|
|
141
203
|
sub._emit(data);
|
|
142
|
-
}
|
|
143
204
|
}
|
|
144
205
|
}
|
|
145
206
|
startHeartbeat() {
|
|
146
207
|
this.heartbeatTimer = setInterval(() => {
|
|
147
208
|
this._send({ type: 'ping' });
|
|
209
|
+
if (this._lastPong > 0 && Date.now() - this._lastPong > 70000) {
|
|
210
|
+
console.warn('Realtime: no pong received, forcing reconnect');
|
|
211
|
+
this.ws?.close();
|
|
212
|
+
}
|
|
148
213
|
}, 30000);
|
|
149
214
|
}
|
|
150
215
|
stopHeartbeat() {
|
|
151
|
-
if (this.heartbeatTimer)
|
|
216
|
+
if (this.heartbeatTimer) {
|
|
152
217
|
clearInterval(this.heartbeatTimer);
|
|
218
|
+
this.heartbeatTimer = null;
|
|
219
|
+
}
|
|
153
220
|
}
|
|
154
|
-
/** Exponential backoff with jitter: 1s → 2s → 4s → ... → 30s */
|
|
155
221
|
scheduleReconnect() {
|
|
156
222
|
this.stopReconnect();
|
|
223
|
+
if (this.reconnectAttempts >= this._maxReconnectAttempts) {
|
|
224
|
+
this._setStatus('disconnected');
|
|
225
|
+
this._maxRetriesListeners.forEach(cb => cb());
|
|
226
|
+
return;
|
|
227
|
+
}
|
|
157
228
|
const delay = Math.min(1000 * Math.pow(2, this.reconnectAttempts), 30000);
|
|
158
229
|
const jitter = delay * 0.3 * Math.random();
|
|
159
230
|
this.reconnectAttempts++;
|
|
@@ -162,8 +233,22 @@ class RealtimeClient {
|
|
|
162
233
|
}, delay + jitter);
|
|
163
234
|
}
|
|
164
235
|
stopReconnect() {
|
|
165
|
-
if (this.reconnectTimer)
|
|
236
|
+
if (this.reconnectTimer) {
|
|
166
237
|
clearTimeout(this.reconnectTimer);
|
|
238
|
+
this.reconnectTimer = null;
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
_setupOfflineDetection() {
|
|
242
|
+
if (typeof window !== 'undefined') {
|
|
243
|
+
window.addEventListener('online', this._handleOnline);
|
|
244
|
+
window.addEventListener('offline', this._handleOffline);
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
_teardownOfflineDetection() {
|
|
248
|
+
if (typeof window !== 'undefined') {
|
|
249
|
+
window.removeEventListener('online', this._handleOnline);
|
|
250
|
+
window.removeEventListener('offline', this._handleOffline);
|
|
251
|
+
}
|
|
167
252
|
}
|
|
168
253
|
}
|
|
169
254
|
exports.RealtimeClient = RealtimeClient;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"realtime.js","sourceRoot":"","sources":["../../../src/sdk/realtime.ts"],"names":[],"mappings":";;;
|
|
1
|
+
{"version":3,"file":"realtime.js","sourceRoot":"","sources":["../../../src/sdk/realtime.ts"],"names":[],"mappings":"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"}
|
|
@@ -8,19 +8,37 @@ export interface RealtimeSubscriptionOptions {
|
|
|
8
8
|
event?: RealtimeEvent;
|
|
9
9
|
filter?: Record<string, any>;
|
|
10
10
|
}
|
|
11
|
-
export type RealtimeCallback = (payload:
|
|
12
|
-
export
|
|
11
|
+
export type RealtimeCallback<T = any> = (payload: RealtimePayload<T>) => void;
|
|
12
|
+
export interface RealtimePayload<T = any> {
|
|
13
|
+
type: 'db_change' | 'chat_message';
|
|
14
|
+
topic: string;
|
|
15
|
+
operation: RealtimeEvent;
|
|
16
|
+
data: T;
|
|
17
|
+
old?: T;
|
|
18
|
+
timestamp?: string;
|
|
19
|
+
[key: string]: any;
|
|
20
|
+
}
|
|
21
|
+
export declare class RealtimeSubscription<T = any> {
|
|
13
22
|
private client;
|
|
14
23
|
private topic;
|
|
15
24
|
private options;
|
|
16
25
|
private callbacks;
|
|
17
26
|
private isSubscribed;
|
|
18
27
|
constructor(client: RealtimeClient, topic: string, options?: RealtimeSubscriptionOptions);
|
|
19
|
-
on(event: RealtimeEvent, callback: RealtimeCallback): this;
|
|
28
|
+
on(event: RealtimeEvent, callback: RealtimeCallback<T>): this;
|
|
20
29
|
subscribe(): this;
|
|
21
30
|
unsubscribe(): void;
|
|
22
31
|
/** @internal */
|
|
23
|
-
_emit(payload:
|
|
32
|
+
_emit(payload: RealtimePayload<T>): void;
|
|
33
|
+
}
|
|
34
|
+
export type RealtimeStatus = 'idle' | 'connecting' | 'connected' | 'reconnecting' | 'disconnected';
|
|
35
|
+
export interface RealtimeClientOptions {
|
|
36
|
+
baseUrl: string;
|
|
37
|
+
projectId: string;
|
|
38
|
+
token?: string;
|
|
39
|
+
userId?: string;
|
|
40
|
+
apiKey?: string;
|
|
41
|
+
maxReconnectAttempts?: number;
|
|
24
42
|
}
|
|
25
43
|
export declare class RealtimeClient {
|
|
26
44
|
private baseUrl;
|
|
@@ -33,23 +51,35 @@ export declare class RealtimeClient {
|
|
|
33
51
|
private reconnectTimer;
|
|
34
52
|
private heartbeatTimer;
|
|
35
53
|
private reconnectAttempts;
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
54
|
+
private _sendQueue;
|
|
55
|
+
private _connectingPromise;
|
|
56
|
+
private _status;
|
|
57
|
+
private _statusListeners;
|
|
58
|
+
private _lastPong;
|
|
59
|
+
private _maxReconnectAttempts;
|
|
60
|
+
private _maxRetriesListeners;
|
|
61
|
+
constructor(options: RealtimeClientOptions);
|
|
62
|
+
get status(): RealtimeStatus;
|
|
63
|
+
onStatusChange(cb: (status: RealtimeStatus) => void): () => void;
|
|
64
|
+
onMaxRetriesExceeded(cb: () => void): () => void;
|
|
65
|
+
private _setStatus;
|
|
66
|
+
setToken(newToken: string): void;
|
|
43
67
|
connect(): Promise<void>;
|
|
68
|
+
private _doConnect;
|
|
44
69
|
disconnect(): void;
|
|
45
|
-
channel(topic: string, options?: RealtimeSubscriptionOptions): RealtimeSubscription
|
|
70
|
+
channel<T = any>(topic: string, options?: RealtimeSubscriptionOptions): RealtimeSubscription<T>;
|
|
71
|
+
sendChat(roomId: string, text: string): void;
|
|
72
|
+
chatRoom(roomId: string): RealtimeSubscription;
|
|
46
73
|
/** @internal */
|
|
47
74
|
_send(data: any): void;
|
|
48
75
|
private handleMessage;
|
|
49
76
|
private startHeartbeat;
|
|
50
77
|
private stopHeartbeat;
|
|
51
|
-
/** Exponential backoff with jitter: 1s → 2s → 4s → ... → 30s */
|
|
52
78
|
private scheduleReconnect;
|
|
53
79
|
private stopReconnect;
|
|
80
|
+
private _handleOnline;
|
|
81
|
+
private _handleOffline;
|
|
82
|
+
private _setupOfflineDetection;
|
|
83
|
+
private _teardownOfflineDetection;
|
|
54
84
|
}
|
|
55
85
|
//# sourceMappingURL=realtime.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"realtime.d.ts","sourceRoot":"","sources":["../../../src/sdk/realtime.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,aAAa,GAAG,QAAQ,GAAG,QAAQ,GAAG,QAAQ,GAAG,GAAG,CAAC;AAEjE,MAAM,WAAW,eAAe;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAC;CACtB;AAED,MAAM,WAAW,2BAA2B;IACxC,KAAK,CAAC,EAAE,aAAa,CAAC;IACtB,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;CAChC;AAED,MAAM,MAAM,gBAAgB,GAAG,CAAC,OAAO,EAAE,GAAG,KAAK,IAAI,CAAC;
|
|
1
|
+
{"version":3,"file":"realtime.d.ts","sourceRoot":"","sources":["../../../src/sdk/realtime.ts"],"names":[],"mappings":"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"}
|
package/dist/esm/sdk/realtime.js
CHANGED
|
@@ -37,13 +37,8 @@ export class RealtimeSubscription {
|
|
|
37
37
|
/** @internal */
|
|
38
38
|
_emit(payload) {
|
|
39
39
|
const event = payload.operation;
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
// Emit to specific event listeners
|
|
43
|
-
this.callbacks.get(event)?.forEach(cb => cb(payload));
|
|
44
|
-
// Emit to catch-all listeners
|
|
45
|
-
this.callbacks.get('*')?.forEach(cb => cb(payload));
|
|
46
|
-
}
|
|
40
|
+
this.callbacks.get(event)?.forEach(cb => cb(payload));
|
|
41
|
+
this.callbacks.get('*')?.forEach(cb => cb(payload));
|
|
47
42
|
}
|
|
48
43
|
}
|
|
49
44
|
export class RealtimeClient {
|
|
@@ -53,7 +48,24 @@ export class RealtimeClient {
|
|
|
53
48
|
this.reconnectTimer = null;
|
|
54
49
|
this.heartbeatTimer = null;
|
|
55
50
|
this.reconnectAttempts = 0;
|
|
56
|
-
this.
|
|
51
|
+
this._sendQueue = [];
|
|
52
|
+
this._connectingPromise = null;
|
|
53
|
+
this._status = 'idle';
|
|
54
|
+
this._statusListeners = new Set();
|
|
55
|
+
this._lastPong = 0;
|
|
56
|
+
this._maxRetriesListeners = new Set();
|
|
57
|
+
this._handleOnline = () => {
|
|
58
|
+
if (this._status !== 'connected') {
|
|
59
|
+
this.reconnectAttempts = 0;
|
|
60
|
+
this.connect().catch(() => { });
|
|
61
|
+
}
|
|
62
|
+
};
|
|
63
|
+
this._handleOffline = () => {
|
|
64
|
+
this.stopReconnect();
|
|
65
|
+
this._setStatus('disconnected');
|
|
66
|
+
};
|
|
67
|
+
const wsBase = options.baseUrl.replace(/\/v1\/?$/, '').replace(/^http/, 'ws');
|
|
68
|
+
this.baseUrl = `${wsBase}/api/realtime`;
|
|
57
69
|
this.projectId = options.projectId || '';
|
|
58
70
|
if (options.token)
|
|
59
71
|
this.token = options.token;
|
|
@@ -61,10 +73,37 @@ export class RealtimeClient {
|
|
|
61
73
|
this.userId = options.userId;
|
|
62
74
|
if (options.apiKey)
|
|
63
75
|
this.apiKey = options.apiKey;
|
|
76
|
+
this._maxReconnectAttempts = options.maxReconnectAttempts ?? Infinity;
|
|
77
|
+
}
|
|
78
|
+
get status() { return this._status; }
|
|
79
|
+
onStatusChange(cb) {
|
|
80
|
+
this._statusListeners.add(cb);
|
|
81
|
+
return () => this._statusListeners.delete(cb);
|
|
82
|
+
}
|
|
83
|
+
onMaxRetriesExceeded(cb) {
|
|
84
|
+
this._maxRetriesListeners.add(cb);
|
|
85
|
+
return () => this._maxRetriesListeners.delete(cb);
|
|
86
|
+
}
|
|
87
|
+
_setStatus(s) {
|
|
88
|
+
this._status = s;
|
|
89
|
+
this._statusListeners.forEach(cb => cb(s));
|
|
90
|
+
}
|
|
91
|
+
setToken(newToken) {
|
|
92
|
+
this.token = newToken;
|
|
93
|
+
this._send({ type: 'auth', token: newToken });
|
|
64
94
|
}
|
|
65
95
|
connect() {
|
|
66
|
-
if (this.ws)
|
|
96
|
+
if (this.ws && this.ws.readyState === WebSocket.OPEN)
|
|
67
97
|
return Promise.resolve();
|
|
98
|
+
if (this._connectingPromise)
|
|
99
|
+
return this._connectingPromise;
|
|
100
|
+
this._connectingPromise = this._doConnect().finally(() => {
|
|
101
|
+
this._connectingPromise = null;
|
|
102
|
+
});
|
|
103
|
+
return this._connectingPromise;
|
|
104
|
+
}
|
|
105
|
+
_doConnect() {
|
|
106
|
+
this._setStatus('connecting');
|
|
68
107
|
return new Promise((resolve, reject) => {
|
|
69
108
|
const url = new URL(this.baseUrl);
|
|
70
109
|
if (this.apiKey) {
|
|
@@ -79,10 +118,14 @@ export class RealtimeClient {
|
|
|
79
118
|
url.searchParams.set('token', this.token);
|
|
80
119
|
this.ws = new WebSocket(url.toString());
|
|
81
120
|
this.ws.onopen = () => {
|
|
82
|
-
|
|
121
|
+
this._setStatus('connected');
|
|
83
122
|
this.reconnectAttempts = 0;
|
|
123
|
+
this._lastPong = Date.now();
|
|
84
124
|
this.startHeartbeat();
|
|
85
|
-
|
|
125
|
+
this._setupOfflineDetection();
|
|
126
|
+
while (this._sendQueue.length > 0) {
|
|
127
|
+
this.ws.send(JSON.stringify(this._sendQueue.shift()));
|
|
128
|
+
}
|
|
86
129
|
for (const sub of this.subscriptions.values()) {
|
|
87
130
|
sub.subscribe();
|
|
88
131
|
}
|
|
@@ -98,22 +141,28 @@ export class RealtimeClient {
|
|
|
98
141
|
}
|
|
99
142
|
};
|
|
100
143
|
this.ws.onclose = () => {
|
|
101
|
-
|
|
144
|
+
this._setStatus('reconnecting');
|
|
102
145
|
this.stopHeartbeat();
|
|
103
146
|
this.ws = null;
|
|
104
147
|
this.scheduleReconnect();
|
|
105
148
|
};
|
|
106
149
|
this.ws.onerror = (err) => {
|
|
107
150
|
console.error('Realtime connection error:', err);
|
|
151
|
+
this._setStatus('disconnected');
|
|
108
152
|
reject(err);
|
|
109
153
|
};
|
|
110
154
|
});
|
|
111
155
|
}
|
|
112
156
|
disconnect() {
|
|
157
|
+
this._setStatus('disconnected');
|
|
158
|
+
this.stopReconnect();
|
|
159
|
+
this.stopHeartbeat();
|
|
160
|
+
this._teardownOfflineDetection();
|
|
113
161
|
if (this.ws) {
|
|
114
162
|
this.ws.close();
|
|
163
|
+
this.ws = null;
|
|
115
164
|
}
|
|
116
|
-
this.
|
|
165
|
+
this._sendQueue = [];
|
|
117
166
|
}
|
|
118
167
|
channel(topic, options = {}) {
|
|
119
168
|
const fullTopic = topic.includes('/') ? topic : `table/${topic}/${this.projectId}`;
|
|
@@ -124,32 +173,54 @@ export class RealtimeClient {
|
|
|
124
173
|
}
|
|
125
174
|
return sub;
|
|
126
175
|
}
|
|
176
|
+
sendChat(roomId, text) {
|
|
177
|
+
this._send({ type: 'chat', roomId, text });
|
|
178
|
+
}
|
|
179
|
+
chatRoom(roomId) {
|
|
180
|
+
return this.channel(`chat/${roomId}/${this.projectId}`);
|
|
181
|
+
}
|
|
127
182
|
/** @internal */
|
|
128
183
|
_send(data) {
|
|
129
184
|
if (this.ws && this.ws.readyState === WebSocket.OPEN) {
|
|
130
185
|
this.ws.send(JSON.stringify(data));
|
|
131
186
|
}
|
|
187
|
+
else {
|
|
188
|
+
this._sendQueue.push(data);
|
|
189
|
+
}
|
|
132
190
|
}
|
|
133
191
|
handleMessage(data) {
|
|
192
|
+
if (data.type === 'pong') {
|
|
193
|
+
this._lastPong = Date.now();
|
|
194
|
+
return;
|
|
195
|
+
}
|
|
134
196
|
if (data.type === 'db_change' || data.type === 'chat_message') {
|
|
135
197
|
const sub = this.subscriptions.get(data.topic);
|
|
136
|
-
if (sub)
|
|
198
|
+
if (sub)
|
|
137
199
|
sub._emit(data);
|
|
138
|
-
}
|
|
139
200
|
}
|
|
140
201
|
}
|
|
141
202
|
startHeartbeat() {
|
|
142
203
|
this.heartbeatTimer = setInterval(() => {
|
|
143
204
|
this._send({ type: 'ping' });
|
|
205
|
+
if (this._lastPong > 0 && Date.now() - this._lastPong > 70000) {
|
|
206
|
+
console.warn('Realtime: no pong received, forcing reconnect');
|
|
207
|
+
this.ws?.close();
|
|
208
|
+
}
|
|
144
209
|
}, 30000);
|
|
145
210
|
}
|
|
146
211
|
stopHeartbeat() {
|
|
147
|
-
if (this.heartbeatTimer)
|
|
212
|
+
if (this.heartbeatTimer) {
|
|
148
213
|
clearInterval(this.heartbeatTimer);
|
|
214
|
+
this.heartbeatTimer = null;
|
|
215
|
+
}
|
|
149
216
|
}
|
|
150
|
-
/** Exponential backoff with jitter: 1s → 2s → 4s → ... → 30s */
|
|
151
217
|
scheduleReconnect() {
|
|
152
218
|
this.stopReconnect();
|
|
219
|
+
if (this.reconnectAttempts >= this._maxReconnectAttempts) {
|
|
220
|
+
this._setStatus('disconnected');
|
|
221
|
+
this._maxRetriesListeners.forEach(cb => cb());
|
|
222
|
+
return;
|
|
223
|
+
}
|
|
153
224
|
const delay = Math.min(1000 * Math.pow(2, this.reconnectAttempts), 30000);
|
|
154
225
|
const jitter = delay * 0.3 * Math.random();
|
|
155
226
|
this.reconnectAttempts++;
|
|
@@ -158,8 +229,22 @@ export class RealtimeClient {
|
|
|
158
229
|
}, delay + jitter);
|
|
159
230
|
}
|
|
160
231
|
stopReconnect() {
|
|
161
|
-
if (this.reconnectTimer)
|
|
232
|
+
if (this.reconnectTimer) {
|
|
162
233
|
clearTimeout(this.reconnectTimer);
|
|
234
|
+
this.reconnectTimer = null;
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
_setupOfflineDetection() {
|
|
238
|
+
if (typeof window !== 'undefined') {
|
|
239
|
+
window.addEventListener('online', this._handleOnline);
|
|
240
|
+
window.addEventListener('offline', this._handleOffline);
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
_teardownOfflineDetection() {
|
|
244
|
+
if (typeof window !== 'undefined') {
|
|
245
|
+
window.removeEventListener('online', this._handleOnline);
|
|
246
|
+
window.removeEventListener('offline', this._handleOffline);
|
|
247
|
+
}
|
|
163
248
|
}
|
|
164
249
|
}
|
|
165
250
|
//# sourceMappingURL=realtime.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"realtime.js","sourceRoot":"","sources":["../../../src/sdk/realtime.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"realtime.js","sourceRoot":"","sources":["../../../src/sdk/realtime.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aerostack/sdk-web",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.7",
|
|
4
4
|
"author": "Aerostack",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"tshy": {
|
|
@@ -36,7 +36,7 @@
|
|
|
36
36
|
"ws": "^8.19.0"
|
|
37
37
|
},
|
|
38
38
|
"dependencies": {
|
|
39
|
-
"@aerostack/core": "^0.6.
|
|
39
|
+
"@aerostack/core": "^0.6.7",
|
|
40
40
|
"zod": "^3.25.0 || ^4.0.0"
|
|
41
41
|
},
|
|
42
42
|
"exports": {
|
package/src/sdk/realtime.ts
CHANGED
|
@@ -11,13 +11,23 @@ export interface RealtimeSubscriptionOptions {
|
|
|
11
11
|
filter?: Record<string, any>;
|
|
12
12
|
}
|
|
13
13
|
|
|
14
|
-
export type RealtimeCallback = (payload:
|
|
14
|
+
export type RealtimeCallback<T = any> = (payload: RealtimePayload<T>) => void;
|
|
15
15
|
|
|
16
|
-
export
|
|
16
|
+
export interface RealtimePayload<T = any> {
|
|
17
|
+
type: 'db_change' | 'chat_message';
|
|
18
|
+
topic: string;
|
|
19
|
+
operation: RealtimeEvent;
|
|
20
|
+
data: T;
|
|
21
|
+
old?: T;
|
|
22
|
+
timestamp?: string;
|
|
23
|
+
[key: string]: any;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export class RealtimeSubscription<T = any> {
|
|
17
27
|
private client: RealtimeClient;
|
|
18
28
|
private topic: string;
|
|
19
29
|
private options: RealtimeSubscriptionOptions;
|
|
20
|
-
private callbacks: Map<RealtimeEvent, Set<RealtimeCallback
|
|
30
|
+
private callbacks: Map<RealtimeEvent, Set<RealtimeCallback<T>>> = new Map();
|
|
21
31
|
private isSubscribed: boolean = false;
|
|
22
32
|
|
|
23
33
|
constructor(client: RealtimeClient, topic: string, options: RealtimeSubscriptionOptions = {}) {
|
|
@@ -26,7 +36,7 @@ export class RealtimeSubscription {
|
|
|
26
36
|
this.options = options;
|
|
27
37
|
}
|
|
28
38
|
|
|
29
|
-
on(event: RealtimeEvent, callback: RealtimeCallback): this {
|
|
39
|
+
on(event: RealtimeEvent, callback: RealtimeCallback<T>): this {
|
|
30
40
|
if (!this.callbacks.has(event)) {
|
|
31
41
|
this.callbacks.set(event, new Set());
|
|
32
42
|
}
|
|
@@ -56,19 +66,24 @@ export class RealtimeSubscription {
|
|
|
56
66
|
}
|
|
57
67
|
|
|
58
68
|
/** @internal */
|
|
59
|
-
_emit(payload:
|
|
69
|
+
_emit(payload: RealtimePayload<T>): void {
|
|
60
70
|
const event = payload.operation as RealtimeEvent;
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
if (requestedEvent === '*' || requestedEvent === event) {
|
|
64
|
-
// Emit to specific event listeners
|
|
65
|
-
this.callbacks.get(event)?.forEach(cb => cb(payload));
|
|
66
|
-
// Emit to catch-all listeners
|
|
67
|
-
this.callbacks.get('*')?.forEach(cb => cb(payload));
|
|
68
|
-
}
|
|
71
|
+
this.callbacks.get(event)?.forEach(cb => cb(payload));
|
|
72
|
+
this.callbacks.get('*')?.forEach(cb => cb(payload));
|
|
69
73
|
}
|
|
70
74
|
}
|
|
71
75
|
|
|
76
|
+
export type RealtimeStatus = 'idle' | 'connecting' | 'connected' | 'reconnecting' | 'disconnected';
|
|
77
|
+
|
|
78
|
+
export interface RealtimeClientOptions {
|
|
79
|
+
baseUrl: string;
|
|
80
|
+
projectId: string;
|
|
81
|
+
token?: string;
|
|
82
|
+
userId?: string;
|
|
83
|
+
apiKey?: string;
|
|
84
|
+
maxReconnectAttempts?: number;
|
|
85
|
+
}
|
|
86
|
+
|
|
72
87
|
export class RealtimeClient {
|
|
73
88
|
private baseUrl: string;
|
|
74
89
|
private projectId: string;
|
|
@@ -80,18 +95,57 @@ export class RealtimeClient {
|
|
|
80
95
|
private reconnectTimer: any = null;
|
|
81
96
|
private heartbeatTimer: any = null;
|
|
82
97
|
private reconnectAttempts: number = 0;
|
|
98
|
+
private _sendQueue: any[] = [];
|
|
99
|
+
private _connectingPromise: Promise<void> | null = null;
|
|
100
|
+
private _status: RealtimeStatus = 'idle';
|
|
101
|
+
private _statusListeners: Set<(s: RealtimeStatus) => void> = new Set();
|
|
102
|
+
private _lastPong: number = 0;
|
|
103
|
+
private _maxReconnectAttempts: number;
|
|
104
|
+
private _maxRetriesListeners: Set<() => void> = new Set();
|
|
83
105
|
|
|
84
|
-
constructor(options:
|
|
85
|
-
|
|
106
|
+
constructor(options: RealtimeClientOptions) {
|
|
107
|
+
const wsBase = options.baseUrl.replace(/\/v1\/?$/, '').replace(/^http/, 'ws');
|
|
108
|
+
this.baseUrl = `${wsBase}/api/realtime`;
|
|
86
109
|
this.projectId = options.projectId || '';
|
|
87
110
|
if (options.token) this.token = options.token;
|
|
88
111
|
if (options.userId) this.userId = options.userId;
|
|
89
112
|
if (options.apiKey) this.apiKey = options.apiKey;
|
|
113
|
+
this._maxReconnectAttempts = options.maxReconnectAttempts ?? Infinity;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
get status(): RealtimeStatus { return this._status; }
|
|
117
|
+
|
|
118
|
+
onStatusChange(cb: (status: RealtimeStatus) => void): () => void {
|
|
119
|
+
this._statusListeners.add(cb);
|
|
120
|
+
return () => this._statusListeners.delete(cb);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
onMaxRetriesExceeded(cb: () => void): () => void {
|
|
124
|
+
this._maxRetriesListeners.add(cb);
|
|
125
|
+
return () => this._maxRetriesListeners.delete(cb);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
private _setStatus(s: RealtimeStatus) {
|
|
129
|
+
this._status = s;
|
|
130
|
+
this._statusListeners.forEach(cb => cb(s));
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
setToken(newToken: string): void {
|
|
134
|
+
this.token = newToken;
|
|
135
|
+
this._send({ type: 'auth', token: newToken });
|
|
90
136
|
}
|
|
91
137
|
|
|
92
138
|
connect(): Promise<void> {
|
|
93
|
-
if (this.ws) return Promise.resolve();
|
|
139
|
+
if (this.ws && this.ws.readyState === WebSocket.OPEN) return Promise.resolve();
|
|
140
|
+
if (this._connectingPromise) return this._connectingPromise;
|
|
141
|
+
this._connectingPromise = this._doConnect().finally(() => {
|
|
142
|
+
this._connectingPromise = null;
|
|
143
|
+
});
|
|
144
|
+
return this._connectingPromise;
|
|
145
|
+
}
|
|
94
146
|
|
|
147
|
+
private _doConnect(): Promise<void> {
|
|
148
|
+
this._setStatus('connecting');
|
|
95
149
|
return new Promise((resolve, reject) => {
|
|
96
150
|
const url = new URL(this.baseUrl);
|
|
97
151
|
if (this.apiKey) {
|
|
@@ -105,10 +159,14 @@ export class RealtimeClient {
|
|
|
105
159
|
this.ws = new WebSocket(url.toString());
|
|
106
160
|
|
|
107
161
|
this.ws.onopen = () => {
|
|
108
|
-
|
|
162
|
+
this._setStatus('connected');
|
|
109
163
|
this.reconnectAttempts = 0;
|
|
164
|
+
this._lastPong = Date.now();
|
|
110
165
|
this.startHeartbeat();
|
|
111
|
-
|
|
166
|
+
this._setupOfflineDetection();
|
|
167
|
+
while (this._sendQueue.length > 0) {
|
|
168
|
+
this.ws!.send(JSON.stringify(this._sendQueue.shift()));
|
|
169
|
+
}
|
|
112
170
|
for (const sub of this.subscriptions.values()) {
|
|
113
171
|
sub.subscribe();
|
|
114
172
|
}
|
|
@@ -125,7 +183,7 @@ export class RealtimeClient {
|
|
|
125
183
|
};
|
|
126
184
|
|
|
127
185
|
this.ws.onclose = () => {
|
|
128
|
-
|
|
186
|
+
this._setStatus('reconnecting');
|
|
129
187
|
this.stopHeartbeat();
|
|
130
188
|
this.ws = null;
|
|
131
189
|
this.scheduleReconnect();
|
|
@@ -133,58 +191,86 @@ export class RealtimeClient {
|
|
|
133
191
|
|
|
134
192
|
this.ws.onerror = (err) => {
|
|
135
193
|
console.error('Realtime connection error:', err);
|
|
194
|
+
this._setStatus('disconnected');
|
|
136
195
|
reject(err);
|
|
137
196
|
};
|
|
138
197
|
});
|
|
139
198
|
}
|
|
140
199
|
|
|
141
200
|
disconnect() {
|
|
201
|
+
this._setStatus('disconnected');
|
|
202
|
+
this.stopReconnect();
|
|
203
|
+
this.stopHeartbeat();
|
|
204
|
+
this._teardownOfflineDetection();
|
|
142
205
|
if (this.ws) {
|
|
143
206
|
this.ws.close();
|
|
207
|
+
this.ws = null;
|
|
144
208
|
}
|
|
145
|
-
this.
|
|
209
|
+
this._sendQueue = [];
|
|
146
210
|
}
|
|
147
211
|
|
|
148
|
-
channel(topic: string, options: RealtimeSubscriptionOptions = {}): RealtimeSubscription {
|
|
212
|
+
channel<T = any>(topic: string, options: RealtimeSubscriptionOptions = {}): RealtimeSubscription<T> {
|
|
149
213
|
const fullTopic = topic.includes('/') ? topic : `table/${topic}/${this.projectId}`;
|
|
150
|
-
|
|
151
214
|
let sub = this.subscriptions.get(fullTopic);
|
|
152
215
|
if (!sub) {
|
|
153
|
-
sub = new RealtimeSubscription(this, fullTopic, options);
|
|
216
|
+
sub = new RealtimeSubscription<T>(this, fullTopic, options);
|
|
154
217
|
this.subscriptions.set(fullTopic, sub);
|
|
155
218
|
}
|
|
156
|
-
return sub
|
|
219
|
+
return sub as RealtimeSubscription<T>;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
sendChat(roomId: string, text: string): void {
|
|
223
|
+
this._send({ type: 'chat', roomId, text });
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
chatRoom(roomId: string): RealtimeSubscription {
|
|
227
|
+
return this.channel(`chat/${roomId}/${this.projectId}`);
|
|
157
228
|
}
|
|
158
229
|
|
|
159
230
|
/** @internal */
|
|
160
231
|
_send(data: any) {
|
|
161
232
|
if (this.ws && this.ws.readyState === WebSocket.OPEN) {
|
|
162
233
|
this.ws.send(JSON.stringify(data));
|
|
234
|
+
} else {
|
|
235
|
+
this._sendQueue.push(data);
|
|
163
236
|
}
|
|
164
237
|
}
|
|
165
238
|
|
|
166
239
|
private handleMessage(data: RealtimeMessage) {
|
|
240
|
+
if (data.type === 'pong') {
|
|
241
|
+
this._lastPong = Date.now();
|
|
242
|
+
return;
|
|
243
|
+
}
|
|
167
244
|
if (data.type === 'db_change' || data.type === 'chat_message') {
|
|
168
245
|
const sub = this.subscriptions.get(data.topic);
|
|
169
|
-
if (sub)
|
|
170
|
-
sub._emit(data);
|
|
171
|
-
}
|
|
246
|
+
if (sub) sub._emit(data as any);
|
|
172
247
|
}
|
|
173
248
|
}
|
|
174
249
|
|
|
175
250
|
private startHeartbeat() {
|
|
176
251
|
this.heartbeatTimer = setInterval(() => {
|
|
177
252
|
this._send({ type: 'ping' });
|
|
253
|
+
if (this._lastPong > 0 && Date.now() - this._lastPong > 70000) {
|
|
254
|
+
console.warn('Realtime: no pong received, forcing reconnect');
|
|
255
|
+
this.ws?.close();
|
|
256
|
+
}
|
|
178
257
|
}, 30000);
|
|
179
258
|
}
|
|
180
259
|
|
|
181
260
|
private stopHeartbeat() {
|
|
182
|
-
if (this.heartbeatTimer)
|
|
261
|
+
if (this.heartbeatTimer) {
|
|
262
|
+
clearInterval(this.heartbeatTimer);
|
|
263
|
+
this.heartbeatTimer = null;
|
|
264
|
+
}
|
|
183
265
|
}
|
|
184
266
|
|
|
185
|
-
/** Exponential backoff with jitter: 1s → 2s → 4s → ... → 30s */
|
|
186
267
|
private scheduleReconnect() {
|
|
187
268
|
this.stopReconnect();
|
|
269
|
+
if (this.reconnectAttempts >= this._maxReconnectAttempts) {
|
|
270
|
+
this._setStatus('disconnected');
|
|
271
|
+
this._maxRetriesListeners.forEach(cb => cb());
|
|
272
|
+
return;
|
|
273
|
+
}
|
|
188
274
|
const delay = Math.min(1000 * Math.pow(2, this.reconnectAttempts), 30000);
|
|
189
275
|
const jitter = delay * 0.3 * Math.random();
|
|
190
276
|
this.reconnectAttempts++;
|
|
@@ -194,6 +280,35 @@ export class RealtimeClient {
|
|
|
194
280
|
}
|
|
195
281
|
|
|
196
282
|
private stopReconnect() {
|
|
197
|
-
if (this.reconnectTimer)
|
|
283
|
+
if (this.reconnectTimer) {
|
|
284
|
+
clearTimeout(this.reconnectTimer);
|
|
285
|
+
this.reconnectTimer = null;
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
private _handleOnline = () => {
|
|
290
|
+
if (this._status !== 'connected') {
|
|
291
|
+
this.reconnectAttempts = 0;
|
|
292
|
+
this.connect().catch(() => { });
|
|
293
|
+
}
|
|
294
|
+
};
|
|
295
|
+
|
|
296
|
+
private _handleOffline = () => {
|
|
297
|
+
this.stopReconnect();
|
|
298
|
+
this._setStatus('disconnected');
|
|
299
|
+
};
|
|
300
|
+
|
|
301
|
+
private _setupOfflineDetection() {
|
|
302
|
+
if (typeof window !== 'undefined') {
|
|
303
|
+
window.addEventListener('online', this._handleOnline);
|
|
304
|
+
window.addEventListener('offline', this._handleOffline);
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
private _teardownOfflineDetection() {
|
|
309
|
+
if (typeof window !== 'undefined') {
|
|
310
|
+
window.removeEventListener('online', this._handleOnline);
|
|
311
|
+
window.removeEventListener('offline', this._handleOffline);
|
|
312
|
+
}
|
|
198
313
|
}
|
|
199
314
|
}
|