@aerostack/sdk-web 0.6.6 → 0.6.8

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.
@@ -8,19 +8,37 @@ export interface RealtimeSubscriptionOptions {
8
8
  event?: RealtimeEvent;
9
9
  filter?: Record<string, any>;
10
10
  }
11
- export type RealtimeCallback = (payload: any) => void;
12
- export declare class RealtimeSubscription {
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: any): void;
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
- constructor(options: {
37
- baseUrl: string;
38
- projectId: string;
39
- token?: string;
40
- userId?: string;
41
- apiKey?: string;
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;AAEtD,qBAAa,oBAAoB;IAC7B,OAAO,CAAC,MAAM,CAAiB;IAC/B,OAAO,CAAC,KAAK,CAAS;IACtB,OAAO,CAAC,OAAO,CAA8B;IAC7C,OAAO,CAAC,SAAS,CAAwD;IACzE,OAAO,CAAC,YAAY,CAAkB;gBAE1B,MAAM,EAAE,cAAc,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,GAAE,2BAAgC;IAM5F,EAAE,CAAC,KAAK,EAAE,aAAa,EAAE,QAAQ,EAAE,gBAAgB,GAAG,IAAI;IAQ1D,SAAS,IAAI,IAAI;IAWjB,WAAW,IAAI,IAAI;IAUnB,gBAAgB;IAChB,KAAK,CAAC,OAAO,EAAE,GAAG,GAAG,IAAI;CAW5B;AAED,qBAAa,cAAc;IACvB,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,KAAK,CAAC,CAAS;IACvB,OAAO,CAAC,MAAM,CAAC,CAAS;IACxB,OAAO,CAAC,MAAM,CAAC,CAAS;IACxB,OAAO,CAAC,EAAE,CAA0B;IACpC,OAAO,CAAC,aAAa,CAAgD;IACrE,OAAO,CAAC,cAAc,CAAa;IACnC,OAAO,CAAC,cAAc,CAAa;IACnC,OAAO,CAAC,iBAAiB,CAAa;gBAE1B,OAAO,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE;IAQ7G,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAiDxB,UAAU;IAOV,OAAO,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,GAAE,2BAAgC,GAAG,oBAAoB;IAWvF,gBAAgB;IAChB,KAAK,CAAC,IAAI,EAAE,GAAG;IAMf,OAAO,CAAC,aAAa;IASrB,OAAO,CAAC,cAAc;IAMtB,OAAO,CAAC,aAAa;IAIrB,gEAAgE;IAChE,OAAO,CAAC,iBAAiB;IAUzB,OAAO,CAAC,aAAa;CAGxB"}
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
- const requestedEvent = this.options.event || '*';
44
- if (requestedEvent === '*' || requestedEvent === event) {
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.baseUrl = options.baseUrl.replace(/^http/, 'ws') + '/realtime';
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,28 +77,64 @@ 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
- if (this.apiKey) {
75
- url.searchParams.set('apiKey', this.apiKey);
76
- }
77
- else {
78
- url.searchParams.set('projectId', this.projectId);
79
- }
113
+ // Always include projectId in URL for routing (not auth)
114
+ url.searchParams.set('projectId', this.projectId);
80
115
  if (this.userId)
81
116
  url.searchParams.set('userId', this.userId);
82
117
  if (this.token)
83
118
  url.searchParams.set('token', this.token);
84
- this.ws = new WebSocket(url.toString());
119
+ // SECURITY: Pass API key via Sec-WebSocket-Protocol header, NOT as a URL query param.
120
+ // URL query params appear in CDN logs, browser history, and Referer headers.
121
+ // Protocol format: "aerostack-key.<base64-encoded-key>"
122
+ const protocols = [];
123
+ if (this.apiKey) {
124
+ protocols.push(`aerostack-key.${this.apiKey}`);
125
+ }
126
+ this.ws = protocols.length > 0
127
+ ? new WebSocket(url.toString(), protocols)
128
+ : new WebSocket(url.toString());
85
129
  this.ws.onopen = () => {
86
- console.log('Aerostack Realtime Connected');
130
+ this._setStatus('connected');
87
131
  this.reconnectAttempts = 0;
132
+ this._lastPong = Date.now();
88
133
  this.startHeartbeat();
89
- // Re-subscribe
134
+ this._setupOfflineDetection();
135
+ while (this._sendQueue.length > 0) {
136
+ this.ws.send(JSON.stringify(this._sendQueue.shift()));
137
+ }
90
138
  for (const sub of this.subscriptions.values()) {
91
139
  sub.subscribe();
92
140
  }
@@ -102,22 +150,28 @@ class RealtimeClient {
102
150
  }
103
151
  };
104
152
  this.ws.onclose = () => {
105
- console.log('Aerostack Realtime Disconnected');
153
+ this._setStatus('reconnecting');
106
154
  this.stopHeartbeat();
107
155
  this.ws = null;
108
156
  this.scheduleReconnect();
109
157
  };
110
158
  this.ws.onerror = (err) => {
111
159
  console.error('Realtime connection error:', err);
160
+ this._setStatus('disconnected');
112
161
  reject(err);
113
162
  };
114
163
  });
115
164
  }
116
165
  disconnect() {
166
+ this._setStatus('disconnected');
167
+ this.stopReconnect();
168
+ this.stopHeartbeat();
169
+ this._teardownOfflineDetection();
117
170
  if (this.ws) {
118
171
  this.ws.close();
172
+ this.ws = null;
119
173
  }
120
- this.stopReconnect();
174
+ this._sendQueue = [];
121
175
  }
122
176
  channel(topic, options = {}) {
123
177
  const fullTopic = topic.includes('/') ? topic : `table/${topic}/${this.projectId}`;
@@ -128,32 +182,54 @@ class RealtimeClient {
128
182
  }
129
183
  return sub;
130
184
  }
185
+ sendChat(roomId, text) {
186
+ this._send({ type: 'chat', roomId, text });
187
+ }
188
+ chatRoom(roomId) {
189
+ return this.channel(`chat/${roomId}/${this.projectId}`);
190
+ }
131
191
  /** @internal */
132
192
  _send(data) {
133
193
  if (this.ws && this.ws.readyState === WebSocket.OPEN) {
134
194
  this.ws.send(JSON.stringify(data));
135
195
  }
196
+ else {
197
+ this._sendQueue.push(data);
198
+ }
136
199
  }
137
200
  handleMessage(data) {
201
+ if (data.type === 'pong') {
202
+ this._lastPong = Date.now();
203
+ return;
204
+ }
138
205
  if (data.type === 'db_change' || data.type === 'chat_message') {
139
206
  const sub = this.subscriptions.get(data.topic);
140
- if (sub) {
207
+ if (sub)
141
208
  sub._emit(data);
142
- }
143
209
  }
144
210
  }
145
211
  startHeartbeat() {
146
212
  this.heartbeatTimer = setInterval(() => {
147
213
  this._send({ type: 'ping' });
214
+ if (this._lastPong > 0 && Date.now() - this._lastPong > 70000) {
215
+ console.warn('Realtime: no pong received, forcing reconnect');
216
+ this.ws?.close();
217
+ }
148
218
  }, 30000);
149
219
  }
150
220
  stopHeartbeat() {
151
- if (this.heartbeatTimer)
221
+ if (this.heartbeatTimer) {
152
222
  clearInterval(this.heartbeatTimer);
223
+ this.heartbeatTimer = null;
224
+ }
153
225
  }
154
- /** Exponential backoff with jitter: 1s → 2s → 4s → ... → 30s */
155
226
  scheduleReconnect() {
156
227
  this.stopReconnect();
228
+ if (this.reconnectAttempts >= this._maxReconnectAttempts) {
229
+ this._setStatus('disconnected');
230
+ this._maxRetriesListeners.forEach(cb => cb());
231
+ return;
232
+ }
157
233
  const delay = Math.min(1000 * Math.pow(2, this.reconnectAttempts), 30000);
158
234
  const jitter = delay * 0.3 * Math.random();
159
235
  this.reconnectAttempts++;
@@ -162,8 +238,22 @@ class RealtimeClient {
162
238
  }, delay + jitter);
163
239
  }
164
240
  stopReconnect() {
165
- if (this.reconnectTimer)
241
+ if (this.reconnectTimer) {
166
242
  clearTimeout(this.reconnectTimer);
243
+ this.reconnectTimer = null;
244
+ }
245
+ }
246
+ _setupOfflineDetection() {
247
+ if (typeof window !== 'undefined') {
248
+ window.addEventListener('online', this._handleOnline);
249
+ window.addEventListener('offline', this._handleOffline);
250
+ }
251
+ }
252
+ _teardownOfflineDetection() {
253
+ if (typeof window !== 'undefined') {
254
+ window.removeEventListener('online', this._handleOnline);
255
+ window.removeEventListener('offline', this._handleOffline);
256
+ }
167
257
  }
168
258
  }
169
259
  exports.RealtimeClient = RealtimeClient;
@@ -1 +1 @@
1
- {"version":3,"file":"realtime.js","sourceRoot":"","sources":["../../../src/sdk/realtime.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"realtime.js","sourceRoot":"","sources":["../../../src/sdk/realtime.ts"],"names":[],"mappings":";;;AAyBA,MAAa,oBAAoB;IAO7B,YAAY,MAAsB,EAAE,KAAa,EAAE,UAAuC,EAAE;QAHpF,cAAS,GAAiD,IAAI,GAAG,EAAE,CAAC;QACpE,iBAAY,GAAY,KAAK,CAAC;QAGlC,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;QACrB,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;QACnB,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;IAC3B,CAAC;IAED,EAAE,CAAC,KAAoB,EAAE,QAA6B;QAClD,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC;YAC7B,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,KAAK,EAAE,IAAI,GAAG,EAAE,CAAC,CAAC;QACzC,CAAC;QACD,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,KAAK,CAAE,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;QACzC,OAAO,IAAI,CAAC;IAChB,CAAC;IAED,SAAS;QACL,IAAI,IAAI,CAAC,YAAY;YAAE,OAAO,IAAI,CAAC;QACnC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC;YACd,IAAI,EAAE,WAAW;YACjB,KAAK,EAAE,IAAI,CAAC,KAAK;YACjB,MAAM,EAAE,IAAI,CAAC,OAAO,CAAC,MAAM;SAC9B,CAAC,CAAC;QACH,IAAI,CAAC,YAAY,GAAG,IAAI,CAAC;QACzB,OAAO,IAAI,CAAC;IAChB,CAAC;IAED,WAAW;QACP,IAAI,CAAC,IAAI,CAAC,YAAY;YAAE,OAAO;QAC/B,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC;YACd,IAAI,EAAE,aAAa;YACnB,KAAK,EAAE,IAAI,CAAC,KAAK;SACpB,CAAC,CAAC;QACH,IAAI,CAAC,YAAY,GAAG,KAAK,CAAC;QAC1B,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,CAAC;IAC3B,CAAC;IAED,gBAAgB;IAChB,KAAK,CAAC,OAA2B;QAC7B,MAAM,KAAK,GAAG,OAAO,CAAC,SAA0B,CAAC;QACjD,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,OAAO,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC;QACtD,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,OAAO,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC;IACxD,CAAC;CACJ;AAhDD,oDAgDC;AAaD,MAAa,cAAc;IAmBvB,YAAY,OAA8B;QAblC,OAAE,GAAqB,IAAI,CAAC;QAC5B,kBAAa,GAAsC,IAAI,GAAG,EAAE,CAAC;QAC7D,mBAAc,GAAQ,IAAI,CAAC;QAC3B,mBAAc,GAAQ,IAAI,CAAC;QAC3B,sBAAiB,GAAW,CAAC,CAAC;QAC9B,eAAU,GAAU,EAAE,CAAC;QACvB,uBAAkB,GAAyB,IAAI,CAAC;QAChD,YAAO,GAAmB,MAAM,CAAC;QACjC,qBAAgB,GAAqC,IAAI,GAAG,EAAE,CAAC;QAC/D,cAAS,GAAW,CAAC,CAAC;QAEtB,yBAAoB,GAAoB,IAAI,GAAG,EAAE,CAAC;QAgMlD,kBAAa,GAAG,GAAG,EAAE;YACzB,IAAI,IAAI,CAAC,OAAO,KAAK,WAAW,EAAE,CAAC;gBAC/B,IAAI,CAAC,iBAAiB,GAAG,CAAC,CAAC;gBAC3B,IAAI,CAAC,OAAO,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC,CAAC;YACpC,CAAC;QACL,CAAC,CAAC;QAEM,mBAAc,GAAG,GAAG,EAAE;YAC1B,IAAI,CAAC,aAAa,EAAE,CAAC;YACrB,IAAI,CAAC,UAAU,CAAC,cAAc,CAAC,CAAC;QACpC,CAAC,CAAC;QAvME,MAAM,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;QAC9E,IAAI,CAAC,OAAO,GAAG,GAAG,MAAM,eAAe,CAAC;QACxC,IAAI,CAAC,SAAS,GAAG,OAAO,CAAC,SAAS,IAAI,EAAE,CAAC;QACzC,IAAI,OAAO,CAAC,KAAK;YAAE,IAAI,CAAC,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC;QAC9C,IAAI,OAAO,CAAC,MAAM;YAAE,IAAI,CAAC,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;QACjD,IAAI,OAAO,CAAC,MAAM;YAAE,IAAI,CAAC,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;QACjD,IAAI,CAAC,qBAAqB,GAAG,OAAO,CAAC,oBAAoB,IAAI,QAAQ,CAAC;IAC1E,CAAC;IAED,IAAI,MAAM,KAAqB,OAAO,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC;IAErD,cAAc,CAAC,EAAoC;QAC/C,IAAI,CAAC,gBAAgB,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QAC9B,OAAO,GAAG,EAAE,CAAC,IAAI,CAAC,gBAAgB,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;IAClD,CAAC;IAED,oBAAoB,CAAC,EAAc;QAC/B,IAAI,CAAC,oBAAoB,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QAClC,OAAO,GAAG,EAAE,CAAC,IAAI,CAAC,oBAAoB,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;IACtD,CAAC;IAEO,UAAU,CAAC,CAAiB;QAChC,IAAI,CAAC,OAAO,GAAG,CAAC,CAAC;QACjB,IAAI,CAAC,gBAAgB,CAAC,OAAO,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;IAC/C,CAAC;IAED,QAAQ,CAAC,QAAgB;QACrB,IAAI,CAAC,KAAK,GAAG,QAAQ,CAAC;QACtB,IAAI,CAAC,KAAK,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAC,CAAC;IAClD,CAAC;IAED,OAAO;QACH,IAAI,IAAI,CAAC,EAAE,IAAI,IAAI,CAAC,EAAE,CAAC,UAAU,KAAK,SAAS,CAAC,IAAI;YAAE,OAAO,OAAO,CAAC,OAAO,EAAE,CAAC;QAC/E,IAAI,IAAI,CAAC,kBAAkB;YAAE,OAAO,IAAI,CAAC,kBAAkB,CAAC;QAC5D,IAAI,CAAC,kBAAkB,GAAG,IAAI,CAAC,UAAU,EAAE,CAAC,OAAO,CAAC,GAAG,EAAE;YACrD,IAAI,CAAC,kBAAkB,GAAG,IAAI,CAAC;QACnC,CAAC,CAAC,CAAC;QACH,OAAO,IAAI,CAAC,kBAAkB,CAAC;IACnC,CAAC;IAEO,UAAU;QACd,IAAI,CAAC,UAAU,CAAC,YAAY,CAAC,CAAC;QAC9B,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;YACnC,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;YAClC,yDAAyD;YACzD,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,WAAW,EAAE,IAAI,CAAC,SAAS,CAAC,CAAC;YAClD,IAAI,IAAI,CAAC,MAAM;gBAAE,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,QAAQ,EAAE,IAAI,CAAC,MAAM,CAAC,CAAC;YAC7D,IAAI,IAAI,CAAC,KAAK;gBAAE,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,OAAO,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC;YAE1D,sFAAsF;YACtF,6EAA6E;YAC7E,wDAAwD;YACxD,MAAM,SAAS,GAAa,EAAE,CAAC;YAC/B,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC;gBACd,SAAS,CAAC,IAAI,CAAC,iBAAiB,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC;YACnD,CAAC;YAED,IAAI,CAAC,EAAE,GAAG,SAAS,CAAC,MAAM,GAAG,CAAC;gBAC1B,CAAC,CAAC,IAAI,SAAS,CAAC,GAAG,CAAC,QAAQ,EAAE,EAAE,SAAS,CAAC;gBAC1C,CAAC,CAAC,IAAI,SAAS,CAAC,GAAG,CAAC,QAAQ,EAAE,CAAC,CAAC;YAEpC,IAAI,CAAC,EAAE,CAAC,MAAM,GAAG,GAAG,EAAE;gBAClB,IAAI,CAAC,UAAU,CAAC,WAAW,CAAC,CAAC;gBAC7B,IAAI,CAAC,iBAAiB,GAAG,CAAC,CAAC;gBAC3B,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;gBAC5B,IAAI,CAAC,cAAc,EAAE,CAAC;gBACtB,IAAI,CAAC,sBAAsB,EAAE,CAAC;gBAC9B,OAAO,IAAI,CAAC,UAAU,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;oBAChC,IAAI,CAAC,EAAG,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,UAAU,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC;gBAC3D,CAAC;gBACD,KAAK,MAAM,GAAG,IAAI,IAAI,CAAC,aAAa,CAAC,MAAM,EAAE,EAAE,CAAC;oBAC5C,GAAG,CAAC,SAAS,EAAE,CAAC;gBACpB,CAAC;gBACD,OAAO,EAAE,CAAC;YACd,CAAC,CAAC;YAEF,IAAI,CAAC,EAAE,CAAC,SAAS,GAAG,CAAC,KAAK,EAAE,EAAE;gBAC1B,IAAI,CAAC;oBACD,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;oBACpC,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,CAAC;gBAC7B,CAAC;gBAAC,OAAO,CAAC,EAAE,CAAC;oBACT,OAAO,CAAC,KAAK,CAAC,+BAA+B,EAAE,CAAC,CAAC,CAAC;gBACtD,CAAC;YACL,CAAC,CAAC;YAEF,IAAI,CAAC,EAAE,CAAC,OAAO,GAAG,GAAG,EAAE;gBACnB,IAAI,CAAC,UAAU,CAAC,cAAc,CAAC,CAAC;gBAChC,IAAI,CAAC,aAAa,EAAE,CAAC;gBACrB,IAAI,CAAC,EAAE,GAAG,IAAI,CAAC;gBACf,IAAI,CAAC,iBAAiB,EAAE,CAAC;YAC7B,CAAC,CAAC;YAEF,IAAI,CAAC,EAAE,CAAC,OAAO,GAAG,CAAC,GAAG,EAAE,EAAE;gBACtB,OAAO,CAAC,KAAK,CAAC,4BAA4B,EAAE,GAAG,CAAC,CAAC;gBACjD,IAAI,CAAC,UAAU,CAAC,cAAc,CAAC,CAAC;gBAChC,MAAM,CAAC,GAAG,CAAC,CAAC;YAChB,CAAC,CAAC;QACN,CAAC,CAAC,CAAC;IACP,CAAC;IAED,UAAU;QACN,IAAI,CAAC,UAAU,CAAC,cAAc,CAAC,CAAC;QAChC,IAAI,CAAC,aAAa,EAAE,CAAC;QACrB,IAAI,CAAC,aAAa,EAAE,CAAC;QACrB,IAAI,CAAC,yBAAyB,EAAE,CAAC;QACjC,IAAI,IAAI,CAAC,EAAE,EAAE,CAAC;YACV,IAAI,CAAC,EAAE,CAAC,KAAK,EAAE,CAAC;YAChB,IAAI,CAAC,EAAE,GAAG,IAAI,CAAC;QACnB,CAAC;QACD,IAAI,CAAC,UAAU,GAAG,EAAE,CAAC;IACzB,CAAC;IAED,OAAO,CAAU,KAAa,EAAE,UAAuC,EAAE;QACrE,MAAM,SAAS,GAAG,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS,KAAK,IAAI,IAAI,CAAC,SAAS,EAAE,CAAC;QACnF,IAAI,GAAG,GAAG,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;QAC5C,IAAI,CAAC,GAAG,EAAE,CAAC;YACP,GAAG,GAAG,IAAI,oBAAoB,CAAI,IAAI,EAAE,SAAS,EAAE,OAAO,CAAC,CAAC;YAC5D,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,SAAS,EAAE,GAAG,CAAC,CAAC;QAC3C,CAAC;QACD,OAAO,GAA8B,CAAC;IAC1C,CAAC;IAED,QAAQ,CAAC,MAAc,EAAE,IAAY;QACjC,IAAI,CAAC,KAAK,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC;IAC/C,CAAC;IAED,QAAQ,CAAC,MAAc;QACnB,OAAO,IAAI,CAAC,OAAO,CAAC,QAAQ,MAAM,IAAI,IAAI,CAAC,SAAS,EAAE,CAAC,CAAC;IAC5D,CAAC;IAED,gBAAgB;IAChB,KAAK,CAAC,IAAS;QACX,IAAI,IAAI,CAAC,EAAE,IAAI,IAAI,CAAC,EAAE,CAAC,UAAU,KAAK,SAAS,CAAC,IAAI,EAAE,CAAC;YACnD,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC;QACvC,CAAC;aAAM,CAAC;YACJ,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC/B,CAAC;IACL,CAAC;IAEO,aAAa,CAAC,IAAqB;QACvC,IAAI,IAAI,CAAC,IAAI,KAAK,MAAM,EAAE,CAAC;YACvB,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;YAC5B,OAAO;QACX,CAAC;QACD,IAAI,IAAI,CAAC,IAAI,KAAK,WAAW,IAAI,IAAI,CAAC,IAAI,KAAK,cAAc,EAAE,CAAC;YAC5D,MAAM,GAAG,GAAG,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YAC/C,IAAI,GAAG;gBAAE,GAAG,CAAC,KAAK,CAAC,IAAW,CAAC,CAAC;QACpC,CAAC;IACL,CAAC;IAEO,cAAc;QAClB,IAAI,CAAC,cAAc,GAAG,WAAW,CAAC,GAAG,EAAE;YACnC,IAAI,CAAC,KAAK,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,CAAC;YAC7B,IAAI,IAAI,CAAC,SAAS,GAAG,CAAC,IAAI,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,SAAS,GAAG,KAAK,EAAE,CAAC;gBAC5D,OAAO,CAAC,IAAI,CAAC,+CAA+C,CAAC,CAAC;gBAC9D,IAAI,CAAC,EAAE,EAAE,KAAK,EAAE,CAAC;YACrB,CAAC;QACL,CAAC,EAAE,KAAK,CAAC,CAAC;IACd,CAAC;IAEO,aAAa;QACjB,IAAI,IAAI,CAAC,cAAc,EAAE,CAAC;YACtB,aAAa,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;YACnC,IAAI,CAAC,cAAc,GAAG,IAAI,CAAC;QAC/B,CAAC;IACL,CAAC;IAEO,iBAAiB;QACrB,IAAI,CAAC,aAAa,EAAE,CAAC;QACrB,IAAI,IAAI,CAAC,iBAAiB,IAAI,IAAI,CAAC,qBAAqB,EAAE,CAAC;YACvD,IAAI,CAAC,UAAU,CAAC,cAAc,CAAC,CAAC;YAChC,IAAI,CAAC,oBAAoB,CAAC,OAAO,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC;YAC9C,OAAO;QACX,CAAC;QACD,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,iBAAiB,CAAC,EAAE,KAAK,CAAC,CAAC;QAC1E,MAAM,MAAM,GAAG,KAAK,GAAG,GAAG,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC;QAC3C,IAAI,CAAC,iBAAiB,EAAE,CAAC;QACzB,IAAI,CAAC,cAAc,GAAG,UAAU,CAAC,GAAG,EAAE;YAClC,IAAI,CAAC,OAAO,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC,CAAC;QACpC,CAAC,EAAE,KAAK,GAAG,MAAM,CAAC,CAAC;IACvB,CAAC;IAEO,aAAa;QACjB,IAAI,IAAI,CAAC,cAAc,EAAE,CAAC;YACtB,YAAY,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;YAClC,IAAI,CAAC,cAAc,GAAG,IAAI,CAAC;QAC/B,CAAC;IACL,CAAC;IAcO,sBAAsB;QAC1B,IAAI,OAAO,MAAM,KAAK,WAAW,EAAE,CAAC;YAChC,MAAM,CAAC,gBAAgB,CAAC,QAAQ,EAAE,IAAI,CAAC,aAAa,CAAC,CAAC;YACtD,MAAM,CAAC,gBAAgB,CAAC,SAAS,EAAE,IAAI,CAAC,cAAc,CAAC,CAAC;QAC5D,CAAC;IACL,CAAC;IAEO,yBAAyB;QAC7B,IAAI,OAAO,MAAM,KAAK,WAAW,EAAE,CAAC;YAChC,MAAM,CAAC,mBAAmB,CAAC,QAAQ,EAAE,IAAI,CAAC,aAAa,CAAC,CAAC;YACzD,MAAM,CAAC,mBAAmB,CAAC,SAAS,EAAE,IAAI,CAAC,cAAc,CAAC,CAAC;QAC/D,CAAC;IACL,CAAC;CACJ;AA1OD,wCA0OC"}
@@ -8,19 +8,37 @@ export interface RealtimeSubscriptionOptions {
8
8
  event?: RealtimeEvent;
9
9
  filter?: Record<string, any>;
10
10
  }
11
- export type RealtimeCallback = (payload: any) => void;
12
- export declare class RealtimeSubscription {
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: any): void;
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
- constructor(options: {
37
- baseUrl: string;
38
- projectId: string;
39
- token?: string;
40
- userId?: string;
41
- apiKey?: string;
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;AAEtD,qBAAa,oBAAoB;IAC7B,OAAO,CAAC,MAAM,CAAiB;IAC/B,OAAO,CAAC,KAAK,CAAS;IACtB,OAAO,CAAC,OAAO,CAA8B;IAC7C,OAAO,CAAC,SAAS,CAAwD;IACzE,OAAO,CAAC,YAAY,CAAkB;gBAE1B,MAAM,EAAE,cAAc,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,GAAE,2BAAgC;IAM5F,EAAE,CAAC,KAAK,EAAE,aAAa,EAAE,QAAQ,EAAE,gBAAgB,GAAG,IAAI;IAQ1D,SAAS,IAAI,IAAI;IAWjB,WAAW,IAAI,IAAI;IAUnB,gBAAgB;IAChB,KAAK,CAAC,OAAO,EAAE,GAAG,GAAG,IAAI;CAW5B;AAED,qBAAa,cAAc;IACvB,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,KAAK,CAAC,CAAS;IACvB,OAAO,CAAC,MAAM,CAAC,CAAS;IACxB,OAAO,CAAC,MAAM,CAAC,CAAS;IACxB,OAAO,CAAC,EAAE,CAA0B;IACpC,OAAO,CAAC,aAAa,CAAgD;IACrE,OAAO,CAAC,cAAc,CAAa;IACnC,OAAO,CAAC,cAAc,CAAa;IACnC,OAAO,CAAC,iBAAiB,CAAa;gBAE1B,OAAO,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE;IAQ7G,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAiDxB,UAAU;IAOV,OAAO,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,GAAE,2BAAgC,GAAG,oBAAoB;IAWvF,gBAAgB;IAChB,KAAK,CAAC,IAAI,EAAE,GAAG;IAMf,OAAO,CAAC,aAAa;IASrB,OAAO,CAAC,cAAc;IAMtB,OAAO,CAAC,aAAa;IAIrB,gEAAgE;IAChE,OAAO,CAAC,iBAAiB;IAUzB,OAAO,CAAC,aAAa;CAGxB"}
1
+ {"version":3,"file":"realtime.d.ts","sourceRoot":"","sources":["../../../src/sdk/realtime.ts"],"names":[],"mappings":"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"}
@@ -37,13 +37,8 @@ export class RealtimeSubscription {
37
37
  /** @internal */
38
38
  _emit(payload) {
39
39
  const event = payload.operation;
40
- const requestedEvent = this.options.event || '*';
41
- if (requestedEvent === '*' || requestedEvent === event) {
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.baseUrl = options.baseUrl.replace(/^http/, 'ws') + '/realtime';
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,28 +73,64 @@ 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
- if (this.apiKey) {
71
- url.searchParams.set('apiKey', this.apiKey);
72
- }
73
- else {
74
- url.searchParams.set('projectId', this.projectId);
75
- }
109
+ // Always include projectId in URL for routing (not auth)
110
+ url.searchParams.set('projectId', this.projectId);
76
111
  if (this.userId)
77
112
  url.searchParams.set('userId', this.userId);
78
113
  if (this.token)
79
114
  url.searchParams.set('token', this.token);
80
- this.ws = new WebSocket(url.toString());
115
+ // SECURITY: Pass API key via Sec-WebSocket-Protocol header, NOT as a URL query param.
116
+ // URL query params appear in CDN logs, browser history, and Referer headers.
117
+ // Protocol format: "aerostack-key.<base64-encoded-key>"
118
+ const protocols = [];
119
+ if (this.apiKey) {
120
+ protocols.push(`aerostack-key.${this.apiKey}`);
121
+ }
122
+ this.ws = protocols.length > 0
123
+ ? new WebSocket(url.toString(), protocols)
124
+ : new WebSocket(url.toString());
81
125
  this.ws.onopen = () => {
82
- console.log('Aerostack Realtime Connected');
126
+ this._setStatus('connected');
83
127
  this.reconnectAttempts = 0;
128
+ this._lastPong = Date.now();
84
129
  this.startHeartbeat();
85
- // Re-subscribe
130
+ this._setupOfflineDetection();
131
+ while (this._sendQueue.length > 0) {
132
+ this.ws.send(JSON.stringify(this._sendQueue.shift()));
133
+ }
86
134
  for (const sub of this.subscriptions.values()) {
87
135
  sub.subscribe();
88
136
  }
@@ -98,22 +146,28 @@ export class RealtimeClient {
98
146
  }
99
147
  };
100
148
  this.ws.onclose = () => {
101
- console.log('Aerostack Realtime Disconnected');
149
+ this._setStatus('reconnecting');
102
150
  this.stopHeartbeat();
103
151
  this.ws = null;
104
152
  this.scheduleReconnect();
105
153
  };
106
154
  this.ws.onerror = (err) => {
107
155
  console.error('Realtime connection error:', err);
156
+ this._setStatus('disconnected');
108
157
  reject(err);
109
158
  };
110
159
  });
111
160
  }
112
161
  disconnect() {
162
+ this._setStatus('disconnected');
163
+ this.stopReconnect();
164
+ this.stopHeartbeat();
165
+ this._teardownOfflineDetection();
113
166
  if (this.ws) {
114
167
  this.ws.close();
168
+ this.ws = null;
115
169
  }
116
- this.stopReconnect();
170
+ this._sendQueue = [];
117
171
  }
118
172
  channel(topic, options = {}) {
119
173
  const fullTopic = topic.includes('/') ? topic : `table/${topic}/${this.projectId}`;
@@ -124,32 +178,54 @@ export class RealtimeClient {
124
178
  }
125
179
  return sub;
126
180
  }
181
+ sendChat(roomId, text) {
182
+ this._send({ type: 'chat', roomId, text });
183
+ }
184
+ chatRoom(roomId) {
185
+ return this.channel(`chat/${roomId}/${this.projectId}`);
186
+ }
127
187
  /** @internal */
128
188
  _send(data) {
129
189
  if (this.ws && this.ws.readyState === WebSocket.OPEN) {
130
190
  this.ws.send(JSON.stringify(data));
131
191
  }
192
+ else {
193
+ this._sendQueue.push(data);
194
+ }
132
195
  }
133
196
  handleMessage(data) {
197
+ if (data.type === 'pong') {
198
+ this._lastPong = Date.now();
199
+ return;
200
+ }
134
201
  if (data.type === 'db_change' || data.type === 'chat_message') {
135
202
  const sub = this.subscriptions.get(data.topic);
136
- if (sub) {
203
+ if (sub)
137
204
  sub._emit(data);
138
- }
139
205
  }
140
206
  }
141
207
  startHeartbeat() {
142
208
  this.heartbeatTimer = setInterval(() => {
143
209
  this._send({ type: 'ping' });
210
+ if (this._lastPong > 0 && Date.now() - this._lastPong > 70000) {
211
+ console.warn('Realtime: no pong received, forcing reconnect');
212
+ this.ws?.close();
213
+ }
144
214
  }, 30000);
145
215
  }
146
216
  stopHeartbeat() {
147
- if (this.heartbeatTimer)
217
+ if (this.heartbeatTimer) {
148
218
  clearInterval(this.heartbeatTimer);
219
+ this.heartbeatTimer = null;
220
+ }
149
221
  }
150
- /** Exponential backoff with jitter: 1s → 2s → 4s → ... → 30s */
151
222
  scheduleReconnect() {
152
223
  this.stopReconnect();
224
+ if (this.reconnectAttempts >= this._maxReconnectAttempts) {
225
+ this._setStatus('disconnected');
226
+ this._maxRetriesListeners.forEach(cb => cb());
227
+ return;
228
+ }
153
229
  const delay = Math.min(1000 * Math.pow(2, this.reconnectAttempts), 30000);
154
230
  const jitter = delay * 0.3 * Math.random();
155
231
  this.reconnectAttempts++;
@@ -158,8 +234,22 @@ export class RealtimeClient {
158
234
  }, delay + jitter);
159
235
  }
160
236
  stopReconnect() {
161
- if (this.reconnectTimer)
237
+ if (this.reconnectTimer) {
162
238
  clearTimeout(this.reconnectTimer);
239
+ this.reconnectTimer = null;
240
+ }
241
+ }
242
+ _setupOfflineDetection() {
243
+ if (typeof window !== 'undefined') {
244
+ window.addEventListener('online', this._handleOnline);
245
+ window.addEventListener('offline', this._handleOffline);
246
+ }
247
+ }
248
+ _teardownOfflineDetection() {
249
+ if (typeof window !== 'undefined') {
250
+ window.removeEventListener('online', this._handleOnline);
251
+ window.removeEventListener('offline', this._handleOffline);
252
+ }
163
253
  }
164
254
  }
165
255
  //# sourceMappingURL=realtime.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"realtime.js","sourceRoot":"","sources":["../../../src/sdk/realtime.ts"],"names":[],"mappings":"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"}
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.6",
3
+ "version": "0.6.8",
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.0",
39
+ "@aerostack/core": "^0.6.8",
40
40
  "zod": "^3.25.0 || ^4.0.0"
41
41
  },
42
42
  "exports": {
@@ -11,13 +11,23 @@ export interface RealtimeSubscriptionOptions {
11
11
  filter?: Record<string, any>;
12
12
  }
13
13
 
14
- export type RealtimeCallback = (payload: any) => void;
14
+ export type RealtimeCallback<T = any> = (payload: RealtimePayload<T>) => void;
15
15
 
16
- export class RealtimeSubscription {
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>> = new Map();
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: any): void {
69
+ _emit(payload: RealtimePayload<T>): void {
60
70
  const event = payload.operation as RealtimeEvent;
61
- const requestedEvent = this.options.event || '*';
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,35 +95,85 @@ 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: { baseUrl: string; projectId: string; token?: string; userId?: string; apiKey?: string }) {
85
- this.baseUrl = options.baseUrl.replace(/^http/, 'ws') + '/realtime';
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
- if (this.apiKey) {
98
- url.searchParams.set('apiKey', this.apiKey);
99
- } else {
100
- url.searchParams.set('projectId', this.projectId);
101
- }
151
+ // Always include projectId in URL for routing (not auth)
152
+ url.searchParams.set('projectId', this.projectId);
102
153
  if (this.userId) url.searchParams.set('userId', this.userId);
103
154
  if (this.token) url.searchParams.set('token', this.token);
104
155
 
105
- this.ws = new WebSocket(url.toString());
156
+ // SECURITY: Pass API key via Sec-WebSocket-Protocol header, NOT as a URL query param.
157
+ // URL query params appear in CDN logs, browser history, and Referer headers.
158
+ // Protocol format: "aerostack-key.<base64-encoded-key>"
159
+ const protocols: string[] = [];
160
+ if (this.apiKey) {
161
+ protocols.push(`aerostack-key.${this.apiKey}`);
162
+ }
163
+
164
+ this.ws = protocols.length > 0
165
+ ? new WebSocket(url.toString(), protocols)
166
+ : new WebSocket(url.toString());
106
167
 
107
168
  this.ws.onopen = () => {
108
- console.log('Aerostack Realtime Connected');
169
+ this._setStatus('connected');
109
170
  this.reconnectAttempts = 0;
171
+ this._lastPong = Date.now();
110
172
  this.startHeartbeat();
111
- // Re-subscribe
173
+ this._setupOfflineDetection();
174
+ while (this._sendQueue.length > 0) {
175
+ this.ws!.send(JSON.stringify(this._sendQueue.shift()));
176
+ }
112
177
  for (const sub of this.subscriptions.values()) {
113
178
  sub.subscribe();
114
179
  }
@@ -125,7 +190,7 @@ export class RealtimeClient {
125
190
  };
126
191
 
127
192
  this.ws.onclose = () => {
128
- console.log('Aerostack Realtime Disconnected');
193
+ this._setStatus('reconnecting');
129
194
  this.stopHeartbeat();
130
195
  this.ws = null;
131
196
  this.scheduleReconnect();
@@ -133,58 +198,86 @@ export class RealtimeClient {
133
198
 
134
199
  this.ws.onerror = (err) => {
135
200
  console.error('Realtime connection error:', err);
201
+ this._setStatus('disconnected');
136
202
  reject(err);
137
203
  };
138
204
  });
139
205
  }
140
206
 
141
207
  disconnect() {
208
+ this._setStatus('disconnected');
209
+ this.stopReconnect();
210
+ this.stopHeartbeat();
211
+ this._teardownOfflineDetection();
142
212
  if (this.ws) {
143
213
  this.ws.close();
214
+ this.ws = null;
144
215
  }
145
- this.stopReconnect();
216
+ this._sendQueue = [];
146
217
  }
147
218
 
148
- channel(topic: string, options: RealtimeSubscriptionOptions = {}): RealtimeSubscription {
219
+ channel<T = any>(topic: string, options: RealtimeSubscriptionOptions = {}): RealtimeSubscription<T> {
149
220
  const fullTopic = topic.includes('/') ? topic : `table/${topic}/${this.projectId}`;
150
-
151
221
  let sub = this.subscriptions.get(fullTopic);
152
222
  if (!sub) {
153
- sub = new RealtimeSubscription(this, fullTopic, options);
223
+ sub = new RealtimeSubscription<T>(this, fullTopic, options);
154
224
  this.subscriptions.set(fullTopic, sub);
155
225
  }
156
- return sub;
226
+ return sub as RealtimeSubscription<T>;
227
+ }
228
+
229
+ sendChat(roomId: string, text: string): void {
230
+ this._send({ type: 'chat', roomId, text });
231
+ }
232
+
233
+ chatRoom(roomId: string): RealtimeSubscription {
234
+ return this.channel(`chat/${roomId}/${this.projectId}`);
157
235
  }
158
236
 
159
237
  /** @internal */
160
238
  _send(data: any) {
161
239
  if (this.ws && this.ws.readyState === WebSocket.OPEN) {
162
240
  this.ws.send(JSON.stringify(data));
241
+ } else {
242
+ this._sendQueue.push(data);
163
243
  }
164
244
  }
165
245
 
166
246
  private handleMessage(data: RealtimeMessage) {
247
+ if (data.type === 'pong') {
248
+ this._lastPong = Date.now();
249
+ return;
250
+ }
167
251
  if (data.type === 'db_change' || data.type === 'chat_message') {
168
252
  const sub = this.subscriptions.get(data.topic);
169
- if (sub) {
170
- sub._emit(data);
171
- }
253
+ if (sub) sub._emit(data as any);
172
254
  }
173
255
  }
174
256
 
175
257
  private startHeartbeat() {
176
258
  this.heartbeatTimer = setInterval(() => {
177
259
  this._send({ type: 'ping' });
260
+ if (this._lastPong > 0 && Date.now() - this._lastPong > 70000) {
261
+ console.warn('Realtime: no pong received, forcing reconnect');
262
+ this.ws?.close();
263
+ }
178
264
  }, 30000);
179
265
  }
180
266
 
181
267
  private stopHeartbeat() {
182
- if (this.heartbeatTimer) clearInterval(this.heartbeatTimer);
268
+ if (this.heartbeatTimer) {
269
+ clearInterval(this.heartbeatTimer);
270
+ this.heartbeatTimer = null;
271
+ }
183
272
  }
184
273
 
185
- /** Exponential backoff with jitter: 1s → 2s → 4s → ... → 30s */
186
274
  private scheduleReconnect() {
187
275
  this.stopReconnect();
276
+ if (this.reconnectAttempts >= this._maxReconnectAttempts) {
277
+ this._setStatus('disconnected');
278
+ this._maxRetriesListeners.forEach(cb => cb());
279
+ return;
280
+ }
188
281
  const delay = Math.min(1000 * Math.pow(2, this.reconnectAttempts), 30000);
189
282
  const jitter = delay * 0.3 * Math.random();
190
283
  this.reconnectAttempts++;
@@ -194,6 +287,35 @@ export class RealtimeClient {
194
287
  }
195
288
 
196
289
  private stopReconnect() {
197
- if (this.reconnectTimer) clearTimeout(this.reconnectTimer);
290
+ if (this.reconnectTimer) {
291
+ clearTimeout(this.reconnectTimer);
292
+ this.reconnectTimer = null;
293
+ }
294
+ }
295
+
296
+ private _handleOnline = () => {
297
+ if (this._status !== 'connected') {
298
+ this.reconnectAttempts = 0;
299
+ this.connect().catch(() => { });
300
+ }
301
+ };
302
+
303
+ private _handleOffline = () => {
304
+ this.stopReconnect();
305
+ this._setStatus('disconnected');
306
+ };
307
+
308
+ private _setupOfflineDetection() {
309
+ if (typeof window !== 'undefined') {
310
+ window.addEventListener('online', this._handleOnline);
311
+ window.addEventListener('offline', this._handleOffline);
312
+ }
313
+ }
314
+
315
+ private _teardownOfflineDetection() {
316
+ if (typeof window !== 'undefined') {
317
+ window.removeEventListener('online', this._handleOnline);
318
+ window.removeEventListener('offline', this._handleOffline);
319
+ }
198
320
  }
199
321
  }