@basictech/react 0.2.0-beta.5 → 0.2.0-beta.6
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 +2 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.js +108 -97
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +108 -97
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
- package/src/AuthContext.tsx +27 -25
- package/src/config.ts +19 -3
- package/src/sync/index.ts +10 -10
- package/src/sync/syncProtocol.js +7 -6
package/src/AuthContext.tsx
CHANGED
|
@@ -139,7 +139,7 @@ type ErrorObject = {
|
|
|
139
139
|
message: string;
|
|
140
140
|
}
|
|
141
141
|
|
|
142
|
-
export function BasicProvider({ children, project_id, schema }: { children: React.ReactNode, project_id: string, schema: any }) {
|
|
142
|
+
export function BasicProvider({ children, project_id, schema, debug = false }: { children: React.ReactNode, project_id: string, schema: any, debug?: boolean }) {
|
|
143
143
|
const [isLoaded, setIsLoaded] = useState(false)
|
|
144
144
|
const [isSignedIn, setIsSignedIn] = useState(false)
|
|
145
145
|
const [token, setToken] = useState<Token | null>(null)
|
|
@@ -155,11 +155,11 @@ export function BasicProvider({ children, project_id, schema }: { children: Reac
|
|
|
155
155
|
useEffect(() => {
|
|
156
156
|
function initDb() {
|
|
157
157
|
if (!validator(schema)) {
|
|
158
|
-
|
|
158
|
+
log('Basic Schema is invalid!', validator.errors)
|
|
159
159
|
console.group('Schema Errors')
|
|
160
160
|
let errorMessage = ''
|
|
161
161
|
validator.errors.forEach((error, index) => {
|
|
162
|
-
|
|
162
|
+
log(`${index + 1}:`, error.message, ` - at ${error.instancePath}`)
|
|
163
163
|
errorMessage += `${index + 1}: ${error.message} - at ${error.instancePath}\n`
|
|
164
164
|
})
|
|
165
165
|
console.groupEnd('Schema Errors')
|
|
@@ -175,10 +175,10 @@ export function BasicProvider({ children, project_id, schema }: { children: Reac
|
|
|
175
175
|
if (!syncRef.current) {
|
|
176
176
|
syncRef.current = new BasicSync('basicdb', { schema: schema });
|
|
177
177
|
|
|
178
|
-
//
|
|
178
|
+
// log('db is open', syncRef.current.isOpen())
|
|
179
179
|
// syncRef.current.open()
|
|
180
180
|
// .then(() => {
|
|
181
|
-
//
|
|
181
|
+
// log("is open now:", syncRef.current.isOpen())
|
|
182
182
|
// })
|
|
183
183
|
|
|
184
184
|
syncRef.current.handleStatusChange((status: number, url: string) => {
|
|
@@ -186,7 +186,7 @@ export function BasicProvider({ children, project_id, schema }: { children: Reac
|
|
|
186
186
|
})
|
|
187
187
|
|
|
188
188
|
syncRef.current.syncable.getStatus().then((status) => {
|
|
189
|
-
|
|
189
|
+
log('sync status', getSyncStatus(status))
|
|
190
190
|
})
|
|
191
191
|
}
|
|
192
192
|
|
|
@@ -203,11 +203,11 @@ export function BasicProvider({ children, project_id, schema }: { children: Reac
|
|
|
203
203
|
|
|
204
204
|
const tok = await getToken()
|
|
205
205
|
|
|
206
|
-
|
|
206
|
+
log('connecting to db...', tok.substring(0, 10))
|
|
207
207
|
|
|
208
208
|
syncRef.current.connect({ access_token: tok })
|
|
209
209
|
.catch((e) => {
|
|
210
|
-
|
|
210
|
+
log('error connecting to db', e)
|
|
211
211
|
})
|
|
212
212
|
}
|
|
213
213
|
|
|
@@ -218,7 +218,7 @@ export function BasicProvider({ children, project_id, schema }: { children: Reac
|
|
|
218
218
|
}, [token])
|
|
219
219
|
|
|
220
220
|
const getSignInLink = () => {
|
|
221
|
-
|
|
221
|
+
log('getting sign in link...')
|
|
222
222
|
|
|
223
223
|
const randomState = Math.random().toString(36).substring(7);
|
|
224
224
|
|
|
@@ -233,14 +233,14 @@ export function BasicProvider({ children, project_id, schema }: { children: Reac
|
|
|
233
233
|
}
|
|
234
234
|
|
|
235
235
|
const signin = () => {
|
|
236
|
-
|
|
236
|
+
log('signing in: ', getSignInLink())
|
|
237
237
|
const signInLink = getSignInLink()
|
|
238
238
|
//todo: change to the other thing?
|
|
239
239
|
window.location.href = signInLink;
|
|
240
240
|
}
|
|
241
241
|
|
|
242
242
|
const signout = () => {
|
|
243
|
-
|
|
243
|
+
log('signing out!')
|
|
244
244
|
setUser({})
|
|
245
245
|
setIsSignedIn(false)
|
|
246
246
|
setToken(null)
|
|
@@ -249,10 +249,10 @@ export function BasicProvider({ children, project_id, schema }: { children: Reac
|
|
|
249
249
|
}
|
|
250
250
|
|
|
251
251
|
const getToken = async (): Promise<string> => {
|
|
252
|
-
|
|
252
|
+
log('getting token...')
|
|
253
253
|
|
|
254
254
|
if (!token) {
|
|
255
|
-
|
|
255
|
+
log('no token found')
|
|
256
256
|
throw new Error('no token found')
|
|
257
257
|
}
|
|
258
258
|
|
|
@@ -260,7 +260,7 @@ export function BasicProvider({ children, project_id, schema }: { children: Reac
|
|
|
260
260
|
const isExpired = decoded.exp && decoded.exp < Date.now() / 1000
|
|
261
261
|
|
|
262
262
|
if (isExpired) {
|
|
263
|
-
|
|
263
|
+
log('token is expired - refreshing ...')
|
|
264
264
|
const newToken = await fetchToken(token?.refresh)
|
|
265
265
|
return newToken?.access_token || ''
|
|
266
266
|
}
|
|
@@ -292,19 +292,21 @@ export function BasicProvider({ children, project_id, schema }: { children: Reac
|
|
|
292
292
|
body: JSON.stringify({ code: code })
|
|
293
293
|
})
|
|
294
294
|
.then(response => response.json())
|
|
295
|
-
.catch(error =>
|
|
295
|
+
.catch(error => log('Error:', error))
|
|
296
296
|
|
|
297
297
|
if (token.error) {
|
|
298
|
-
|
|
298
|
+
log('error fetching token', token.error)
|
|
299
299
|
return
|
|
300
300
|
} else {
|
|
301
|
-
//
|
|
301
|
+
// log('token', token)
|
|
302
302
|
setToken(token)
|
|
303
303
|
}
|
|
304
304
|
return token
|
|
305
305
|
}
|
|
306
306
|
|
|
307
307
|
useEffect(() => {
|
|
308
|
+
localStorage.setItem('basic_debug', debug ? 'true' : 'false')
|
|
309
|
+
|
|
308
310
|
try {
|
|
309
311
|
let cookie_token = getCookie('basic_token')
|
|
310
312
|
if (cookie_token !== '') {
|
|
@@ -313,7 +315,7 @@ export function BasicProvider({ children, project_id, schema }: { children: Reac
|
|
|
313
315
|
|
|
314
316
|
if (window.location.search.includes('code')) {
|
|
315
317
|
let code = window.location?.search?.split('code=')[1].split('&')[0]
|
|
316
|
-
//
|
|
318
|
+
// log('code found', code)
|
|
317
319
|
|
|
318
320
|
// todo: check state is valid
|
|
319
321
|
setAuthCode(code) // remove this? dont need to store code?
|
|
@@ -325,7 +327,7 @@ export function BasicProvider({ children, project_id, schema }: { children: Reac
|
|
|
325
327
|
setIsLoaded(true)
|
|
326
328
|
}
|
|
327
329
|
} catch (e) {
|
|
328
|
-
|
|
330
|
+
log('error getting cookie', e)
|
|
329
331
|
}
|
|
330
332
|
}, [])
|
|
331
333
|
|
|
@@ -338,14 +340,14 @@ export function BasicProvider({ children, project_id, schema }: { children: Reac
|
|
|
338
340
|
}
|
|
339
341
|
})
|
|
340
342
|
.then(response => response.json())
|
|
341
|
-
.catch(error =>
|
|
343
|
+
.catch(error => log('Error:', error))
|
|
342
344
|
|
|
343
345
|
if (user.error) {
|
|
344
|
-
|
|
346
|
+
log('error fetching user', user.error)
|
|
345
347
|
// refreshToken()
|
|
346
348
|
return
|
|
347
349
|
} else {
|
|
348
|
-
//
|
|
350
|
+
// log('user', user)
|
|
349
351
|
document.cookie = `basic_token=${JSON.stringify(token)}; Secure; SameSite=Strict`;
|
|
350
352
|
setUser(user)
|
|
351
353
|
setIsSignedIn(true)
|
|
@@ -355,7 +357,7 @@ export function BasicProvider({ children, project_id, schema }: { children: Reac
|
|
|
355
357
|
|
|
356
358
|
async function checkToken() {
|
|
357
359
|
if (!token) {
|
|
358
|
-
|
|
360
|
+
log('error: no user token found')
|
|
359
361
|
return
|
|
360
362
|
}
|
|
361
363
|
|
|
@@ -363,7 +365,7 @@ export function BasicProvider({ children, project_id, schema }: { children: Reac
|
|
|
363
365
|
const isExpired = decoded.exp && decoded.exp < Date.now() / 1000
|
|
364
366
|
|
|
365
367
|
if (isExpired) {
|
|
366
|
-
|
|
368
|
+
log('token is expired - refreshing ...')
|
|
367
369
|
const newToken = await fetchToken(token?.refresh)
|
|
368
370
|
fetchUser(newToken.access_token)
|
|
369
371
|
} else {
|
|
@@ -459,4 +461,4 @@ possible errors:
|
|
|
459
461
|
|
|
460
462
|
export function useBasic() {
|
|
461
463
|
return useContext(BasicContext);
|
|
462
|
-
}
|
|
464
|
+
}
|
package/src/config.ts
CHANGED
|
@@ -4,11 +4,27 @@ export const SERVER_URL = "https://api.basic.tech"
|
|
|
4
4
|
// export const WS_URL = `${SERVER_URL}/ws`
|
|
5
5
|
|
|
6
6
|
export const log = (...args: any[]) => {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
7
|
+
try {
|
|
8
|
+
if (localStorage.getItem('basic_debug') === 'true') {
|
|
9
|
+
console.log('[basic]', ...args)
|
|
10
|
+
}
|
|
11
|
+
} catch (e) {
|
|
12
|
+
// console.log('error logging', e)
|
|
13
|
+
}
|
|
10
14
|
}
|
|
11
15
|
|
|
16
|
+
// export const log = (message: string, ...args: any[]) => {
|
|
17
|
+
// try {
|
|
18
|
+
// if (process.env.NODE_ENV === 'development') {
|
|
19
|
+
// const stack = new Error().stack;
|
|
20
|
+
// const caller = stack?.split('\n')[2]?.trim();
|
|
21
|
+
// console.log(`[basic] ${message}`, ...args);
|
|
22
|
+
// // console.log(`[stack] ${caller}`);
|
|
23
|
+
// }
|
|
24
|
+
// } catch (e) {
|
|
25
|
+
// console.error('Error in logWithStack:', e);
|
|
26
|
+
// }
|
|
27
|
+
// }
|
|
12
28
|
|
|
13
29
|
const basicJsonSchema = {
|
|
14
30
|
"$schema": "http://json-schema.org/draft-07/schema#",
|
package/src/sync/index.ts
CHANGED
|
@@ -9,7 +9,7 @@ import 'dexie-syncable';
|
|
|
9
9
|
import 'dexie-observable';
|
|
10
10
|
|
|
11
11
|
import { syncProtocol } from './syncProtocol'
|
|
12
|
-
import { SERVER_URL } from '../config'
|
|
12
|
+
import { SERVER_URL, log } from '../config'
|
|
13
13
|
syncProtocol()
|
|
14
14
|
|
|
15
15
|
|
|
@@ -67,7 +67,7 @@ export class BasicSync extends Dexie {
|
|
|
67
67
|
|
|
68
68
|
// Proceed with the WebSocket connection
|
|
69
69
|
|
|
70
|
-
|
|
70
|
+
log('Starting connection...')
|
|
71
71
|
return this.syncable.connect("websocket", WS_URL, { authToken: access_token });
|
|
72
72
|
}
|
|
73
73
|
|
|
@@ -75,7 +75,7 @@ export class BasicSync extends Dexie {
|
|
|
75
75
|
try {
|
|
76
76
|
const syncNodes = await this.table('_syncNodes').toArray();
|
|
77
77
|
const localSyncNodes = syncNodes.filter(node => node.type === 'local');
|
|
78
|
-
|
|
78
|
+
log('Local sync nodes:', localSyncNodes);
|
|
79
79
|
|
|
80
80
|
if (localSyncNodes.length > 1) {
|
|
81
81
|
|
|
@@ -84,19 +84,19 @@ export class BasicSync extends Dexie {
|
|
|
84
84
|
// Check if the largest node is already the master
|
|
85
85
|
const largestNode = localSyncNodes.find(node => node.id === largestNodeId);
|
|
86
86
|
if (largestNode && largestNode.isMaster === 1) {
|
|
87
|
-
|
|
87
|
+
log('Largest node is already the master. No changes needed.');
|
|
88
88
|
return; // Exit the function early as no changes are needed
|
|
89
89
|
}
|
|
90
90
|
|
|
91
91
|
|
|
92
|
-
|
|
93
|
-
|
|
92
|
+
log('Largest node id:', largestNodeId);
|
|
93
|
+
log('HEISENBUG: More than one local sync node found.')
|
|
94
94
|
|
|
95
95
|
for (const node of localSyncNodes) {
|
|
96
|
-
|
|
96
|
+
log(`Local sync node keys:`, node.id, node.isMaster);
|
|
97
97
|
await this.table('_syncNodes').update(node.id, { isMaster: node.id === largestNodeId ? 1 : 0 });
|
|
98
98
|
|
|
99
|
-
|
|
99
|
+
log(`HEISENBUG: Setting ${node.id} to ${node.id === largestNodeId ? 'master' : '0'}`);
|
|
100
100
|
}
|
|
101
101
|
|
|
102
102
|
// Add a 1 second delay before returning // i dont think this helps?
|
|
@@ -104,7 +104,7 @@ export class BasicSync extends Dexie {
|
|
|
104
104
|
|
|
105
105
|
}
|
|
106
106
|
|
|
107
|
-
|
|
107
|
+
log('Sync nodes updated');
|
|
108
108
|
} catch (error) {
|
|
109
109
|
console.error('Error updating _syncNodes table:', error);
|
|
110
110
|
}
|
|
@@ -150,7 +150,7 @@ export class BasicSync extends Dexie {
|
|
|
150
150
|
|
|
151
151
|
// --- WRITE ---- //
|
|
152
152
|
add: (data: any) => {
|
|
153
|
-
|
|
153
|
+
log("Adding data to", name, data)
|
|
154
154
|
return this.table(name).add({
|
|
155
155
|
id: uuidv7(),
|
|
156
156
|
...data
|
package/src/sync/syncProtocol.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
"use client"
|
|
2
2
|
import { Dexie } from "dexie";
|
|
3
|
+
import { log } from "../config";
|
|
3
4
|
|
|
4
5
|
export const syncProtocol = function () {
|
|
5
|
-
|
|
6
|
+
log("Initializing syncProtocol");
|
|
6
7
|
// Constants:
|
|
7
8
|
var RECONNECT_DELAY = 5000; // Reconnect delay in case of errors such as network down.
|
|
8
9
|
|
|
@@ -31,7 +32,7 @@ export const syncProtocol = function () {
|
|
|
31
32
|
|
|
32
33
|
// sendChanges() method:
|
|
33
34
|
function sendChanges(changes, baseRevision, partial, onChangesAccepted) {
|
|
34
|
-
|
|
35
|
+
log("sendChanges", changes.length, baseRevision);
|
|
35
36
|
++requestId;
|
|
36
37
|
acceptCallbacks[requestId.toString()] = onChangesAccepted;
|
|
37
38
|
|
|
@@ -65,7 +66,7 @@ export const syncProtocol = function () {
|
|
|
65
66
|
// Initiate this socket connection by sending our clientIdentity. If we dont have a clientIdentity yet,
|
|
66
67
|
// server will call back with a new client identity that we should use in future WebSocket connections.
|
|
67
68
|
|
|
68
|
-
|
|
69
|
+
log("Opening socket - sending clientIdentity", context.clientIdentity);
|
|
69
70
|
ws.send(
|
|
70
71
|
JSON.stringify({
|
|
71
72
|
type: "clientIdentity",
|
|
@@ -79,7 +80,7 @@ export const syncProtocol = function () {
|
|
|
79
80
|
// If network down or other error, tell the framework to reconnect again in some time:
|
|
80
81
|
ws.onerror = function (event) {
|
|
81
82
|
ws.close();
|
|
82
|
-
|
|
83
|
+
log("ws.onerror", event);
|
|
83
84
|
onError(event?.message, RECONNECT_DELAY);
|
|
84
85
|
};
|
|
85
86
|
|
|
@@ -109,7 +110,7 @@ export const syncProtocol = function () {
|
|
|
109
110
|
// partial: true if server has additionalChanges to send. False if these changes were the last known. (applicable if type="changes")
|
|
110
111
|
// }
|
|
111
112
|
var requestFromServer = JSON.parse(event.data);
|
|
112
|
-
|
|
113
|
+
log("requestFromServer", requestFromServer, { acceptCallback, isFirstRound });
|
|
113
114
|
|
|
114
115
|
if (requestFromServer.type == "clientIdentity") {
|
|
115
116
|
context.clientIdentity = requestFromServer.clientIdentity;
|
|
@@ -164,7 +165,7 @@ export const syncProtocol = function () {
|
|
|
164
165
|
ws.close();
|
|
165
166
|
onError(requestFromServer.message, Infinity); // Don't reconnect - an error in application level means we have done something wrong.
|
|
166
167
|
} else {
|
|
167
|
-
|
|
168
|
+
log("unknown message", requestFromServer);
|
|
168
169
|
ws.close();
|
|
169
170
|
onError("unknown message", Infinity);
|
|
170
171
|
}
|