@basictech/react 0.2.0-beta.4 → 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 +12 -0
- package/dist/index.d.mts +2 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.js +109 -127
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +109 -127
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
- package/src/AuthContext.tsx +48 -56
- package/src/config.ts +19 -3
- package/src/sync/index.ts +10 -10
- package/src/sync/syncProtocol.js +7 -6
package/changelog.md
CHANGED
package/dist/index.d.mts
CHANGED
|
@@ -19,10 +19,11 @@ type User = {
|
|
|
19
19
|
};
|
|
20
20
|
fullName?: string;
|
|
21
21
|
};
|
|
22
|
-
declare function BasicProvider({ children, project_id, schema }: {
|
|
22
|
+
declare function BasicProvider({ children, project_id, schema, debug }: {
|
|
23
23
|
children: React.ReactNode;
|
|
24
24
|
project_id: string;
|
|
25
25
|
schema: any;
|
|
26
|
+
debug?: boolean;
|
|
26
27
|
}): react_jsx_runtime.JSX.Element;
|
|
27
28
|
declare function useBasic(): {
|
|
28
29
|
unicorn: string;
|
package/dist/index.d.ts
CHANGED
|
@@ -19,10 +19,11 @@ type User = {
|
|
|
19
19
|
};
|
|
20
20
|
fullName?: string;
|
|
21
21
|
};
|
|
22
|
-
declare function BasicProvider({ children, project_id, schema }: {
|
|
22
|
+
declare function BasicProvider({ children, project_id, schema, debug }: {
|
|
23
23
|
children: React.ReactNode;
|
|
24
24
|
project_id: string;
|
|
25
25
|
schema: any;
|
|
26
|
+
debug?: boolean;
|
|
26
27
|
}): react_jsx_runtime.JSX.Element;
|
|
27
28
|
declare function useBasic(): {
|
|
28
29
|
unicorn: string;
|
package/dist/index.js
CHANGED
|
@@ -48,8 +48,81 @@ var import_dexie_observable = require("dexie-observable");
|
|
|
48
48
|
|
|
49
49
|
// src/sync/syncProtocol.js
|
|
50
50
|
var import_dexie = require("dexie");
|
|
51
|
+
|
|
52
|
+
// src/config.ts
|
|
53
|
+
var import_ajv = __toESM(require("ajv"));
|
|
54
|
+
var SERVER_URL = "https://api.basic.tech";
|
|
55
|
+
var log = (...args) => {
|
|
56
|
+
try {
|
|
57
|
+
if (localStorage.getItem("basic_debug") === "true") {
|
|
58
|
+
console.log("[basic]", ...args);
|
|
59
|
+
}
|
|
60
|
+
} catch (e) {
|
|
61
|
+
}
|
|
62
|
+
};
|
|
63
|
+
var basicJsonSchema = {
|
|
64
|
+
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
65
|
+
"type": "object",
|
|
66
|
+
"properties": {
|
|
67
|
+
"project_id": {
|
|
68
|
+
"type": "string"
|
|
69
|
+
},
|
|
70
|
+
"namespace": {
|
|
71
|
+
"type": "string"
|
|
72
|
+
},
|
|
73
|
+
"version": {
|
|
74
|
+
"type": "integer",
|
|
75
|
+
"minimum": 0
|
|
76
|
+
},
|
|
77
|
+
"tables": {
|
|
78
|
+
"type": "object",
|
|
79
|
+
"patternProperties": {
|
|
80
|
+
"^[a-zA-Z0-9_]+$": {
|
|
81
|
+
"type": "object",
|
|
82
|
+
"properties": {
|
|
83
|
+
"name": {
|
|
84
|
+
"type": "string"
|
|
85
|
+
},
|
|
86
|
+
"type": {
|
|
87
|
+
"type": "string",
|
|
88
|
+
"enum": ["collection"]
|
|
89
|
+
},
|
|
90
|
+
"fields": {
|
|
91
|
+
"type": "object",
|
|
92
|
+
"patternProperties": {
|
|
93
|
+
"^[a-zA-Z0-9_]+$": {
|
|
94
|
+
"type": "object",
|
|
95
|
+
"properties": {
|
|
96
|
+
"type": {
|
|
97
|
+
"type": "string"
|
|
98
|
+
},
|
|
99
|
+
"primary": {
|
|
100
|
+
"type": "boolean"
|
|
101
|
+
},
|
|
102
|
+
"indexed": {
|
|
103
|
+
"type": "boolean"
|
|
104
|
+
}
|
|
105
|
+
},
|
|
106
|
+
"required": ["type"]
|
|
107
|
+
}
|
|
108
|
+
},
|
|
109
|
+
"additionalProperties": true
|
|
110
|
+
}
|
|
111
|
+
},
|
|
112
|
+
"required": ["fields"]
|
|
113
|
+
}
|
|
114
|
+
},
|
|
115
|
+
"additionalProperties": true
|
|
116
|
+
}
|
|
117
|
+
},
|
|
118
|
+
"required": ["project_id", "version", "tables"]
|
|
119
|
+
};
|
|
120
|
+
var ajv = new import_ajv.default();
|
|
121
|
+
var validator = ajv.compile(basicJsonSchema);
|
|
122
|
+
|
|
123
|
+
// src/sync/syncProtocol.js
|
|
51
124
|
var syncProtocol = function() {
|
|
52
|
-
|
|
125
|
+
log("Initializing syncProtocol");
|
|
53
126
|
var RECONNECT_DELAY = 5e3;
|
|
54
127
|
import_dexie.Dexie.Syncable.registerSyncProtocol("websocket", {
|
|
55
128
|
sync: function(context, url, options, baseRevision, syncedRevision, changes, partial, applyRemoteChanges, onChangesAccepted, onSuccess, onError) {
|
|
@@ -57,7 +130,7 @@ var syncProtocol = function() {
|
|
|
57
130
|
var acceptCallbacks = {};
|
|
58
131
|
var ws = new WebSocket(url);
|
|
59
132
|
function sendChanges(changes2, baseRevision2, partial2, onChangesAccepted2) {
|
|
60
|
-
|
|
133
|
+
log("sendChanges", changes2.length, baseRevision2);
|
|
61
134
|
++requestId;
|
|
62
135
|
acceptCallbacks[requestId.toString()] = onChangesAccepted2;
|
|
63
136
|
ws.send(
|
|
@@ -71,7 +144,7 @@ var syncProtocol = function() {
|
|
|
71
144
|
);
|
|
72
145
|
}
|
|
73
146
|
ws.onopen = function(event) {
|
|
74
|
-
|
|
147
|
+
log("Opening socket - sending clientIdentity", context.clientIdentity);
|
|
75
148
|
ws.send(
|
|
76
149
|
JSON.stringify({
|
|
77
150
|
type: "clientIdentity",
|
|
@@ -82,7 +155,7 @@ var syncProtocol = function() {
|
|
|
82
155
|
};
|
|
83
156
|
ws.onerror = function(event) {
|
|
84
157
|
ws.close();
|
|
85
|
-
|
|
158
|
+
log("ws.onerror", event);
|
|
86
159
|
onError(event?.message, RECONNECT_DELAY);
|
|
87
160
|
};
|
|
88
161
|
ws.onclose = function(event) {
|
|
@@ -92,7 +165,7 @@ var syncProtocol = function() {
|
|
|
92
165
|
ws.onmessage = function(event) {
|
|
93
166
|
try {
|
|
94
167
|
var requestFromServer = JSON.parse(event.data);
|
|
95
|
-
|
|
168
|
+
log("requestFromServer", requestFromServer, { acceptCallback, isFirstRound });
|
|
96
169
|
if (requestFromServer.type == "clientIdentity") {
|
|
97
170
|
context.clientIdentity = requestFromServer.clientIdentity;
|
|
98
171
|
context.save();
|
|
@@ -138,7 +211,7 @@ var syncProtocol = function() {
|
|
|
138
211
|
ws.close();
|
|
139
212
|
onError(requestFromServer.message, Infinity);
|
|
140
213
|
} else {
|
|
141
|
-
|
|
214
|
+
log("unknown message", requestFromServer);
|
|
142
215
|
ws.close();
|
|
143
216
|
onError("unknown message", Infinity);
|
|
144
217
|
}
|
|
@@ -151,69 +224,6 @@ var syncProtocol = function() {
|
|
|
151
224
|
});
|
|
152
225
|
};
|
|
153
226
|
|
|
154
|
-
// src/config.ts
|
|
155
|
-
var import_ajv = __toESM(require("ajv"));
|
|
156
|
-
var SERVER_URL = "https://api.basic.tech";
|
|
157
|
-
var basicJsonSchema = {
|
|
158
|
-
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
159
|
-
"type": "object",
|
|
160
|
-
"properties": {
|
|
161
|
-
"project_id": {
|
|
162
|
-
"type": "string"
|
|
163
|
-
},
|
|
164
|
-
"namespace": {
|
|
165
|
-
"type": "string"
|
|
166
|
-
},
|
|
167
|
-
"version": {
|
|
168
|
-
"type": "integer",
|
|
169
|
-
"minimum": 0
|
|
170
|
-
},
|
|
171
|
-
"tables": {
|
|
172
|
-
"type": "object",
|
|
173
|
-
"patternProperties": {
|
|
174
|
-
"^[a-zA-Z0-9_]+$": {
|
|
175
|
-
"type": "object",
|
|
176
|
-
"properties": {
|
|
177
|
-
"name": {
|
|
178
|
-
"type": "string"
|
|
179
|
-
},
|
|
180
|
-
"type": {
|
|
181
|
-
"type": "string",
|
|
182
|
-
"enum": ["collection"]
|
|
183
|
-
},
|
|
184
|
-
"fields": {
|
|
185
|
-
"type": "object",
|
|
186
|
-
"patternProperties": {
|
|
187
|
-
"^[a-zA-Z0-9_]+$": {
|
|
188
|
-
"type": "object",
|
|
189
|
-
"properties": {
|
|
190
|
-
"type": {
|
|
191
|
-
"type": "string"
|
|
192
|
-
},
|
|
193
|
-
"primary": {
|
|
194
|
-
"type": "boolean"
|
|
195
|
-
},
|
|
196
|
-
"indexed": {
|
|
197
|
-
"type": "boolean"
|
|
198
|
-
}
|
|
199
|
-
},
|
|
200
|
-
"required": ["type"]
|
|
201
|
-
}
|
|
202
|
-
},
|
|
203
|
-
"additionalProperties": true
|
|
204
|
-
}
|
|
205
|
-
},
|
|
206
|
-
"required": ["fields"]
|
|
207
|
-
}
|
|
208
|
-
},
|
|
209
|
-
"additionalProperties": true
|
|
210
|
-
}
|
|
211
|
-
},
|
|
212
|
-
"required": ["project_id", "version", "tables"]
|
|
213
|
-
};
|
|
214
|
-
var ajv = new import_ajv.default();
|
|
215
|
-
var validator = ajv.compile(basicJsonSchema);
|
|
216
|
-
|
|
217
227
|
// src/sync/index.ts
|
|
218
228
|
syncProtocol();
|
|
219
229
|
var BasicSync = class extends import_dexie2.Dexie {
|
|
@@ -228,31 +238,31 @@ var BasicSync = class extends import_dexie2.Dexie {
|
|
|
228
238
|
async connect({ access_token }) {
|
|
229
239
|
const WS_URL = `${SERVER_URL}/ws`;
|
|
230
240
|
await this.updateSyncNodes();
|
|
231
|
-
|
|
241
|
+
log("Starting connection...");
|
|
232
242
|
return this.syncable.connect("websocket", WS_URL, { authToken: access_token });
|
|
233
243
|
}
|
|
234
244
|
async updateSyncNodes() {
|
|
235
245
|
try {
|
|
236
246
|
const syncNodes = await this.table("_syncNodes").toArray();
|
|
237
247
|
const localSyncNodes = syncNodes.filter((node) => node.type === "local");
|
|
238
|
-
|
|
248
|
+
log("Local sync nodes:", localSyncNodes);
|
|
239
249
|
if (localSyncNodes.length > 1) {
|
|
240
250
|
const largestNodeId = Math.max(...localSyncNodes.map((node) => node.id));
|
|
241
251
|
const largestNode = localSyncNodes.find((node) => node.id === largestNodeId);
|
|
242
252
|
if (largestNode && largestNode.isMaster === 1) {
|
|
243
|
-
|
|
253
|
+
log("Largest node is already the master. No changes needed.");
|
|
244
254
|
return;
|
|
245
255
|
}
|
|
246
|
-
|
|
247
|
-
|
|
256
|
+
log("Largest node id:", largestNodeId);
|
|
257
|
+
log("HEISENBUG: More than one local sync node found.");
|
|
248
258
|
for (const node of localSyncNodes) {
|
|
249
|
-
|
|
259
|
+
log(`Local sync node keys:`, node.id, node.isMaster);
|
|
250
260
|
await this.table("_syncNodes").update(node.id, { isMaster: node.id === largestNodeId ? 1 : 0 });
|
|
251
|
-
|
|
261
|
+
log(`HEISENBUG: Setting ${node.id} to ${node.id === largestNodeId ? "master" : "0"}`);
|
|
252
262
|
}
|
|
253
263
|
await new Promise((resolve) => setTimeout(resolve, 2e3));
|
|
254
264
|
}
|
|
255
|
-
|
|
265
|
+
log("Sync nodes updated");
|
|
256
266
|
} catch (error) {
|
|
257
267
|
console.error("Error updating _syncNodes table:", error);
|
|
258
268
|
}
|
|
@@ -281,7 +291,7 @@ var BasicSync = class extends import_dexie2.Dexie {
|
|
|
281
291
|
ref: this.table(name),
|
|
282
292
|
// --- WRITE ---- //
|
|
283
293
|
add: (data) => {
|
|
284
|
-
|
|
294
|
+
log("Adding data to", name, data);
|
|
285
295
|
return this.table(name).add({
|
|
286
296
|
id: (0, import_uuid.v7)(),
|
|
287
297
|
...data
|
|
@@ -362,34 +372,6 @@ async function deleteRecord({ projectId, accountId, tableName, id, token }) {
|
|
|
362
372
|
|
|
363
373
|
// src/AuthContext.tsx
|
|
364
374
|
var import_jsx_runtime = require("react/jsx-runtime");
|
|
365
|
-
var example = {
|
|
366
|
-
project_id: "123",
|
|
367
|
-
version: 0,
|
|
368
|
-
tables: {
|
|
369
|
-
example: {
|
|
370
|
-
name: "example",
|
|
371
|
-
type: "collection",
|
|
372
|
-
fields: {
|
|
373
|
-
id: {
|
|
374
|
-
type: "uuid",
|
|
375
|
-
primary: true
|
|
376
|
-
},
|
|
377
|
-
value: {
|
|
378
|
-
type: "string",
|
|
379
|
-
indexed: true
|
|
380
|
-
}
|
|
381
|
-
}
|
|
382
|
-
},
|
|
383
|
-
example2: {
|
|
384
|
-
name: "example2",
|
|
385
|
-
type: "collection",
|
|
386
|
-
fields: {
|
|
387
|
-
id: { type: "string", primary: true },
|
|
388
|
-
id: { type: "string", primary: true }
|
|
389
|
-
}
|
|
390
|
-
}
|
|
391
|
-
}
|
|
392
|
-
};
|
|
393
375
|
var BasicContext = (0, import_react.createContext)({
|
|
394
376
|
unicorn: "\u{1F984}",
|
|
395
377
|
isLoaded: false,
|
|
@@ -423,7 +405,7 @@ function getSyncStatus(statusCode) {
|
|
|
423
405
|
return "UNKNOWN";
|
|
424
406
|
}
|
|
425
407
|
}
|
|
426
|
-
function BasicProvider({ children, project_id, schema }) {
|
|
408
|
+
function BasicProvider({ children, project_id, schema, debug = false }) {
|
|
427
409
|
const [isLoaded, setIsLoaded] = (0, import_react.useState)(false);
|
|
428
410
|
const [isSignedIn, setIsSignedIn] = (0, import_react.useState)(false);
|
|
429
411
|
const [token, setToken] = (0, import_react.useState)(null);
|
|
@@ -434,13 +416,12 @@ function BasicProvider({ children, project_id, schema }) {
|
|
|
434
416
|
const [error, setError] = (0, import_react.useState)(null);
|
|
435
417
|
(0, import_react.useEffect)(() => {
|
|
436
418
|
function initDb() {
|
|
437
|
-
if (!validator(
|
|
438
|
-
|
|
419
|
+
if (!validator(schema)) {
|
|
420
|
+
log("Basic Schema is invalid!", validator.errors);
|
|
439
421
|
console.group("Schema Errors");
|
|
440
422
|
let errorMessage = "";
|
|
441
423
|
validator.errors.forEach((error2, index) => {
|
|
442
|
-
|
|
443
|
-
console.log(`${index + 1}:`, error2.message, ` - at ${error2.instancePath}`);
|
|
424
|
+
log(`${index + 1}:`, error2.message, ` - at ${error2.instancePath}`);
|
|
444
425
|
errorMessage += `${index + 1}: ${error2.message} - at ${error2.instancePath}
|
|
445
426
|
`;
|
|
446
427
|
});
|
|
@@ -458,7 +439,7 @@ function BasicProvider({ children, project_id, schema }) {
|
|
|
458
439
|
setDbStatus(getSyncStatus(status));
|
|
459
440
|
});
|
|
460
441
|
syncRef.current.syncable.getStatus().then((status) => {
|
|
461
|
-
|
|
442
|
+
log("sync status", getSyncStatus(status));
|
|
462
443
|
});
|
|
463
444
|
}
|
|
464
445
|
}
|
|
@@ -466,9 +447,9 @@ function BasicProvider({ children, project_id, schema }) {
|
|
|
466
447
|
}, []);
|
|
467
448
|
const connectToDb = async () => {
|
|
468
449
|
const tok = await getToken();
|
|
469
|
-
|
|
450
|
+
log("connecting to db...", tok.substring(0, 10));
|
|
470
451
|
syncRef.current.connect({ access_token: tok }).catch((e) => {
|
|
471
|
-
|
|
452
|
+
log("error connecting to db", e);
|
|
472
453
|
});
|
|
473
454
|
};
|
|
474
455
|
(0, import_react.useEffect)(() => {
|
|
@@ -477,7 +458,7 @@ function BasicProvider({ children, project_id, schema }) {
|
|
|
477
458
|
}
|
|
478
459
|
}, [token]);
|
|
479
460
|
const getSignInLink = () => {
|
|
480
|
-
|
|
461
|
+
log("getting sign in link...");
|
|
481
462
|
const randomState = Math.random().toString(36).substring(7);
|
|
482
463
|
let baseUrl2 = "https://api.basic.tech/auth/authorize";
|
|
483
464
|
baseUrl2 += `?client_id=${project_id}`;
|
|
@@ -488,12 +469,12 @@ function BasicProvider({ children, project_id, schema }) {
|
|
|
488
469
|
return baseUrl2;
|
|
489
470
|
};
|
|
490
471
|
const signin = () => {
|
|
491
|
-
|
|
472
|
+
log("signing in: ", getSignInLink());
|
|
492
473
|
const signInLink = getSignInLink();
|
|
493
474
|
window.location.href = signInLink;
|
|
494
475
|
};
|
|
495
476
|
const signout = () => {
|
|
496
|
-
|
|
477
|
+
log("signing out!");
|
|
497
478
|
setUser({});
|
|
498
479
|
setIsSignedIn(false);
|
|
499
480
|
setToken(null);
|
|
@@ -501,15 +482,15 @@ function BasicProvider({ children, project_id, schema }) {
|
|
|
501
482
|
document.cookie = `basic_token=; Secure; SameSite=Strict`;
|
|
502
483
|
};
|
|
503
484
|
const getToken = async () => {
|
|
504
|
-
|
|
485
|
+
log("getting token...");
|
|
505
486
|
if (!token) {
|
|
506
|
-
|
|
487
|
+
log("no token found");
|
|
507
488
|
throw new Error("no token found");
|
|
508
489
|
}
|
|
509
490
|
const decoded = (0, import_jwt_decode.jwtDecode)(token?.access_token);
|
|
510
491
|
const isExpired = decoded.exp && decoded.exp < Date.now() / 1e3;
|
|
511
492
|
if (isExpired) {
|
|
512
|
-
|
|
493
|
+
log("token is expired - refreshing ...");
|
|
513
494
|
const newToken = await fetchToken(token?.refresh);
|
|
514
495
|
return newToken?.access_token || "";
|
|
515
496
|
}
|
|
@@ -536,9 +517,9 @@ function BasicProvider({ children, project_id, schema }) {
|
|
|
536
517
|
"Content-Type": "application/json"
|
|
537
518
|
},
|
|
538
519
|
body: JSON.stringify({ code })
|
|
539
|
-
}).then((response) => response.json()).catch((error2) =>
|
|
520
|
+
}).then((response) => response.json()).catch((error2) => log("Error:", error2));
|
|
540
521
|
if (token2.error) {
|
|
541
|
-
|
|
522
|
+
log("error fetching token", token2.error);
|
|
542
523
|
return;
|
|
543
524
|
} else {
|
|
544
525
|
setToken(token2);
|
|
@@ -546,6 +527,7 @@ function BasicProvider({ children, project_id, schema }) {
|
|
|
546
527
|
return token2;
|
|
547
528
|
};
|
|
548
529
|
(0, import_react.useEffect)(() => {
|
|
530
|
+
localStorage.setItem("basic_debug", debug ? "true" : "false");
|
|
549
531
|
try {
|
|
550
532
|
let cookie_token = getCookie("basic_token");
|
|
551
533
|
if (cookie_token !== "") {
|
|
@@ -560,7 +542,7 @@ function BasicProvider({ children, project_id, schema }) {
|
|
|
560
542
|
setIsLoaded(true);
|
|
561
543
|
}
|
|
562
544
|
} catch (e) {
|
|
563
|
-
|
|
545
|
+
log("error getting cookie", e);
|
|
564
546
|
}
|
|
565
547
|
}, []);
|
|
566
548
|
(0, import_react.useEffect)(() => {
|
|
@@ -570,9 +552,9 @@ function BasicProvider({ children, project_id, schema }) {
|
|
|
570
552
|
headers: {
|
|
571
553
|
"Authorization": `Bearer ${acc_token}`
|
|
572
554
|
}
|
|
573
|
-
}).then((response) => response.json()).catch((error2) =>
|
|
555
|
+
}).then((response) => response.json()).catch((error2) => log("Error:", error2));
|
|
574
556
|
if (user2.error) {
|
|
575
|
-
|
|
557
|
+
log("error fetching user", user2.error);
|
|
576
558
|
return;
|
|
577
559
|
} else {
|
|
578
560
|
document.cookie = `basic_token=${JSON.stringify(token)}; Secure; SameSite=Strict`;
|
|
@@ -583,13 +565,13 @@ function BasicProvider({ children, project_id, schema }) {
|
|
|
583
565
|
}
|
|
584
566
|
async function checkToken() {
|
|
585
567
|
if (!token) {
|
|
586
|
-
|
|
568
|
+
log("error: no user token found");
|
|
587
569
|
return;
|
|
588
570
|
}
|
|
589
571
|
const decoded = (0, import_jwt_decode.jwtDecode)(token?.access_token);
|
|
590
572
|
const isExpired = decoded.exp && decoded.exp < Date.now() / 1e3;
|
|
591
573
|
if (isExpired) {
|
|
592
|
-
|
|
574
|
+
log("token is expired - refreshing ...");
|
|
593
575
|
const newToken = await fetchToken(token?.refresh);
|
|
594
576
|
fetchUser(newToken.access_token);
|
|
595
577
|
} else {
|