@basictech/react 0.7.0 → 0.8.0-beta.2
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 +13 -12
- package/AUTH_IMPLEMENTATION_GUIDE.md +20 -18
- package/changelog.md +24 -2
- package/dist/index.d.mts +121 -48
- package/dist/index.d.ts +121 -48
- package/dist/index.js +1996 -758
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1981 -749
- package/dist/index.mjs.map +1 -1
- package/package.json +5 -3
- package/readme.md +50 -1
- package/src/AuthContext.tsx +294 -818
- package/src/config.ts +1 -19
- package/src/context.tsx +104 -0
- package/src/core/auth/AuthManager.ts +858 -0
- package/src/core/db/RemoteCollection.ts +30 -16
- package/src/core/db/index.ts +1 -1
- package/src/core/db/types.ts +13 -1
- package/src/dev/BasicDevToolbar.tsx +665 -0
- package/src/index.ts +10 -3
- package/src/sync/index.ts +15 -29
- package/src/sync/syncProtocol.js +84 -22
- package/src/sync/tokenRegistry.ts +20 -0
- package/src/updater/updateMigrations.ts +3 -3
- package/src/updater/versionUpdater.ts +3 -10
- package/src/utils/network.ts +68 -15
- package/src/utils/normalizeClientId.ts +22 -0
- package/src/utils/resolveDid.ts +101 -0
- package/src/utils/schema.ts +3 -4
- package/src/utils/storage.ts +4 -1
|
@@ -59,23 +59,26 @@ export class RemoteCollection<T extends { id: string } = Record<string, any> & {
|
|
|
59
59
|
|
|
60
60
|
this.log(`${method} ${url}`, body ? JSON.stringify(body) : '')
|
|
61
61
|
|
|
62
|
+
const headers: Record<string, string> = {
|
|
63
|
+
'Authorization': `Bearer ${token}`
|
|
64
|
+
}
|
|
65
|
+
if (body) {
|
|
66
|
+
headers['Content-Type'] = 'application/json'
|
|
67
|
+
}
|
|
68
|
+
|
|
62
69
|
const response = await fetch(url, {
|
|
63
70
|
method,
|
|
64
|
-
headers
|
|
65
|
-
'Content-Type': 'application/json',
|
|
66
|
-
'Authorization': `Bearer ${token}`
|
|
67
|
-
},
|
|
71
|
+
headers,
|
|
68
72
|
...(body ? { body: JSON.stringify(body) } : {})
|
|
69
73
|
})
|
|
70
74
|
|
|
71
75
|
const responseData = await response.json().catch(() => ({}))
|
|
72
76
|
|
|
73
77
|
if (!response.ok) {
|
|
74
|
-
// Handle 401 Unauthorized -
|
|
78
|
+
// Handle 401 Unauthorized - force refresh then retry once
|
|
75
79
|
if (response.status === 401 && !isRetry) {
|
|
76
|
-
this.log('Got 401,
|
|
77
|
-
|
|
78
|
-
// Retry the request once
|
|
80
|
+
this.log('Got 401, forcing token refresh and retrying...')
|
|
81
|
+
await this.config.getToken({ forceRefresh: true })
|
|
79
82
|
return this.request<R>(method, path, body, true)
|
|
80
83
|
}
|
|
81
84
|
|
|
@@ -83,16 +86,27 @@ export class RemoteCollection<T extends { id: string } = Record<string, any> & {
|
|
|
83
86
|
console.error(`[RemoteDB] Error ${response.status}:`, responseData)
|
|
84
87
|
}
|
|
85
88
|
|
|
86
|
-
// Call onAuthError callback
|
|
87
|
-
if (
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
89
|
+
// Call onAuthError callback for auth/authz errors
|
|
90
|
+
if (this.config.onAuthError) {
|
|
91
|
+
if (response.status === 401) {
|
|
92
|
+
this.config.onAuthError({
|
|
93
|
+
status: response.status,
|
|
94
|
+
message: 'Authentication failed',
|
|
95
|
+
response: responseData,
|
|
96
|
+
errorType: 'expired',
|
|
97
|
+
afterRetry: isRetry,
|
|
98
|
+
})
|
|
99
|
+
} else if (response.status === 403) {
|
|
100
|
+
this.config.onAuthError({
|
|
101
|
+
status: response.status,
|
|
102
|
+
message: responseData.message || 'Forbidden - insufficient permissions or missing scope',
|
|
103
|
+
response: responseData,
|
|
104
|
+
errorType: 'forbidden',
|
|
105
|
+
afterRetry: isRetry,
|
|
106
|
+
})
|
|
107
|
+
}
|
|
93
108
|
}
|
|
94
109
|
|
|
95
|
-
// Try different error message fields that APIs commonly use
|
|
96
110
|
const errorMessage = responseData.message || responseData.error || responseData.detail ||
|
|
97
111
|
(typeof responseData === 'string' ? responseData : `API request failed: ${response.status}`)
|
|
98
112
|
throw new RemoteDBError(errorMessage, response.status, responseData)
|
package/src/core/db/index.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// Core DB exports
|
|
2
|
-
export type { Collection, BasicDB, DBMode, RemoteDBConfig } from './types'
|
|
2
|
+
export type { Collection, BasicDB, DBMode, RemoteDBConfig, GetTokenOptions } from './types'
|
|
3
3
|
export type { AuthError } from './types'
|
|
4
4
|
export { RemoteDBError } from './types'
|
|
5
5
|
export { RemoteDB } from './RemoteDB'
|
package/src/core/db/types.ts
CHANGED
|
@@ -91,6 +91,10 @@ export interface AuthError {
|
|
|
91
91
|
status: number
|
|
92
92
|
message: string
|
|
93
93
|
response?: any
|
|
94
|
+
/** Classifies the error for UI display (e.g. "session expired" vs "forbidden") */
|
|
95
|
+
errorType: 'expired' | 'forbidden' | 'revoked' | 'network' | 'unknown'
|
|
96
|
+
/** True if this error occurred after a retry with a refreshed token */
|
|
97
|
+
afterRetry: boolean
|
|
94
98
|
}
|
|
95
99
|
|
|
96
100
|
/**
|
|
@@ -109,13 +113,21 @@ export class RemoteDBError extends Error {
|
|
|
109
113
|
}
|
|
110
114
|
}
|
|
111
115
|
|
|
116
|
+
/**
|
|
117
|
+
* Options for getToken (e.g. force refresh after 401)
|
|
118
|
+
*/
|
|
119
|
+
export interface GetTokenOptions {
|
|
120
|
+
/** When true, refresh the access token before returning (e.g. after server returned 401) */
|
|
121
|
+
forceRefresh?: boolean
|
|
122
|
+
}
|
|
123
|
+
|
|
112
124
|
/**
|
|
113
125
|
* Configuration for RemoteDB
|
|
114
126
|
*/
|
|
115
127
|
export interface RemoteDBConfig {
|
|
116
128
|
serverUrl: string
|
|
117
129
|
projectId: string
|
|
118
|
-
getToken: () => Promise<string>
|
|
130
|
+
getToken: (options?: GetTokenOptions) => Promise<string>
|
|
119
131
|
schema?: any
|
|
120
132
|
/** Enable debug logging (default: false) */
|
|
121
133
|
debug?: boolean
|