@basictech/react 0.2.0-beta.7 → 0.2.0-beta.9

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/dist/index.js CHANGED
@@ -31,6 +31,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
31
31
  var src_exports = {};
32
32
  __export(src_exports, {
33
33
  BasicProvider: () => BasicProvider,
34
+ sc: () => sc,
34
35
  useBasic: () => useBasic,
35
36
  useQuery: () => useQuery
36
37
  });
@@ -50,7 +51,6 @@ var import_dexie_observable = require("dexie-observable");
50
51
  var import_dexie = require("dexie");
51
52
 
52
53
  // src/config.ts
53
- var import_ajv = __toESM(require("ajv"));
54
54
  var SERVER_URL = "https://api.basic.tech";
55
55
  var log = (...args) => {
56
56
  try {
@@ -60,65 +60,6 @@ var log = (...args) => {
60
60
  } catch (e) {
61
61
  }
62
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
63
 
123
64
  // src/sync/syncProtocol.js
124
65
  var syncProtocol = function() {
@@ -176,7 +117,6 @@ var syncProtocol = function() {
176
117
  syncedRevision
177
118
  })
178
119
  );
179
- } else if (requestFromServer.type == "error") {
180
120
  } else if (requestFromServer.type == "changes") {
181
121
  applyRemoteChanges(
182
122
  requestFromServer.changes,
@@ -224,6 +164,117 @@ var syncProtocol = function() {
224
164
  });
225
165
  };
226
166
 
167
+ // src/schema.ts
168
+ var import_ajv = __toESM(require("ajv"));
169
+ var basicJsonSchema = {
170
+ "$schema": "http://json-schema.org/draft-07/schema#",
171
+ "type": "object",
172
+ "properties": {
173
+ "project_id": {
174
+ "type": "string"
175
+ },
176
+ "namespace": {
177
+ "type": "string"
178
+ },
179
+ "version": {
180
+ "type": "integer",
181
+ "minimum": 0
182
+ },
183
+ "tables": {
184
+ "type": "object",
185
+ "patternProperties": {
186
+ "^[a-zA-Z0-9_]+$": {
187
+ "type": "object",
188
+ "properties": {
189
+ "name": {
190
+ "type": "string"
191
+ },
192
+ "type": {
193
+ "type": "string",
194
+ "enum": ["collection"]
195
+ },
196
+ "fields": {
197
+ "type": "object",
198
+ "patternProperties": {
199
+ "^[a-zA-Z0-9_]+$": {
200
+ "type": "object",
201
+ "properties": {
202
+ "type": {
203
+ "type": "string",
204
+ "enum": ["string", "boolean", "number", "json"]
205
+ },
206
+ "indexed": {
207
+ "type": "boolean"
208
+ },
209
+ "required": {
210
+ "type": "boolean"
211
+ }
212
+ },
213
+ "required": ["type"]
214
+ }
215
+ },
216
+ "additionalProperties": true
217
+ }
218
+ },
219
+ "required": ["fields"]
220
+ }
221
+ },
222
+ "additionalProperties": true
223
+ }
224
+ },
225
+ "required": ["project_id", "version", "tables"]
226
+ };
227
+ var ajv = new import_ajv.default();
228
+ var validator = ajv.compile(basicJsonSchema);
229
+ function generateEmptySchema() {
230
+ }
231
+ function validateSchema(schema) {
232
+ const v = validator(schema);
233
+ return {
234
+ valid: v,
235
+ errors: validator.errors || []
236
+ };
237
+ }
238
+ function validateData(schema, table, data, checkRequired = true) {
239
+ const valid = validateSchema(schema);
240
+ if (!valid.valid) {
241
+ return { valid: false, errors: valid.errors, message: "Schema is invalid" };
242
+ }
243
+ const tableSchema = schema.tables[table];
244
+ if (!tableSchema) {
245
+ return { valid: false, errors: [{ message: `Table ${table} not found in schema` }], message: "Table not found" };
246
+ }
247
+ for (const [fieldName, fieldValue] of Object.entries(data)) {
248
+ const fieldSchema = tableSchema.fields[fieldName];
249
+ if (!fieldSchema) {
250
+ return {
251
+ valid: false,
252
+ errors: [{ message: `Field ${fieldName} not found in schema` }],
253
+ message: "Invalid field"
254
+ };
255
+ }
256
+ const schemaType = fieldSchema.type;
257
+ const valueType = typeof fieldValue;
258
+ if (schemaType === "string" && valueType !== "string" || schemaType === "number" && valueType !== "number" || schemaType === "boolean" && valueType !== "boolean" || schemaType === "json" && valueType !== "object") {
259
+ return {
260
+ valid: false,
261
+ errors: [{
262
+ message: `Field ${fieldName} should be type ${schemaType}, got ${valueType}`
263
+ }],
264
+ message: "invalid type"
265
+ };
266
+ }
267
+ }
268
+ if (checkRequired) {
269
+ for (const [fieldName, fieldSchema] of Object.entries(tableSchema.fields)) {
270
+ if (fieldSchema.required && !data[fieldName]) {
271
+ return { valid: false, errors: [{ message: `Field ${fieldName} is required` }], message: "Required field missing" };
272
+ }
273
+ }
274
+ }
275
+ return { valid: true, errors: [] };
276
+ }
277
+
227
278
  // src/sync/index.ts
228
279
  syncProtocol();
229
280
  var BasicSync = class extends import_dexie2.Dexie {
@@ -291,19 +342,33 @@ var BasicSync = class extends import_dexie2.Dexie {
291
342
  ref: this.table(name),
292
343
  // --- WRITE ---- //
293
344
  add: (data) => {
294
- log("Adding data to", name, data);
345
+ const valid = validateData(this.basic_schema, name, data);
346
+ if (!valid.valid) {
347
+ log("Invalid data", valid);
348
+ return Promise.reject({ ...valid });
349
+ }
295
350
  return this.table(name).add({
296
351
  id: (0, import_uuid.v7)(),
297
352
  ...data
298
353
  });
299
354
  },
300
355
  put: (data) => {
356
+ const valid = validateData(this.basic_schema, name, data);
357
+ if (!valid.valid) {
358
+ log("Invalid data", valid);
359
+ return Promise.reject({ ...valid });
360
+ }
301
361
  return this.table(name).put({
302
362
  id: (0, import_uuid.v7)(),
303
363
  ...data
304
364
  });
305
365
  },
306
366
  update: (id, data) => {
367
+ const valid = validateData(this.basic_schema, name, data, false);
368
+ if (!valid.valid) {
369
+ log("Invalid data", valid);
370
+ return Promise.reject({ ...valid });
371
+ }
307
372
  return this.table(name).update(id, data);
308
373
  },
309
374
  delete: (id) => {
@@ -374,7 +439,7 @@ async function deleteRecord({ projectId, accountId, tableName, id, token }) {
374
439
  var import_jsx_runtime = require("react/jsx-runtime");
375
440
  var BasicContext = (0, import_react.createContext)({
376
441
  unicorn: "\u{1F984}",
377
- isLoaded: false,
442
+ isAuthReady: false,
378
443
  isSignedIn: false,
379
444
  user: null,
380
445
  signout: () => {
@@ -385,7 +450,7 @@ var BasicContext = (0, import_react.createContext)({
385
450
  }),
386
451
  getSignInLink: () => "",
387
452
  db: {},
388
- dbStatus: "OFFLINE" /* OFFLINE */
453
+ dbStatus: "LOADING" /* LOADING */
389
454
  });
390
455
  function getSyncStatus(statusCode) {
391
456
  switch (statusCode) {
@@ -406,21 +471,21 @@ function getSyncStatus(statusCode) {
406
471
  }
407
472
  }
408
473
  function BasicProvider({ children, project_id, schema, debug = false }) {
409
- const [isLoaded, setIsLoaded] = (0, import_react.useState)(false);
474
+ const [isAuthReady, setIsAuthReady] = (0, import_react.useState)(false);
410
475
  const [isSignedIn, setIsSignedIn] = (0, import_react.useState)(false);
411
476
  const [token, setToken] = (0, import_react.useState)(null);
412
- const [authCode, setAuthCode] = (0, import_react.useState)(null);
413
477
  const [user, setUser] = (0, import_react.useState)({});
414
478
  const [dbStatus, setDbStatus] = (0, import_react.useState)("LOADING" /* LOADING */);
415
- const syncRef = (0, import_react.useRef)(null);
416
479
  const [error, setError] = (0, import_react.useState)(null);
480
+ const syncRef = (0, import_react.useRef)(null);
417
481
  (0, import_react.useEffect)(() => {
418
482
  function initDb() {
419
- if (!validator(schema)) {
420
- log("Basic Schema is invalid!", validator.errors);
483
+ const valid = validateSchema(schema);
484
+ if (!valid.valid) {
485
+ log("Basic Schema is invalid!", valid.errors);
421
486
  console.group("Schema Errors");
422
487
  let errorMessage = "";
423
- validator.errors.forEach((error2, index) => {
488
+ valid.errors.forEach((error2, index) => {
424
489
  log(`${index + 1}:`, error2.message, ` - at ${error2.instancePath}`);
425
490
  errorMessage += `${index + 1}: ${error2.message} - at ${error2.instancePath}
426
491
  `;
@@ -434,20 +499,30 @@ function BasicProvider({ children, project_id, schema, debug = false }) {
434
499
  return null;
435
500
  }
436
501
  if (!syncRef.current) {
502
+ log("Initializing BasicDB");
437
503
  syncRef.current = new BasicSync("basicdb", { schema });
438
- syncRef.current.handleStatusChange((status, url) => {
439
- setDbStatus(getSyncStatus(status));
440
- });
441
- syncRef.current.syncable.getStatus().then((status) => {
442
- log("sync status", getSyncStatus(status));
443
- });
444
504
  }
445
505
  }
446
506
  initDb();
447
507
  }, []);
508
+ (0, import_react.useEffect)(() => {
509
+ if (!syncRef.current) {
510
+ return;
511
+ }
512
+ syncRef.current.syncable.on("statusChanged", (status, url) => {
513
+ setDbStatus(getSyncStatus(status));
514
+ });
515
+ syncRef.current.syncable.getStatus().then((status) => {
516
+ setDbStatus(getSyncStatus(status));
517
+ });
518
+ }, [syncRef.current]);
448
519
  const connectToDb = async () => {
449
520
  const tok = await getToken();
450
- log("connecting to db...", tok.substring(0, 10));
521
+ if (!tok) {
522
+ log("no token found");
523
+ return;
524
+ }
525
+ log("connecting to db...");
451
526
  syncRef.current.connect({ access_token: tok }).catch((e) => {
452
527
  log("error connecting to db", e);
453
528
  });
@@ -459,13 +534,14 @@ function BasicProvider({ children, project_id, schema, debug = false }) {
459
534
  }, [token]);
460
535
  const getSignInLink = () => {
461
536
  log("getting sign in link...");
462
- const randomState = Math.random().toString(36).substring(7);
537
+ const randomState = Math.random().toString(36).substring(6);
538
+ localStorage.setItem("basic_auth_state", randomState);
463
539
  let baseUrl2 = "https://api.basic.tech/auth/authorize";
464
540
  baseUrl2 += `?client_id=${project_id}`;
465
541
  baseUrl2 += `&redirect_uri=${encodeURIComponent(window.location.href)}`;
466
542
  baseUrl2 += `&response_type=code`;
467
543
  baseUrl2 += `&scope=openid`;
468
- baseUrl2 += `&state=1234zyx`;
544
+ baseUrl2 += `&state=${randomState}`;
469
545
  return baseUrl2;
470
546
  };
471
547
  const signin = () => {
@@ -478,8 +554,8 @@ function BasicProvider({ children, project_id, schema, debug = false }) {
478
554
  setUser({});
479
555
  setIsSignedIn(false);
480
556
  setToken(null);
481
- setAuthCode(null);
482
557
  document.cookie = `basic_token=; Secure; SameSite=Strict`;
558
+ localStorage.removeItem("basic_auth_state");
483
559
  };
484
560
  const getToken = async () => {
485
561
  log("getting token...");
@@ -529,17 +605,25 @@ function BasicProvider({ children, project_id, schema, debug = false }) {
529
605
  (0, import_react.useEffect)(() => {
530
606
  localStorage.setItem("basic_debug", debug ? "true" : "false");
531
607
  try {
532
- let cookie_token = getCookie("basic_token");
533
- if (cookie_token !== "") {
534
- setToken(JSON.parse(cookie_token));
535
- }
536
608
  if (window.location.search.includes("code")) {
537
609
  let code = window.location?.search?.split("code=")[1].split("&")[0];
538
- setAuthCode(code);
610
+ const state = localStorage.getItem("basic_auth_state");
611
+ if (!state || state !== window.location.search.split("state=")[1].split("&")[0]) {
612
+ log("error: auth state does not match");
613
+ setIsAuthReady(true);
614
+ localStorage.removeItem("basic_auth_state");
615
+ window.history.pushState({}, document.title, "/");
616
+ return;
617
+ }
618
+ localStorage.removeItem("basic_auth_state");
539
619
  fetchToken(code);
540
- window.history.pushState({}, document.title, "/");
541
620
  } else {
542
- setIsLoaded(true);
621
+ let cookie_token = getCookie("basic_token");
622
+ if (cookie_token !== "") {
623
+ setToken(JSON.parse(cookie_token));
624
+ } else {
625
+ setIsAuthReady(true);
626
+ }
543
627
  }
544
628
  } catch (e) {
545
629
  log("error getting cookie", e);
@@ -547,6 +631,7 @@ function BasicProvider({ children, project_id, schema, debug = false }) {
547
631
  }, []);
548
632
  (0, import_react.useEffect)(() => {
549
633
  async function fetchUser(acc_token) {
634
+ console.info("fetching user");
550
635
  const user2 = await fetch("https://api.basic.tech/auth/userInfo", {
551
636
  method: "GET",
552
637
  headers: {
@@ -558,14 +643,18 @@ function BasicProvider({ children, project_id, schema, debug = false }) {
558
643
  return;
559
644
  } else {
560
645
  document.cookie = `basic_token=${JSON.stringify(token)}; Secure; SameSite=Strict`;
646
+ if (window.location.search.includes("code")) {
647
+ window.history.pushState({}, document.title, "/");
648
+ }
561
649
  setUser(user2);
562
650
  setIsSignedIn(true);
563
- setIsLoaded(true);
651
+ setIsAuthReady(true);
564
652
  }
565
653
  }
566
654
  async function checkToken() {
567
655
  if (!token) {
568
656
  log("error: no user token found");
657
+ setIsAuthReady(true);
569
658
  return;
570
659
  }
571
660
  const decoded = (0, import_jwt_decode.jwtDecode)(token?.access_token);
@@ -580,7 +669,6 @@ function BasicProvider({ children, project_id, schema, debug = false }) {
580
669
  }
581
670
  if (token) {
582
671
  checkToken();
583
- setIsLoaded(true);
584
672
  }
585
673
  }, [token]);
586
674
  const db_ = (tableName) => {
@@ -614,7 +702,7 @@ function BasicProvider({ children, project_id, schema, debug = false }) {
614
702
  };
615
703
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(BasicContext.Provider, { value: {
616
704
  unicorn: "\u{1F984}",
617
- isLoaded,
705
+ isAuthReady,
618
706
  isSignedIn,
619
707
  user,
620
708
  signout,
@@ -665,9 +753,15 @@ function useQuery(queryable) {
665
753
  return queryable;
666
754
  }, [queryable], []);
667
755
  }
756
+ var sc = {
757
+ validateSchema,
758
+ validateData,
759
+ generateEmptySchema
760
+ };
668
761
  // Annotate the CommonJS export names for ESM import in node:
669
762
  0 && (module.exports = {
670
763
  BasicProvider,
764
+ sc,
671
765
  useBasic,
672
766
  useQuery
673
767
  });