@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.mjs CHANGED
@@ -127,8 +127,116 @@ var syncProtocol = function() {
127
127
  });
128
128
  };
129
129
 
130
+ // src/schema.ts
131
+ import Ajv from "ajv";
132
+ var basicJsonSchema = {
133
+ "$schema": "http://json-schema.org/draft-07/schema#",
134
+ "type": "object",
135
+ "properties": {
136
+ "project_id": {
137
+ "type": "string"
138
+ },
139
+ "namespace": {
140
+ "type": "string"
141
+ },
142
+ "version": {
143
+ "type": "integer",
144
+ "minimum": 0
145
+ },
146
+ "tables": {
147
+ "type": "object",
148
+ "patternProperties": {
149
+ "^[a-zA-Z0-9_]+$": {
150
+ "type": "object",
151
+ "properties": {
152
+ "name": {
153
+ "type": "string"
154
+ },
155
+ "type": {
156
+ "type": "string",
157
+ "enum": ["collection"]
158
+ },
159
+ "fields": {
160
+ "type": "object",
161
+ "patternProperties": {
162
+ "^[a-zA-Z0-9_]+$": {
163
+ "type": "object",
164
+ "properties": {
165
+ "type": {
166
+ "type": "string",
167
+ "enum": ["string", "boolean", "number", "json"]
168
+ },
169
+ "indexed": {
170
+ "type": "boolean"
171
+ },
172
+ "required": {
173
+ "type": "boolean"
174
+ }
175
+ },
176
+ "required": ["type"]
177
+ }
178
+ },
179
+ "additionalProperties": true
180
+ }
181
+ },
182
+ "required": ["fields"]
183
+ }
184
+ },
185
+ "additionalProperties": true
186
+ }
187
+ },
188
+ "required": ["project_id", "version", "tables"]
189
+ };
190
+ var ajv = new Ajv();
191
+ var validator = ajv.compile(basicJsonSchema);
192
+ function validateSchema(schema) {
193
+ const v = validator(schema);
194
+ return {
195
+ valid: v,
196
+ errors: validator.errors || []
197
+ };
198
+ }
199
+ function validateData(schema, table, data, checkRequired = true) {
200
+ const valid = validateSchema(schema);
201
+ if (!valid.valid) {
202
+ return { valid: false, errors: valid.errors, message: "Schema is invalid" };
203
+ }
204
+ const tableSchema = schema.tables[table];
205
+ if (!tableSchema) {
206
+ return { valid: false, errors: [{ message: `Table ${table} not found in schema` }], message: "Table not found" };
207
+ }
208
+ for (const [fieldName, fieldValue] of Object.entries(data)) {
209
+ const fieldSchema = tableSchema.fields[fieldName];
210
+ if (!fieldSchema) {
211
+ return {
212
+ valid: false,
213
+ errors: [{ message: `Field ${fieldName} not found in schema` }],
214
+ message: "Invalid field"
215
+ };
216
+ }
217
+ const schemaType = fieldSchema.type;
218
+ const valueType = typeof fieldValue;
219
+ if (schemaType === "string" && valueType !== "string" || schemaType === "number" && valueType !== "number" || schemaType === "boolean" && valueType !== "boolean" || schemaType === "json" && valueType !== "object") {
220
+ return {
221
+ valid: false,
222
+ errors: [{
223
+ message: `Field ${fieldName} should be type ${schemaType}, got ${valueType}`
224
+ }],
225
+ message: "invalid type"
226
+ };
227
+ }
228
+ }
229
+ if (checkRequired) {
230
+ for (const [fieldName, fieldSchema] of Object.entries(tableSchema.fields)) {
231
+ if (fieldSchema.required && !data[fieldName]) {
232
+ return { valid: false, errors: [{ message: `Field ${fieldName} is required` }], message: "Required field missing" };
233
+ }
234
+ }
235
+ }
236
+ return { valid: true, errors: [] };
237
+ }
238
+
130
239
  // src/sync/index.ts
131
- import { validateData } from "@basictech/schema";
132
240
  syncProtocol();
133
241
  var BasicSync = class extends Dexie2 {
134
242
  basic_schema;
@@ -297,32 +405,43 @@ async function deleteRecord({ projectId, accountId, tableName, id, token }) {
297
405
  }
298
406
 
299
407
  // src/AuthContext.tsx
300
- import { validateSchema as validateSchema2, compareSchemas } from "@basictech/schema";
408
+ import { validateSchema as validateSchema3, compareSchemas } from "@basictech/schema";
301
409
 
302
410
  // package.json
303
411
  var version = "0.6.0";
304
412
 
305
413
  // src/AuthContext.tsx
306
414
  import { jsx, jsxs } from "react/jsx-runtime";
415
+ var LocalStorageAdapter = class {
416
+ async get(key) {
417
+ return localStorage.getItem(key);
418
+ }
419
+ async set(key, value) {
420
+ localStorage.setItem(key, value);
421
+ }
422
+ async remove(key) {
423
+ localStorage.removeItem(key);
424
+ }
425
+ };
307
426
  var BasicContext = createContext({
308
427
  unicorn: "\u{1F984}",
309
428
  isAuthReady: false,
310
429
  isSignedIn: false,
311
430
  user: null,
312
- signout: () => {
313
- },
314
- signin: () => {
315
- },
431
+ signout: () => Promise.resolve(),
432
+ signin: () => Promise.resolve(),
433
+ signinWithCode: () => new Promise(() => {
434
+ }),
316
435
  getToken: () => new Promise(() => {
317
436
  }),
318
- getSignInLink: () => "",
437
+ getSignInLink: () => Promise.resolve(""),
319
438
  db: {},
320
439
  dbStatus: "LOADING" /* LOADING */
321
440
  });
322
441
  async function getSchemaStatus(schema) {
323
442
  const projectId = schema.project_id;
324
443
  let status = "";
325
- const valid = validateSchema2(schema);
444
+ const valid = validateSchema3(schema);
326
445
  if (!valid.valid) {
327
446
  console.warn("BasicDB Error: your local schema is invalid. Please fix errors and try again - sync is disabled");
328
447
  return {
@@ -432,7 +551,13 @@ run "npm install @basictech/react@${latestVersion}" to update`);
432
551
  };
433
552
  }
434
553
  }
435
- function BasicProvider({ children, project_id, schema, debug = false }) {
554
+ function BasicProvider({
555
+ children,
556
+ project_id,
557
+ schema,
558
+ debug = false,
559
+ storage
560
+ }) {
436
561
  const [isAuthReady, setIsAuthReady] = useState(false);
437
562
  const [isSignedIn, setIsSignedIn] = useState(false);
438
563
  const [token, setToken] = useState(null);
@@ -441,7 +566,56 @@ function BasicProvider({ children, project_id, schema, debug = false }) {
441
566
  const [isReady, setIsReady] = useState(false);
442
567
  const [dbStatus, setDbStatus] = useState("OFFLINE" /* OFFLINE */);
443
568
  const [error, setError] = useState(null);
569
+ const [isOnline, setIsOnline] = useState(navigator.onLine);
570
+ const [pendingRefresh, setPendingRefresh] = useState(false);
444
571
  const syncRef = useRef(null);
572
+ const storageAdapter = storage || new LocalStorageAdapter();
573
+ const STORAGE_KEYS = {
574
+ REFRESH_TOKEN: "basic_refresh_token",
575
+ USER_INFO: "basic_user_info",
576
+ AUTH_STATE: "basic_auth_state",
577
+ DEBUG: "basic_debug"
578
+ };
579
+ const isDevelopment = () => {
580
+ 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;
581
+ };
582
+ const cleanOAuthParamsFromUrl = () => {
583
+ if (window.location.search.includes("code") || window.location.search.includes("state")) {
584
+ const url = new URL(window.location.href);
585
+ url.searchParams.delete("code");
586
+ url.searchParams.delete("state");
587
+ window.history.pushState({}, document.title, url.pathname + url.search);
588
+ log("Cleaned OAuth parameters from URL");
589
+ }
590
+ };
591
+ useEffect(() => {
592
+ const handleOnline = () => {
593
+ log("Network came back online");
594
+ setIsOnline(true);
595
+ if (pendingRefresh) {
596
+ log("Retrying pending token refresh");
597
+ setPendingRefresh(false);
598
+ if (token) {
599
+ const refreshToken = token.refresh_token || localStorage.getItem("basic_refresh_token");
600
+ if (refreshToken) {
601
+ fetchToken(refreshToken).catch((error2) => {
602
+ log("Retry refresh failed:", error2);
603
+ });
604
+ }
605
+ }
606
+ }
607
+ };
608
+ const handleOffline = () => {
609
+ log("Network went offline");
610
+ setIsOnline(false);
611
+ };
612
+ window.addEventListener("online", handleOnline);
613
+ window.addEventListener("offline", handleOffline);
614
+ return () => {
615
+ window.removeEventListener("online", handleOnline);
616
+ window.removeEventListener("offline", handleOffline);
617
+ };
618
+ }, [pendingRefresh, token]);
445
619
  useEffect(() => {
446
620
  function initDb(options) {
447
621
  if (!syncRef.current) {
@@ -462,7 +636,7 @@ function BasicProvider({ children, project_id, schema, debug = false }) {
462
636
  }
463
637
  }
464
638
  async function checkSchema() {
465
- const valid = validateSchema2(schema);
639
+ const valid = validateSchema3(schema);
466
640
  if (!valid.valid) {
467
641
  log("Basic Schema is invalid!", valid.errors);
468
642
  console.group("Schema Errors");
@@ -507,32 +681,68 @@ function BasicProvider({ children, project_id, schema, debug = false }) {
507
681
  connectToDb();
508
682
  }
509
683
  }, [isSignedIn, shouldConnect]);
684
+ const connectToDb = async () => {
685
+ const tok = await getToken();
686
+ if (!tok) {
687
+ log("no token found");
688
+ return;
689
+ }
690
+ log("connecting to db...");
691
+ syncRef.current.connect({ access_token: tok }).catch((e) => {
692
+ log("error connecting to db", e);
693
+ });
694
+ };
510
695
  useEffect(() => {
511
- localStorage.setItem("basic_debug", debug ? "true" : "false");
512
- try {
513
- if (window.location.search.includes("code")) {
514
- let code = window.location?.search?.split("code=")[1].split("&")[0];
515
- const state = localStorage.getItem("basic_auth_state");
516
- if (!state || state !== window.location.search.split("state=")[1].split("&")[0]) {
517
- log("error: auth state does not match");
518
- setIsAuthReady(true);
519
- localStorage.removeItem("basic_auth_state");
520
- window.history.pushState({}, document.title, "/");
521
- return;
522
- }
523
- localStorage.removeItem("basic_auth_state");
524
- fetchToken(code);
525
- } else {
526
- let cookie_token = getCookie("basic_token");
527
- if (cookie_token !== "") {
528
- setToken(JSON.parse(cookie_token));
696
+ const initializeAuth = async () => {
697
+ await storageAdapter.set(STORAGE_KEYS.DEBUG, debug ? "true" : "false");
698
+ try {
699
+ if (window.location.search.includes("code")) {
700
+ let code = window.location?.search?.split("code=")[1].split("&")[0];
701
+ const state = await storageAdapter.get(STORAGE_KEYS.AUTH_STATE);
702
+ if (!state || state !== window.location.search.split("state=")[1].split("&")[0]) {
703
+ log("error: auth state does not match");
704
+ setIsAuthReady(true);
705
+ await storageAdapter.remove(STORAGE_KEYS.AUTH_STATE);
706
+ cleanOAuthParamsFromUrl();
707
+ return;
708
+ }
709
+ await storageAdapter.remove(STORAGE_KEYS.AUTH_STATE);
710
+ cleanOAuthParamsFromUrl();
711
+ fetchToken(code);
529
712
  } else {
530
- setIsAuthReady(true);
713
+ const refreshToken = await storageAdapter.get(STORAGE_KEYS.REFRESH_TOKEN);
714
+ if (refreshToken) {
715
+ log("Found refresh token in storage, attempting to refresh access token");
716
+ fetchToken(refreshToken);
717
+ } else {
718
+ let cookie_token = getCookie("basic_token");
719
+ if (cookie_token !== "") {
720
+ const tokenData = JSON.parse(cookie_token);
721
+ setToken(tokenData);
722
+ if (tokenData.refresh_token) {
723
+ await storageAdapter.set(STORAGE_KEYS.REFRESH_TOKEN, tokenData.refresh_token);
724
+ }
725
+ } else {
726
+ const cachedUserInfo = await storageAdapter.get(STORAGE_KEYS.USER_INFO);
727
+ if (cachedUserInfo) {
728
+ try {
729
+ const userData = JSON.parse(cachedUserInfo);
730
+ setUser(userData);
731
+ setIsSignedIn(true);
732
+ log("Loaded cached user info for offline mode");
733
+ } catch (error2) {
734
+ log("Error parsing cached user info:", error2);
735
+ }
736
+ }
737
+ setIsAuthReady(true);
738
+ }
739
+ }
531
740
  }
741
+ } catch (e) {
742
+ log("error getting token", e);
532
743
  }
533
- } catch (e) {
534
- log("error getting cookie", e);
535
- }
744
+ };
745
+ initializeAuth();
536
746
  }, []);
537
747
  useEffect(() => {
538
748
  async function fetchUser(acc_token) {
@@ -547,10 +757,13 @@ function BasicProvider({ children, project_id, schema, debug = false }) {
547
757
  log("error fetching user", user2.error);
548
758
  return;
549
759
  } else {
550
- document.cookie = `basic_token=${JSON.stringify(token)}; Secure; SameSite=Strict`;
551
- if (window.location.search.includes("code")) {
552
- window.history.pushState({}, document.title, "/");
760
+ if (token?.refresh_token) {
761
+ await storageAdapter.set(STORAGE_KEYS.REFRESH_TOKEN, token.refresh_token);
553
762
  }
763
+ await storageAdapter.set(STORAGE_KEYS.USER_INFO, JSON.stringify(user2));
764
+ log("Cached user info in storage");
765
+ document.cookie = `basic_access_token=${token.access_token}; Secure; SameSite=Strict; HttpOnly=false`;
766
+ document.cookie = `basic_token=${JSON.stringify(token)}; Secure; SameSite=Strict`;
554
767
  setUser(user2);
555
768
  setIsSignedIn(true);
556
769
  setIsAuthReady(true);
@@ -566,8 +779,18 @@ function BasicProvider({ children, project_id, schema, debug = false }) {
566
779
  const isExpired = decoded.exp && decoded.exp < Date.now() / 1e3;
567
780
  if (isExpired) {
568
781
  log("token is expired - refreshing ...");
569
- const newToken = await fetchToken(token?.refresh);
570
- fetchUser(newToken.access_token);
782
+ try {
783
+ const newToken = await fetchToken(token?.refresh_token);
784
+ fetchUser(newToken.access_token);
785
+ } catch (error2) {
786
+ log("Failed to refresh token in checkToken:", error2);
787
+ if (error2.message.includes("offline") || error2.message.includes("Network")) {
788
+ log("Network issue - continuing with expired token until online");
789
+ fetchUser(token.access_token);
790
+ } else {
791
+ setIsAuthReady(true);
792
+ }
793
+ }
571
794
  } else {
572
795
  fetchUser(token.access_token);
573
796
  }
@@ -576,41 +799,97 @@ function BasicProvider({ children, project_id, schema, debug = false }) {
576
799
  checkToken();
577
800
  }
578
801
  }, [token]);
579
- const connectToDb = async () => {
580
- const tok = await getToken();
581
- if (!tok) {
582
- log("no token found");
583
- return;
802
+ const getSignInLink = async (redirectUri) => {
803
+ try {
804
+ log("getting sign in link...");
805
+ if (!project_id) {
806
+ throw new Error("Project ID is required to generate sign-in link");
807
+ }
808
+ const randomState = Math.random().toString(36).substring(6);
809
+ await storageAdapter.set(STORAGE_KEYS.AUTH_STATE, randomState);
810
+ const redirectUrl = redirectUri || window.location.href;
811
+ if (!redirectUrl || !redirectUrl.startsWith("http://") && !redirectUrl.startsWith("https://")) {
812
+ throw new Error("Invalid redirect URI provided");
813
+ }
814
+ let baseUrl2 = "https://api.basic.tech/auth/authorize";
815
+ baseUrl2 += `?client_id=${project_id}`;
816
+ baseUrl2 += `&redirect_uri=${encodeURIComponent(redirectUrl)}`;
817
+ baseUrl2 += `&response_type=code`;
818
+ baseUrl2 += `&scope=profile`;
819
+ baseUrl2 += `&state=${randomState}`;
820
+ log("Generated sign-in link successfully");
821
+ return baseUrl2;
822
+ } catch (error2) {
823
+ log("Error generating sign-in link:", error2);
824
+ throw error2;
584
825
  }
585
- log("connecting to db...");
586
- syncRef.current.connect({ access_token: tok }).catch((e) => {
587
- log("error connecting to db", e);
588
- });
589
826
  };
590
- const getSignInLink = () => {
591
- log("getting sign in link...");
592
- const randomState = Math.random().toString(36).substring(6);
593
- localStorage.setItem("basic_auth_state", randomState);
594
- let baseUrl2 = "https://api.basic.tech/auth/authorize";
595
- baseUrl2 += `?client_id=${project_id}`;
596
- baseUrl2 += `&redirect_uri=${encodeURIComponent(window.location.href)}`;
597
- baseUrl2 += `&response_type=code`;
598
- baseUrl2 += `&scope=profile`;
599
- baseUrl2 += `&state=${randomState}`;
600
- return baseUrl2;
827
+ const signin = async () => {
828
+ try {
829
+ log("signing in...");
830
+ if (!project_id) {
831
+ log("Error: project_id is required for sign-in");
832
+ throw new Error("Project ID is required for authentication");
833
+ }
834
+ const signInLink = await getSignInLink();
835
+ log("Generated sign-in link:", signInLink);
836
+ if (!signInLink || !signInLink.startsWith("https://")) {
837
+ log("Error: Invalid sign-in link generated");
838
+ throw new Error("Failed to generate valid sign-in URL");
839
+ }
840
+ window.location.href = signInLink;
841
+ } catch (error2) {
842
+ log("Error during sign-in:", error2);
843
+ if (isDevelopment()) {
844
+ setError({
845
+ code: "signin_error",
846
+ title: "Sign-in Failed",
847
+ message: error2.message || "An error occurred during sign-in. Please try again."
848
+ });
849
+ }
850
+ throw error2;
851
+ }
601
852
  };
602
- const signin = () => {
603
- log("signing in: ", getSignInLink());
604
- const signInLink = getSignInLink();
605
- window.location.href = signInLink;
853
+ const signinWithCode = async (code, state) => {
854
+ try {
855
+ log("signinWithCode called with code:", code);
856
+ if (!code || typeof code !== "string") {
857
+ return { success: false, error: "Invalid authorization code" };
858
+ }
859
+ if (state) {
860
+ const storedState = await storageAdapter.get(STORAGE_KEYS.AUTH_STATE);
861
+ if (storedState && storedState !== state) {
862
+ log("State parameter mismatch:", { provided: state, stored: storedState });
863
+ return { success: false, error: "State parameter mismatch" };
864
+ }
865
+ }
866
+ await storageAdapter.remove(STORAGE_KEYS.AUTH_STATE);
867
+ cleanOAuthParamsFromUrl();
868
+ const token2 = await fetchToken(code);
869
+ if (token2) {
870
+ log("signinWithCode successful");
871
+ return { success: true };
872
+ } else {
873
+ return { success: false, error: "Failed to exchange code for token" };
874
+ }
875
+ } catch (error2) {
876
+ log("signinWithCode error:", error2);
877
+ return {
878
+ success: false,
879
+ error: error2.message || "Authentication failed"
880
+ };
881
+ }
606
882
  };
607
- const signout = () => {
883
+ const signout = async () => {
608
884
  log("signing out!");
609
885
  setUser({});
610
886
  setIsSignedIn(false);
611
887
  setToken(null);
612
888
  document.cookie = `basic_token=; Secure; SameSite=Strict`;
613
- localStorage.removeItem("basic_auth_state");
889
+ document.cookie = `basic_access_token=; Secure; SameSite=Strict`;
890
+ await storageAdapter.remove(STORAGE_KEYS.AUTH_STATE);
891
+ await storageAdapter.remove(STORAGE_KEYS.REFRESH_TOKEN);
892
+ await storageAdapter.remove(STORAGE_KEYS.USER_INFO);
614
893
  if (syncRef.current) {
615
894
  (async () => {
616
895
  try {
@@ -627,6 +906,27 @@ function BasicProvider({ children, project_id, schema, debug = false }) {
627
906
  const getToken = async () => {
628
907
  log("getting token...");
629
908
  if (!token) {
909
+ const refreshToken = await storageAdapter.get(STORAGE_KEYS.REFRESH_TOKEN);
910
+ if (refreshToken) {
911
+ log("No token in memory, attempting to refresh from storage");
912
+ try {
913
+ const newToken = await fetchToken(refreshToken);
914
+ if (newToken?.access_token) {
915
+ return newToken.access_token;
916
+ }
917
+ } catch (error2) {
918
+ log("Failed to refresh token from storage:", error2);
919
+ if (error2.message.includes("offline") || error2.message.includes("Network")) {
920
+ log("Network issue - continuing with potentially expired token");
921
+ const lastToken = localStorage.getItem("basic_access_token");
922
+ if (lastToken) {
923
+ return lastToken;
924
+ }
925
+ throw new Error("Network offline - authentication will be retried when online");
926
+ }
927
+ throw new Error("Authentication expired. Please sign in again.");
928
+ }
929
+ }
630
930
  log("no token found");
631
931
  throw new Error("no token found");
632
932
  }
@@ -634,8 +934,22 @@ function BasicProvider({ children, project_id, schema, debug = false }) {
634
934
  const isExpired = decoded.exp && decoded.exp < Date.now() / 1e3;
635
935
  if (isExpired) {
636
936
  log("token is expired - refreshing ...");
637
- const newToken = await fetchToken(token?.refresh);
638
- return newToken?.access_token || "";
937
+ const refreshToken = token?.refresh_token || await storageAdapter.get(STORAGE_KEYS.REFRESH_TOKEN);
938
+ if (refreshToken) {
939
+ try {
940
+ const newToken = await fetchToken(refreshToken);
941
+ return newToken?.access_token || "";
942
+ } catch (error2) {
943
+ log("Failed to refresh expired token:", error2);
944
+ if (error2.message.includes("offline") || error2.message.includes("Network")) {
945
+ log("Network issue - using expired token until network is restored");
946
+ return token.access_token;
947
+ }
948
+ throw new Error("Authentication expired. Please sign in again.");
949
+ }
950
+ } else {
951
+ throw new Error("no refresh token available");
952
+ }
639
953
  }
640
954
  return token?.access_token || "";
641
955
  };
@@ -654,20 +968,66 @@ function BasicProvider({ children, project_id, schema, debug = false }) {
654
968
  return cookieValue;
655
969
  }
656
970
  const fetchToken = async (code) => {
657
- const token2 = await fetch("https://api.basic.tech/auth/token", {
658
- method: "POST",
659
- headers: {
660
- "Content-Type": "application/json"
661
- },
662
- body: JSON.stringify({ code })
663
- }).then((response) => response.json()).catch((error2) => log("Error:", error2));
664
- if (token2.error) {
665
- log("error fetching token", token2.error);
666
- return;
667
- } else {
668
- setToken(token2);
971
+ try {
972
+ if (!isOnline) {
973
+ log("Network is offline, marking refresh as pending");
974
+ setPendingRefresh(true);
975
+ throw new Error("Network offline - refresh will be retried when online");
976
+ }
977
+ const token2 = await fetch("https://api.basic.tech/auth/token", {
978
+ method: "POST",
979
+ headers: {
980
+ "Content-Type": "application/json"
981
+ },
982
+ body: JSON.stringify({ code })
983
+ }).then((response) => response.json()).catch((error2) => {
984
+ log("Network error fetching token:", error2);
985
+ if (!isOnline) {
986
+ setPendingRefresh(true);
987
+ throw new Error("Network offline - refresh will be retried when online");
988
+ }
989
+ throw new Error("Network error during token refresh");
990
+ });
991
+ if (token2.error) {
992
+ log("error fetching token", token2.error);
993
+ if (token2.error.includes("network") || token2.error.includes("timeout")) {
994
+ setPendingRefresh(true);
995
+ throw new Error("Network issue - refresh will be retried when online");
996
+ }
997
+ await storageAdapter.remove(STORAGE_KEYS.REFRESH_TOKEN);
998
+ await storageAdapter.remove(STORAGE_KEYS.USER_INFO);
999
+ document.cookie = `basic_token=; Secure; SameSite=Strict`;
1000
+ document.cookie = `basic_access_token=; Secure; SameSite=Strict`;
1001
+ setUser({});
1002
+ setIsSignedIn(false);
1003
+ setToken(null);
1004
+ setIsAuthReady(true);
1005
+ throw new Error(`Token refresh failed: ${token2.error}`);
1006
+ } else {
1007
+ setToken(token2);
1008
+ setPendingRefresh(false);
1009
+ if (token2.refresh_token) {
1010
+ await storageAdapter.set(STORAGE_KEYS.REFRESH_TOKEN, token2.refresh_token);
1011
+ log("Updated refresh token in storage");
1012
+ }
1013
+ document.cookie = `basic_access_token=${token2.access_token}; Secure; SameSite=Strict; HttpOnly=false`;
1014
+ log("Updated access token in cookie");
1015
+ }
1016
+ return token2;
1017
+ } catch (error2) {
1018
+ log("Token refresh error:", error2);
1019
+ if (!error2.message.includes("offline") && !error2.message.includes("Network")) {
1020
+ await storageAdapter.remove(STORAGE_KEYS.REFRESH_TOKEN);
1021
+ await storageAdapter.remove(STORAGE_KEYS.USER_INFO);
1022
+ document.cookie = `basic_token=; Secure; SameSite=Strict`;
1023
+ document.cookie = `basic_access_token=; Secure; SameSite=Strict`;
1024
+ setUser({});
1025
+ setIsSignedIn(false);
1026
+ setToken(null);
1027
+ setIsAuthReady(true);
1028
+ }
1029
+ throw error2;
669
1030
  }
670
- return token2;
671
1031
  };
672
1032
  const db_ = (tableName) => {
673
1033
  const checkSignIn = () => {
@@ -710,12 +1070,13 @@ function BasicProvider({ children, project_id, schema, debug = false }) {
710
1070
  user,
711
1071
  signout,
712
1072
  signin,
1073
+ signinWithCode,
713
1074
  getToken,
714
1075
  getSignInLink,
715
1076
  db: syncRef.current ? syncRef.current : noDb,
716
1077
  dbStatus
717
1078
  }, children: [
718
- error && /* @__PURE__ */ jsx(ErrorDisplay, { error }),
1079
+ error && isDevelopment() && /* @__PURE__ */ jsx(ErrorDisplay, { error }),
719
1080
  isReady && children
720
1081
  ] });
721
1082
  }