@basictech/react 0.8.0-beta.4 → 0.9.0-beta.0
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 +31 -0
- package/dist/index.d.mts +1025 -294
- package/dist/index.d.ts +1025 -294
- package/dist/index.js +2256 -1444
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +2230 -1448
- package/dist/index.mjs.map +1 -1
- package/package.json +27 -8
- package/readme.md +280 -217
- package/.turbo/turbo-build.log +0 -24
- package/AUTH_IMPLEMENTATION_GUIDE.md +0 -2011
- package/src/AuthContext.tsx +0 -591
- package/src/config.ts +0 -9
- package/src/context.tsx +0 -122
- package/src/core/auth/AuthManager.ts +0 -1371
- package/src/core/db/RemoteCollection.ts +0 -308
- package/src/core/db/RemoteDB.ts +0 -40
- package/src/core/db/index.ts +0 -7
- package/src/core/db/types.ts +0 -140
- package/src/dev/BasicDevToolbar.tsx +0 -665
- package/src/index.ts +0 -36
- package/src/sync/index.ts +0 -288
- package/src/sync/syncProtocol.js +0 -291
- package/src/sync/tokenRegistry.ts +0 -20
- package/src/updater/updateMigrations.ts +0 -22
- package/src/updater/versionUpdater.ts +0 -153
- package/src/utils/network.ts +0 -135
- package/src/utils/normalizeClientId.ts +0 -22
- package/src/utils/resolveDid.ts +0 -101
- package/src/utils/schema.ts +0 -119
- package/src/utils/storage.ts +0 -67
- package/tsconfig.json +0 -9
- package/tsup.config.ts +0 -11
|
@@ -1,308 +0,0 @@
|
|
|
1
|
-
import { Collection, RemoteDBConfig, RemoteDBError } from './types'
|
|
2
|
-
import { validateData } from '@basictech/schema'
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Error thrown when user is not authenticated
|
|
6
|
-
*/
|
|
7
|
-
export class NotAuthenticatedError extends Error {
|
|
8
|
-
constructor(message: string = 'Not authenticated') {
|
|
9
|
-
super(message)
|
|
10
|
-
this.name = 'NotAuthenticatedError'
|
|
11
|
-
}
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* RemoteCollection - REST API based implementation of the Collection interface
|
|
16
|
-
* All operations make HTTP calls to the Basic API server
|
|
17
|
-
*/
|
|
18
|
-
export class RemoteCollection<T extends { id: string } = Record<string, any> & { id: string }> implements Collection<T> {
|
|
19
|
-
private tableName: string
|
|
20
|
-
private config: RemoteDBConfig
|
|
21
|
-
|
|
22
|
-
constructor(tableName: string, config: RemoteDBConfig) {
|
|
23
|
-
this.tableName = tableName
|
|
24
|
-
this.config = config
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
private log(...args: any[]) {
|
|
28
|
-
if (this.config.debug) {
|
|
29
|
-
console.log('[RemoteDB]', ...args)
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
/**
|
|
34
|
-
* Check if an error is a "not authenticated" error
|
|
35
|
-
*/
|
|
36
|
-
private isNotAuthenticatedError(error: unknown): boolean {
|
|
37
|
-
if (error instanceof Error) {
|
|
38
|
-
const message = error.message.toLowerCase()
|
|
39
|
-
return message.includes('no token') ||
|
|
40
|
-
message.includes('not authenticated') ||
|
|
41
|
-
message.includes('please sign in')
|
|
42
|
-
}
|
|
43
|
-
return false
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
/**
|
|
47
|
-
* Helper to make authenticated API requests
|
|
48
|
-
* Automatically retries once on 401 (token expired) by refreshing the token
|
|
49
|
-
*/
|
|
50
|
-
private async request<R>(
|
|
51
|
-
method: string,
|
|
52
|
-
path: string,
|
|
53
|
-
body?: any,
|
|
54
|
-
isRetry: boolean = false
|
|
55
|
-
): Promise<R> {
|
|
56
|
-
// Try to get token - may throw if not authenticated
|
|
57
|
-
const token = await this.config.getToken()
|
|
58
|
-
const url = `${this.config.serverUrl}${path}`
|
|
59
|
-
|
|
60
|
-
this.log(`${method} ${url}`, body ? JSON.stringify(body) : '')
|
|
61
|
-
|
|
62
|
-
const headers: Record<string, string> = {
|
|
63
|
-
'Authorization': `Bearer ${token}`
|
|
64
|
-
}
|
|
65
|
-
if (body) {
|
|
66
|
-
headers['Content-Type'] = 'application/json'
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
const response = await fetch(url, {
|
|
70
|
-
method,
|
|
71
|
-
headers,
|
|
72
|
-
...(body ? { body: JSON.stringify(body) } : {})
|
|
73
|
-
})
|
|
74
|
-
|
|
75
|
-
const responseData = await response.json().catch(() => ({}))
|
|
76
|
-
|
|
77
|
-
if (!response.ok) {
|
|
78
|
-
// Handle 401 Unauthorized - force refresh then retry once
|
|
79
|
-
if (response.status === 401 && !isRetry) {
|
|
80
|
-
this.log('Got 401, forcing token refresh and retrying...')
|
|
81
|
-
await this.config.getToken({ forceRefresh: true })
|
|
82
|
-
return this.request<R>(method, path, body, true)
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
if (this.config.debug) {
|
|
86
|
-
console.error(`[RemoteDB] Error ${response.status}:`, responseData)
|
|
87
|
-
}
|
|
88
|
-
|
|
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
|
-
}
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
const errorMessage = responseData.message || responseData.error || responseData.detail ||
|
|
111
|
-
(typeof responseData === 'string' ? responseData : `API request failed: ${response.status}`)
|
|
112
|
-
throw new RemoteDBError(errorMessage, response.status, responseData)
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
this.log('Response:', responseData)
|
|
116
|
-
return responseData
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
/**
|
|
120
|
-
* Validate data against schema if available
|
|
121
|
-
*/
|
|
122
|
-
private validateData(data: any, checkRequired: boolean = true): void {
|
|
123
|
-
if (this.config.schema) {
|
|
124
|
-
const result = validateData(this.config.schema, this.tableName, data, checkRequired)
|
|
125
|
-
if (!result.valid) {
|
|
126
|
-
throw new Error(result.message || 'Data validation failed')
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
/**
|
|
132
|
-
* Get the base path for this collection
|
|
133
|
-
*/
|
|
134
|
-
private get basePath(): string {
|
|
135
|
-
return `/account/${this.config.projectId}/db/${this.tableName}`
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
/**
|
|
139
|
-
* Add a new record to the collection
|
|
140
|
-
* The server generates the ID
|
|
141
|
-
* Requires authentication - throws NotAuthenticatedError if not signed in
|
|
142
|
-
*/
|
|
143
|
-
async add(data: Omit<T, 'id'>): Promise<T> {
|
|
144
|
-
this.validateData(data, true)
|
|
145
|
-
|
|
146
|
-
try {
|
|
147
|
-
const result = await this.request<{ data: T }>(
|
|
148
|
-
'POST',
|
|
149
|
-
this.basePath,
|
|
150
|
-
{ value: data }
|
|
151
|
-
)
|
|
152
|
-
// Server returns the created record with the generated ID
|
|
153
|
-
return result.data
|
|
154
|
-
} catch (error) {
|
|
155
|
-
if (this.isNotAuthenticatedError(error)) {
|
|
156
|
-
throw new NotAuthenticatedError('Sign in required to add items')
|
|
157
|
-
}
|
|
158
|
-
throw error
|
|
159
|
-
}
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
/**
|
|
163
|
-
* Put (upsert) a record - requires id
|
|
164
|
-
* Requires authentication - throws NotAuthenticatedError if not signed in
|
|
165
|
-
*/
|
|
166
|
-
async put(data: T): Promise<T> {
|
|
167
|
-
if (!data.id) {
|
|
168
|
-
throw new Error('put() requires an id field')
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
// Extract id from data, send the rest in the body
|
|
172
|
-
const { id, ...rest } = data
|
|
173
|
-
this.validateData(rest, true)
|
|
174
|
-
|
|
175
|
-
try {
|
|
176
|
-
const result = await this.request<{ data: T }>(
|
|
177
|
-
'PUT',
|
|
178
|
-
`${this.basePath}/${id}`,
|
|
179
|
-
{ value: rest }
|
|
180
|
-
)
|
|
181
|
-
return result.data || data
|
|
182
|
-
} catch (error) {
|
|
183
|
-
if (this.isNotAuthenticatedError(error)) {
|
|
184
|
-
throw new NotAuthenticatedError('Sign in required to update items')
|
|
185
|
-
}
|
|
186
|
-
throw error
|
|
187
|
-
}
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
/**
|
|
191
|
-
* Update an existing record by id
|
|
192
|
-
* Requires authentication - throws NotAuthenticatedError if not signed in
|
|
193
|
-
*/
|
|
194
|
-
async update(id: string, data: Partial<Omit<T, 'id'>>): Promise<T | null> {
|
|
195
|
-
if (!id) {
|
|
196
|
-
throw new Error('update() requires an id')
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
this.validateData(data, false)
|
|
200
|
-
|
|
201
|
-
try {
|
|
202
|
-
const result = await this.request<{ data: T }>(
|
|
203
|
-
'PATCH',
|
|
204
|
-
`${this.basePath}/${id}`,
|
|
205
|
-
{ value: data }
|
|
206
|
-
)
|
|
207
|
-
|
|
208
|
-
return result.data || null
|
|
209
|
-
} catch (error) {
|
|
210
|
-
// If record not found, return null instead of throwing
|
|
211
|
-
if (error instanceof RemoteDBError && error.status === 404) {
|
|
212
|
-
return null
|
|
213
|
-
}
|
|
214
|
-
if (this.isNotAuthenticatedError(error)) {
|
|
215
|
-
throw new NotAuthenticatedError('Sign in required to update items')
|
|
216
|
-
}
|
|
217
|
-
throw error
|
|
218
|
-
}
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
/**
|
|
222
|
-
* Delete a record by id
|
|
223
|
-
* Requires authentication - throws NotAuthenticatedError if not signed in
|
|
224
|
-
*/
|
|
225
|
-
async delete(id: string): Promise<boolean> {
|
|
226
|
-
if (!id) {
|
|
227
|
-
throw new Error('delete() requires an id')
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
try {
|
|
231
|
-
await this.request<any>(
|
|
232
|
-
'DELETE',
|
|
233
|
-
`${this.basePath}/${id}`
|
|
234
|
-
)
|
|
235
|
-
return true
|
|
236
|
-
} catch (error) {
|
|
237
|
-
// If record not found, return false instead of throwing
|
|
238
|
-
if (error instanceof RemoteDBError && error.status === 404) {
|
|
239
|
-
return false
|
|
240
|
-
}
|
|
241
|
-
if (this.isNotAuthenticatedError(error)) {
|
|
242
|
-
throw new NotAuthenticatedError('Sign in required to delete items')
|
|
243
|
-
}
|
|
244
|
-
throw error
|
|
245
|
-
}
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
/**
|
|
249
|
-
* Get a single record by id
|
|
250
|
-
* Returns null if not authenticated (graceful degradation for read operations)
|
|
251
|
-
*/
|
|
252
|
-
async get(id: string): Promise<T | null> {
|
|
253
|
-
if (!id) {
|
|
254
|
-
throw new Error('get() requires an id')
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
try {
|
|
258
|
-
// Use the API's id query parameter for efficient single-record fetch
|
|
259
|
-
const result = await this.request<{ data: T[] }>(
|
|
260
|
-
'GET',
|
|
261
|
-
`${this.basePath}?id=${id}`
|
|
262
|
-
)
|
|
263
|
-
return result.data?.[0] || null
|
|
264
|
-
} catch (error) {
|
|
265
|
-
// For get(), return null on any error (not found, not authenticated, etc.)
|
|
266
|
-
if (this.isNotAuthenticatedError(error)) {
|
|
267
|
-
this.log('Not authenticated - returning null for get()')
|
|
268
|
-
}
|
|
269
|
-
return null
|
|
270
|
-
}
|
|
271
|
-
}
|
|
272
|
-
|
|
273
|
-
/**
|
|
274
|
-
* Get all records in the collection
|
|
275
|
-
* Returns empty array if not authenticated (graceful degradation for read operations)
|
|
276
|
-
*/
|
|
277
|
-
async getAll(): Promise<T[]> {
|
|
278
|
-
try {
|
|
279
|
-
const result = await this.request<{ data: T[] }>(
|
|
280
|
-
'GET',
|
|
281
|
-
this.basePath
|
|
282
|
-
)
|
|
283
|
-
return result.data || []
|
|
284
|
-
} catch (error) {
|
|
285
|
-
// If not authenticated, return empty array gracefully
|
|
286
|
-
if (this.isNotAuthenticatedError(error)) {
|
|
287
|
-
this.log('Not authenticated - returning empty array for getAll()')
|
|
288
|
-
return []
|
|
289
|
-
}
|
|
290
|
-
throw error
|
|
291
|
-
}
|
|
292
|
-
}
|
|
293
|
-
|
|
294
|
-
/**
|
|
295
|
-
* Filter records using a predicate function
|
|
296
|
-
* Note: This fetches all records and filters client-side
|
|
297
|
-
* Returns empty array if not authenticated (graceful degradation for read operations)
|
|
298
|
-
*/
|
|
299
|
-
async filter(fn: (item: T) => boolean): Promise<T[]> {
|
|
300
|
-
const all = await this.getAll()
|
|
301
|
-
return all.filter(fn)
|
|
302
|
-
}
|
|
303
|
-
|
|
304
|
-
/**
|
|
305
|
-
* ref is not available for remote collections
|
|
306
|
-
*/
|
|
307
|
-
ref = undefined
|
|
308
|
-
}
|
package/src/core/db/RemoteDB.ts
DELETED
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
import { BasicDB, Collection, RemoteDBConfig } from './types'
|
|
2
|
-
import { RemoteCollection } from './RemoteCollection'
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* RemoteDB - REST API based implementation of BasicDB
|
|
6
|
-
* Creates RemoteCollection instances for each table
|
|
7
|
-
*/
|
|
8
|
-
export class RemoteDB implements BasicDB {
|
|
9
|
-
private config: RemoteDBConfig
|
|
10
|
-
private collections: Map<string, RemoteCollection<any>> = new Map()
|
|
11
|
-
|
|
12
|
-
constructor(config: RemoteDBConfig) {
|
|
13
|
-
this.config = config
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
/**
|
|
17
|
-
* Get a collection by name
|
|
18
|
-
* Collections are cached for reuse
|
|
19
|
-
*/
|
|
20
|
-
collection<T extends { id: string } = Record<string, any> & { id: string }>(
|
|
21
|
-
name: string
|
|
22
|
-
): Collection<T> {
|
|
23
|
-
// Return cached collection if exists
|
|
24
|
-
if (this.collections.has(name)) {
|
|
25
|
-
return this.collections.get(name) as RemoteCollection<T>
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
// Validate table exists in schema if schema is provided
|
|
29
|
-
if (this.config.schema?.tables && !this.config.schema.tables[name]) {
|
|
30
|
-
throw new Error(`Table "${name}" not found in schema`)
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
// Create and cache new collection
|
|
34
|
-
const collection = new RemoteCollection<T>(name, this.config)
|
|
35
|
-
this.collections.set(name, collection)
|
|
36
|
-
|
|
37
|
-
return collection
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
|
package/src/core/db/index.ts
DELETED
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
// Core DB exports
|
|
2
|
-
export type { Collection, BasicDB, DBMode, RemoteDBConfig, GetTokenOptions } from './types'
|
|
3
|
-
export type { AuthError } from './types'
|
|
4
|
-
export { RemoteDBError } from './types'
|
|
5
|
-
export { RemoteDB } from './RemoteDB'
|
|
6
|
-
export { RemoteCollection, NotAuthenticatedError } from './RemoteCollection'
|
|
7
|
-
|
package/src/core/db/types.ts
DELETED
|
@@ -1,140 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Core DB types for Basic SDK
|
|
3
|
-
* These interfaces are implemented by both SyncDB (Dexie-based) and RemoteDB (REST-based)
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* Collection interface for CRUD operations on a table
|
|
8
|
-
* All write operations return the full object (not just the id)
|
|
9
|
-
*/
|
|
10
|
-
export interface Collection<T extends { id: string } = Record<string, any> & { id: string }> {
|
|
11
|
-
/**
|
|
12
|
-
* Add a new record to the collection
|
|
13
|
-
* @param data - The data to add (without id, which will be generated)
|
|
14
|
-
* @returns The created object with its generated id
|
|
15
|
-
*/
|
|
16
|
-
add(data: Omit<T, 'id'>): Promise<T>
|
|
17
|
-
|
|
18
|
-
/**
|
|
19
|
-
* Put (upsert) a record - requires id
|
|
20
|
-
* @param data - The full object including id
|
|
21
|
-
* @returns The upserted object
|
|
22
|
-
*/
|
|
23
|
-
put(data: T): Promise<T>
|
|
24
|
-
|
|
25
|
-
/**
|
|
26
|
-
* Update an existing record by id
|
|
27
|
-
* @param id - The record id to update
|
|
28
|
-
* @param data - Partial data to merge
|
|
29
|
-
* @returns The updated object, or null if not found
|
|
30
|
-
*/
|
|
31
|
-
update(id: string, data: Partial<Omit<T, 'id'>>): Promise<T | null>
|
|
32
|
-
|
|
33
|
-
/**
|
|
34
|
-
* Delete a record by id
|
|
35
|
-
* @param id - The record id to delete
|
|
36
|
-
* @returns true if deleted, false if not found
|
|
37
|
-
*/
|
|
38
|
-
delete(id: string): Promise<boolean>
|
|
39
|
-
|
|
40
|
-
/**
|
|
41
|
-
* Get a single record by id
|
|
42
|
-
* @param id - The record id to fetch
|
|
43
|
-
* @returns The object or null if not found
|
|
44
|
-
*/
|
|
45
|
-
get(id: string): Promise<T | null>
|
|
46
|
-
|
|
47
|
-
/**
|
|
48
|
-
* Get all records in the collection
|
|
49
|
-
* @returns Array of all objects
|
|
50
|
-
*/
|
|
51
|
-
getAll(): Promise<T[]>
|
|
52
|
-
|
|
53
|
-
/**
|
|
54
|
-
* Filter records using a predicate function
|
|
55
|
-
* @param fn - Filter function that returns true for matches
|
|
56
|
-
* @returns Array of matching objects
|
|
57
|
-
*/
|
|
58
|
-
filter(fn: (item: T) => boolean): Promise<T[]>
|
|
59
|
-
|
|
60
|
-
/**
|
|
61
|
-
* Direct access to underlying storage (optional)
|
|
62
|
-
* For sync mode: Dexie table reference
|
|
63
|
-
* For remote mode: undefined
|
|
64
|
-
*/
|
|
65
|
-
ref?: any
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
/**
|
|
69
|
-
* BasicDB interface - factory for creating collections
|
|
70
|
-
*/
|
|
71
|
-
export interface BasicDB {
|
|
72
|
-
/**
|
|
73
|
-
* Get a collection by name
|
|
74
|
-
* @param name - The table/collection name (must match schema)
|
|
75
|
-
* @returns A Collection instance for CRUD operations
|
|
76
|
-
*/
|
|
77
|
-
collection<T extends { id: string } = Record<string, any> & { id: string }>(name: string): Collection<T>
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
/**
|
|
81
|
-
* Database mode - determines which implementation is used
|
|
82
|
-
* - 'sync': Uses Dexie + WebSocket for local-first sync (default)
|
|
83
|
-
* - 'remote': Uses REST API calls directly to server
|
|
84
|
-
*/
|
|
85
|
-
export type DBMode = 'sync' | 'remote'
|
|
86
|
-
|
|
87
|
-
/**
|
|
88
|
-
* Auth error information passed to onAuthError callback
|
|
89
|
-
*/
|
|
90
|
-
export interface AuthError {
|
|
91
|
-
status: number
|
|
92
|
-
message: string
|
|
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
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
/**
|
|
101
|
-
* Custom error class for Remote DB API errors
|
|
102
|
-
* Includes HTTP status code for reliable error handling
|
|
103
|
-
*/
|
|
104
|
-
export class RemoteDBError extends Error {
|
|
105
|
-
status: number
|
|
106
|
-
response?: any
|
|
107
|
-
|
|
108
|
-
constructor(message: string, status: number, response?: any) {
|
|
109
|
-
super(message)
|
|
110
|
-
this.name = 'RemoteDBError'
|
|
111
|
-
this.status = status
|
|
112
|
-
this.response = response
|
|
113
|
-
}
|
|
114
|
-
}
|
|
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
|
-
|
|
124
|
-
/**
|
|
125
|
-
* Configuration for RemoteDB
|
|
126
|
-
*/
|
|
127
|
-
export interface RemoteDBConfig {
|
|
128
|
-
serverUrl: string
|
|
129
|
-
projectId: string
|
|
130
|
-
getToken: (options?: GetTokenOptions) => Promise<string>
|
|
131
|
-
schema?: any
|
|
132
|
-
/** Enable debug logging (default: false) */
|
|
133
|
-
debug?: boolean
|
|
134
|
-
/**
|
|
135
|
-
* Optional callback when authentication fails (401 error after retry)
|
|
136
|
-
* Use this to show login UI or redirect to sign-in
|
|
137
|
-
*/
|
|
138
|
-
onAuthError?: (error: AuthError) => void
|
|
139
|
-
}
|
|
140
|
-
|