@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.js
CHANGED
|
@@ -102,6 +102,9 @@ var HttpClient = class {
|
|
|
102
102
|
this.tokenManager = tokenManager;
|
|
103
103
|
}
|
|
104
104
|
async request(options) {
|
|
105
|
+
return this.requestWithRetry(options, false);
|
|
106
|
+
}
|
|
107
|
+
async requestWithRetry(options, isRetry) {
|
|
105
108
|
const url = this.buildUrl(options.path, options.query);
|
|
106
109
|
const headers = await this.buildHeaders(options);
|
|
107
110
|
const timeout = this.config.timeout ?? 3e4;
|
|
@@ -118,6 +121,11 @@ var HttpClient = class {
|
|
|
118
121
|
});
|
|
119
122
|
const responseBody = await this.parseResponse(response);
|
|
120
123
|
if (!response.ok) {
|
|
124
|
+
if (options.authenticated && response.status === 401) {
|
|
125
|
+
if (await this.recoverFromAuthFailure(isRetry)) {
|
|
126
|
+
return this.requestWithRetry(options, true);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
121
129
|
this.throwHttpError(response.status, responseBody);
|
|
122
130
|
}
|
|
123
131
|
return this.extractData(responseBody);
|
|
@@ -134,12 +142,45 @@ var HttpClient = class {
|
|
|
134
142
|
}
|
|
135
143
|
}
|
|
136
144
|
async uploadForm(path, formData, onProgress) {
|
|
145
|
+
return this.uploadFormWithRetry(path, formData, onProgress, false);
|
|
146
|
+
}
|
|
147
|
+
async uploadFormWithRetry(path, formData, onProgress, isRetry) {
|
|
137
148
|
const url = this.buildUrl(path);
|
|
138
149
|
const headers = await this.buildFormHeaders();
|
|
139
150
|
const timeoutMs = this.config.timeout ?? 3e4;
|
|
140
|
-
|
|
141
|
-
|
|
151
|
+
try {
|
|
152
|
+
if (onProgress) {
|
|
153
|
+
return await this.uploadFormWithProgress(url, headers, formData, onProgress, timeoutMs);
|
|
154
|
+
}
|
|
155
|
+
return await this.uploadFormOnce(url, headers, formData, timeoutMs);
|
|
156
|
+
} catch (error) {
|
|
157
|
+
if (error instanceof SpacelrAuthError && error.statusCode === 401) {
|
|
158
|
+
if (await this.recoverFromAuthFailure(isRetry)) {
|
|
159
|
+
return this.uploadFormWithRetry(path, formData, onProgress, true);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
throw error;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* Shared handler for a 401 on an authenticated request. Returns true if the
|
|
167
|
+
* caller should retry with a refreshed token. 403 is never routed here —
|
|
168
|
+
* it means "forbidden for this action" and must not trigger logout.
|
|
169
|
+
*
|
|
170
|
+
* `emitAuthLost('unauthorized')` is deduped inside TokenManager, so it's a
|
|
171
|
+
* no-op if `executeRefresh` already emitted 'refresh-failed'.
|
|
172
|
+
*/
|
|
173
|
+
async recoverFromAuthFailure(isRetry) {
|
|
174
|
+
if (isRetry) {
|
|
175
|
+
this.tokenManager.emitAuthLost("unauthorized");
|
|
176
|
+
return false;
|
|
142
177
|
}
|
|
178
|
+
const refreshed = await this.tokenManager.forceRefresh();
|
|
179
|
+
if (refreshed) return true;
|
|
180
|
+
this.tokenManager.emitAuthLost("unauthorized");
|
|
181
|
+
return false;
|
|
182
|
+
}
|
|
183
|
+
async uploadFormOnce(url, headers, formData, timeoutMs) {
|
|
143
184
|
const controller = new AbortController();
|
|
144
185
|
const timeoutId = setTimeout(() => controller.abort(), timeoutMs);
|
|
145
186
|
try {
|
|
@@ -185,6 +226,8 @@ var HttpClient = class {
|
|
|
185
226
|
if (!contentType.includes("application/json")) {
|
|
186
227
|
if (xhr.status >= 200 && xhr.status < 300) {
|
|
187
228
|
resolve({ success: true, data: xhr.responseText });
|
|
229
|
+
} else if (xhr.status === 401 || xhr.status === 403) {
|
|
230
|
+
reject(new SpacelrAuthError(`HTTP ${xhr.status}`, xhr.status));
|
|
188
231
|
} else {
|
|
189
232
|
reject(new SpacelrNetworkError(`HTTP ${xhr.status}: ${xhr.responseText.slice(0, 200)}`));
|
|
190
233
|
}
|
|
@@ -341,6 +384,12 @@ var TokenManager = class {
|
|
|
341
384
|
constructor(storage, refreshBufferSeconds = 60) {
|
|
342
385
|
this.refreshCallback = null;
|
|
343
386
|
this.refreshPromise = null;
|
|
387
|
+
this.tokenRefreshedListeners = /* @__PURE__ */ new Set();
|
|
388
|
+
this.authLostListeners = /* @__PURE__ */ new Set();
|
|
389
|
+
// Guard so callbacks that run during auth-loss handling (e.g. a logout()
|
|
390
|
+
// that hits `/auth/logout` with a dead token) don't re-emit and loop.
|
|
391
|
+
// Cleared by setTokens() / clearTokens() to re-arm for the next session.
|
|
392
|
+
this.authLostEmitted = false;
|
|
344
393
|
this.storage = storage ?? new MemoryTokenStorage();
|
|
345
394
|
this.refreshBufferSeconds = refreshBufferSeconds;
|
|
346
395
|
}
|
|
@@ -362,14 +411,53 @@ var TokenManager = class {
|
|
|
362
411
|
}
|
|
363
412
|
async setTokens(tokens) {
|
|
364
413
|
await this.storage.setTokens(tokens);
|
|
414
|
+
this.authLostEmitted = false;
|
|
365
415
|
}
|
|
366
416
|
async clearTokens() {
|
|
367
417
|
await this.storage.clearTokens();
|
|
368
418
|
this.refreshPromise = null;
|
|
419
|
+
this.authLostEmitted = false;
|
|
369
420
|
}
|
|
370
421
|
async getStoredTokens() {
|
|
371
422
|
return this.storage.getTokens();
|
|
372
423
|
}
|
|
424
|
+
/**
|
|
425
|
+
* Force a refresh using the current stored refresh token.
|
|
426
|
+
* Returns the new access token, or null if refresh is not possible or fails.
|
|
427
|
+
* A rejecting custom `TokenStorage.getTokens()` is treated as "no tokens"
|
|
428
|
+
* so callers (notably `HttpClient`'s 401 retry path) see a clean `null`
|
|
429
|
+
* rather than an exception that would get wrapped as a network error.
|
|
430
|
+
*/
|
|
431
|
+
async forceRefresh() {
|
|
432
|
+
if (this.authLostEmitted) return null;
|
|
433
|
+
let tokens;
|
|
434
|
+
try {
|
|
435
|
+
tokens = await this.storage.getTokens();
|
|
436
|
+
} catch {
|
|
437
|
+
return null;
|
|
438
|
+
}
|
|
439
|
+
if (!tokens) return null;
|
|
440
|
+
const refreshed = await this.tryRefresh(tokens);
|
|
441
|
+
return refreshed?.accessToken ?? null;
|
|
442
|
+
}
|
|
443
|
+
onTokenRefreshed(listener) {
|
|
444
|
+
this.tokenRefreshedListeners.add(listener);
|
|
445
|
+
return () => this.tokenRefreshedListeners.delete(listener);
|
|
446
|
+
}
|
|
447
|
+
onAuthLost(listener) {
|
|
448
|
+
this.authLostListeners.add(listener);
|
|
449
|
+
return () => this.authLostListeners.delete(listener);
|
|
450
|
+
}
|
|
451
|
+
emitAuthLost(reason) {
|
|
452
|
+
if (this.authLostEmitted) return;
|
|
453
|
+
this.authLostEmitted = true;
|
|
454
|
+
for (const listener of this.authLostListeners) {
|
|
455
|
+
try {
|
|
456
|
+
listener(reason);
|
|
457
|
+
} catch {
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
}
|
|
373
461
|
isTokenExpired(tokens) {
|
|
374
462
|
if (!tokens.expiresAt) return false;
|
|
375
463
|
return Date.now() >= tokens.expiresAt * 1e3;
|
|
@@ -382,21 +470,40 @@ var TokenManager = class {
|
|
|
382
470
|
async tryRefresh(tokens) {
|
|
383
471
|
if (!tokens.refreshToken || !this.refreshCallback) return null;
|
|
384
472
|
if (this.refreshPromise) {
|
|
385
|
-
|
|
473
|
+
try {
|
|
474
|
+
return await this.refreshPromise;
|
|
475
|
+
} catch {
|
|
476
|
+
return null;
|
|
477
|
+
}
|
|
386
478
|
}
|
|
387
479
|
this.refreshPromise = this.executeRefresh(tokens.refreshToken);
|
|
388
480
|
try {
|
|
389
|
-
|
|
390
|
-
|
|
481
|
+
return await this.refreshPromise;
|
|
482
|
+
} catch {
|
|
483
|
+
return null;
|
|
391
484
|
} finally {
|
|
392
485
|
this.refreshPromise = null;
|
|
393
486
|
}
|
|
394
487
|
}
|
|
395
488
|
async executeRefresh(refreshToken) {
|
|
396
489
|
const callback = this.refreshCallback;
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
490
|
+
try {
|
|
491
|
+
const newTokens = await callback(refreshToken);
|
|
492
|
+
await this.storage.setTokens(newTokens);
|
|
493
|
+
this.emitTokenRefreshed(newTokens);
|
|
494
|
+
return newTokens;
|
|
495
|
+
} catch (error) {
|
|
496
|
+
this.emitAuthLost("refresh-failed");
|
|
497
|
+
throw error;
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
emitTokenRefreshed(tokens) {
|
|
501
|
+
for (const listener of this.tokenRefreshedListeners) {
|
|
502
|
+
try {
|
|
503
|
+
listener(tokens);
|
|
504
|
+
} catch {
|
|
505
|
+
}
|
|
506
|
+
}
|
|
400
507
|
}
|
|
401
508
|
};
|
|
402
509
|
|
|
@@ -467,6 +574,7 @@ async function generatePKCEChallenge() {
|
|
|
467
574
|
|
|
468
575
|
// libs/sdk/src/core/realtime.ts
|
|
469
576
|
var import_socket = require("socket.io-client");
|
|
577
|
+
var REBUILD_RETRY_DELAY_MS = 5e3;
|
|
470
578
|
var RealtimeClient = class {
|
|
471
579
|
constructor(config) {
|
|
472
580
|
this.socket = null;
|
|
@@ -474,9 +582,26 @@ var RealtimeClient = class {
|
|
|
474
582
|
this.connecting = null;
|
|
475
583
|
// Store original where objects per room for reconnect resubscription
|
|
476
584
|
this.roomWhereMap = /* @__PURE__ */ new Map();
|
|
585
|
+
this.unsubscribeFromTokenRefreshed = null;
|
|
586
|
+
// Wake-up listeners (browser only) — recover from long OS suspends where
|
|
587
|
+
// socket.io's internal reconnect loop may have already given up.
|
|
588
|
+
this.onVisibilityChange = null;
|
|
589
|
+
this.onOnline = null;
|
|
590
|
+
// Set by `disconnect()`; checked by async paths (rebuildSocket, deferred
|
|
591
|
+
// retries) to avoid reviving a client the consumer has torn down.
|
|
592
|
+
this.disposed = false;
|
|
593
|
+
// Scheduled retry after a failed `rebuildSocket()`; cleared when it fires
|
|
594
|
+
// or when `disconnect()` tears down.
|
|
595
|
+
this.rebuildRetryTimer = null;
|
|
596
|
+
this.connectionState = "disconnected";
|
|
597
|
+
this.connectionStateListeners = /* @__PURE__ */ new Set();
|
|
477
598
|
this.config = config;
|
|
478
599
|
}
|
|
479
600
|
async subscribe(projectId, collectionName, callback, onError, where) {
|
|
601
|
+
this.ensureWakeListeners();
|
|
602
|
+
if (this.connectionState === "disconnected") {
|
|
603
|
+
this.setConnectionState("reconnecting");
|
|
604
|
+
}
|
|
480
605
|
await this.ensureConnected();
|
|
481
606
|
const room = this.buildRoomKey(projectId, collectionName, where);
|
|
482
607
|
if (!this.subscriptions.has(room)) {
|
|
@@ -518,7 +643,24 @@ var RealtimeClient = class {
|
|
|
518
643
|
}
|
|
519
644
|
};
|
|
520
645
|
}
|
|
646
|
+
/**
|
|
647
|
+
* Tear down the realtime client permanently. After this call the instance
|
|
648
|
+
* is disposed — subsequent `subscribe()` calls will not re-establish a
|
|
649
|
+
* connection. Create a new `RealtimeClient` (via `createClient`) if you
|
|
650
|
+
* need to reconnect after a logout.
|
|
651
|
+
*/
|
|
521
652
|
disconnect() {
|
|
653
|
+
this.setConnectionState("disconnected");
|
|
654
|
+
this.disposed = true;
|
|
655
|
+
if (this.rebuildRetryTimer) {
|
|
656
|
+
clearTimeout(this.rebuildRetryTimer);
|
|
657
|
+
this.rebuildRetryTimer = null;
|
|
658
|
+
}
|
|
659
|
+
if (this.unsubscribeFromTokenRefreshed) {
|
|
660
|
+
this.unsubscribeFromTokenRefreshed();
|
|
661
|
+
this.unsubscribeFromTokenRefreshed = null;
|
|
662
|
+
}
|
|
663
|
+
this.detachWakeListeners();
|
|
522
664
|
if (this.socket) {
|
|
523
665
|
this.socket.disconnect();
|
|
524
666
|
this.socket = null;
|
|
@@ -527,6 +669,23 @@ var RealtimeClient = class {
|
|
|
527
669
|
this.roomWhereMap.clear();
|
|
528
670
|
this.connecting = null;
|
|
529
671
|
}
|
|
672
|
+
getConnectionState() {
|
|
673
|
+
return this.connectionState;
|
|
674
|
+
}
|
|
675
|
+
onConnectionStateChanged(listener) {
|
|
676
|
+
this.connectionStateListeners.add(listener);
|
|
677
|
+
return () => this.connectionStateListeners.delete(listener);
|
|
678
|
+
}
|
|
679
|
+
setConnectionState(next) {
|
|
680
|
+
if (this.connectionState === next) return;
|
|
681
|
+
this.connectionState = next;
|
|
682
|
+
for (const listener of this.connectionStateListeners) {
|
|
683
|
+
try {
|
|
684
|
+
listener(next);
|
|
685
|
+
} catch {
|
|
686
|
+
}
|
|
687
|
+
}
|
|
688
|
+
}
|
|
530
689
|
buildRoomKey(projectId, collectionName, where) {
|
|
531
690
|
const base = `db:${projectId}:${collectionName}`;
|
|
532
691
|
if (!where || Object.keys(where).length === 0) {
|
|
@@ -569,6 +728,8 @@ var RealtimeClient = class {
|
|
|
569
728
|
// Disabled until first successful connect
|
|
570
729
|
});
|
|
571
730
|
this.socket.on("authenticated", () => {
|
|
731
|
+
if (this.disposed) return;
|
|
732
|
+
this.setConnectionState("connected");
|
|
572
733
|
if (initialConnect) {
|
|
573
734
|
initialConnect = false;
|
|
574
735
|
if (this.socket) {
|
|
@@ -577,11 +738,15 @@ var RealtimeClient = class {
|
|
|
577
738
|
this.socket.io.opts.reconnectionDelayMax = 5e3;
|
|
578
739
|
this.socket.io.opts.reconnectionAttempts = 50;
|
|
579
740
|
}
|
|
741
|
+
if (this.config.onTokenRefreshed && !this.unsubscribeFromTokenRefreshed) {
|
|
742
|
+
this.unsubscribeFromTokenRefreshed = this.config.onTokenRefreshed(
|
|
743
|
+
(accessToken) => {
|
|
744
|
+
this.socket?.emit("reauthenticate", { token: accessToken });
|
|
745
|
+
}
|
|
746
|
+
);
|
|
747
|
+
}
|
|
580
748
|
resolve();
|
|
581
|
-
}
|
|
582
|
-
});
|
|
583
|
-
this.socket.on("connect", () => {
|
|
584
|
-
if (!initialConnect) {
|
|
749
|
+
} else {
|
|
585
750
|
this.resubscribeAll();
|
|
586
751
|
}
|
|
587
752
|
});
|
|
@@ -590,6 +755,9 @@ var RealtimeClient = class {
|
|
|
590
755
|
reject(new Error(`WebSocket connection failed: ${err.message}`));
|
|
591
756
|
}
|
|
592
757
|
});
|
|
758
|
+
this.socket.io.on("reconnect_failed", () => {
|
|
759
|
+
void this.rebuildSocket();
|
|
760
|
+
});
|
|
593
761
|
this.socket.on("db:event", (event) => {
|
|
594
762
|
const base = `db:${event.projectId}:${event.collectionName}`;
|
|
595
763
|
const baseCallbacks = this.subscriptions.get(base);
|
|
@@ -613,6 +781,9 @@ var RealtimeClient = class {
|
|
|
613
781
|
}
|
|
614
782
|
});
|
|
615
783
|
this.socket.on("disconnect", () => {
|
|
784
|
+
if (!this.disposed) {
|
|
785
|
+
this.setConnectionState("reconnecting");
|
|
786
|
+
}
|
|
616
787
|
});
|
|
617
788
|
});
|
|
618
789
|
}
|
|
@@ -635,6 +806,70 @@ var RealtimeClient = class {
|
|
|
635
806
|
}
|
|
636
807
|
return true;
|
|
637
808
|
}
|
|
809
|
+
/**
|
|
810
|
+
* Tear down the current socket and create a fresh one. Used by both
|
|
811
|
+
* `reconnect_failed` (socket.io gave up) and the visibility/online wake-up
|
|
812
|
+
* listeners. Preserves the `subscriptions` map so existing rooms can be
|
|
813
|
+
* replayed to the server on the new connection. If the rebuild itself
|
|
814
|
+
* fails it schedules one retry after 5 s — beyond that we rely on the
|
|
815
|
+
* next wake-up event (visibility/online) to try again.
|
|
816
|
+
*/
|
|
817
|
+
async rebuildSocket() {
|
|
818
|
+
if (this.disposed || this.connecting) return;
|
|
819
|
+
const previous = this.socket;
|
|
820
|
+
this.socket = null;
|
|
821
|
+
if (previous) previous.disconnect();
|
|
822
|
+
this.setConnectionState("reconnecting");
|
|
823
|
+
try {
|
|
824
|
+
await this.ensureConnected();
|
|
825
|
+
this.handlePostRebuild();
|
|
826
|
+
} catch {
|
|
827
|
+
this.scheduleRebuildRetry();
|
|
828
|
+
}
|
|
829
|
+
}
|
|
830
|
+
handlePostRebuild() {
|
|
831
|
+
const newSocket = this.socket;
|
|
832
|
+
if (this.disposed) {
|
|
833
|
+
this.socket = null;
|
|
834
|
+
newSocket?.disconnect();
|
|
835
|
+
return;
|
|
836
|
+
}
|
|
837
|
+
if (this.subscriptions.size > 0) {
|
|
838
|
+
this.resubscribeAll();
|
|
839
|
+
}
|
|
840
|
+
}
|
|
841
|
+
scheduleRebuildRetry() {
|
|
842
|
+
if (this.disposed || this.rebuildRetryTimer) return;
|
|
843
|
+
this.rebuildRetryTimer = setTimeout(() => {
|
|
844
|
+
this.rebuildRetryTimer = null;
|
|
845
|
+
void this.rebuildSocket();
|
|
846
|
+
}, REBUILD_RETRY_DELAY_MS);
|
|
847
|
+
this.rebuildRetryTimer.unref?.();
|
|
848
|
+
}
|
|
849
|
+
ensureWakeListeners() {
|
|
850
|
+
if (typeof document === "undefined" || typeof window === "undefined") return;
|
|
851
|
+
if (this.onVisibilityChange !== null) return;
|
|
852
|
+
const wakeIfUnhealthy = () => {
|
|
853
|
+
if (typeof document !== "undefined" && document.visibilityState !== "visible") return;
|
|
854
|
+
if (this.socket?.connected) return;
|
|
855
|
+
if (this.connecting) return;
|
|
856
|
+
void this.rebuildSocket();
|
|
857
|
+
};
|
|
858
|
+
this.onVisibilityChange = wakeIfUnhealthy;
|
|
859
|
+
this.onOnline = wakeIfUnhealthy;
|
|
860
|
+
document.addEventListener("visibilitychange", this.onVisibilityChange);
|
|
861
|
+
window.addEventListener("online", this.onOnline);
|
|
862
|
+
}
|
|
863
|
+
detachWakeListeners() {
|
|
864
|
+
if (typeof document !== "undefined" && this.onVisibilityChange) {
|
|
865
|
+
document.removeEventListener("visibilitychange", this.onVisibilityChange);
|
|
866
|
+
}
|
|
867
|
+
if (typeof window !== "undefined" && this.onOnline) {
|
|
868
|
+
window.removeEventListener("online", this.onOnline);
|
|
869
|
+
}
|
|
870
|
+
this.onVisibilityChange = null;
|
|
871
|
+
this.onOnline = null;
|
|
872
|
+
}
|
|
638
873
|
resubscribeAll() {
|
|
639
874
|
for (const [room] of this.subscriptions) {
|
|
640
875
|
const queryIdx = room.indexOf("?");
|
|
@@ -714,11 +949,14 @@ var AuthModule = class {
|
|
|
714
949
|
});
|
|
715
950
|
}
|
|
716
951
|
async logout() {
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
952
|
+
try {
|
|
953
|
+
await this.http.request({
|
|
954
|
+
method: "POST",
|
|
955
|
+
path: "/auth/logout",
|
|
956
|
+
authenticated: true
|
|
957
|
+
});
|
|
958
|
+
} catch {
|
|
959
|
+
}
|
|
722
960
|
await this.tokenManager.clearTokens();
|
|
723
961
|
}
|
|
724
962
|
async verifyEmail(token) {
|
|
@@ -1156,11 +1394,11 @@ var CollectionRef = class {
|
|
|
1156
1394
|
this.collectionName = collectionName;
|
|
1157
1395
|
this.basePath = `/db/${collectionName}`;
|
|
1158
1396
|
}
|
|
1159
|
-
async insert(
|
|
1397
|
+
async insert(document2) {
|
|
1160
1398
|
return this.http.request({
|
|
1161
1399
|
method: "POST",
|
|
1162
1400
|
path: this.basePath,
|
|
1163
|
-
body: { documents: [
|
|
1401
|
+
body: { documents: [document2] },
|
|
1164
1402
|
authenticated: true
|
|
1165
1403
|
});
|
|
1166
1404
|
}
|
|
@@ -1458,7 +1696,8 @@ function createClient(config) {
|
|
|
1458
1696
|
const httpClient = new HttpClient(config, tokenManager);
|
|
1459
1697
|
const realtime = new RealtimeClient({
|
|
1460
1698
|
baseUrl: config.apiUrl,
|
|
1461
|
-
getToken: () => tokenManager.getAccessToken()
|
|
1699
|
+
getToken: () => tokenManager.getAccessToken(),
|
|
1700
|
+
onTokenRefreshed: (listener) => tokenManager.onTokenRefreshed((tokens) => listener(tokens.accessToken))
|
|
1462
1701
|
});
|
|
1463
1702
|
const auth = new AuthModule(httpClient, tokenManager, config);
|
|
1464
1703
|
const storage = new StorageModule(httpClient, tokenManager, config);
|
|
@@ -1471,8 +1710,26 @@ function createClient(config) {
|
|
|
1471
1710
|
db,
|
|
1472
1711
|
notifications,
|
|
1473
1712
|
functions,
|
|
1713
|
+
setTokens(tokens) {
|
|
1714
|
+
return tokenManager.setTokens(tokens);
|
|
1715
|
+
},
|
|
1716
|
+
clearTokens() {
|
|
1717
|
+
return tokenManager.clearTokens();
|
|
1718
|
+
},
|
|
1474
1719
|
disconnect() {
|
|
1475
1720
|
realtime.disconnect();
|
|
1721
|
+
},
|
|
1722
|
+
onAuthLost(listener) {
|
|
1723
|
+
return tokenManager.onAuthLost(listener);
|
|
1724
|
+
},
|
|
1725
|
+
onTokenRefreshed(listener) {
|
|
1726
|
+
return tokenManager.onTokenRefreshed(listener);
|
|
1727
|
+
},
|
|
1728
|
+
onConnectionStateChanged(listener) {
|
|
1729
|
+
return realtime.onConnectionStateChanged(listener);
|
|
1730
|
+
},
|
|
1731
|
+
getConnectionState() {
|
|
1732
|
+
return realtime.getConnectionState();
|
|
1476
1733
|
}
|
|
1477
1734
|
};
|
|
1478
1735
|
}
|