@spacelr/sdk 0.1.5 → 0.1.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/index.d.mts +114 -1
- package/dist/index.d.ts +114 -1
- package/dist/index.js +277 -20
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +277 -20
- package/dist/index.mjs.map +1 -1
- package/package.json +2 -2
package/dist/index.mjs
CHANGED
|
@@ -70,6 +70,9 @@ var HttpClient = class {
|
|
|
70
70
|
this.tokenManager = tokenManager;
|
|
71
71
|
}
|
|
72
72
|
async request(options) {
|
|
73
|
+
return this.requestWithRetry(options, false);
|
|
74
|
+
}
|
|
75
|
+
async requestWithRetry(options, isRetry) {
|
|
73
76
|
const url = this.buildUrl(options.path, options.query);
|
|
74
77
|
const headers = await this.buildHeaders(options);
|
|
75
78
|
const timeout = this.config.timeout ?? 3e4;
|
|
@@ -86,6 +89,11 @@ var HttpClient = class {
|
|
|
86
89
|
});
|
|
87
90
|
const responseBody = await this.parseResponse(response);
|
|
88
91
|
if (!response.ok) {
|
|
92
|
+
if (options.authenticated && response.status === 401) {
|
|
93
|
+
if (await this.recoverFromAuthFailure(isRetry)) {
|
|
94
|
+
return this.requestWithRetry(options, true);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
89
97
|
this.throwHttpError(response.status, responseBody);
|
|
90
98
|
}
|
|
91
99
|
return this.extractData(responseBody);
|
|
@@ -102,12 +110,45 @@ var HttpClient = class {
|
|
|
102
110
|
}
|
|
103
111
|
}
|
|
104
112
|
async uploadForm(path, formData, onProgress) {
|
|
113
|
+
return this.uploadFormWithRetry(path, formData, onProgress, false);
|
|
114
|
+
}
|
|
115
|
+
async uploadFormWithRetry(path, formData, onProgress, isRetry) {
|
|
105
116
|
const url = this.buildUrl(path);
|
|
106
117
|
const headers = await this.buildFormHeaders();
|
|
107
118
|
const timeoutMs = this.config.timeout ?? 3e4;
|
|
108
|
-
|
|
109
|
-
|
|
119
|
+
try {
|
|
120
|
+
if (onProgress) {
|
|
121
|
+
return await this.uploadFormWithProgress(url, headers, formData, onProgress, timeoutMs);
|
|
122
|
+
}
|
|
123
|
+
return await this.uploadFormOnce(url, headers, formData, timeoutMs);
|
|
124
|
+
} catch (error) {
|
|
125
|
+
if (error instanceof SpacelrAuthError && error.statusCode === 401) {
|
|
126
|
+
if (await this.recoverFromAuthFailure(isRetry)) {
|
|
127
|
+
return this.uploadFormWithRetry(path, formData, onProgress, true);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
throw error;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* Shared handler for a 401 on an authenticated request. Returns true if the
|
|
135
|
+
* caller should retry with a refreshed token. 403 is never routed here —
|
|
136
|
+
* it means "forbidden for this action" and must not trigger logout.
|
|
137
|
+
*
|
|
138
|
+
* `emitAuthLost('unauthorized')` is deduped inside TokenManager, so it's a
|
|
139
|
+
* no-op if `executeRefresh` already emitted 'refresh-failed'.
|
|
140
|
+
*/
|
|
141
|
+
async recoverFromAuthFailure(isRetry) {
|
|
142
|
+
if (isRetry) {
|
|
143
|
+
this.tokenManager.emitAuthLost("unauthorized");
|
|
144
|
+
return false;
|
|
110
145
|
}
|
|
146
|
+
const refreshed = await this.tokenManager.forceRefresh();
|
|
147
|
+
if (refreshed) return true;
|
|
148
|
+
this.tokenManager.emitAuthLost("unauthorized");
|
|
149
|
+
return false;
|
|
150
|
+
}
|
|
151
|
+
async uploadFormOnce(url, headers, formData, timeoutMs) {
|
|
111
152
|
const controller = new AbortController();
|
|
112
153
|
const timeoutId = setTimeout(() => controller.abort(), timeoutMs);
|
|
113
154
|
try {
|
|
@@ -153,6 +194,8 @@ var HttpClient = class {
|
|
|
153
194
|
if (!contentType.includes("application/json")) {
|
|
154
195
|
if (xhr.status >= 200 && xhr.status < 300) {
|
|
155
196
|
resolve({ success: true, data: xhr.responseText });
|
|
197
|
+
} else if (xhr.status === 401 || xhr.status === 403) {
|
|
198
|
+
reject(new SpacelrAuthError(`HTTP ${xhr.status}`, xhr.status));
|
|
156
199
|
} else {
|
|
157
200
|
reject(new SpacelrNetworkError(`HTTP ${xhr.status}: ${xhr.responseText.slice(0, 200)}`));
|
|
158
201
|
}
|
|
@@ -309,6 +352,12 @@ var TokenManager = class {
|
|
|
309
352
|
constructor(storage, refreshBufferSeconds = 60) {
|
|
310
353
|
this.refreshCallback = null;
|
|
311
354
|
this.refreshPromise = null;
|
|
355
|
+
this.tokenRefreshedListeners = /* @__PURE__ */ new Set();
|
|
356
|
+
this.authLostListeners = /* @__PURE__ */ new Set();
|
|
357
|
+
// Guard so callbacks that run during auth-loss handling (e.g. a logout()
|
|
358
|
+
// that hits `/auth/logout` with a dead token) don't re-emit and loop.
|
|
359
|
+
// Cleared by setTokens() / clearTokens() to re-arm for the next session.
|
|
360
|
+
this.authLostEmitted = false;
|
|
312
361
|
this.storage = storage ?? new MemoryTokenStorage();
|
|
313
362
|
this.refreshBufferSeconds = refreshBufferSeconds;
|
|
314
363
|
}
|
|
@@ -330,14 +379,53 @@ var TokenManager = class {
|
|
|
330
379
|
}
|
|
331
380
|
async setTokens(tokens) {
|
|
332
381
|
await this.storage.setTokens(tokens);
|
|
382
|
+
this.authLostEmitted = false;
|
|
333
383
|
}
|
|
334
384
|
async clearTokens() {
|
|
335
385
|
await this.storage.clearTokens();
|
|
336
386
|
this.refreshPromise = null;
|
|
387
|
+
this.authLostEmitted = false;
|
|
337
388
|
}
|
|
338
389
|
async getStoredTokens() {
|
|
339
390
|
return this.storage.getTokens();
|
|
340
391
|
}
|
|
392
|
+
/**
|
|
393
|
+
* Force a refresh using the current stored refresh token.
|
|
394
|
+
* Returns the new access token, or null if refresh is not possible or fails.
|
|
395
|
+
* A rejecting custom `TokenStorage.getTokens()` is treated as "no tokens"
|
|
396
|
+
* so callers (notably `HttpClient`'s 401 retry path) see a clean `null`
|
|
397
|
+
* rather than an exception that would get wrapped as a network error.
|
|
398
|
+
*/
|
|
399
|
+
async forceRefresh() {
|
|
400
|
+
if (this.authLostEmitted) return null;
|
|
401
|
+
let tokens;
|
|
402
|
+
try {
|
|
403
|
+
tokens = await this.storage.getTokens();
|
|
404
|
+
} catch {
|
|
405
|
+
return null;
|
|
406
|
+
}
|
|
407
|
+
if (!tokens) return null;
|
|
408
|
+
const refreshed = await this.tryRefresh(tokens);
|
|
409
|
+
return refreshed?.accessToken ?? null;
|
|
410
|
+
}
|
|
411
|
+
onTokenRefreshed(listener) {
|
|
412
|
+
this.tokenRefreshedListeners.add(listener);
|
|
413
|
+
return () => this.tokenRefreshedListeners.delete(listener);
|
|
414
|
+
}
|
|
415
|
+
onAuthLost(listener) {
|
|
416
|
+
this.authLostListeners.add(listener);
|
|
417
|
+
return () => this.authLostListeners.delete(listener);
|
|
418
|
+
}
|
|
419
|
+
emitAuthLost(reason) {
|
|
420
|
+
if (this.authLostEmitted) return;
|
|
421
|
+
this.authLostEmitted = true;
|
|
422
|
+
for (const listener of this.authLostListeners) {
|
|
423
|
+
try {
|
|
424
|
+
listener(reason);
|
|
425
|
+
} catch {
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
}
|
|
341
429
|
isTokenExpired(tokens) {
|
|
342
430
|
if (!tokens.expiresAt) return false;
|
|
343
431
|
return Date.now() >= tokens.expiresAt * 1e3;
|
|
@@ -350,21 +438,40 @@ var TokenManager = class {
|
|
|
350
438
|
async tryRefresh(tokens) {
|
|
351
439
|
if (!tokens.refreshToken || !this.refreshCallback) return null;
|
|
352
440
|
if (this.refreshPromise) {
|
|
353
|
-
|
|
441
|
+
try {
|
|
442
|
+
return await this.refreshPromise;
|
|
443
|
+
} catch {
|
|
444
|
+
return null;
|
|
445
|
+
}
|
|
354
446
|
}
|
|
355
447
|
this.refreshPromise = this.executeRefresh(tokens.refreshToken);
|
|
356
448
|
try {
|
|
357
|
-
|
|
358
|
-
|
|
449
|
+
return await this.refreshPromise;
|
|
450
|
+
} catch {
|
|
451
|
+
return null;
|
|
359
452
|
} finally {
|
|
360
453
|
this.refreshPromise = null;
|
|
361
454
|
}
|
|
362
455
|
}
|
|
363
456
|
async executeRefresh(refreshToken) {
|
|
364
457
|
const callback = this.refreshCallback;
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
458
|
+
try {
|
|
459
|
+
const newTokens = await callback(refreshToken);
|
|
460
|
+
await this.storage.setTokens(newTokens);
|
|
461
|
+
this.emitTokenRefreshed(newTokens);
|
|
462
|
+
return newTokens;
|
|
463
|
+
} catch (error) {
|
|
464
|
+
this.emitAuthLost("refresh-failed");
|
|
465
|
+
throw error;
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
emitTokenRefreshed(tokens) {
|
|
469
|
+
for (const listener of this.tokenRefreshedListeners) {
|
|
470
|
+
try {
|
|
471
|
+
listener(tokens);
|
|
472
|
+
} catch {
|
|
473
|
+
}
|
|
474
|
+
}
|
|
368
475
|
}
|
|
369
476
|
};
|
|
370
477
|
|
|
@@ -435,6 +542,7 @@ async function generatePKCEChallenge() {
|
|
|
435
542
|
|
|
436
543
|
// libs/sdk/src/core/realtime.ts
|
|
437
544
|
import { io } from "socket.io-client";
|
|
545
|
+
var REBUILD_RETRY_DELAY_MS = 5e3;
|
|
438
546
|
var RealtimeClient = class {
|
|
439
547
|
constructor(config) {
|
|
440
548
|
this.socket = null;
|
|
@@ -442,9 +550,26 @@ var RealtimeClient = class {
|
|
|
442
550
|
this.connecting = null;
|
|
443
551
|
// Store original where objects per room for reconnect resubscription
|
|
444
552
|
this.roomWhereMap = /* @__PURE__ */ new Map();
|
|
553
|
+
this.unsubscribeFromTokenRefreshed = null;
|
|
554
|
+
// Wake-up listeners (browser only) — recover from long OS suspends where
|
|
555
|
+
// socket.io's internal reconnect loop may have already given up.
|
|
556
|
+
this.onVisibilityChange = null;
|
|
557
|
+
this.onOnline = null;
|
|
558
|
+
// Set by `disconnect()`; checked by async paths (rebuildSocket, deferred
|
|
559
|
+
// retries) to avoid reviving a client the consumer has torn down.
|
|
560
|
+
this.disposed = false;
|
|
561
|
+
// Scheduled retry after a failed `rebuildSocket()`; cleared when it fires
|
|
562
|
+
// or when `disconnect()` tears down.
|
|
563
|
+
this.rebuildRetryTimer = null;
|
|
564
|
+
this.connectionState = "disconnected";
|
|
565
|
+
this.connectionStateListeners = /* @__PURE__ */ new Set();
|
|
445
566
|
this.config = config;
|
|
446
567
|
}
|
|
447
568
|
async subscribe(projectId, collectionName, callback, onError, where) {
|
|
569
|
+
this.ensureWakeListeners();
|
|
570
|
+
if (this.connectionState === "disconnected") {
|
|
571
|
+
this.setConnectionState("reconnecting");
|
|
572
|
+
}
|
|
448
573
|
await this.ensureConnected();
|
|
449
574
|
const room = this.buildRoomKey(projectId, collectionName, where);
|
|
450
575
|
if (!this.subscriptions.has(room)) {
|
|
@@ -486,7 +611,24 @@ var RealtimeClient = class {
|
|
|
486
611
|
}
|
|
487
612
|
};
|
|
488
613
|
}
|
|
614
|
+
/**
|
|
615
|
+
* Tear down the realtime client permanently. After this call the instance
|
|
616
|
+
* is disposed — subsequent `subscribe()` calls will not re-establish a
|
|
617
|
+
* connection. Create a new `RealtimeClient` (via `createClient`) if you
|
|
618
|
+
* need to reconnect after a logout.
|
|
619
|
+
*/
|
|
489
620
|
disconnect() {
|
|
621
|
+
this.setConnectionState("disconnected");
|
|
622
|
+
this.disposed = true;
|
|
623
|
+
if (this.rebuildRetryTimer) {
|
|
624
|
+
clearTimeout(this.rebuildRetryTimer);
|
|
625
|
+
this.rebuildRetryTimer = null;
|
|
626
|
+
}
|
|
627
|
+
if (this.unsubscribeFromTokenRefreshed) {
|
|
628
|
+
this.unsubscribeFromTokenRefreshed();
|
|
629
|
+
this.unsubscribeFromTokenRefreshed = null;
|
|
630
|
+
}
|
|
631
|
+
this.detachWakeListeners();
|
|
490
632
|
if (this.socket) {
|
|
491
633
|
this.socket.disconnect();
|
|
492
634
|
this.socket = null;
|
|
@@ -495,6 +637,23 @@ var RealtimeClient = class {
|
|
|
495
637
|
this.roomWhereMap.clear();
|
|
496
638
|
this.connecting = null;
|
|
497
639
|
}
|
|
640
|
+
getConnectionState() {
|
|
641
|
+
return this.connectionState;
|
|
642
|
+
}
|
|
643
|
+
onConnectionStateChanged(listener) {
|
|
644
|
+
this.connectionStateListeners.add(listener);
|
|
645
|
+
return () => this.connectionStateListeners.delete(listener);
|
|
646
|
+
}
|
|
647
|
+
setConnectionState(next) {
|
|
648
|
+
if (this.connectionState === next) return;
|
|
649
|
+
this.connectionState = next;
|
|
650
|
+
for (const listener of this.connectionStateListeners) {
|
|
651
|
+
try {
|
|
652
|
+
listener(next);
|
|
653
|
+
} catch {
|
|
654
|
+
}
|
|
655
|
+
}
|
|
656
|
+
}
|
|
498
657
|
buildRoomKey(projectId, collectionName, where) {
|
|
499
658
|
const base = `db:${projectId}:${collectionName}`;
|
|
500
659
|
if (!where || Object.keys(where).length === 0) {
|
|
@@ -537,6 +696,8 @@ var RealtimeClient = class {
|
|
|
537
696
|
// Disabled until first successful connect
|
|
538
697
|
});
|
|
539
698
|
this.socket.on("authenticated", () => {
|
|
699
|
+
if (this.disposed) return;
|
|
700
|
+
this.setConnectionState("connected");
|
|
540
701
|
if (initialConnect) {
|
|
541
702
|
initialConnect = false;
|
|
542
703
|
if (this.socket) {
|
|
@@ -545,11 +706,15 @@ var RealtimeClient = class {
|
|
|
545
706
|
this.socket.io.opts.reconnectionDelayMax = 5e3;
|
|
546
707
|
this.socket.io.opts.reconnectionAttempts = 50;
|
|
547
708
|
}
|
|
709
|
+
if (this.config.onTokenRefreshed && !this.unsubscribeFromTokenRefreshed) {
|
|
710
|
+
this.unsubscribeFromTokenRefreshed = this.config.onTokenRefreshed(
|
|
711
|
+
(accessToken) => {
|
|
712
|
+
this.socket?.emit("reauthenticate", { token: accessToken });
|
|
713
|
+
}
|
|
714
|
+
);
|
|
715
|
+
}
|
|
548
716
|
resolve();
|
|
549
|
-
}
|
|
550
|
-
});
|
|
551
|
-
this.socket.on("connect", () => {
|
|
552
|
-
if (!initialConnect) {
|
|
717
|
+
} else {
|
|
553
718
|
this.resubscribeAll();
|
|
554
719
|
}
|
|
555
720
|
});
|
|
@@ -558,6 +723,9 @@ var RealtimeClient = class {
|
|
|
558
723
|
reject(new Error(`WebSocket connection failed: ${err.message}`));
|
|
559
724
|
}
|
|
560
725
|
});
|
|
726
|
+
this.socket.io.on("reconnect_failed", () => {
|
|
727
|
+
void this.rebuildSocket();
|
|
728
|
+
});
|
|
561
729
|
this.socket.on("db:event", (event) => {
|
|
562
730
|
const base = `db:${event.projectId}:${event.collectionName}`;
|
|
563
731
|
const baseCallbacks = this.subscriptions.get(base);
|
|
@@ -581,6 +749,9 @@ var RealtimeClient = class {
|
|
|
581
749
|
}
|
|
582
750
|
});
|
|
583
751
|
this.socket.on("disconnect", () => {
|
|
752
|
+
if (!this.disposed) {
|
|
753
|
+
this.setConnectionState("reconnecting");
|
|
754
|
+
}
|
|
584
755
|
});
|
|
585
756
|
});
|
|
586
757
|
}
|
|
@@ -603,6 +774,70 @@ var RealtimeClient = class {
|
|
|
603
774
|
}
|
|
604
775
|
return true;
|
|
605
776
|
}
|
|
777
|
+
/**
|
|
778
|
+
* Tear down the current socket and create a fresh one. Used by both
|
|
779
|
+
* `reconnect_failed` (socket.io gave up) and the visibility/online wake-up
|
|
780
|
+
* listeners. Preserves the `subscriptions` map so existing rooms can be
|
|
781
|
+
* replayed to the server on the new connection. If the rebuild itself
|
|
782
|
+
* fails it schedules one retry after 5 s — beyond that we rely on the
|
|
783
|
+
* next wake-up event (visibility/online) to try again.
|
|
784
|
+
*/
|
|
785
|
+
async rebuildSocket() {
|
|
786
|
+
if (this.disposed || this.connecting) return;
|
|
787
|
+
const previous = this.socket;
|
|
788
|
+
this.socket = null;
|
|
789
|
+
if (previous) previous.disconnect();
|
|
790
|
+
this.setConnectionState("reconnecting");
|
|
791
|
+
try {
|
|
792
|
+
await this.ensureConnected();
|
|
793
|
+
this.handlePostRebuild();
|
|
794
|
+
} catch {
|
|
795
|
+
this.scheduleRebuildRetry();
|
|
796
|
+
}
|
|
797
|
+
}
|
|
798
|
+
handlePostRebuild() {
|
|
799
|
+
const newSocket = this.socket;
|
|
800
|
+
if (this.disposed) {
|
|
801
|
+
this.socket = null;
|
|
802
|
+
newSocket?.disconnect();
|
|
803
|
+
return;
|
|
804
|
+
}
|
|
805
|
+
if (this.subscriptions.size > 0) {
|
|
806
|
+
this.resubscribeAll();
|
|
807
|
+
}
|
|
808
|
+
}
|
|
809
|
+
scheduleRebuildRetry() {
|
|
810
|
+
if (this.disposed || this.rebuildRetryTimer) return;
|
|
811
|
+
this.rebuildRetryTimer = setTimeout(() => {
|
|
812
|
+
this.rebuildRetryTimer = null;
|
|
813
|
+
void this.rebuildSocket();
|
|
814
|
+
}, REBUILD_RETRY_DELAY_MS);
|
|
815
|
+
this.rebuildRetryTimer.unref?.();
|
|
816
|
+
}
|
|
817
|
+
ensureWakeListeners() {
|
|
818
|
+
if (typeof document === "undefined" || typeof window === "undefined") return;
|
|
819
|
+
if (this.onVisibilityChange !== null) return;
|
|
820
|
+
const wakeIfUnhealthy = () => {
|
|
821
|
+
if (typeof document !== "undefined" && document.visibilityState !== "visible") return;
|
|
822
|
+
if (this.socket?.connected) return;
|
|
823
|
+
if (this.connecting) return;
|
|
824
|
+
void this.rebuildSocket();
|
|
825
|
+
};
|
|
826
|
+
this.onVisibilityChange = wakeIfUnhealthy;
|
|
827
|
+
this.onOnline = wakeIfUnhealthy;
|
|
828
|
+
document.addEventListener("visibilitychange", this.onVisibilityChange);
|
|
829
|
+
window.addEventListener("online", this.onOnline);
|
|
830
|
+
}
|
|
831
|
+
detachWakeListeners() {
|
|
832
|
+
if (typeof document !== "undefined" && this.onVisibilityChange) {
|
|
833
|
+
document.removeEventListener("visibilitychange", this.onVisibilityChange);
|
|
834
|
+
}
|
|
835
|
+
if (typeof window !== "undefined" && this.onOnline) {
|
|
836
|
+
window.removeEventListener("online", this.onOnline);
|
|
837
|
+
}
|
|
838
|
+
this.onVisibilityChange = null;
|
|
839
|
+
this.onOnline = null;
|
|
840
|
+
}
|
|
606
841
|
resubscribeAll() {
|
|
607
842
|
for (const [room] of this.subscriptions) {
|
|
608
843
|
const queryIdx = room.indexOf("?");
|
|
@@ -682,11 +917,14 @@ var AuthModule = class {
|
|
|
682
917
|
});
|
|
683
918
|
}
|
|
684
919
|
async logout() {
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
920
|
+
try {
|
|
921
|
+
await this.http.request({
|
|
922
|
+
method: "POST",
|
|
923
|
+
path: "/auth/logout",
|
|
924
|
+
authenticated: true
|
|
925
|
+
});
|
|
926
|
+
} catch {
|
|
927
|
+
}
|
|
690
928
|
await this.tokenManager.clearTokens();
|
|
691
929
|
}
|
|
692
930
|
async verifyEmail(token) {
|
|
@@ -1124,11 +1362,11 @@ var CollectionRef = class {
|
|
|
1124
1362
|
this.collectionName = collectionName;
|
|
1125
1363
|
this.basePath = `/db/${collectionName}`;
|
|
1126
1364
|
}
|
|
1127
|
-
async insert(
|
|
1365
|
+
async insert(document2) {
|
|
1128
1366
|
return this.http.request({
|
|
1129
1367
|
method: "POST",
|
|
1130
1368
|
path: this.basePath,
|
|
1131
|
-
body: { documents: [
|
|
1369
|
+
body: { documents: [document2] },
|
|
1132
1370
|
authenticated: true
|
|
1133
1371
|
});
|
|
1134
1372
|
}
|
|
@@ -1426,7 +1664,8 @@ function createClient(config) {
|
|
|
1426
1664
|
const httpClient = new HttpClient(config, tokenManager);
|
|
1427
1665
|
const realtime = new RealtimeClient({
|
|
1428
1666
|
baseUrl: config.apiUrl,
|
|
1429
|
-
getToken: () => tokenManager.getAccessToken()
|
|
1667
|
+
getToken: () => tokenManager.getAccessToken(),
|
|
1668
|
+
onTokenRefreshed: (listener) => tokenManager.onTokenRefreshed((tokens) => listener(tokens.accessToken))
|
|
1430
1669
|
});
|
|
1431
1670
|
const auth = new AuthModule(httpClient, tokenManager, config);
|
|
1432
1671
|
const storage = new StorageModule(httpClient, tokenManager, config);
|
|
@@ -1439,8 +1678,26 @@ function createClient(config) {
|
|
|
1439
1678
|
db,
|
|
1440
1679
|
notifications,
|
|
1441
1680
|
functions,
|
|
1681
|
+
setTokens(tokens) {
|
|
1682
|
+
return tokenManager.setTokens(tokens);
|
|
1683
|
+
},
|
|
1684
|
+
clearTokens() {
|
|
1685
|
+
return tokenManager.clearTokens();
|
|
1686
|
+
},
|
|
1442
1687
|
disconnect() {
|
|
1443
1688
|
realtime.disconnect();
|
|
1689
|
+
},
|
|
1690
|
+
onAuthLost(listener) {
|
|
1691
|
+
return tokenManager.onAuthLost(listener);
|
|
1692
|
+
},
|
|
1693
|
+
onTokenRefreshed(listener) {
|
|
1694
|
+
return tokenManager.onTokenRefreshed(listener);
|
|
1695
|
+
},
|
|
1696
|
+
onConnectionStateChanged(listener) {
|
|
1697
|
+
return realtime.onConnectionStateChanged(listener);
|
|
1698
|
+
},
|
|
1699
|
+
getConnectionState() {
|
|
1700
|
+
return realtime.getConnectionState();
|
|
1444
1701
|
}
|
|
1445
1702
|
};
|
|
1446
1703
|
}
|