@basictech/react 0.7.0-beta.4 → 0.7.0-beta.5
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/changelog.md +6 -0
- package/dist/index.d.mts +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +179 -103
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +179 -103
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
- package/src/AuthContext.tsx +220 -120
- package/src/sync/index.ts +4 -4
- package/src/utils/storage.ts +1 -0
package/src/AuthContext.tsx
CHANGED
|
@@ -16,6 +16,7 @@ export type { BasicStorage, LocalStorageAdapter } from './utils/storage'
|
|
|
16
16
|
export type AuthConfig = {
|
|
17
17
|
scopes?: string | string[];
|
|
18
18
|
server_url?: string;
|
|
19
|
+
ws_url?: string;
|
|
19
20
|
}
|
|
20
21
|
|
|
21
22
|
export type BasicProviderProps = {
|
|
@@ -27,15 +28,16 @@ export type BasicProviderProps = {
|
|
|
27
28
|
auth?: AuthConfig;
|
|
28
29
|
}
|
|
29
30
|
|
|
30
|
-
const DEFAULT_AUTH_CONFIG
|
|
31
|
-
scopes: 'profile
|
|
32
|
-
server_url: 'https://api.basic.tech'
|
|
33
|
-
|
|
31
|
+
const DEFAULT_AUTH_CONFIG = {
|
|
32
|
+
scopes: 'profile,email,app:admin',
|
|
33
|
+
server_url: 'https://api.basic.tech',
|
|
34
|
+
ws_url: 'wss://pds.basic.id/ws'
|
|
35
|
+
} as const
|
|
34
36
|
|
|
35
37
|
|
|
36
38
|
type BasicSyncType = {
|
|
37
39
|
basic_schema: any;
|
|
38
|
-
connect: (options: { access_token: string }) => void;
|
|
40
|
+
connect: (options: { access_token: string; ws_url?: string }) => void;
|
|
39
41
|
debugeroo: () => void;
|
|
40
42
|
collection: (name: string) => {
|
|
41
43
|
ref: {
|
|
@@ -128,9 +130,10 @@ export function BasicProvider({
|
|
|
128
130
|
const storageAdapter = storage || new LocalStorageAdapter();
|
|
129
131
|
|
|
130
132
|
// Merge auth config with defaults
|
|
131
|
-
const authConfig
|
|
133
|
+
const authConfig = {
|
|
132
134
|
scopes: auth?.scopes || DEFAULT_AUTH_CONFIG.scopes,
|
|
133
|
-
server_url: auth?.server_url || DEFAULT_AUTH_CONFIG.server_url
|
|
135
|
+
server_url: auth?.server_url || DEFAULT_AUTH_CONFIG.server_url,
|
|
136
|
+
ws_url: auth?.ws_url || DEFAULT_AUTH_CONFIG.ws_url
|
|
134
137
|
}
|
|
135
138
|
|
|
136
139
|
// Normalize scopes to space-separated string
|
|
@@ -138,6 +141,9 @@ export function BasicProvider({
|
|
|
138
141
|
? authConfig.scopes.join(' ')
|
|
139
142
|
: authConfig.scopes;
|
|
140
143
|
|
|
144
|
+
// Token refresh mutex to prevent concurrent refreshes
|
|
145
|
+
const refreshPromiseRef = useRef<Promise<Token | null> | null>(null);
|
|
146
|
+
|
|
141
147
|
const isDevMode = () => isDevelopment(debug)
|
|
142
148
|
|
|
143
149
|
const cleanOAuthParams = () => cleanOAuthParamsFromUrl()
|
|
@@ -245,7 +251,10 @@ export function BasicProvider({
|
|
|
245
251
|
|
|
246
252
|
log('connecting to db...')
|
|
247
253
|
|
|
248
|
-
syncRef.current?.connect({
|
|
254
|
+
syncRef.current?.connect({
|
|
255
|
+
access_token: tok,
|
|
256
|
+
ws_url: authConfig.ws_url
|
|
257
|
+
})
|
|
249
258
|
.catch((e) => {
|
|
250
259
|
log('error connecting to db', e)
|
|
251
260
|
})
|
|
@@ -259,6 +268,19 @@ export function BasicProvider({
|
|
|
259
268
|
const initializeAuth = async () => {
|
|
260
269
|
await storageAdapter.set(STORAGE_KEYS.DEBUG, debug ? 'true' : 'false')
|
|
261
270
|
|
|
271
|
+
// Check if server URL has changed - if so, clear tokens
|
|
272
|
+
const storedServerUrl = await storageAdapter.get(STORAGE_KEYS.SERVER_URL)
|
|
273
|
+
if (storedServerUrl && storedServerUrl !== authConfig.server_url) {
|
|
274
|
+
log('Server URL changed, clearing stored tokens')
|
|
275
|
+
await storageAdapter.remove(STORAGE_KEYS.REFRESH_TOKEN)
|
|
276
|
+
await storageAdapter.remove(STORAGE_KEYS.USER_INFO)
|
|
277
|
+
await storageAdapter.remove(STORAGE_KEYS.AUTH_STATE)
|
|
278
|
+
await storageAdapter.remove(STORAGE_KEYS.REDIRECT_URI)
|
|
279
|
+
clearCookie('basic_token')
|
|
280
|
+
clearCookie('basic_access_token')
|
|
281
|
+
}
|
|
282
|
+
await storageAdapter.set(STORAGE_KEYS.SERVER_URL, authConfig.server_url)
|
|
283
|
+
|
|
262
284
|
try {
|
|
263
285
|
const versionUpdater = createVersionUpdater(storageAdapter, currentVersion, getMigrations())
|
|
264
286
|
const updateResult = await versionUpdater.checkAndUpdate()
|
|
@@ -337,19 +359,25 @@ export function BasicProvider({
|
|
|
337
359
|
useEffect(() => {
|
|
338
360
|
async function fetchUser(acc_token: string) {
|
|
339
361
|
console.info('fetching user')
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
362
|
+
try {
|
|
363
|
+
const response = await fetch(`${authConfig.server_url}/auth/userInfo`, {
|
|
364
|
+
method: 'GET',
|
|
365
|
+
headers: {
|
|
366
|
+
'Authorization': `Bearer ${acc_token}`
|
|
367
|
+
}
|
|
368
|
+
})
|
|
369
|
+
|
|
370
|
+
if (!response.ok) {
|
|
371
|
+
throw new Error(`Failed to fetch user info: ${response.status}`)
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
const user = await response.json()
|
|
375
|
+
|
|
376
|
+
if (user.error) {
|
|
377
|
+
log('error fetching user', user.error)
|
|
378
|
+
throw new Error(`User info error: ${user.error}`)
|
|
344
379
|
}
|
|
345
|
-
})
|
|
346
|
-
.then(response => response.json())
|
|
347
|
-
.catch(error => log('Error:', error))
|
|
348
380
|
|
|
349
|
-
if (user.error) {
|
|
350
|
-
log('error fetching user', user.error)
|
|
351
|
-
return
|
|
352
|
-
} else {
|
|
353
381
|
if (token?.refresh_token) {
|
|
354
382
|
await storageAdapter.set(STORAGE_KEYS.REFRESH_TOKEN, token.refresh_token)
|
|
355
383
|
}
|
|
@@ -362,7 +390,10 @@ export function BasicProvider({
|
|
|
362
390
|
|
|
363
391
|
setUser(user)
|
|
364
392
|
setIsSignedIn(true)
|
|
365
|
-
|
|
393
|
+
setIsAuthReady(true)
|
|
394
|
+
} catch (error) {
|
|
395
|
+
log('Failed to fetch user info:', error)
|
|
396
|
+
// Don't clear tokens here - may be temporary network issue
|
|
366
397
|
setIsAuthReady(true)
|
|
367
398
|
}
|
|
368
399
|
}
|
|
@@ -376,7 +407,9 @@ export function BasicProvider({
|
|
|
376
407
|
}
|
|
377
408
|
|
|
378
409
|
const decoded = jwtDecode(token?.access_token)
|
|
379
|
-
|
|
410
|
+
// Add 5 second buffer to prevent edge cases
|
|
411
|
+
const expirationBuffer = 5
|
|
412
|
+
const isExpired = decoded.exp && decoded.exp < (Date.now() / 1000) + expirationBuffer
|
|
380
413
|
|
|
381
414
|
if (isExpired) {
|
|
382
415
|
log('token is expired - refreshing ...')
|
|
@@ -452,7 +485,10 @@ export function BasicProvider({
|
|
|
452
485
|
const signInLink = await getSignInLink()
|
|
453
486
|
log('Generated sign-in link:', signInLink)
|
|
454
487
|
|
|
455
|
-
|
|
488
|
+
// Validate URL format (supports https://, http://, and custom URI schemes)
|
|
489
|
+
try {
|
|
490
|
+
new URL(signInLink)
|
|
491
|
+
} catch {
|
|
456
492
|
log('Error: Invalid sign-in link generated')
|
|
457
493
|
throw new Error('Failed to generate valid sign-in URL')
|
|
458
494
|
}
|
|
@@ -522,6 +558,7 @@ export function BasicProvider({
|
|
|
522
558
|
await storageAdapter.remove(STORAGE_KEYS.REFRESH_TOKEN)
|
|
523
559
|
await storageAdapter.remove(STORAGE_KEYS.USER_INFO)
|
|
524
560
|
await storageAdapter.remove(STORAGE_KEYS.REDIRECT_URI)
|
|
561
|
+
await storageAdapter.remove(STORAGE_KEYS.SERVER_URL)
|
|
525
562
|
if (syncRef.current) {
|
|
526
563
|
(async () => {
|
|
527
564
|
try {
|
|
@@ -544,6 +581,21 @@ export function BasicProvider({
|
|
|
544
581
|
const refreshToken = await storageAdapter.get(STORAGE_KEYS.REFRESH_TOKEN)
|
|
545
582
|
if (refreshToken) {
|
|
546
583
|
log('No token in memory, attempting to refresh from storage')
|
|
584
|
+
|
|
585
|
+
// Check if refresh is already in progress
|
|
586
|
+
if (refreshPromiseRef.current) {
|
|
587
|
+
log('Token refresh already in progress, waiting...')
|
|
588
|
+
try {
|
|
589
|
+
const newToken = await refreshPromiseRef.current
|
|
590
|
+
if (newToken?.access_token) {
|
|
591
|
+
return newToken.access_token
|
|
592
|
+
}
|
|
593
|
+
} catch (error) {
|
|
594
|
+
log('In-flight refresh failed:', error)
|
|
595
|
+
throw error
|
|
596
|
+
}
|
|
597
|
+
}
|
|
598
|
+
|
|
547
599
|
try {
|
|
548
600
|
const newToken = await fetchToken(refreshToken, true)
|
|
549
601
|
if (newToken?.access_token) {
|
|
@@ -569,10 +621,31 @@ export function BasicProvider({
|
|
|
569
621
|
}
|
|
570
622
|
|
|
571
623
|
const decoded = jwtDecode(token?.access_token)
|
|
572
|
-
|
|
624
|
+
// Add 5 second buffer to prevent edge cases where token expires during request
|
|
625
|
+
const expirationBuffer = 5
|
|
626
|
+
const isExpired = decoded.exp && decoded.exp < (Date.now() / 1000) + expirationBuffer
|
|
573
627
|
|
|
574
628
|
if (isExpired) {
|
|
575
629
|
log('token is expired - refreshing ...')
|
|
630
|
+
|
|
631
|
+
// Check if refresh is already in progress
|
|
632
|
+
if (refreshPromiseRef.current) {
|
|
633
|
+
log('Token refresh already in progress, waiting...')
|
|
634
|
+
try {
|
|
635
|
+
const newToken = await refreshPromiseRef.current
|
|
636
|
+
return newToken?.access_token || ''
|
|
637
|
+
} catch (error) {
|
|
638
|
+
log('In-flight refresh failed:', error)
|
|
639
|
+
|
|
640
|
+
if ((error as Error).message.includes('offline') || (error as Error).message.includes('Network')) {
|
|
641
|
+
log('Network issue - using expired token until network is restored')
|
|
642
|
+
return token.access_token
|
|
643
|
+
}
|
|
644
|
+
|
|
645
|
+
throw error
|
|
646
|
+
}
|
|
647
|
+
}
|
|
648
|
+
|
|
576
649
|
const refreshToken = token?.refresh_token || await storageAdapter.get(STORAGE_KEYS.REFRESH_TOKEN)
|
|
577
650
|
if (refreshToken) {
|
|
578
651
|
try {
|
|
@@ -596,124 +669,151 @@ export function BasicProvider({
|
|
|
596
669
|
return token?.access_token || ''
|
|
597
670
|
}
|
|
598
671
|
|
|
599
|
-
const fetchToken = async (codeOrRefreshToken: string, isRefreshToken: boolean = false) => {
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
}
|
|
606
|
-
|
|
607
|
-
let requestBody: any
|
|
672
|
+
const fetchToken = async (codeOrRefreshToken: string, isRefreshToken: boolean = false): Promise<Token | null> => {
|
|
673
|
+
// If this is a refresh token request and one is already in progress, return that promise
|
|
674
|
+
if (isRefreshToken && refreshPromiseRef.current) {
|
|
675
|
+
log('Reusing in-flight refresh token request')
|
|
676
|
+
return refreshPromiseRef.current
|
|
677
|
+
}
|
|
608
678
|
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
if (project_id) {
|
|
617
|
-
requestBody.client_id = project_id
|
|
618
|
-
}
|
|
619
|
-
} else {
|
|
620
|
-
// Authorization code exchange
|
|
621
|
-
requestBody = {
|
|
622
|
-
grant_type: 'authorization_code',
|
|
623
|
-
code: codeOrRefreshToken
|
|
679
|
+
// Create new promise for this refresh attempt
|
|
680
|
+
const refreshPromise = (async (): Promise<Token | null> => {
|
|
681
|
+
try {
|
|
682
|
+
if (!isOnline) {
|
|
683
|
+
log('Network is offline, marking refresh as pending')
|
|
684
|
+
setPendingRefresh(true)
|
|
685
|
+
throw new Error('Network offline - refresh will be retried when online')
|
|
624
686
|
}
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
if (
|
|
629
|
-
|
|
630
|
-
|
|
687
|
+
|
|
688
|
+
let requestBody: any
|
|
689
|
+
|
|
690
|
+
if (isRefreshToken) {
|
|
691
|
+
// Refresh token request
|
|
692
|
+
requestBody = {
|
|
693
|
+
grant_type: 'refresh_token',
|
|
694
|
+
refresh_token: codeOrRefreshToken
|
|
695
|
+
}
|
|
696
|
+
// Include client_id if available for validation
|
|
697
|
+
if (project_id) {
|
|
698
|
+
requestBody.client_id = project_id
|
|
699
|
+
}
|
|
631
700
|
} else {
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
701
|
+
// Authorization code exchange
|
|
702
|
+
requestBody = {
|
|
703
|
+
grant_type: 'authorization_code',
|
|
704
|
+
code: codeOrRefreshToken
|
|
705
|
+
}
|
|
706
|
+
|
|
707
|
+
// Retrieve stored redirect_uri (required by OAuth2 spec)
|
|
708
|
+
const storedRedirectUri = await storageAdapter.get(STORAGE_KEYS.REDIRECT_URI)
|
|
709
|
+
if (storedRedirectUri) {
|
|
710
|
+
requestBody.redirect_uri = storedRedirectUri
|
|
711
|
+
log('Including redirect_uri in token exchange:', storedRedirectUri)
|
|
712
|
+
} else {
|
|
713
|
+
log('Warning: No redirect_uri found in storage for token exchange')
|
|
714
|
+
}
|
|
715
|
+
|
|
716
|
+
// Include client_id for validation
|
|
717
|
+
if (project_id) {
|
|
718
|
+
requestBody.client_id = project_id
|
|
719
|
+
}
|
|
638
720
|
}
|
|
639
|
-
}
|
|
640
721
|
|
|
641
|
-
|
|
722
|
+
log('Token exchange request body:', { ...requestBody, refresh_token: isRefreshToken ? '[REDACTED]' : undefined, code: !isRefreshToken ? '[REDACTED]' : undefined })
|
|
642
723
|
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
724
|
+
const token = await fetch(`${authConfig.server_url}/auth/token`, {
|
|
725
|
+
method: 'POST',
|
|
726
|
+
headers: {
|
|
727
|
+
'Content-Type': 'application/json'
|
|
728
|
+
},
|
|
729
|
+
body: JSON.stringify(requestBody)
|
|
730
|
+
})
|
|
731
|
+
.then(response => response.json())
|
|
732
|
+
.catch(error => {
|
|
733
|
+
log('Network error fetching token:', error)
|
|
734
|
+
if (!isOnline) {
|
|
735
|
+
setPendingRefresh(true)
|
|
736
|
+
throw new Error('Network offline - refresh will be retried when online')
|
|
737
|
+
}
|
|
738
|
+
throw new Error('Network error during token refresh')
|
|
739
|
+
})
|
|
740
|
+
|
|
741
|
+
if (token.error) {
|
|
742
|
+
log('error fetching token', token.error)
|
|
743
|
+
|
|
744
|
+
if (token.error.includes('network') || token.error.includes('timeout')) {
|
|
654
745
|
setPendingRefresh(true)
|
|
655
|
-
throw new Error('Network
|
|
746
|
+
throw new Error('Network issue - refresh will be retried when online')
|
|
656
747
|
}
|
|
657
|
-
throw new Error('Network error during token refresh')
|
|
658
|
-
})
|
|
659
748
|
|
|
660
|
-
|
|
661
|
-
|
|
749
|
+
await storageAdapter.remove(STORAGE_KEYS.REFRESH_TOKEN)
|
|
750
|
+
await storageAdapter.remove(STORAGE_KEYS.USER_INFO)
|
|
751
|
+
await storageAdapter.remove(STORAGE_KEYS.REDIRECT_URI)
|
|
752
|
+
await storageAdapter.remove(STORAGE_KEYS.SERVER_URL)
|
|
753
|
+
clearCookie('basic_token');
|
|
754
|
+
clearCookie('basic_access_token');
|
|
662
755
|
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
756
|
+
setUser({})
|
|
757
|
+
setIsSignedIn(false)
|
|
758
|
+
setToken(null)
|
|
759
|
+
setIsAuthReady(true)
|
|
667
760
|
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
clearCookie('basic_access_token');
|
|
761
|
+
throw new Error(`Token refresh failed: ${token.error}`)
|
|
762
|
+
} else {
|
|
763
|
+
setToken(token)
|
|
764
|
+
setPendingRefresh(false)
|
|
673
765
|
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
766
|
+
if (token.refresh_token) {
|
|
767
|
+
await storageAdapter.set(STORAGE_KEYS.REFRESH_TOKEN, token.refresh_token)
|
|
768
|
+
log('Updated refresh token in storage')
|
|
769
|
+
}
|
|
678
770
|
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
771
|
+
// Clean up redirect_uri after successful token exchange
|
|
772
|
+
if (!isRefreshToken) {
|
|
773
|
+
await storageAdapter.remove(STORAGE_KEYS.REDIRECT_URI)
|
|
774
|
+
log('Cleaned up redirect_uri from storage after successful exchange')
|
|
775
|
+
}
|
|
683
776
|
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
log('Updated
|
|
777
|
+
setCookie('basic_access_token', token.access_token, { httpOnly: false });
|
|
778
|
+
setCookie('basic_token', JSON.stringify(token));
|
|
779
|
+
log('Updated access token and full token in cookies')
|
|
687
780
|
}
|
|
781
|
+
return token
|
|
782
|
+
} catch (error) {
|
|
783
|
+
log('Token refresh error:', error)
|
|
688
784
|
|
|
689
|
-
|
|
690
|
-
|
|
785
|
+
if (!(error as Error).message.includes('offline') && !(error as Error).message.includes('Network')) {
|
|
786
|
+
await storageAdapter.remove(STORAGE_KEYS.REFRESH_TOKEN)
|
|
787
|
+
await storageAdapter.remove(STORAGE_KEYS.USER_INFO)
|
|
691
788
|
await storageAdapter.remove(STORAGE_KEYS.REDIRECT_URI)
|
|
692
|
-
|
|
789
|
+
await storageAdapter.remove(STORAGE_KEYS.SERVER_URL)
|
|
790
|
+
clearCookie('basic_token');
|
|
791
|
+
clearCookie('basic_access_token');
|
|
792
|
+
|
|
793
|
+
setUser({})
|
|
794
|
+
setIsSignedIn(false)
|
|
795
|
+
setToken(null)
|
|
796
|
+
setIsAuthReady(true)
|
|
693
797
|
}
|
|
694
798
|
|
|
695
|
-
|
|
696
|
-
log('Updated access token in cookie')
|
|
697
|
-
}
|
|
698
|
-
return token
|
|
699
|
-
} catch (error) {
|
|
700
|
-
log('Token refresh error:', error)
|
|
701
|
-
|
|
702
|
-
if (!(error as Error).message.includes('offline') && !(error as Error).message.includes('Network')) {
|
|
703
|
-
await storageAdapter.remove(STORAGE_KEYS.REFRESH_TOKEN)
|
|
704
|
-
await storageAdapter.remove(STORAGE_KEYS.USER_INFO)
|
|
705
|
-
await storageAdapter.remove(STORAGE_KEYS.REDIRECT_URI)
|
|
706
|
-
clearCookie('basic_token');
|
|
707
|
-
clearCookie('basic_access_token');
|
|
708
|
-
|
|
709
|
-
setUser({})
|
|
710
|
-
setIsSignedIn(false)
|
|
711
|
-
setToken(null)
|
|
712
|
-
setIsAuthReady(true)
|
|
799
|
+
throw error
|
|
713
800
|
}
|
|
714
|
-
|
|
715
|
-
|
|
801
|
+
})()
|
|
802
|
+
|
|
803
|
+
// Store promise if this is a refresh token request
|
|
804
|
+
if (isRefreshToken) {
|
|
805
|
+
refreshPromiseRef.current = refreshPromise
|
|
806
|
+
|
|
807
|
+
// Clear the promise reference when done (success or failure)
|
|
808
|
+
refreshPromise.finally(() => {
|
|
809
|
+
if (refreshPromiseRef.current === refreshPromise) {
|
|
810
|
+
refreshPromiseRef.current = null
|
|
811
|
+
log('Cleared refresh promise reference')
|
|
812
|
+
}
|
|
813
|
+
})
|
|
716
814
|
}
|
|
815
|
+
|
|
816
|
+
return refreshPromise
|
|
717
817
|
}
|
|
718
818
|
|
|
719
819
|
const noDb = ({
|
package/src/sync/index.ts
CHANGED
|
@@ -56,8 +56,8 @@ export class BasicSync extends Dexie {
|
|
|
56
56
|
|
|
57
57
|
}
|
|
58
58
|
|
|
59
|
-
async connect({ access_token }: { access_token: string }) {
|
|
60
|
-
const WS_URL =
|
|
59
|
+
async connect({ access_token, ws_url }: { access_token: string, ws_url?: string }) {
|
|
60
|
+
const WS_URL = ws_url || 'wss://pds.basic.id/ws'
|
|
61
61
|
|
|
62
62
|
log('Connecting to', WS_URL)
|
|
63
63
|
|
|
@@ -67,8 +67,8 @@ export class BasicSync extends Dexie {
|
|
|
67
67
|
return this.syncable.connect("websocket", WS_URL, { authToken: access_token, schema: this.basic_schema });
|
|
68
68
|
}
|
|
69
69
|
|
|
70
|
-
async disconnect() {
|
|
71
|
-
const WS_URL =
|
|
70
|
+
async disconnect({ ws_url }: { ws_url?: string } = {}) {
|
|
71
|
+
const WS_URL = ws_url || 'wss://pds.basic.id/ws'
|
|
72
72
|
|
|
73
73
|
return this.syncable.disconnect(WS_URL)
|
|
74
74
|
}
|