@basictech/react 0.7.0-beta.3 → 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/.turbo/turbo-build.log +10 -10
- package/changelog.md +12 -0
- package/dist/index.d.mts +20 -8
- package/dist/index.d.ts +20 -8
- package/dist/index.js +196 -81
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +196 -81
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
- package/src/AuthContext.tsx +229 -127
- package/src/index.ts +3 -7
- package/src/sync/index.ts +4 -4
- package/src/utils/storage.ts +1 -0
package/src/AuthContext.tsx
CHANGED
|
@@ -16,17 +16,28 @@ 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
|
-
|
|
23
|
-
|
|
22
|
+
export type BasicProviderProps = {
|
|
23
|
+
children: React.ReactNode;
|
|
24
|
+
project_id?: string;
|
|
25
|
+
schema?: any;
|
|
26
|
+
debug?: boolean;
|
|
27
|
+
storage?: BasicStorage;
|
|
28
|
+
auth?: AuthConfig;
|
|
24
29
|
}
|
|
25
30
|
|
|
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
|
|
36
|
+
|
|
26
37
|
|
|
27
38
|
type BasicSyncType = {
|
|
28
39
|
basic_schema: any;
|
|
29
|
-
connect: (options: { access_token: string }) => void;
|
|
40
|
+
connect: (options: { access_token: string; ws_url?: string }) => void;
|
|
30
41
|
debugeroo: () => void;
|
|
31
42
|
collection: (name: string) => {
|
|
32
43
|
ref: {
|
|
@@ -102,14 +113,7 @@ export function BasicProvider({
|
|
|
102
113
|
debug = false,
|
|
103
114
|
storage,
|
|
104
115
|
auth
|
|
105
|
-
}: {
|
|
106
|
-
children: React.ReactNode,
|
|
107
|
-
project_id?: string,
|
|
108
|
-
schema?: any,
|
|
109
|
-
debug?: boolean,
|
|
110
|
-
storage?: BasicStorage,
|
|
111
|
-
auth?: AuthConfig
|
|
112
|
-
}) {
|
|
116
|
+
}: BasicProviderProps) {
|
|
113
117
|
const [isAuthReady, setIsAuthReady] = useState(false)
|
|
114
118
|
const [isSignedIn, setIsSignedIn] = useState<boolean>(false)
|
|
115
119
|
const [token, setToken] = useState<Token | null>(null)
|
|
@@ -126,9 +130,10 @@ export function BasicProvider({
|
|
|
126
130
|
const storageAdapter = storage || new LocalStorageAdapter();
|
|
127
131
|
|
|
128
132
|
// Merge auth config with defaults
|
|
129
|
-
const authConfig
|
|
133
|
+
const authConfig = {
|
|
130
134
|
scopes: auth?.scopes || DEFAULT_AUTH_CONFIG.scopes,
|
|
131
|
-
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
|
|
132
137
|
}
|
|
133
138
|
|
|
134
139
|
// Normalize scopes to space-separated string
|
|
@@ -136,6 +141,9 @@ export function BasicProvider({
|
|
|
136
141
|
? authConfig.scopes.join(' ')
|
|
137
142
|
: authConfig.scopes;
|
|
138
143
|
|
|
144
|
+
// Token refresh mutex to prevent concurrent refreshes
|
|
145
|
+
const refreshPromiseRef = useRef<Promise<Token | null> | null>(null);
|
|
146
|
+
|
|
139
147
|
const isDevMode = () => isDevelopment(debug)
|
|
140
148
|
|
|
141
149
|
const cleanOAuthParams = () => cleanOAuthParamsFromUrl()
|
|
@@ -243,7 +251,10 @@ export function BasicProvider({
|
|
|
243
251
|
|
|
244
252
|
log('connecting to db...')
|
|
245
253
|
|
|
246
|
-
syncRef.current?.connect({
|
|
254
|
+
syncRef.current?.connect({
|
|
255
|
+
access_token: tok,
|
|
256
|
+
ws_url: authConfig.ws_url
|
|
257
|
+
})
|
|
247
258
|
.catch((e) => {
|
|
248
259
|
log('error connecting to db', e)
|
|
249
260
|
})
|
|
@@ -257,6 +268,19 @@ export function BasicProvider({
|
|
|
257
268
|
const initializeAuth = async () => {
|
|
258
269
|
await storageAdapter.set(STORAGE_KEYS.DEBUG, debug ? 'true' : 'false')
|
|
259
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
|
+
|
|
260
284
|
try {
|
|
261
285
|
const versionUpdater = createVersionUpdater(storageAdapter, currentVersion, getMigrations())
|
|
262
286
|
const updateResult = await versionUpdater.checkAndUpdate()
|
|
@@ -335,19 +359,25 @@ export function BasicProvider({
|
|
|
335
359
|
useEffect(() => {
|
|
336
360
|
async function fetchUser(acc_token: string) {
|
|
337
361
|
console.info('fetching user')
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
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}`)
|
|
342
379
|
}
|
|
343
|
-
})
|
|
344
|
-
.then(response => response.json())
|
|
345
|
-
.catch(error => log('Error:', error))
|
|
346
380
|
|
|
347
|
-
if (user.error) {
|
|
348
|
-
log('error fetching user', user.error)
|
|
349
|
-
return
|
|
350
|
-
} else {
|
|
351
381
|
if (token?.refresh_token) {
|
|
352
382
|
await storageAdapter.set(STORAGE_KEYS.REFRESH_TOKEN, token.refresh_token)
|
|
353
383
|
}
|
|
@@ -360,7 +390,10 @@ export function BasicProvider({
|
|
|
360
390
|
|
|
361
391
|
setUser(user)
|
|
362
392
|
setIsSignedIn(true)
|
|
363
|
-
|
|
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
|
|
364
397
|
setIsAuthReady(true)
|
|
365
398
|
}
|
|
366
399
|
}
|
|
@@ -374,7 +407,9 @@ export function BasicProvider({
|
|
|
374
407
|
}
|
|
375
408
|
|
|
376
409
|
const decoded = jwtDecode(token?.access_token)
|
|
377
|
-
|
|
410
|
+
// Add 5 second buffer to prevent edge cases
|
|
411
|
+
const expirationBuffer = 5
|
|
412
|
+
const isExpired = decoded.exp && decoded.exp < (Date.now() / 1000) + expirationBuffer
|
|
378
413
|
|
|
379
414
|
if (isExpired) {
|
|
380
415
|
log('token is expired - refreshing ...')
|
|
@@ -450,7 +485,10 @@ export function BasicProvider({
|
|
|
450
485
|
const signInLink = await getSignInLink()
|
|
451
486
|
log('Generated sign-in link:', signInLink)
|
|
452
487
|
|
|
453
|
-
|
|
488
|
+
// Validate URL format (supports https://, http://, and custom URI schemes)
|
|
489
|
+
try {
|
|
490
|
+
new URL(signInLink)
|
|
491
|
+
} catch {
|
|
454
492
|
log('Error: Invalid sign-in link generated')
|
|
455
493
|
throw new Error('Failed to generate valid sign-in URL')
|
|
456
494
|
}
|
|
@@ -520,6 +558,7 @@ export function BasicProvider({
|
|
|
520
558
|
await storageAdapter.remove(STORAGE_KEYS.REFRESH_TOKEN)
|
|
521
559
|
await storageAdapter.remove(STORAGE_KEYS.USER_INFO)
|
|
522
560
|
await storageAdapter.remove(STORAGE_KEYS.REDIRECT_URI)
|
|
561
|
+
await storageAdapter.remove(STORAGE_KEYS.SERVER_URL)
|
|
523
562
|
if (syncRef.current) {
|
|
524
563
|
(async () => {
|
|
525
564
|
try {
|
|
@@ -542,6 +581,21 @@ export function BasicProvider({
|
|
|
542
581
|
const refreshToken = await storageAdapter.get(STORAGE_KEYS.REFRESH_TOKEN)
|
|
543
582
|
if (refreshToken) {
|
|
544
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
|
+
|
|
545
599
|
try {
|
|
546
600
|
const newToken = await fetchToken(refreshToken, true)
|
|
547
601
|
if (newToken?.access_token) {
|
|
@@ -567,10 +621,31 @@ export function BasicProvider({
|
|
|
567
621
|
}
|
|
568
622
|
|
|
569
623
|
const decoded = jwtDecode(token?.access_token)
|
|
570
|
-
|
|
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
|
|
571
627
|
|
|
572
628
|
if (isExpired) {
|
|
573
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
|
+
|
|
574
649
|
const refreshToken = token?.refresh_token || await storageAdapter.get(STORAGE_KEYS.REFRESH_TOKEN)
|
|
575
650
|
if (refreshToken) {
|
|
576
651
|
try {
|
|
@@ -594,124 +669,151 @@ export function BasicProvider({
|
|
|
594
669
|
return token?.access_token || ''
|
|
595
670
|
}
|
|
596
671
|
|
|
597
|
-
const fetchToken = async (codeOrRefreshToken: string, isRefreshToken: boolean = false) => {
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
}
|
|
604
|
-
|
|
605
|
-
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
|
+
}
|
|
606
678
|
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
if (project_id) {
|
|
615
|
-
requestBody.client_id = project_id
|
|
616
|
-
}
|
|
617
|
-
} else {
|
|
618
|
-
// Authorization code exchange
|
|
619
|
-
requestBody = {
|
|
620
|
-
grant_type: 'authorization_code',
|
|
621
|
-
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')
|
|
622
686
|
}
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
if (
|
|
627
|
-
|
|
628
|
-
|
|
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
|
+
}
|
|
629
700
|
} else {
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
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
|
+
}
|
|
636
720
|
}
|
|
637
|
-
}
|
|
638
721
|
|
|
639
|
-
|
|
722
|
+
log('Token exchange request body:', { ...requestBody, refresh_token: isRefreshToken ? '[REDACTED]' : undefined, code: !isRefreshToken ? '[REDACTED]' : undefined })
|
|
640
723
|
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
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')) {
|
|
652
745
|
setPendingRefresh(true)
|
|
653
|
-
throw new Error('Network
|
|
746
|
+
throw new Error('Network issue - refresh will be retried when online')
|
|
654
747
|
}
|
|
655
|
-
throw new Error('Network error during token refresh')
|
|
656
|
-
})
|
|
657
748
|
|
|
658
|
-
|
|
659
|
-
|
|
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');
|
|
660
755
|
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
756
|
+
setUser({})
|
|
757
|
+
setIsSignedIn(false)
|
|
758
|
+
setToken(null)
|
|
759
|
+
setIsAuthReady(true)
|
|
665
760
|
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
clearCookie('basic_access_token');
|
|
761
|
+
throw new Error(`Token refresh failed: ${token.error}`)
|
|
762
|
+
} else {
|
|
763
|
+
setToken(token)
|
|
764
|
+
setPendingRefresh(false)
|
|
671
765
|
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
766
|
+
if (token.refresh_token) {
|
|
767
|
+
await storageAdapter.set(STORAGE_KEYS.REFRESH_TOKEN, token.refresh_token)
|
|
768
|
+
log('Updated refresh token in storage')
|
|
769
|
+
}
|
|
676
770
|
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
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
|
+
}
|
|
681
776
|
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
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')
|
|
685
780
|
}
|
|
781
|
+
return token
|
|
782
|
+
} catch (error) {
|
|
783
|
+
log('Token refresh error:', error)
|
|
686
784
|
|
|
687
|
-
|
|
688
|
-
|
|
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)
|
|
689
788
|
await storageAdapter.remove(STORAGE_KEYS.REDIRECT_URI)
|
|
690
|
-
|
|
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)
|
|
691
797
|
}
|
|
692
798
|
|
|
693
|
-
|
|
694
|
-
log('Updated access token in cookie')
|
|
695
|
-
}
|
|
696
|
-
return token
|
|
697
|
-
} catch (error) {
|
|
698
|
-
log('Token refresh error:', error)
|
|
699
|
-
|
|
700
|
-
if (!(error as Error).message.includes('offline') && !(error as Error).message.includes('Network')) {
|
|
701
|
-
await storageAdapter.remove(STORAGE_KEYS.REFRESH_TOKEN)
|
|
702
|
-
await storageAdapter.remove(STORAGE_KEYS.USER_INFO)
|
|
703
|
-
await storageAdapter.remove(STORAGE_KEYS.REDIRECT_URI)
|
|
704
|
-
clearCookie('basic_token');
|
|
705
|
-
clearCookie('basic_access_token');
|
|
706
|
-
|
|
707
|
-
setUser({})
|
|
708
|
-
setIsSignedIn(false)
|
|
709
|
-
setToken(null)
|
|
710
|
-
setIsAuthReady(true)
|
|
799
|
+
throw error
|
|
711
800
|
}
|
|
712
|
-
|
|
713
|
-
|
|
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
|
+
})
|
|
714
814
|
}
|
|
815
|
+
|
|
816
|
+
return refreshPromise
|
|
715
817
|
}
|
|
716
818
|
|
|
717
819
|
const noDb = ({
|
package/src/index.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { useState } from "react";
|
|
2
|
-
import { useBasic, BasicProvider, BasicStorage, LocalStorageAdapter, AuthConfig } from "./AuthContext";
|
|
2
|
+
import { useBasic, BasicProvider, BasicStorage, LocalStorageAdapter, AuthConfig, BasicProviderProps } from "./AuthContext";
|
|
3
3
|
import { useLiveQuery as useQuery } from "dexie-react-hooks";
|
|
4
4
|
// import { createVersionUpdater, VersionUpdater, Migration } from "./versionUpdater";
|
|
5
5
|
|
|
@@ -9,9 +9,5 @@ export {
|
|
|
9
9
|
}
|
|
10
10
|
|
|
11
11
|
export type {
|
|
12
|
-
AuthConfig, BasicStorage, LocalStorageAdapter
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
// export type {
|
|
16
|
-
// VersionUpdater, Migration
|
|
17
|
-
// }
|
|
12
|
+
AuthConfig, BasicStorage, LocalStorageAdapter, BasicProviderProps
|
|
13
|
+
}
|
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
|
}
|