@sanvika/auth 2.5.9 → 2.6.0

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.
Files changed (2) hide show
  1. package/dist/index.js +93 -40
  2. package/package.json +6 -1
package/dist/index.js CHANGED
@@ -1,7 +1,14 @@
1
1
  "use client";
2
2
 
3
3
  // SanvikaAuthProvider.jsx
4
- import { createContext, useContext, useEffect, useState, useCallback } from "react";
4
+ import {
5
+ createContext,
6
+ useContext,
7
+ useEffect,
8
+ useState,
9
+ useCallback,
10
+ useMemo
11
+ } from "react";
5
12
 
6
13
  // constants.js
7
14
  var STORAGE_KEYS = {
@@ -14,29 +21,68 @@ var DEFAULT_AVATAR_SVG = `data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/200
14
21
  import { jsx } from "react/jsx-runtime";
15
22
  var S_AUTH_URL = "https://accounts.sanvikaproduction.com";
16
23
  var SanvikaAuthContext = createContext(null);
24
+ function randomDeviceId() {
25
+ try {
26
+ if (typeof crypto !== "undefined" && typeof crypto.randomUUID === "function") {
27
+ return crypto.randomUUID();
28
+ }
29
+ } catch {
30
+ }
31
+ return `device-${Date.now()}-${Math.random().toString(36).slice(2, 11)}`;
32
+ }
33
+ function createDefaultWebPersistence() {
34
+ return {
35
+ getItem: async (key) => typeof localStorage !== "undefined" ? localStorage.getItem(key) : null,
36
+ setItem: async (key, value) => {
37
+ if (typeof localStorage !== "undefined") {
38
+ localStorage.setItem(key, value);
39
+ }
40
+ },
41
+ removeItem: async (key) => {
42
+ if (typeof localStorage !== "undefined") {
43
+ localStorage.removeItem(key);
44
+ }
45
+ }
46
+ };
47
+ }
17
48
  function SanvikaAuthProvider({
18
49
  children,
19
50
  clientId,
20
51
  redirectUri,
21
- dashboardPath
52
+ dashboardPath,
53
+ persistence: persistenceProp
22
54
  }) {
55
+ const persistence = useMemo(() => {
56
+ return persistenceProp || createDefaultWebPersistence();
57
+ }, [persistenceProp]);
23
58
  const [user, setUser] = useState(null);
24
59
  const [accessToken, setToken] = useState(null);
25
60
  const [loading, setLoading] = useState(true);
26
61
  useEffect(() => {
27
- try {
28
- const storedToken = localStorage.getItem(STORAGE_KEYS.ACCESS_TOKEN);
29
- const storedUser = localStorage.getItem(STORAGE_KEYS.USER);
30
- if (storedToken && storedUser) {
31
- setToken(storedToken);
32
- setUser(JSON.parse(storedUser));
62
+ let cancelled = false;
63
+ (async () => {
64
+ try {
65
+ const storedToken = await persistence.getItem(STORAGE_KEYS.ACCESS_TOKEN);
66
+ const storedUser = await persistence.getItem(STORAGE_KEYS.USER);
67
+ if (cancelled) {
68
+ return;
69
+ }
70
+ if (storedToken && storedUser) {
71
+ setToken(storedToken);
72
+ setUser(JSON.parse(storedUser));
73
+ }
74
+ } catch (e) {
75
+ console.error("[SanvikaAuth] Failed to load session:", e);
76
+ } finally {
77
+ if (!cancelled) {
78
+ setLoading(false);
79
+ }
33
80
  }
34
- } catch (e) {
35
- console.error("[SanvikaAuth] Failed to load from localStorage:", e);
36
- } finally {
37
- setLoading(false);
38
- }
39
- }, []);
81
+ })();
82
+ return () => {
83
+ cancelled = true;
84
+ };
85
+ }, [persistence]);
40
86
  const login = async ({ mobile, password, deviceId, deviceName }) => {
41
87
  const response = await fetch(`${S_AUTH_URL}/api/auth/login`, {
42
88
  method: "POST",
@@ -44,53 +90,61 @@ function SanvikaAuthProvider({
44
90
  body: JSON.stringify({
45
91
  mobile,
46
92
  password,
47
- deviceId: deviceId || crypto.randomUUID(),
93
+ deviceId: deviceId || randomDeviceId(),
48
94
  deviceName: deviceName || "Browser"
49
95
  })
50
96
  });
51
97
  const data = await response.json();
52
- if (!data.success) throw new Error(data.error);
53
- localStorage.setItem(STORAGE_KEYS.ACCESS_TOKEN, data.accessToken);
54
- localStorage.setItem(STORAGE_KEYS.USER, JSON.stringify(data.user));
98
+ if (!data.success) {
99
+ throw new Error(data.error || data.message || "Login failed");
100
+ }
101
+ await persistence.setItem(STORAGE_KEYS.ACCESS_TOKEN, data.accessToken);
102
+ await persistence.setItem(STORAGE_KEYS.USER, JSON.stringify(data.user));
55
103
  setToken(data.accessToken);
56
104
  setUser(data.user);
57
105
  return data;
58
106
  };
59
- const setAuth = (token, userData) => {
60
- localStorage.setItem(STORAGE_KEYS.ACCESS_TOKEN, token);
61
- localStorage.setItem(STORAGE_KEYS.USER, JSON.stringify(userData));
62
- setToken(token);
63
- setUser(userData);
64
- };
107
+ const setAuth = useCallback(
108
+ (token, userData) => {
109
+ setToken(token);
110
+ setUser(userData);
111
+ void persistence.setItem(STORAGE_KEYS.ACCESS_TOKEN, token).catch((e) => console.error("[SanvikaAuth] setAuth token persist:", e));
112
+ void persistence.setItem(STORAGE_KEYS.USER, JSON.stringify(userData)).catch((e) => console.error("[SanvikaAuth] setAuth user persist:", e));
113
+ },
114
+ [persistence]
115
+ );
65
116
  const logout = async () => {
66
117
  try {
67
118
  await fetch(`${S_AUTH_URL}/api/auth/logout`, {
68
119
  method: "POST",
69
120
  headers: {
70
121
  "Content-Type": "application/json",
71
- Authorization: `Bearer ${accessToken}`
122
+ ...accessToken ? { Authorization: `Bearer ${accessToken}` } : {}
72
123
  }
73
124
  });
74
125
  } catch (e) {
75
126
  console.error("[SanvikaAuth] Logout API error:", e);
76
127
  } finally {
77
- localStorage.removeItem(STORAGE_KEYS.ACCESS_TOKEN);
78
- localStorage.removeItem(STORAGE_KEYS.USER);
128
+ await persistence.removeItem(STORAGE_KEYS.ACCESS_TOKEN);
129
+ await persistence.removeItem(STORAGE_KEYS.USER);
79
130
  setToken(null);
80
131
  setUser(null);
81
132
  }
82
133
  };
83
- const authFetch = useCallback(async (url, opts = {}) => {
84
- const currentToken = typeof window !== "undefined" ? localStorage.getItem(STORAGE_KEYS.ACCESS_TOKEN) : null;
85
- const headers = {
86
- "Content-Type": "application/json",
87
- ...currentToken && { Authorization: `Bearer ${currentToken}` },
88
- ...opts.headers
89
- };
90
- const res = await fetch(url, { ...opts, headers });
91
- const data = await res.json();
92
- return { data, success: data.success };
93
- }, []);
134
+ const authFetch = useCallback(
135
+ async (url, opts = {}) => {
136
+ const currentToken = await persistence.getItem(STORAGE_KEYS.ACCESS_TOKEN);
137
+ const headers = {
138
+ "Content-Type": "application/json",
139
+ ...currentToken && { Authorization: `Bearer ${currentToken}` },
140
+ ...opts.headers
141
+ };
142
+ const res = await fetch(url, { ...opts, headers });
143
+ const data = await res.json();
144
+ return { data, success: data.success };
145
+ },
146
+ [persistence]
147
+ );
94
148
  const value = {
95
149
  user,
96
150
  accessToken,
@@ -109,8 +163,7 @@ function SanvikaAuthProvider({
109
163
  return /* @__PURE__ */ jsx(SanvikaAuthContext.Provider, { value, children });
110
164
  }
111
165
  function useSanvikaAuth() {
112
- const ctx = useContext(SanvikaAuthContext);
113
- return ctx;
166
+ return useContext(SanvikaAuthContext);
114
167
  }
115
168
 
116
169
  // SanvikaAccountButton.jsx
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sanvika/auth",
3
- "version": "2.5.9",
3
+ "version": "2.6.0",
4
4
  "description": "Sanvika Auth SDK — React components/hooks + server-side token verification and user proxy",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -23,6 +23,11 @@
23
23
  "next": ">=16.2.1",
24
24
  "react": ">=18"
25
25
  },
26
+ "peerDependenciesMeta": {
27
+ "next": {
28
+ "optional": true
29
+ }
30
+ },
26
31
  "devDependencies": {
27
32
  "tsup": "^8.5.1"
28
33
  },