@basictech/react 0.7.0-beta.0 → 0.7.0-beta.1

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
@@ -1,7 +1,9 @@
1
1
  "use strict";
2
+ var __create = Object.create;
2
3
  var __defProp = Object.defineProperty;
3
4
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
5
  var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
5
7
  var __hasOwnProp = Object.prototype.hasOwnProperty;
6
8
  var __export = (target, all) => {
7
9
  for (var name in all)
@@ -15,6 +17,14 @@ var __copyProps = (to, from, except, desc) => {
15
17
  }
16
18
  return to;
17
19
  };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
18
28
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
29
 
20
30
  // src/index.ts
@@ -155,8 +165,116 @@ var syncProtocol = function() {
155
165
  });
156
166
  };
157
167
 
168
+ // src/schema.ts
169
+ var import_ajv = __toESM(require("ajv"));
170
+ var basicJsonSchema = {
171
+ "$schema": "http://json-schema.org/draft-07/schema#",
172
+ "type": "object",
173
+ "properties": {
174
+ "project_id": {
175
+ "type": "string"
176
+ },
177
+ "namespace": {
178
+ "type": "string"
179
+ },
180
+ "version": {
181
+ "type": "integer",
182
+ "minimum": 0
183
+ },
184
+ "tables": {
185
+ "type": "object",
186
+ "patternProperties": {
187
+ "^[a-zA-Z0-9_]+$": {
188
+ "type": "object",
189
+ "properties": {
190
+ "name": {
191
+ "type": "string"
192
+ },
193
+ "type": {
194
+ "type": "string",
195
+ "enum": ["collection"]
196
+ },
197
+ "fields": {
198
+ "type": "object",
199
+ "patternProperties": {
200
+ "^[a-zA-Z0-9_]+$": {
201
+ "type": "object",
202
+ "properties": {
203
+ "type": {
204
+ "type": "string",
205
+ "enum": ["string", "boolean", "number", "json"]
206
+ },
207
+ "indexed": {
208
+ "type": "boolean"
209
+ },
210
+ "required": {
211
+ "type": "boolean"
212
+ }
213
+ },
214
+ "required": ["type"]
215
+ }
216
+ },
217
+ "additionalProperties": true
218
+ }
219
+ },
220
+ "required": ["fields"]
221
+ }
222
+ },
223
+ "additionalProperties": true
224
+ }
225
+ },
226
+ "required": ["project_id", "version", "tables"]
227
+ };
228
+ var ajv = new import_ajv.default();
229
+ var validator = ajv.compile(basicJsonSchema);
230
+ function validateSchema(schema) {
231
+ const v = validator(schema);
232
+ return {
233
+ valid: v,
234
+ errors: validator.errors || []
235
+ };
236
+ }
237
+ function validateData(schema, table, data, checkRequired = true) {
238
+ const valid = validateSchema(schema);
239
+ if (!valid.valid) {
240
+ return { valid: false, errors: valid.errors, message: "Schema is invalid" };
241
+ }
242
+ const tableSchema = schema.tables[table];
243
+ if (!tableSchema) {
244
+ return { valid: false, errors: [{ message: `Table ${table} not found in schema` }], message: "Table not found" };
245
+ }
246
+ for (const [fieldName, fieldValue] of Object.entries(data)) {
247
+ const fieldSchema = tableSchema.fields[fieldName];
248
+ if (!fieldSchema) {
249
+ return {
250
+ valid: false,
251
+ errors: [{ message: `Field ${fieldName} not found in schema` }],
252
+ message: "Invalid field"
253
+ };
254
+ }
255
+ const schemaType = fieldSchema.type;
256
+ const valueType = typeof fieldValue;
257
+ if (schemaType === "string" && valueType !== "string" || schemaType === "number" && valueType !== "number" || schemaType === "boolean" && valueType !== "boolean" || schemaType === "json" && valueType !== "object") {
258
+ return {
259
+ valid: false,
260
+ errors: [{
261
+ message: `Field ${fieldName} should be type ${schemaType}, got ${valueType}`
262
+ }],
263
+ message: "invalid type"
264
+ };
265
+ }
266
+ }
267
+ if (checkRequired) {
268
+ for (const [fieldName, fieldSchema] of Object.entries(tableSchema.fields)) {
269
+ if (fieldSchema.required && !data[fieldName]) {
270
+ return { valid: false, errors: [{ message: `Field ${fieldName} is required` }], message: "Required field missing" };
271
+ }
272
+ }
273
+ }
274
+ return { valid: true, errors: [] };
275
+ }
276
+
158
277
  // src/sync/index.ts
159
- var import_schema = require("@basictech/schema");
160
278
  syncProtocol();
161
279
  var BasicSync = class extends import_dexie2.Dexie {
162
280
  basic_schema;
@@ -231,7 +349,7 @@ var BasicSync = class extends import_dexie2.Dexie {
231
349
  ref: this.table(name),
232
350
  // --- WRITE ---- //
233
351
  add: (data) => {
234
- const valid = (0, import_schema.validateData)(this.basic_schema, name, data);
352
+ const valid = validateData(this.basic_schema, name, data);
235
353
  if (!valid.valid) {
236
354
  log("Invalid data", valid);
237
355
  return Promise.reject({ ...valid });
@@ -242,7 +360,7 @@ var BasicSync = class extends import_dexie2.Dexie {
242
360
  });
243
361
  },
244
362
  put: (data) => {
245
- const valid = (0, import_schema.validateData)(this.basic_schema, name, data);
363
+ const valid = validateData(this.basic_schema, name, data);
246
364
  if (!valid.valid) {
247
365
  log("Invalid data", valid);
248
366
  return Promise.reject({ ...valid });
@@ -253,7 +371,7 @@ var BasicSync = class extends import_dexie2.Dexie {
253
371
  });
254
372
  },
255
373
  update: (id, data) => {
256
- const valid = (0, import_schema.validateData)(this.basic_schema, name, data, false);
374
+ const valid = validateData(this.basic_schema, name, data, false);
257
375
  if (!valid.valid) {
258
376
  log("Invalid data", valid);
259
377
  return Promise.reject({ ...valid });
@@ -332,18 +450,29 @@ var version = "0.6.0";
332
450
 
333
451
  // src/AuthContext.tsx
334
452
  var import_jsx_runtime = require("react/jsx-runtime");
453
+ var LocalStorageAdapter = class {
454
+ async get(key) {
455
+ return localStorage.getItem(key);
456
+ }
457
+ async set(key, value) {
458
+ localStorage.setItem(key, value);
459
+ }
460
+ async remove(key) {
461
+ localStorage.removeItem(key);
462
+ }
463
+ };
335
464
  var BasicContext = (0, import_react.createContext)({
336
465
  unicorn: "\u{1F984}",
337
466
  isAuthReady: false,
338
467
  isSignedIn: false,
339
468
  user: null,
340
- signout: () => {
341
- },
342
- signin: () => {
343
- },
469
+ signout: () => Promise.resolve(),
470
+ signin: () => Promise.resolve(),
471
+ signinWithCode: () => new Promise(() => {
472
+ }),
344
473
  getToken: () => new Promise(() => {
345
474
  }),
346
- getSignInLink: () => "",
475
+ getSignInLink: () => Promise.resolve(""),
347
476
  db: {},
348
477
  dbStatus: "LOADING" /* LOADING */
349
478
  });
@@ -460,7 +589,13 @@ run "npm install @basictech/react@${latestVersion}" to update`);
460
589
  };
461
590
  }
462
591
  }
463
- function BasicProvider({ children, project_id, schema, debug = false }) {
592
+ function BasicProvider({
593
+ children,
594
+ project_id,
595
+ schema,
596
+ debug = false,
597
+ storage
598
+ }) {
464
599
  const [isAuthReady, setIsAuthReady] = (0, import_react.useState)(false);
465
600
  const [isSignedIn, setIsSignedIn] = (0, import_react.useState)(false);
466
601
  const [token, setToken] = (0, import_react.useState)(null);
@@ -469,7 +604,56 @@ function BasicProvider({ children, project_id, schema, debug = false }) {
469
604
  const [isReady, setIsReady] = (0, import_react.useState)(false);
470
605
  const [dbStatus, setDbStatus] = (0, import_react.useState)("OFFLINE" /* OFFLINE */);
471
606
  const [error, setError] = (0, import_react.useState)(null);
607
+ const [isOnline, setIsOnline] = (0, import_react.useState)(navigator.onLine);
608
+ const [pendingRefresh, setPendingRefresh] = (0, import_react.useState)(false);
472
609
  const syncRef = (0, import_react.useRef)(null);
610
+ const storageAdapter = storage || new LocalStorageAdapter();
611
+ const STORAGE_KEYS = {
612
+ REFRESH_TOKEN: "basic_refresh_token",
613
+ USER_INFO: "basic_user_info",
614
+ AUTH_STATE: "basic_auth_state",
615
+ DEBUG: "basic_debug"
616
+ };
617
+ const isDevelopment = () => {
618
+ return window.location.hostname === "localhost" || window.location.hostname === "127.0.0.1" || window.location.hostname.includes("localhost") || window.location.hostname.includes("127.0.0.1") || window.location.hostname.includes(".local") || process.env.NODE_ENV === "development" || debug === true;
619
+ };
620
+ const cleanOAuthParamsFromUrl = () => {
621
+ if (window.location.search.includes("code") || window.location.search.includes("state")) {
622
+ const url = new URL(window.location.href);
623
+ url.searchParams.delete("code");
624
+ url.searchParams.delete("state");
625
+ window.history.pushState({}, document.title, url.pathname + url.search);
626
+ log("Cleaned OAuth parameters from URL");
627
+ }
628
+ };
629
+ (0, import_react.useEffect)(() => {
630
+ const handleOnline = () => {
631
+ log("Network came back online");
632
+ setIsOnline(true);
633
+ if (pendingRefresh) {
634
+ log("Retrying pending token refresh");
635
+ setPendingRefresh(false);
636
+ if (token) {
637
+ const refreshToken = token.refresh_token || localStorage.getItem("basic_refresh_token");
638
+ if (refreshToken) {
639
+ fetchToken(refreshToken).catch((error2) => {
640
+ log("Retry refresh failed:", error2);
641
+ });
642
+ }
643
+ }
644
+ }
645
+ };
646
+ const handleOffline = () => {
647
+ log("Network went offline");
648
+ setIsOnline(false);
649
+ };
650
+ window.addEventListener("online", handleOnline);
651
+ window.addEventListener("offline", handleOffline);
652
+ return () => {
653
+ window.removeEventListener("online", handleOnline);
654
+ window.removeEventListener("offline", handleOffline);
655
+ };
656
+ }, [pendingRefresh, token]);
473
657
  (0, import_react.useEffect)(() => {
474
658
  function initDb(options) {
475
659
  if (!syncRef.current) {
@@ -535,32 +719,68 @@ function BasicProvider({ children, project_id, schema, debug = false }) {
535
719
  connectToDb();
536
720
  }
537
721
  }, [isSignedIn, shouldConnect]);
722
+ const connectToDb = async () => {
723
+ const tok = await getToken();
724
+ if (!tok) {
725
+ log("no token found");
726
+ return;
727
+ }
728
+ log("connecting to db...");
729
+ syncRef.current.connect({ access_token: tok }).catch((e) => {
730
+ log("error connecting to db", e);
731
+ });
732
+ };
538
733
  (0, import_react.useEffect)(() => {
539
- localStorage.setItem("basic_debug", debug ? "true" : "false");
540
- try {
541
- if (window.location.search.includes("code")) {
542
- let code = window.location?.search?.split("code=")[1].split("&")[0];
543
- const state = localStorage.getItem("basic_auth_state");
544
- if (!state || state !== window.location.search.split("state=")[1].split("&")[0]) {
545
- log("error: auth state does not match");
546
- setIsAuthReady(true);
547
- localStorage.removeItem("basic_auth_state");
548
- window.history.pushState({}, document.title, "/");
549
- return;
550
- }
551
- localStorage.removeItem("basic_auth_state");
552
- fetchToken(code);
553
- } else {
554
- let cookie_token = getCookie("basic_token");
555
- if (cookie_token !== "") {
556
- setToken(JSON.parse(cookie_token));
734
+ const initializeAuth = async () => {
735
+ await storageAdapter.set(STORAGE_KEYS.DEBUG, debug ? "true" : "false");
736
+ try {
737
+ if (window.location.search.includes("code")) {
738
+ let code = window.location?.search?.split("code=")[1].split("&")[0];
739
+ const state = await storageAdapter.get(STORAGE_KEYS.AUTH_STATE);
740
+ if (!state || state !== window.location.search.split("state=")[1].split("&")[0]) {
741
+ log("error: auth state does not match");
742
+ setIsAuthReady(true);
743
+ await storageAdapter.remove(STORAGE_KEYS.AUTH_STATE);
744
+ cleanOAuthParamsFromUrl();
745
+ return;
746
+ }
747
+ await storageAdapter.remove(STORAGE_KEYS.AUTH_STATE);
748
+ cleanOAuthParamsFromUrl();
749
+ fetchToken(code);
557
750
  } else {
558
- setIsAuthReady(true);
751
+ const refreshToken = await storageAdapter.get(STORAGE_KEYS.REFRESH_TOKEN);
752
+ if (refreshToken) {
753
+ log("Found refresh token in storage, attempting to refresh access token");
754
+ fetchToken(refreshToken);
755
+ } else {
756
+ let cookie_token = getCookie("basic_token");
757
+ if (cookie_token !== "") {
758
+ const tokenData = JSON.parse(cookie_token);
759
+ setToken(tokenData);
760
+ if (tokenData.refresh_token) {
761
+ await storageAdapter.set(STORAGE_KEYS.REFRESH_TOKEN, tokenData.refresh_token);
762
+ }
763
+ } else {
764
+ const cachedUserInfo = await storageAdapter.get(STORAGE_KEYS.USER_INFO);
765
+ if (cachedUserInfo) {
766
+ try {
767
+ const userData = JSON.parse(cachedUserInfo);
768
+ setUser(userData);
769
+ setIsSignedIn(true);
770
+ log("Loaded cached user info for offline mode");
771
+ } catch (error2) {
772
+ log("Error parsing cached user info:", error2);
773
+ }
774
+ }
775
+ setIsAuthReady(true);
776
+ }
777
+ }
559
778
  }
779
+ } catch (e) {
780
+ log("error getting token", e);
560
781
  }
561
- } catch (e) {
562
- log("error getting cookie", e);
563
- }
782
+ };
783
+ initializeAuth();
564
784
  }, []);
565
785
  (0, import_react.useEffect)(() => {
566
786
  async function fetchUser(acc_token) {
@@ -575,10 +795,13 @@ function BasicProvider({ children, project_id, schema, debug = false }) {
575
795
  log("error fetching user", user2.error);
576
796
  return;
577
797
  } else {
578
- document.cookie = `basic_token=${JSON.stringify(token)}; Secure; SameSite=Strict`;
579
- if (window.location.search.includes("code")) {
580
- window.history.pushState({}, document.title, "/");
798
+ if (token?.refresh_token) {
799
+ await storageAdapter.set(STORAGE_KEYS.REFRESH_TOKEN, token.refresh_token);
581
800
  }
801
+ await storageAdapter.set(STORAGE_KEYS.USER_INFO, JSON.stringify(user2));
802
+ log("Cached user info in storage");
803
+ document.cookie = `basic_access_token=${token.access_token}; Secure; SameSite=Strict; HttpOnly=false`;
804
+ document.cookie = `basic_token=${JSON.stringify(token)}; Secure; SameSite=Strict`;
582
805
  setUser(user2);
583
806
  setIsSignedIn(true);
584
807
  setIsAuthReady(true);
@@ -594,8 +817,18 @@ function BasicProvider({ children, project_id, schema, debug = false }) {
594
817
  const isExpired = decoded.exp && decoded.exp < Date.now() / 1e3;
595
818
  if (isExpired) {
596
819
  log("token is expired - refreshing ...");
597
- const newToken = await fetchToken(token?.refresh);
598
- fetchUser(newToken.access_token);
820
+ try {
821
+ const newToken = await fetchToken(token?.refresh_token);
822
+ fetchUser(newToken.access_token);
823
+ } catch (error2) {
824
+ log("Failed to refresh token in checkToken:", error2);
825
+ if (error2.message.includes("offline") || error2.message.includes("Network")) {
826
+ log("Network issue - continuing with expired token until online");
827
+ fetchUser(token.access_token);
828
+ } else {
829
+ setIsAuthReady(true);
830
+ }
831
+ }
599
832
  } else {
600
833
  fetchUser(token.access_token);
601
834
  }
@@ -604,41 +837,97 @@ function BasicProvider({ children, project_id, schema, debug = false }) {
604
837
  checkToken();
605
838
  }
606
839
  }, [token]);
607
- const connectToDb = async () => {
608
- const tok = await getToken();
609
- if (!tok) {
610
- log("no token found");
611
- return;
840
+ const getSignInLink = async (redirectUri) => {
841
+ try {
842
+ log("getting sign in link...");
843
+ if (!project_id) {
844
+ throw new Error("Project ID is required to generate sign-in link");
845
+ }
846
+ const randomState = Math.random().toString(36).substring(6);
847
+ await storageAdapter.set(STORAGE_KEYS.AUTH_STATE, randomState);
848
+ const redirectUrl = redirectUri || window.location.href;
849
+ if (!redirectUrl || !redirectUrl.startsWith("http://") && !redirectUrl.startsWith("https://")) {
850
+ throw new Error("Invalid redirect URI provided");
851
+ }
852
+ let baseUrl2 = "https://api.basic.tech/auth/authorize";
853
+ baseUrl2 += `?client_id=${project_id}`;
854
+ baseUrl2 += `&redirect_uri=${encodeURIComponent(redirectUrl)}`;
855
+ baseUrl2 += `&response_type=code`;
856
+ baseUrl2 += `&scope=profile`;
857
+ baseUrl2 += `&state=${randomState}`;
858
+ log("Generated sign-in link successfully");
859
+ return baseUrl2;
860
+ } catch (error2) {
861
+ log("Error generating sign-in link:", error2);
862
+ throw error2;
612
863
  }
613
- log("connecting to db...");
614
- syncRef.current.connect({ access_token: tok }).catch((e) => {
615
- log("error connecting to db", e);
616
- });
617
864
  };
618
- const getSignInLink = () => {
619
- log("getting sign in link...");
620
- const randomState = Math.random().toString(36).substring(6);
621
- localStorage.setItem("basic_auth_state", randomState);
622
- let baseUrl2 = "https://api.basic.tech/auth/authorize";
623
- baseUrl2 += `?client_id=${project_id}`;
624
- baseUrl2 += `&redirect_uri=${encodeURIComponent(window.location.href)}`;
625
- baseUrl2 += `&response_type=code`;
626
- baseUrl2 += `&scope=profile`;
627
- baseUrl2 += `&state=${randomState}`;
628
- return baseUrl2;
865
+ const signin = async () => {
866
+ try {
867
+ log("signing in...");
868
+ if (!project_id) {
869
+ log("Error: project_id is required for sign-in");
870
+ throw new Error("Project ID is required for authentication");
871
+ }
872
+ const signInLink = await getSignInLink();
873
+ log("Generated sign-in link:", signInLink);
874
+ if (!signInLink || !signInLink.startsWith("https://")) {
875
+ log("Error: Invalid sign-in link generated");
876
+ throw new Error("Failed to generate valid sign-in URL");
877
+ }
878
+ window.location.href = signInLink;
879
+ } catch (error2) {
880
+ log("Error during sign-in:", error2);
881
+ if (isDevelopment()) {
882
+ setError({
883
+ code: "signin_error",
884
+ title: "Sign-in Failed",
885
+ message: error2.message || "An error occurred during sign-in. Please try again."
886
+ });
887
+ }
888
+ throw error2;
889
+ }
629
890
  };
630
- const signin = () => {
631
- log("signing in: ", getSignInLink());
632
- const signInLink = getSignInLink();
633
- window.location.href = signInLink;
891
+ const signinWithCode = async (code, state) => {
892
+ try {
893
+ log("signinWithCode called with code:", code);
894
+ if (!code || typeof code !== "string") {
895
+ return { success: false, error: "Invalid authorization code" };
896
+ }
897
+ if (state) {
898
+ const storedState = await storageAdapter.get(STORAGE_KEYS.AUTH_STATE);
899
+ if (storedState && storedState !== state) {
900
+ log("State parameter mismatch:", { provided: state, stored: storedState });
901
+ return { success: false, error: "State parameter mismatch" };
902
+ }
903
+ }
904
+ await storageAdapter.remove(STORAGE_KEYS.AUTH_STATE);
905
+ cleanOAuthParamsFromUrl();
906
+ const token2 = await fetchToken(code);
907
+ if (token2) {
908
+ log("signinWithCode successful");
909
+ return { success: true };
910
+ } else {
911
+ return { success: false, error: "Failed to exchange code for token" };
912
+ }
913
+ } catch (error2) {
914
+ log("signinWithCode error:", error2);
915
+ return {
916
+ success: false,
917
+ error: error2.message || "Authentication failed"
918
+ };
919
+ }
634
920
  };
635
- const signout = () => {
921
+ const signout = async () => {
636
922
  log("signing out!");
637
923
  setUser({});
638
924
  setIsSignedIn(false);
639
925
  setToken(null);
640
926
  document.cookie = `basic_token=; Secure; SameSite=Strict`;
641
- localStorage.removeItem("basic_auth_state");
927
+ document.cookie = `basic_access_token=; Secure; SameSite=Strict`;
928
+ await storageAdapter.remove(STORAGE_KEYS.AUTH_STATE);
929
+ await storageAdapter.remove(STORAGE_KEYS.REFRESH_TOKEN);
930
+ await storageAdapter.remove(STORAGE_KEYS.USER_INFO);
642
931
  if (syncRef.current) {
643
932
  (async () => {
644
933
  try {
@@ -655,6 +944,27 @@ function BasicProvider({ children, project_id, schema, debug = false }) {
655
944
  const getToken = async () => {
656
945
  log("getting token...");
657
946
  if (!token) {
947
+ const refreshToken = await storageAdapter.get(STORAGE_KEYS.REFRESH_TOKEN);
948
+ if (refreshToken) {
949
+ log("No token in memory, attempting to refresh from storage");
950
+ try {
951
+ const newToken = await fetchToken(refreshToken);
952
+ if (newToken?.access_token) {
953
+ return newToken.access_token;
954
+ }
955
+ } catch (error2) {
956
+ log("Failed to refresh token from storage:", error2);
957
+ if (error2.message.includes("offline") || error2.message.includes("Network")) {
958
+ log("Network issue - continuing with potentially expired token");
959
+ const lastToken = localStorage.getItem("basic_access_token");
960
+ if (lastToken) {
961
+ return lastToken;
962
+ }
963
+ throw new Error("Network offline - authentication will be retried when online");
964
+ }
965
+ throw new Error("Authentication expired. Please sign in again.");
966
+ }
967
+ }
658
968
  log("no token found");
659
969
  throw new Error("no token found");
660
970
  }
@@ -662,8 +972,22 @@ function BasicProvider({ children, project_id, schema, debug = false }) {
662
972
  const isExpired = decoded.exp && decoded.exp < Date.now() / 1e3;
663
973
  if (isExpired) {
664
974
  log("token is expired - refreshing ...");
665
- const newToken = await fetchToken(token?.refresh);
666
- return newToken?.access_token || "";
975
+ const refreshToken = token?.refresh_token || await storageAdapter.get(STORAGE_KEYS.REFRESH_TOKEN);
976
+ if (refreshToken) {
977
+ try {
978
+ const newToken = await fetchToken(refreshToken);
979
+ return newToken?.access_token || "";
980
+ } catch (error2) {
981
+ log("Failed to refresh expired token:", error2);
982
+ if (error2.message.includes("offline") || error2.message.includes("Network")) {
983
+ log("Network issue - using expired token until network is restored");
984
+ return token.access_token;
985
+ }
986
+ throw new Error("Authentication expired. Please sign in again.");
987
+ }
988
+ } else {
989
+ throw new Error("no refresh token available");
990
+ }
667
991
  }
668
992
  return token?.access_token || "";
669
993
  };
@@ -682,20 +1006,66 @@ function BasicProvider({ children, project_id, schema, debug = false }) {
682
1006
  return cookieValue;
683
1007
  }
684
1008
  const fetchToken = async (code) => {
685
- const token2 = await fetch("https://api.basic.tech/auth/token", {
686
- method: "POST",
687
- headers: {
688
- "Content-Type": "application/json"
689
- },
690
- body: JSON.stringify({ code })
691
- }).then((response) => response.json()).catch((error2) => log("Error:", error2));
692
- if (token2.error) {
693
- log("error fetching token", token2.error);
694
- return;
695
- } else {
696
- setToken(token2);
1009
+ try {
1010
+ if (!isOnline) {
1011
+ log("Network is offline, marking refresh as pending");
1012
+ setPendingRefresh(true);
1013
+ throw new Error("Network offline - refresh will be retried when online");
1014
+ }
1015
+ const token2 = await fetch("https://api.basic.tech/auth/token", {
1016
+ method: "POST",
1017
+ headers: {
1018
+ "Content-Type": "application/json"
1019
+ },
1020
+ body: JSON.stringify({ code })
1021
+ }).then((response) => response.json()).catch((error2) => {
1022
+ log("Network error fetching token:", error2);
1023
+ if (!isOnline) {
1024
+ setPendingRefresh(true);
1025
+ throw new Error("Network offline - refresh will be retried when online");
1026
+ }
1027
+ throw new Error("Network error during token refresh");
1028
+ });
1029
+ if (token2.error) {
1030
+ log("error fetching token", token2.error);
1031
+ if (token2.error.includes("network") || token2.error.includes("timeout")) {
1032
+ setPendingRefresh(true);
1033
+ throw new Error("Network issue - refresh will be retried when online");
1034
+ }
1035
+ await storageAdapter.remove(STORAGE_KEYS.REFRESH_TOKEN);
1036
+ await storageAdapter.remove(STORAGE_KEYS.USER_INFO);
1037
+ document.cookie = `basic_token=; Secure; SameSite=Strict`;
1038
+ document.cookie = `basic_access_token=; Secure; SameSite=Strict`;
1039
+ setUser({});
1040
+ setIsSignedIn(false);
1041
+ setToken(null);
1042
+ setIsAuthReady(true);
1043
+ throw new Error(`Token refresh failed: ${token2.error}`);
1044
+ } else {
1045
+ setToken(token2);
1046
+ setPendingRefresh(false);
1047
+ if (token2.refresh_token) {
1048
+ await storageAdapter.set(STORAGE_KEYS.REFRESH_TOKEN, token2.refresh_token);
1049
+ log("Updated refresh token in storage");
1050
+ }
1051
+ document.cookie = `basic_access_token=${token2.access_token}; Secure; SameSite=Strict; HttpOnly=false`;
1052
+ log("Updated access token in cookie");
1053
+ }
1054
+ return token2;
1055
+ } catch (error2) {
1056
+ log("Token refresh error:", error2);
1057
+ if (!error2.message.includes("offline") && !error2.message.includes("Network")) {
1058
+ await storageAdapter.remove(STORAGE_KEYS.REFRESH_TOKEN);
1059
+ await storageAdapter.remove(STORAGE_KEYS.USER_INFO);
1060
+ document.cookie = `basic_token=; Secure; SameSite=Strict`;
1061
+ document.cookie = `basic_access_token=; Secure; SameSite=Strict`;
1062
+ setUser({});
1063
+ setIsSignedIn(false);
1064
+ setToken(null);
1065
+ setIsAuthReady(true);
1066
+ }
1067
+ throw error2;
697
1068
  }
698
- return token2;
699
1069
  };
700
1070
  const db_ = (tableName) => {
701
1071
  const checkSignIn = () => {
@@ -738,12 +1108,13 @@ function BasicProvider({ children, project_id, schema, debug = false }) {
738
1108
  user,
739
1109
  signout,
740
1110
  signin,
1111
+ signinWithCode,
741
1112
  getToken,
742
1113
  getSignInLink,
743
1114
  db: syncRef.current ? syncRef.current : noDb,
744
1115
  dbStatus
745
1116
  }, children: [
746
- error && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ErrorDisplay, { error }),
1117
+ error && isDevelopment() && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ErrorDisplay, { error }),
747
1118
  isReady && children
748
1119
  ] });
749
1120
  }