@insforge/sdk 1.0.1-refresh.9 → 1.0.1
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/LICENSE +201 -201
- package/README.md +249 -249
- package/dist/index.d.mts +132 -78
- package/dist/index.d.ts +132 -78
- package/dist/index.js +273 -240
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +272 -240
- package/dist/index.mjs.map +1 -1
- package/package.json +68 -67
package/dist/index.js
CHANGED
|
@@ -27,6 +27,7 @@ __export(index_exports, {
|
|
|
27
27
|
HttpClient: () => HttpClient,
|
|
28
28
|
InsForgeClient: () => InsForgeClient,
|
|
29
29
|
InsForgeError: () => InsForgeError,
|
|
30
|
+
Realtime: () => Realtime,
|
|
30
31
|
Storage: () => Storage,
|
|
31
32
|
StorageBucket: () => StorageBucket,
|
|
32
33
|
TokenManager: () => TokenManager,
|
|
@@ -111,7 +112,6 @@ var HttpClient = class {
|
|
|
111
112
|
method,
|
|
112
113
|
headers: requestHeaders,
|
|
113
114
|
body: processedBody,
|
|
114
|
-
credentials: "include",
|
|
115
115
|
...fetchOptions
|
|
116
116
|
});
|
|
117
117
|
if (response.status === 204) {
|
|
@@ -176,45 +176,8 @@ var HttpClient = class {
|
|
|
176
176
|
// src/lib/token-manager.ts
|
|
177
177
|
var TOKEN_KEY = "insforge-auth-token";
|
|
178
178
|
var USER_KEY = "insforge-auth-user";
|
|
179
|
-
var AUTH_FLAG_COOKIE = "isAuthenticated";
|
|
180
|
-
var CSRF_TOKEN_COOKIE = "insforge_csrf_token";
|
|
181
|
-
function hasAuthCookie() {
|
|
182
|
-
if (typeof document === "undefined") return false;
|
|
183
|
-
return document.cookie.split(";").some(
|
|
184
|
-
(c) => c.trim().startsWith(`${AUTH_FLAG_COOKIE}=`)
|
|
185
|
-
);
|
|
186
|
-
}
|
|
187
|
-
function setAuthCookie() {
|
|
188
|
-
if (typeof document === "undefined") return;
|
|
189
|
-
const maxAge = 7 * 24 * 60 * 60;
|
|
190
|
-
document.cookie = `${AUTH_FLAG_COOKIE}=true; path=/; max-age=${maxAge}; SameSite=Lax`;
|
|
191
|
-
}
|
|
192
|
-
function clearAuthCookie() {
|
|
193
|
-
if (typeof document === "undefined") return;
|
|
194
|
-
document.cookie = `${AUTH_FLAG_COOKIE}=; path=/; max-age=0; SameSite=Lax`;
|
|
195
|
-
}
|
|
196
|
-
function getCsrfToken() {
|
|
197
|
-
if (typeof document === "undefined") return null;
|
|
198
|
-
const match = document.cookie.split(";").find((c) => c.trim().startsWith(`${CSRF_TOKEN_COOKIE}=`));
|
|
199
|
-
if (!match) return null;
|
|
200
|
-
return match.split("=")[1] || null;
|
|
201
|
-
}
|
|
202
|
-
function setCsrfToken(token) {
|
|
203
|
-
if (typeof document === "undefined") return;
|
|
204
|
-
const maxAge = 7 * 24 * 60 * 60;
|
|
205
|
-
document.cookie = `${CSRF_TOKEN_COOKIE}=${token}; path=/; max-age=${maxAge}; SameSite=Lax`;
|
|
206
|
-
}
|
|
207
|
-
function clearCsrfToken() {
|
|
208
|
-
if (typeof document === "undefined") return;
|
|
209
|
-
document.cookie = `${CSRF_TOKEN_COOKIE}=; path=/; max-age=0; SameSite=Lax`;
|
|
210
|
-
}
|
|
211
179
|
var TokenManager = class {
|
|
212
180
|
constructor(storage) {
|
|
213
|
-
// In-memory storage
|
|
214
|
-
this.accessToken = null;
|
|
215
|
-
this.user = null;
|
|
216
|
-
// Mode: 'memory' (new backend) or 'storage' (legacy backend, default)
|
|
217
|
-
this._mode = "storage";
|
|
218
181
|
if (storage) {
|
|
219
182
|
this.storage = storage;
|
|
220
183
|
} else if (typeof window !== "undefined" && window.localStorage) {
|
|
@@ -232,112 +195,32 @@ var TokenManager = class {
|
|
|
232
195
|
};
|
|
233
196
|
}
|
|
234
197
|
}
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
get mode() {
|
|
239
|
-
return this._mode;
|
|
240
|
-
}
|
|
241
|
-
/**
|
|
242
|
-
* Set mode to memory (new backend with cookies + memory)
|
|
243
|
-
*/
|
|
244
|
-
setMemoryMode() {
|
|
245
|
-
if (this._mode === "storage") {
|
|
246
|
-
this.storage.removeItem(TOKEN_KEY);
|
|
247
|
-
this.storage.removeItem(USER_KEY);
|
|
248
|
-
}
|
|
249
|
-
this._mode = "memory";
|
|
250
|
-
}
|
|
251
|
-
/**
|
|
252
|
-
* Set mode to storage (legacy backend with localStorage)
|
|
253
|
-
* Also loads existing session from localStorage
|
|
254
|
-
*/
|
|
255
|
-
setStorageMode() {
|
|
256
|
-
this._mode = "storage";
|
|
257
|
-
this.loadFromStorage();
|
|
198
|
+
saveSession(session) {
|
|
199
|
+
this.storage.setItem(TOKEN_KEY, session.accessToken);
|
|
200
|
+
this.storage.setItem(USER_KEY, JSON.stringify(session.user));
|
|
258
201
|
}
|
|
259
|
-
|
|
260
|
-
* Load session from localStorage
|
|
261
|
-
*/
|
|
262
|
-
loadFromStorage() {
|
|
202
|
+
getSession() {
|
|
263
203
|
const token = this.storage.getItem(TOKEN_KEY);
|
|
264
204
|
const userStr = this.storage.getItem(USER_KEY);
|
|
265
|
-
if (token
|
|
266
|
-
|
|
267
|
-
this.accessToken = token;
|
|
268
|
-
this.user = JSON.parse(userStr);
|
|
269
|
-
} catch {
|
|
270
|
-
this.clearSession();
|
|
271
|
-
}
|
|
205
|
+
if (!token || !userStr) {
|
|
206
|
+
return null;
|
|
272
207
|
}
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
this.user = session.user;
|
|
280
|
-
if (this._mode === "storage") {
|
|
281
|
-
this.storage.setItem(TOKEN_KEY, session.accessToken);
|
|
282
|
-
this.storage.setItem(USER_KEY, JSON.stringify(session.user));
|
|
208
|
+
try {
|
|
209
|
+
const user = JSON.parse(userStr);
|
|
210
|
+
return { accessToken: token, user };
|
|
211
|
+
} catch {
|
|
212
|
+
this.clearSession();
|
|
213
|
+
return null;
|
|
283
214
|
}
|
|
284
215
|
}
|
|
285
|
-
/**
|
|
286
|
-
* Get current session
|
|
287
|
-
*/
|
|
288
|
-
getSession() {
|
|
289
|
-
if (!this.accessToken || !this.user) return null;
|
|
290
|
-
return {
|
|
291
|
-
accessToken: this.accessToken,
|
|
292
|
-
user: this.user
|
|
293
|
-
};
|
|
294
|
-
}
|
|
295
|
-
/**
|
|
296
|
-
* Get access token
|
|
297
|
-
*/
|
|
298
216
|
getAccessToken() {
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
/**
|
|
302
|
-
* Set access token
|
|
303
|
-
*/
|
|
304
|
-
setAccessToken(token) {
|
|
305
|
-
this.accessToken = token;
|
|
306
|
-
if (this._mode === "storage") {
|
|
307
|
-
this.storage.setItem(TOKEN_KEY, token);
|
|
308
|
-
}
|
|
309
|
-
}
|
|
310
|
-
/**
|
|
311
|
-
* Get user
|
|
312
|
-
*/
|
|
313
|
-
getUser() {
|
|
314
|
-
return this.user;
|
|
315
|
-
}
|
|
316
|
-
/**
|
|
317
|
-
* Set user
|
|
318
|
-
*/
|
|
319
|
-
setUser(user) {
|
|
320
|
-
this.user = user;
|
|
321
|
-
if (this._mode === "storage") {
|
|
322
|
-
this.storage.setItem(USER_KEY, JSON.stringify(user));
|
|
323
|
-
}
|
|
217
|
+
const token = this.storage.getItem(TOKEN_KEY);
|
|
218
|
+
return typeof token === "string" ? token : null;
|
|
324
219
|
}
|
|
325
|
-
/**
|
|
326
|
-
* Clear session (both memory and localStorage)
|
|
327
|
-
*/
|
|
328
220
|
clearSession() {
|
|
329
|
-
this.accessToken = null;
|
|
330
|
-
this.user = null;
|
|
331
221
|
this.storage.removeItem(TOKEN_KEY);
|
|
332
222
|
this.storage.removeItem(USER_KEY);
|
|
333
223
|
}
|
|
334
|
-
/**
|
|
335
|
-
* Check if there's a session in localStorage (for legacy detection)
|
|
336
|
-
*/
|
|
337
|
-
hasStoredSession() {
|
|
338
|
-
const token = this.storage.getItem(TOKEN_KEY);
|
|
339
|
-
return !!token;
|
|
340
|
-
}
|
|
341
224
|
};
|
|
342
225
|
|
|
343
226
|
// src/modules/database-postgrest.ts
|
|
@@ -452,79 +335,6 @@ var Auth = class {
|
|
|
452
335
|
this.database = new Database(http, tokenManager);
|
|
453
336
|
this.detectAuthCallback();
|
|
454
337
|
}
|
|
455
|
-
/**
|
|
456
|
-
* Restore session on app initialization
|
|
457
|
-
*
|
|
458
|
-
* @returns Object with isLoggedIn status
|
|
459
|
-
*
|
|
460
|
-
* @example
|
|
461
|
-
* ```typescript
|
|
462
|
-
* const client = new InsForgeClient({ baseUrl: '...' });
|
|
463
|
-
* const { isLoggedIn } = await client.auth.restoreSession();
|
|
464
|
-
*
|
|
465
|
-
* if (isLoggedIn) {
|
|
466
|
-
* const { data } = await client.auth.getCurrentUser();
|
|
467
|
-
* }
|
|
468
|
-
* ```
|
|
469
|
-
*/
|
|
470
|
-
async restoreSession() {
|
|
471
|
-
if (typeof window === "undefined") {
|
|
472
|
-
return { isLoggedIn: false };
|
|
473
|
-
}
|
|
474
|
-
if (this.tokenManager.getAccessToken()) {
|
|
475
|
-
return { isLoggedIn: true };
|
|
476
|
-
}
|
|
477
|
-
if (hasAuthCookie()) {
|
|
478
|
-
try {
|
|
479
|
-
const csrfToken = getCsrfToken();
|
|
480
|
-
const response = await this.http.post(
|
|
481
|
-
"/api/auth/refresh",
|
|
482
|
-
{
|
|
483
|
-
headers: csrfToken ? { "X-CSRF-Token": csrfToken } : {}
|
|
484
|
-
}
|
|
485
|
-
);
|
|
486
|
-
if (response.accessToken) {
|
|
487
|
-
this.tokenManager.setMemoryMode();
|
|
488
|
-
this.tokenManager.setAccessToken(response.accessToken);
|
|
489
|
-
this.http.setAuthToken(response.accessToken);
|
|
490
|
-
if (response.user) {
|
|
491
|
-
this.tokenManager.setUser(response.user);
|
|
492
|
-
}
|
|
493
|
-
if (response.csrfToken) {
|
|
494
|
-
setCsrfToken(response.csrfToken);
|
|
495
|
-
}
|
|
496
|
-
return { isLoggedIn: true };
|
|
497
|
-
}
|
|
498
|
-
} catch (error) {
|
|
499
|
-
if (error instanceof InsForgeError) {
|
|
500
|
-
if (error.statusCode === 404) {
|
|
501
|
-
this.tokenManager.setStorageMode();
|
|
502
|
-
const token = this.tokenManager.getAccessToken();
|
|
503
|
-
if (token) {
|
|
504
|
-
this.http.setAuthToken(token);
|
|
505
|
-
return { isLoggedIn: true };
|
|
506
|
-
}
|
|
507
|
-
return { isLoggedIn: false };
|
|
508
|
-
}
|
|
509
|
-
if (error.statusCode === 401 || error.statusCode === 403) {
|
|
510
|
-
clearAuthCookie();
|
|
511
|
-
clearCsrfToken();
|
|
512
|
-
return { isLoggedIn: false };
|
|
513
|
-
}
|
|
514
|
-
}
|
|
515
|
-
return { isLoggedIn: false };
|
|
516
|
-
}
|
|
517
|
-
}
|
|
518
|
-
if (this.tokenManager.hasStoredSession()) {
|
|
519
|
-
this.tokenManager.setStorageMode();
|
|
520
|
-
const token = this.tokenManager.getAccessToken();
|
|
521
|
-
if (token) {
|
|
522
|
-
this.http.setAuthToken(token);
|
|
523
|
-
return { isLoggedIn: true };
|
|
524
|
-
}
|
|
525
|
-
}
|
|
526
|
-
return { isLoggedIn: false };
|
|
527
|
-
}
|
|
528
338
|
/**
|
|
529
339
|
* Automatically detect and handle OAuth callback parameters in the URL
|
|
530
340
|
* This runs on initialization to seamlessly complete the OAuth flow
|
|
@@ -538,7 +348,6 @@ var Auth = class {
|
|
|
538
348
|
const userId = params.get("user_id");
|
|
539
349
|
const email = params.get("email");
|
|
540
350
|
const name = params.get("name");
|
|
541
|
-
const csrfToken = params.get("csrf_token");
|
|
542
351
|
if (accessToken && userId && email) {
|
|
543
352
|
const session = {
|
|
544
353
|
accessToken,
|
|
@@ -553,18 +362,13 @@ var Auth = class {
|
|
|
553
362
|
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
554
363
|
}
|
|
555
364
|
};
|
|
556
|
-
this.http.setAuthToken(accessToken);
|
|
557
365
|
this.tokenManager.saveSession(session);
|
|
558
|
-
|
|
559
|
-
if (csrfToken) {
|
|
560
|
-
setCsrfToken(csrfToken);
|
|
561
|
-
}
|
|
366
|
+
this.http.setAuthToken(accessToken);
|
|
562
367
|
const url = new URL(window.location.href);
|
|
563
368
|
url.searchParams.delete("access_token");
|
|
564
369
|
url.searchParams.delete("user_id");
|
|
565
370
|
url.searchParams.delete("email");
|
|
566
371
|
url.searchParams.delete("name");
|
|
567
|
-
url.searchParams.delete("csrf_token");
|
|
568
372
|
if (params.has("error")) {
|
|
569
373
|
url.searchParams.delete("error");
|
|
570
374
|
}
|
|
@@ -580,17 +384,15 @@ var Auth = class {
|
|
|
580
384
|
async signUp(request) {
|
|
581
385
|
try {
|
|
582
386
|
const response = await this.http.post("/api/auth/users", request);
|
|
583
|
-
if (response.accessToken && response.user
|
|
387
|
+
if (response.accessToken && response.user) {
|
|
584
388
|
const session = {
|
|
585
389
|
accessToken: response.accessToken,
|
|
586
390
|
user: response.user
|
|
587
391
|
};
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
this.http.setAuthToken(response.accessToken);
|
|
591
|
-
if (response.csrfToken) {
|
|
592
|
-
setCsrfToken(response.csrfToken);
|
|
392
|
+
if (!isHostedAuthEnvironment()) {
|
|
393
|
+
this.tokenManager.saveSession(session);
|
|
593
394
|
}
|
|
395
|
+
this.http.setAuthToken(response.accessToken);
|
|
594
396
|
}
|
|
595
397
|
return {
|
|
596
398
|
data: response,
|
|
@@ -616,18 +418,21 @@ var Auth = class {
|
|
|
616
418
|
async signInWithPassword(request) {
|
|
617
419
|
try {
|
|
618
420
|
const response = await this.http.post("/api/auth/sessions", request);
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
setCsrfToken(response.csrfToken);
|
|
421
|
+
const session = {
|
|
422
|
+
accessToken: response.accessToken || "",
|
|
423
|
+
user: response.user || {
|
|
424
|
+
id: "",
|
|
425
|
+
email: "",
|
|
426
|
+
name: "",
|
|
427
|
+
emailVerified: false,
|
|
428
|
+
createdAt: "",
|
|
429
|
+
updatedAt: ""
|
|
629
430
|
}
|
|
431
|
+
};
|
|
432
|
+
if (!isHostedAuthEnvironment()) {
|
|
433
|
+
this.tokenManager.saveSession(session);
|
|
630
434
|
}
|
|
435
|
+
this.http.setAuthToken(response.accessToken || "");
|
|
631
436
|
return {
|
|
632
437
|
data: response,
|
|
633
438
|
error: null
|
|
@@ -685,14 +490,8 @@ var Auth = class {
|
|
|
685
490
|
*/
|
|
686
491
|
async signOut() {
|
|
687
492
|
try {
|
|
688
|
-
try {
|
|
689
|
-
await this.http.post("/api/auth/logout");
|
|
690
|
-
} catch {
|
|
691
|
-
}
|
|
692
493
|
this.tokenManager.clearSession();
|
|
693
494
|
this.http.setAuthToken(null);
|
|
694
|
-
clearAuthCookie();
|
|
695
|
-
clearCsrfToken();
|
|
696
495
|
return { error: null };
|
|
697
496
|
} catch (error) {
|
|
698
497
|
return {
|
|
@@ -1014,17 +813,13 @@ var Auth = class {
|
|
|
1014
813
|
"/api/auth/email/verify",
|
|
1015
814
|
request
|
|
1016
815
|
);
|
|
1017
|
-
if (response.accessToken
|
|
816
|
+
if (response.accessToken) {
|
|
1018
817
|
const session = {
|
|
1019
818
|
accessToken: response.accessToken,
|
|
1020
819
|
user: response.user || {}
|
|
1021
820
|
};
|
|
1022
821
|
this.tokenManager.saveSession(session);
|
|
1023
822
|
this.http.setAuthToken(response.accessToken);
|
|
1024
|
-
setAuthCookie();
|
|
1025
|
-
if (response.csrfToken) {
|
|
1026
|
-
setCsrfToken(response.csrfToken);
|
|
1027
|
-
}
|
|
1028
823
|
}
|
|
1029
824
|
return {
|
|
1030
825
|
data: response,
|
|
@@ -1567,6 +1362,239 @@ var Functions = class {
|
|
|
1567
1362
|
}
|
|
1568
1363
|
};
|
|
1569
1364
|
|
|
1365
|
+
// src/modules/realtime.ts
|
|
1366
|
+
var import_socket = require("socket.io-client");
|
|
1367
|
+
var CONNECT_TIMEOUT = 1e4;
|
|
1368
|
+
var Realtime = class {
|
|
1369
|
+
constructor(baseUrl, tokenManager) {
|
|
1370
|
+
this.socket = null;
|
|
1371
|
+
this.connectPromise = null;
|
|
1372
|
+
this.subscribedChannels = /* @__PURE__ */ new Set();
|
|
1373
|
+
this.eventListeners = /* @__PURE__ */ new Map();
|
|
1374
|
+
this.baseUrl = baseUrl;
|
|
1375
|
+
this.tokenManager = tokenManager;
|
|
1376
|
+
}
|
|
1377
|
+
notifyListeners(event, payload) {
|
|
1378
|
+
const listeners = this.eventListeners.get(event);
|
|
1379
|
+
if (!listeners) return;
|
|
1380
|
+
for (const cb of listeners) {
|
|
1381
|
+
try {
|
|
1382
|
+
cb(payload);
|
|
1383
|
+
} catch (err) {
|
|
1384
|
+
console.error(`Error in ${event} callback:`, err);
|
|
1385
|
+
}
|
|
1386
|
+
}
|
|
1387
|
+
}
|
|
1388
|
+
/**
|
|
1389
|
+
* Connect to the realtime server
|
|
1390
|
+
* @returns Promise that resolves when connected
|
|
1391
|
+
*/
|
|
1392
|
+
connect() {
|
|
1393
|
+
if (this.socket?.connected) {
|
|
1394
|
+
return Promise.resolve();
|
|
1395
|
+
}
|
|
1396
|
+
if (this.connectPromise) {
|
|
1397
|
+
return this.connectPromise;
|
|
1398
|
+
}
|
|
1399
|
+
this.connectPromise = new Promise((resolve, reject) => {
|
|
1400
|
+
const session = this.tokenManager.getSession();
|
|
1401
|
+
const token = session?.accessToken;
|
|
1402
|
+
this.socket = (0, import_socket.io)(this.baseUrl, {
|
|
1403
|
+
transports: ["websocket"],
|
|
1404
|
+
auth: token ? { token } : void 0
|
|
1405
|
+
});
|
|
1406
|
+
let initialConnection = true;
|
|
1407
|
+
let timeoutId = null;
|
|
1408
|
+
const cleanup = () => {
|
|
1409
|
+
if (timeoutId) {
|
|
1410
|
+
clearTimeout(timeoutId);
|
|
1411
|
+
timeoutId = null;
|
|
1412
|
+
}
|
|
1413
|
+
};
|
|
1414
|
+
timeoutId = setTimeout(() => {
|
|
1415
|
+
if (initialConnection) {
|
|
1416
|
+
initialConnection = false;
|
|
1417
|
+
this.connectPromise = null;
|
|
1418
|
+
this.socket?.disconnect();
|
|
1419
|
+
this.socket = null;
|
|
1420
|
+
reject(new Error(`Connection timeout after ${CONNECT_TIMEOUT}ms`));
|
|
1421
|
+
}
|
|
1422
|
+
}, CONNECT_TIMEOUT);
|
|
1423
|
+
this.socket.on("connect", () => {
|
|
1424
|
+
cleanup();
|
|
1425
|
+
for (const channel of this.subscribedChannels) {
|
|
1426
|
+
this.socket.emit("realtime:subscribe", { channel });
|
|
1427
|
+
}
|
|
1428
|
+
this.notifyListeners("connect");
|
|
1429
|
+
if (initialConnection) {
|
|
1430
|
+
initialConnection = false;
|
|
1431
|
+
this.connectPromise = null;
|
|
1432
|
+
resolve();
|
|
1433
|
+
}
|
|
1434
|
+
});
|
|
1435
|
+
this.socket.on("connect_error", (error) => {
|
|
1436
|
+
cleanup();
|
|
1437
|
+
this.notifyListeners("connect_error", error);
|
|
1438
|
+
if (initialConnection) {
|
|
1439
|
+
initialConnection = false;
|
|
1440
|
+
this.connectPromise = null;
|
|
1441
|
+
reject(error);
|
|
1442
|
+
}
|
|
1443
|
+
});
|
|
1444
|
+
this.socket.on("disconnect", (reason) => {
|
|
1445
|
+
this.notifyListeners("disconnect", reason);
|
|
1446
|
+
});
|
|
1447
|
+
this.socket.on("realtime:error", (error) => {
|
|
1448
|
+
this.notifyListeners("error", error);
|
|
1449
|
+
});
|
|
1450
|
+
this.socket.onAny((event, message) => {
|
|
1451
|
+
if (event === "realtime:error") return;
|
|
1452
|
+
this.notifyListeners(event, message);
|
|
1453
|
+
});
|
|
1454
|
+
});
|
|
1455
|
+
return this.connectPromise;
|
|
1456
|
+
}
|
|
1457
|
+
/**
|
|
1458
|
+
* Disconnect from the realtime server
|
|
1459
|
+
*/
|
|
1460
|
+
disconnect() {
|
|
1461
|
+
if (this.socket) {
|
|
1462
|
+
this.socket.disconnect();
|
|
1463
|
+
this.socket = null;
|
|
1464
|
+
}
|
|
1465
|
+
this.subscribedChannels.clear();
|
|
1466
|
+
}
|
|
1467
|
+
/**
|
|
1468
|
+
* Check if connected to the realtime server
|
|
1469
|
+
*/
|
|
1470
|
+
get isConnected() {
|
|
1471
|
+
return this.socket?.connected ?? false;
|
|
1472
|
+
}
|
|
1473
|
+
/**
|
|
1474
|
+
* Get the current connection state
|
|
1475
|
+
*/
|
|
1476
|
+
get connectionState() {
|
|
1477
|
+
if (!this.socket) return "disconnected";
|
|
1478
|
+
if (this.socket.connected) return "connected";
|
|
1479
|
+
return "connecting";
|
|
1480
|
+
}
|
|
1481
|
+
/**
|
|
1482
|
+
* Get the socket ID (if connected)
|
|
1483
|
+
*/
|
|
1484
|
+
get socketId() {
|
|
1485
|
+
return this.socket?.id;
|
|
1486
|
+
}
|
|
1487
|
+
/**
|
|
1488
|
+
* Subscribe to a channel
|
|
1489
|
+
*
|
|
1490
|
+
* Automatically connects if not already connected.
|
|
1491
|
+
*
|
|
1492
|
+
* @param channel - Channel name (e.g., 'orders:123', 'broadcast')
|
|
1493
|
+
* @returns Promise with the subscription response
|
|
1494
|
+
*/
|
|
1495
|
+
async subscribe(channel) {
|
|
1496
|
+
if (this.subscribedChannels.has(channel)) {
|
|
1497
|
+
return { ok: true, channel };
|
|
1498
|
+
}
|
|
1499
|
+
if (!this.socket?.connected) {
|
|
1500
|
+
try {
|
|
1501
|
+
await this.connect();
|
|
1502
|
+
} catch (error) {
|
|
1503
|
+
const message = error instanceof Error ? error.message : "Connection failed";
|
|
1504
|
+
return { ok: false, channel, error: { code: "CONNECTION_FAILED", message } };
|
|
1505
|
+
}
|
|
1506
|
+
}
|
|
1507
|
+
return new Promise((resolve) => {
|
|
1508
|
+
this.socket.emit("realtime:subscribe", { channel }, (response) => {
|
|
1509
|
+
if (response.ok) {
|
|
1510
|
+
this.subscribedChannels.add(channel);
|
|
1511
|
+
}
|
|
1512
|
+
resolve(response);
|
|
1513
|
+
});
|
|
1514
|
+
});
|
|
1515
|
+
}
|
|
1516
|
+
/**
|
|
1517
|
+
* Unsubscribe from a channel (fire-and-forget)
|
|
1518
|
+
*
|
|
1519
|
+
* @param channel - Channel name to unsubscribe from
|
|
1520
|
+
*/
|
|
1521
|
+
unsubscribe(channel) {
|
|
1522
|
+
this.subscribedChannels.delete(channel);
|
|
1523
|
+
if (this.socket?.connected) {
|
|
1524
|
+
this.socket.emit("realtime:unsubscribe", { channel });
|
|
1525
|
+
}
|
|
1526
|
+
}
|
|
1527
|
+
/**
|
|
1528
|
+
* Publish a message to a channel
|
|
1529
|
+
*
|
|
1530
|
+
* @param channel - Channel name
|
|
1531
|
+
* @param event - Event name
|
|
1532
|
+
* @param payload - Message payload
|
|
1533
|
+
*/
|
|
1534
|
+
async publish(channel, event, payload) {
|
|
1535
|
+
if (!this.socket?.connected) {
|
|
1536
|
+
throw new Error("Not connected to realtime server. Call connect() first.");
|
|
1537
|
+
}
|
|
1538
|
+
this.socket.emit("realtime:publish", { channel, event, payload });
|
|
1539
|
+
}
|
|
1540
|
+
/**
|
|
1541
|
+
* Listen for events
|
|
1542
|
+
*
|
|
1543
|
+
* Reserved event names:
|
|
1544
|
+
* - 'connect' - Fired when connected to the server
|
|
1545
|
+
* - 'connect_error' - Fired when connection fails (payload: Error)
|
|
1546
|
+
* - 'disconnect' - Fired when disconnected (payload: reason string)
|
|
1547
|
+
* - 'error' - Fired when a realtime error occurs (payload: RealtimeErrorPayload)
|
|
1548
|
+
*
|
|
1549
|
+
* All other events receive a `SocketMessage` payload with metadata.
|
|
1550
|
+
*
|
|
1551
|
+
* @param event - Event name to listen for
|
|
1552
|
+
* @param callback - Callback function when event is received
|
|
1553
|
+
*/
|
|
1554
|
+
on(event, callback) {
|
|
1555
|
+
if (!this.eventListeners.has(event)) {
|
|
1556
|
+
this.eventListeners.set(event, /* @__PURE__ */ new Set());
|
|
1557
|
+
}
|
|
1558
|
+
this.eventListeners.get(event).add(callback);
|
|
1559
|
+
}
|
|
1560
|
+
/**
|
|
1561
|
+
* Remove a listener for a specific event
|
|
1562
|
+
*
|
|
1563
|
+
* @param event - Event name
|
|
1564
|
+
* @param callback - The callback function to remove
|
|
1565
|
+
*/
|
|
1566
|
+
off(event, callback) {
|
|
1567
|
+
const listeners = this.eventListeners.get(event);
|
|
1568
|
+
if (listeners) {
|
|
1569
|
+
listeners.delete(callback);
|
|
1570
|
+
if (listeners.size === 0) {
|
|
1571
|
+
this.eventListeners.delete(event);
|
|
1572
|
+
}
|
|
1573
|
+
}
|
|
1574
|
+
}
|
|
1575
|
+
/**
|
|
1576
|
+
* Listen for an event only once, then automatically remove the listener
|
|
1577
|
+
*
|
|
1578
|
+
* @param event - Event name to listen for
|
|
1579
|
+
* @param callback - Callback function when event is received
|
|
1580
|
+
*/
|
|
1581
|
+
once(event, callback) {
|
|
1582
|
+
const wrapper = (payload) => {
|
|
1583
|
+
this.off(event, wrapper);
|
|
1584
|
+
callback(payload);
|
|
1585
|
+
};
|
|
1586
|
+
this.on(event, wrapper);
|
|
1587
|
+
}
|
|
1588
|
+
/**
|
|
1589
|
+
* Get all currently subscribed channels
|
|
1590
|
+
*
|
|
1591
|
+
* @returns Array of channel names
|
|
1592
|
+
*/
|
|
1593
|
+
getSubscribedChannels() {
|
|
1594
|
+
return Array.from(this.subscribedChannels);
|
|
1595
|
+
}
|
|
1596
|
+
};
|
|
1597
|
+
|
|
1570
1598
|
// src/client.ts
|
|
1571
1599
|
var InsForgeClient = class {
|
|
1572
1600
|
constructor(config = {}) {
|
|
@@ -1584,11 +1612,15 @@ var InsForgeClient = class {
|
|
|
1584
1612
|
if (existingSession?.accessToken) {
|
|
1585
1613
|
this.http.setAuthToken(existingSession.accessToken);
|
|
1586
1614
|
}
|
|
1587
|
-
this.auth = new Auth(
|
|
1615
|
+
this.auth = new Auth(
|
|
1616
|
+
this.http,
|
|
1617
|
+
this.tokenManager
|
|
1618
|
+
);
|
|
1588
1619
|
this.database = new Database(this.http, this.tokenManager);
|
|
1589
1620
|
this.storage = new Storage(this.http);
|
|
1590
1621
|
this.ai = new AI(this.http);
|
|
1591
1622
|
this.functions = new Functions(this.http);
|
|
1623
|
+
this.realtime = new Realtime(this.http.baseUrl, this.tokenManager);
|
|
1592
1624
|
}
|
|
1593
1625
|
/**
|
|
1594
1626
|
* Get the underlying HTTP client for custom requests
|
|
@@ -1626,6 +1658,7 @@ var index_default = InsForgeClient;
|
|
|
1626
1658
|
HttpClient,
|
|
1627
1659
|
InsForgeClient,
|
|
1628
1660
|
InsForgeError,
|
|
1661
|
+
Realtime,
|
|
1629
1662
|
Storage,
|
|
1630
1663
|
StorageBucket,
|
|
1631
1664
|
TokenManager,
|