@k3-universe/react-kit 0.0.29 → 0.0.31

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 (157) hide show
  1. package/dist/index.d.ts +1 -0
  2. package/dist/index.d.ts.map +1 -1
  3. package/dist/index.js +1023 -25
  4. package/dist/kit/builder/auth/components/Can.d.ts +13 -0
  5. package/dist/kit/builder/auth/components/Can.d.ts.map +1 -0
  6. package/dist/kit/builder/auth/components/RequireAuth.d.ts +45 -0
  7. package/dist/kit/builder/auth/components/RequireAuth.d.ts.map +1 -0
  8. package/dist/kit/builder/auth/components/ShowWhenAuthenticated.d.ts +8 -0
  9. package/dist/kit/builder/auth/components/ShowWhenAuthenticated.d.ts.map +1 -0
  10. package/dist/kit/builder/auth/components/ShowWhenError.d.ts +8 -0
  11. package/dist/kit/builder/auth/components/ShowWhenError.d.ts.map +1 -0
  12. package/dist/kit/builder/auth/components/ShowWhenLoading.d.ts +8 -0
  13. package/dist/kit/builder/auth/components/ShowWhenLoading.d.ts.map +1 -0
  14. package/dist/kit/builder/auth/components/ShowWhenUnauthenticated.d.ts +8 -0
  15. package/dist/kit/builder/auth/components/ShowWhenUnauthenticated.d.ts.map +1 -0
  16. package/dist/kit/builder/auth/components/withPermission.d.ts +7 -0
  17. package/dist/kit/builder/auth/components/withPermission.d.ts.map +1 -0
  18. package/dist/kit/builder/auth/hooks/action-hooks.d.ts +18 -0
  19. package/dist/kit/builder/auth/hooks/action-hooks.d.ts.map +1 -0
  20. package/dist/kit/builder/auth/hooks/core-hooks.d.ts +56 -0
  21. package/dist/kit/builder/auth/hooks/core-hooks.d.ts.map +1 -0
  22. package/dist/kit/builder/auth/hooks/index.d.ts +5 -0
  23. package/dist/kit/builder/auth/hooks/index.d.ts.map +1 -0
  24. package/dist/kit/builder/auth/hooks/permission-hooks.d.ts +18 -0
  25. package/dist/kit/builder/auth/hooks/permission-hooks.d.ts.map +1 -0
  26. package/dist/kit/builder/auth/hooks/token-hooks.d.ts +13 -0
  27. package/dist/kit/builder/auth/hooks/token-hooks.d.ts.map +1 -0
  28. package/dist/kit/builder/auth/index.d.ts +14 -8
  29. package/dist/kit/builder/auth/index.d.ts.map +1 -1
  30. package/dist/kit/builder/auth/{AuthProvider.d.ts → providers/AuthProvider.d.ts} +1 -1
  31. package/dist/kit/builder/auth/providers/AuthProvider.d.ts.map +1 -0
  32. package/dist/kit/builder/auth/types/adapter-config.d.ts +31 -0
  33. package/dist/kit/builder/auth/types/adapter-config.d.ts.map +1 -0
  34. package/dist/kit/builder/auth/types/adapter.d.ts +80 -0
  35. package/dist/kit/builder/auth/types/adapter.d.ts.map +1 -0
  36. package/dist/kit/builder/auth/types/core.d.ts +16 -0
  37. package/dist/kit/builder/auth/types/core.d.ts.map +1 -0
  38. package/dist/kit/builder/auth/types/index.d.ts +10 -0
  39. package/dist/kit/builder/auth/types/index.d.ts.map +1 -0
  40. package/dist/kit/builder/auth/types/middleware.d.ts +11 -0
  41. package/dist/kit/builder/auth/types/middleware.d.ts.map +1 -0
  42. package/dist/kit/builder/auth/types/permissions.d.ts +17 -0
  43. package/dist/kit/builder/auth/types/permissions.d.ts.map +1 -0
  44. package/dist/kit/builder/auth/types/state.d.ts +13 -0
  45. package/dist/kit/builder/auth/types/state.d.ts.map +1 -0
  46. package/dist/kit/builder/auth/types/storage.d.ts +20 -0
  47. package/dist/kit/builder/auth/types/storage.d.ts.map +1 -0
  48. package/dist/kit/builder/auth/types/token-manager.d.ts +7 -0
  49. package/dist/kit/builder/auth/types/token-manager.d.ts.map +1 -0
  50. package/dist/kit/builder/auth/types/utils.d.ts +7 -0
  51. package/dist/kit/builder/auth/types/utils.d.ts.map +1 -0
  52. package/dist/kit/builder/auth/{adapter.d.ts → utils/auth-adapter.d.ts} +2 -2
  53. package/dist/kit/builder/auth/utils/auth-adapter.d.ts.map +1 -0
  54. package/dist/kit/builder/auth/utils/client-adapters/apollo-link.d.ts +4 -0
  55. package/dist/kit/builder/auth/utils/client-adapters/apollo-link.d.ts.map +1 -0
  56. package/dist/kit/builder/auth/utils/client-adapters/axios.d.ts +6 -0
  57. package/dist/kit/builder/auth/utils/client-adapters/axios.d.ts.map +1 -0
  58. package/dist/kit/builder/auth/utils/client-adapters/fetch.d.ts +6 -0
  59. package/dist/kit/builder/auth/utils/client-adapters/fetch.d.ts.map +1 -0
  60. package/dist/kit/builder/auth/utils/client-adapters/graphql.d.ts +9 -0
  61. package/dist/kit/builder/auth/utils/client-adapters/graphql.d.ts.map +1 -0
  62. package/dist/kit/builder/auth/utils/client-adapters/index.d.ts +7 -0
  63. package/dist/kit/builder/auth/utils/client-adapters/index.d.ts.map +1 -0
  64. package/dist/kit/builder/auth/utils/client-adapters/rest.d.ts +9 -0
  65. package/dist/kit/builder/auth/utils/client-adapters/rest.d.ts.map +1 -0
  66. package/dist/kit/builder/auth/utils/client-adapters/urql-exchange.d.ts +14 -0
  67. package/dist/kit/builder/auth/utils/client-adapters/urql-exchange.d.ts.map +1 -0
  68. package/dist/kit/builder/auth/{permission-checker.d.ts → utils/permission-checker.d.ts} +1 -1
  69. package/dist/kit/builder/auth/utils/permission-checker.d.ts.map +1 -0
  70. package/dist/kit/builder/auth/utils/storage/browser.d.ts +11 -0
  71. package/dist/kit/builder/auth/utils/storage/browser.d.ts.map +1 -0
  72. package/dist/kit/builder/auth/utils/storage/cookie.d.ts +3 -0
  73. package/dist/kit/builder/auth/utils/storage/cookie.d.ts.map +1 -0
  74. package/dist/kit/builder/auth/utils/storage/encryption.d.ts +7 -0
  75. package/dist/kit/builder/auth/utils/storage/encryption.d.ts.map +1 -0
  76. package/dist/kit/builder/auth/utils/storage/env.d.ts +2 -0
  77. package/dist/kit/builder/auth/utils/storage/env.d.ts.map +1 -0
  78. package/dist/kit/builder/auth/utils/storage/factory.d.ts +6 -0
  79. package/dist/kit/builder/auth/utils/storage/factory.d.ts.map +1 -0
  80. package/dist/kit/builder/auth/utils/storage/index.d.ts +7 -0
  81. package/dist/kit/builder/auth/utils/storage/index.d.ts.map +1 -0
  82. package/dist/kit/builder/auth/utils/storage/memory.d.ts +3 -0
  83. package/dist/kit/builder/auth/utils/storage/memory.d.ts.map +1 -0
  84. package/dist/kit/builder/auth/{token-manager.d.ts → utils/token-manager.d.ts} +1 -1
  85. package/dist/kit/builder/auth/utils/token-manager.d.ts.map +1 -0
  86. package/dist/kit/components/login/Login.d.ts +2 -1
  87. package/dist/kit/components/login/Login.d.ts.map +1 -1
  88. package/dist/kit/layouts/admin/components/AdminLayout.d.ts +2 -1
  89. package/dist/kit/layouts/admin/components/AdminLayout.d.ts.map +1 -1
  90. package/dist/kit/themes/clean-slate.css +28 -4
  91. package/dist/kit/themes/default.css +28 -4
  92. package/dist/kit/themes/minimal-modern.css +28 -4
  93. package/dist/kit/themes/spotify.css +28 -4
  94. package/package.json +1 -1
  95. package/src/index.ts +1 -0
  96. package/src/kit/builder/auth/components/Can.tsx +27 -0
  97. package/src/kit/builder/auth/components/RequireAuth.tsx +78 -0
  98. package/src/kit/builder/auth/components/ShowWhenAuthenticated.tsx +10 -0
  99. package/src/kit/builder/auth/components/ShowWhenError.tsx +10 -0
  100. package/src/kit/builder/auth/components/ShowWhenLoading.tsx +10 -0
  101. package/src/kit/builder/auth/components/ShowWhenUnauthenticated.tsx +10 -0
  102. package/src/kit/builder/auth/components/withPermission.tsx +23 -0
  103. package/src/kit/builder/auth/hooks/action-hooks.ts +34 -0
  104. package/src/kit/builder/auth/hooks/core-hooks.ts +65 -0
  105. package/src/kit/builder/auth/hooks/index.ts +4 -0
  106. package/src/kit/builder/auth/hooks/permission-hooks.ts +43 -0
  107. package/src/kit/builder/auth/hooks/token-hooks.ts +25 -0
  108. package/src/kit/builder/auth/index.ts +16 -18
  109. package/src/kit/builder/auth/{AuthProvider.tsx → providers/AuthProvider.tsx} +1 -1
  110. package/src/kit/builder/auth/types/adapter-config.ts +44 -0
  111. package/src/kit/builder/auth/types/adapter.ts +132 -0
  112. package/src/kit/builder/auth/types/core.ts +27 -0
  113. package/src/kit/builder/auth/types/index.ts +9 -0
  114. package/src/kit/builder/auth/types/middleware.ts +20 -0
  115. package/src/kit/builder/auth/types/permissions.ts +23 -0
  116. package/src/kit/builder/auth/types/state.ts +16 -0
  117. package/src/kit/builder/auth/types/storage.ts +21 -0
  118. package/src/kit/builder/auth/types/token-manager.ts +9 -0
  119. package/src/kit/builder/auth/types/utils.ts +55 -0
  120. package/src/kit/builder/auth/{adapter.ts → utils/auth-adapter.ts} +3 -2
  121. package/src/kit/builder/auth/utils/client-adapters/apollo-link.ts +30 -0
  122. package/src/kit/builder/auth/utils/client-adapters/axios.ts +61 -0
  123. package/src/kit/builder/auth/utils/client-adapters/fetch.ts +48 -0
  124. package/src/kit/builder/auth/utils/client-adapters/graphql.ts +60 -0
  125. package/src/kit/builder/auth/utils/client-adapters/index.ts +6 -0
  126. package/src/kit/builder/auth/utils/client-adapters/rest.ts +60 -0
  127. package/src/kit/builder/auth/utils/client-adapters/urql-exchange.ts +76 -0
  128. package/src/kit/builder/auth/{permission-checker.ts → utils/permission-checker.ts} +1 -1
  129. package/src/kit/builder/auth/utils/storage/browser.ts +99 -0
  130. package/src/kit/builder/auth/utils/storage/cookie.ts +116 -0
  131. package/src/kit/builder/auth/utils/storage/encryption.ts +80 -0
  132. package/src/kit/builder/auth/utils/storage/env.ts +2 -0
  133. package/src/kit/builder/auth/utils/storage/factory.ts +37 -0
  134. package/src/kit/builder/auth/utils/storage/index.ts +6 -0
  135. package/src/kit/builder/auth/utils/storage/memory.ts +15 -0
  136. package/src/kit/builder/auth/{token-manager.ts → utils/token-manager.ts} +1 -1
  137. package/src/kit/components/login/Login.tsx +36 -21
  138. package/src/kit/layouts/admin/components/AdminLayout.tsx +24 -17
  139. package/dist/kit/builder/auth/AuthProvider.d.ts.map +0 -1
  140. package/dist/kit/builder/auth/adapter.d.ts.map +0 -1
  141. package/dist/kit/builder/auth/client-adapters.d.ts +0 -149
  142. package/dist/kit/builder/auth/client-adapters.d.ts.map +0 -1
  143. package/dist/kit/builder/auth/components.d.ts +0 -119
  144. package/dist/kit/builder/auth/components.d.ts.map +0 -1
  145. package/dist/kit/builder/auth/hooks.d.ts +0 -158
  146. package/dist/kit/builder/auth/hooks.d.ts.map +0 -1
  147. package/dist/kit/builder/auth/permission-checker.d.ts.map +0 -1
  148. package/dist/kit/builder/auth/storage.d.ts +0 -17
  149. package/dist/kit/builder/auth/storage.d.ts.map +0 -1
  150. package/dist/kit/builder/auth/token-manager.d.ts.map +0 -1
  151. package/dist/kit/builder/auth/types.d.ts +0 -183
  152. package/dist/kit/builder/auth/types.d.ts.map +0 -1
  153. package/src/kit/builder/auth/client-adapters.ts +0 -398
  154. package/src/kit/builder/auth/components.tsx +0 -221
  155. package/src/kit/builder/auth/hooks.ts +0 -237
  156. package/src/kit/builder/auth/storage.ts +0 -366
  157. package/src/kit/builder/auth/types.ts +0 -393
@@ -1,237 +0,0 @@
1
- import type {
2
- AuthSession,
3
- PermissionPolicy,
4
- PermissionRule,
5
- WithPermissionOptions,
6
- } from './types';
7
- import { useAuthContext } from './AuthProvider';
8
-
9
- // ============================================================================
10
- // Core Hooks
11
- // ============================================================================
12
-
13
- /**
14
- * Hook to access the entire auth context
15
- */
16
- export function useAuth() {
17
- return useAuthContext();
18
- }
19
-
20
- /**
21
- * Hook to get the current auth status
22
- */
23
- export function useAuthStatus() {
24
- const { status } = useAuthContext();
25
- return status;
26
- }
27
-
28
- /**
29
- * Hook to check if user is authenticated
30
- */
31
- export function useIsAuthenticated() {
32
- const { status } = useAuthContext();
33
- return status === 'authenticated';
34
- }
35
-
36
- /**
37
- * Hook to get the current session
38
- */
39
- export function useAuthSession<TSession extends AuthSession>() {
40
- const { session } = useAuthContext();
41
- return session as TSession | null;
42
- }
43
-
44
- /**
45
- * Hook to get the current user
46
- */
47
- export function useAuthUser<TUser>() {
48
- const { user } = useAuthContext();
49
- return user as TUser | null;
50
- }
51
-
52
- /**
53
- * Hook to get user roles
54
- */
55
- export function useAuthRoles<TRole extends string = string>() {
56
- const { roles } = useAuthContext();
57
- return roles as TRole[];
58
- }
59
-
60
- /**
61
- * Hook to get user permissions
62
- */
63
- export function useAuthPermissions<TPermission extends string = string>() {
64
- const { permissions } = useAuthContext();
65
- return permissions as TPermission[];
66
- }
67
-
68
- /**
69
- * Hook to get authentication error
70
- */
71
- export function useAuthError() {
72
- const { error } = useAuthContext();
73
- return error;
74
- }
75
-
76
- // ============================================================================
77
- // Permission Hooks
78
- // ============================================================================
79
-
80
- /**
81
- * Hook to check if user has specific permission(s)
82
- * Supports permission policies with AND/OR operators
83
- *
84
- * @example
85
- * ```tsx
86
- * // Check single permission
87
- * const canEdit = usePermission('post:edit');
88
- *
89
- * // Check multiple permissions (all required by default)
90
- * const canManage = usePermission(['post:edit', 'post:delete']);
91
- *
92
- * // Check multiple permissions (any one required)
93
- * const canModify = usePermission(['post:edit', 'post:delete'], { requireAll: false });
94
- *
95
- * // Check with policy
96
- * const canAccess = usePermission({
97
- * operator: 'OR',
98
- * permissions: ['admin:access', 'moderator:access']
99
- * });
100
- * ```
101
- */
102
- export function usePermission<TPermission extends string = string>(
103
- permission: TPermission | TPermission[] | PermissionPolicy<TPermission>,
104
- options?: { requireAll?: boolean },
105
- ) {
106
- const { hasPermission } = useAuthContext();
107
- return hasPermission(permission, options);
108
- }
109
-
110
- /**
111
- * Hook to check if user has specific role(s)
112
- *
113
- * @example
114
- * ```tsx
115
- * // Check single role
116
- * const isAdmin = useRole('admin');
117
- *
118
- * // Check multiple roles (all required by default)
119
- * const isSuperAdmin = useRole(['admin', 'super']);
120
- *
121
- * // Check multiple roles (any one required)
122
- * const isStaff = useRole(['admin', 'moderator'], { requireAll: false });
123
- * ```
124
- */
125
- export function useRole<TRole extends string = string>(
126
- roles: TRole | TRole[],
127
- options?: { requireAll?: boolean },
128
- ) {
129
- const { hasRole } = useAuthContext();
130
- return hasRole(roles, options);
131
- }
132
-
133
- /**
134
- * Hook to check complex permission rules
135
- * Combines role and permission checks with flexible logic
136
- *
137
- * @example
138
- * ```tsx
139
- * // Check if user has admin role AND edit permission
140
- * const canAdminEdit = useCan({
141
- * roles: 'admin',
142
- * permissions: 'post:edit',
143
- * requireAll: true
144
- * });
145
- *
146
- * // Check if user has admin role OR edit permission
147
- * const canAccess = useCan({
148
- * roles: 'admin',
149
- * permissions: 'post:edit',
150
- * requireAll: false
151
- * });
152
- *
153
- * // Complex policy
154
- * const canManage = useCan({
155
- * roles: ['admin', 'moderator'],
156
- * permissions: {
157
- * operator: 'OR',
158
- * permissions: ['post:edit', 'post:delete']
159
- * }
160
- * });
161
- * ```
162
- */
163
- export function useCan<
164
- TRole extends string = string,
165
- TPermission extends string = string,
166
- >(
167
- rule:
168
- | WithPermissionOptions<TRole, TPermission>
169
- | PermissionRule<TRole, TPermission>,
170
- ) {
171
- const { can } = useAuthContext();
172
- return can(rule as PermissionRule<TRole, TPermission>);
173
- }
174
-
175
- // ============================================================================
176
- // Token Hooks
177
- // ============================================================================
178
-
179
- /**
180
- * Hook to get the current auth token
181
- */
182
- export function useAuthToken() {
183
- const { getToken } = useAuthContext();
184
- return getToken();
185
- }
186
-
187
- /**
188
- * Hook to check if token is expired
189
- */
190
- export function useIsTokenExpired() {
191
- const { isTokenExpired } = useAuthContext();
192
- return isTokenExpired();
193
- }
194
-
195
- /**
196
- * Hook to get time until token expiry in milliseconds
197
- */
198
- export function useTimeUntilExpiry() {
199
- const { getTimeUntilExpiry } = useAuthContext();
200
- return getTimeUntilExpiry();
201
- }
202
-
203
- // ============================================================================
204
- // Action Hooks
205
- // ============================================================================
206
-
207
- /**
208
- * Hook to get the login function
209
- */
210
- export function useLogin<TCredentials = unknown>() {
211
- const { login } = useAuthContext();
212
- return login as ((credentials: TCredentials) => Promise<unknown>) | undefined;
213
- }
214
-
215
- /**
216
- * Hook to get the logout function
217
- */
218
- export function useLogout() {
219
- const { logout } = useAuthContext();
220
- return logout;
221
- }
222
-
223
- /**
224
- * Hook to get the refresh function
225
- */
226
- export function useRefresh() {
227
- const { refresh } = useAuthContext();
228
- return refresh;
229
- }
230
-
231
- /**
232
- * Hook to get the setSession function
233
- */
234
- export function useSetSession<TSession extends AuthSession>() {
235
- const { setSession } = useAuthContext();
236
- return setSession as (session: TSession | null) => Promise<unknown>;
237
- }
@@ -1,366 +0,0 @@
1
- import type { AuthStorage, StorageOptions } from './types';
2
-
3
- const isBrowser =
4
- typeof window !== 'undefined' && typeof window.localStorage !== 'undefined';
5
-
6
- // ============================================================================
7
- // Memory Storage
8
- // ============================================================================
9
-
10
- export const createMemoryStorage = <T>(): AuthStorage<T> => {
11
- let value: T | null = null;
12
-
13
- return {
14
- get: () => value,
15
- set: (next) => {
16
- value = next;
17
- },
18
- clear: () => {
19
- value = null;
20
- },
21
- };
22
- };
23
-
24
- // ============================================================================
25
- // Encryption Utilities
26
- // ============================================================================
27
-
28
- /**
29
- * Simple encryption/decryption using browser's subtle crypto
30
- * For production, consider using a more robust encryption library
31
- */
32
- class SimpleEncryption {
33
- private key: string;
34
-
35
- constructor(key: string) {
36
- this.key = key;
37
- }
38
-
39
- async encrypt(text: string): Promise<string> {
40
- if (!crypto?.subtle) {
41
- console.warn(
42
- '[auth2][storage] Crypto API not available, storing unencrypted',
43
- );
44
- return text;
45
- }
46
-
47
- try {
48
- const encoder = new TextEncoder();
49
- const data = encoder.encode(text);
50
- const keyData = encoder.encode(this.key.padEnd(32, '0').slice(0, 32));
51
-
52
- const cryptoKey = await crypto.subtle.importKey(
53
- 'raw',
54
- keyData,
55
- { name: 'AES-GCM', length: 256 },
56
- false,
57
- ['encrypt'],
58
- );
59
-
60
- const iv = crypto.getRandomValues(new Uint8Array(12));
61
- const encrypted = await crypto.subtle.encrypt(
62
- { name: 'AES-GCM', iv },
63
- cryptoKey,
64
- data,
65
- );
66
-
67
- const result = new Uint8Array(iv.length + encrypted.byteLength);
68
- result.set(iv, 0);
69
- result.set(new Uint8Array(encrypted), iv.length);
70
-
71
- return btoa(String.fromCharCode(...result));
72
- } catch (error) {
73
- console.error('[auth2][storage] Encryption failed:', error);
74
- return text;
75
- }
76
- }
77
-
78
- async decrypt(encrypted: string): Promise<string> {
79
- if (!crypto?.subtle) {
80
- console.warn(
81
- '[auth2][storage] Crypto API not available, reading unencrypted',
82
- );
83
- return encrypted;
84
- }
85
-
86
- try {
87
- const encoder = new TextEncoder();
88
- const decoder = new TextDecoder();
89
- const keyData = encoder.encode(this.key.padEnd(32, '0').slice(0, 32));
90
-
91
- const cryptoKey = await crypto.subtle.importKey(
92
- 'raw',
93
- keyData,
94
- { name: 'AES-GCM', length: 256 },
95
- false,
96
- ['decrypt'],
97
- );
98
-
99
- const data = Uint8Array.from(atob(encrypted), (c) => c.charCodeAt(0));
100
- const iv = data.slice(0, 12);
101
- const encryptedData = data.slice(12);
102
-
103
- const decrypted = await crypto.subtle.decrypt(
104
- { name: 'AES-GCM', iv },
105
- cryptoKey,
106
- encryptedData,
107
- );
108
-
109
- return decoder.decode(decrypted);
110
- } catch (error) {
111
- console.error('[auth2][storage] Decryption failed:', error);
112
- return encrypted;
113
- }
114
- }
115
- }
116
-
117
- // ============================================================================
118
- // Cookie Storage
119
- // ============================================================================
120
-
121
- export const createCookieStorage = <T>(
122
- options: StorageOptions,
123
- ): AuthStorage<T> => {
124
- const key = options.key ?? 'auth2_session';
125
- const cookieOpts = options.cookieOptions ?? {};
126
- const fallback = createMemoryStorage<T>();
127
- const encryption =
128
- options.encrypt && options.encryptionKey
129
- ? new SimpleEncryption(options.encryptionKey)
130
- : null;
131
- const cookieSetter =
132
- typeof Document !== 'undefined'
133
- ? Object.getOwnPropertyDescriptor(Document.prototype, 'cookie')?.set
134
- : undefined;
135
-
136
- const assignCookie = (value: string) => {
137
- if (!isBrowser || typeof document === 'undefined') return;
138
- if (cookieSetter) {
139
- cookieSetter.call(document, value);
140
- return;
141
- }
142
- Reflect.set(document, 'cookie', value);
143
- };
144
-
145
- const serialize = async (value: T | null): Promise<string | null> => {
146
- if (value === null) return null;
147
- const json = JSON.stringify(value);
148
- return encryption ? await encryption.encrypt(json) : json;
149
- };
150
-
151
- const deserialize = async (value: string | null): Promise<T | null> => {
152
- if (!value) return null;
153
- try {
154
- const decrypted = encryption ? await encryption.decrypt(value) : value;
155
- return JSON.parse(decrypted) as T;
156
- } catch {
157
- return null;
158
- }
159
- };
160
-
161
- const getCookie = (name: string): string | null => {
162
- if (!isBrowser) return null;
163
- const matches = document.cookie.match(
164
- new RegExp(
165
- `(?:^|; )${name.replace(/([.$?*|{}()[\]\\/+^])/g, '\\$1')}=([^;]*)`,
166
- ),
167
- );
168
- return matches ? decodeURIComponent(matches[1]) : null;
169
- };
170
-
171
- const setCookie = (
172
- name: string,
173
- value: string,
174
- opts: typeof cookieOpts = {},
175
- ) => {
176
- if (!isBrowser) return;
177
-
178
- let cookie = `${encodeURIComponent(name)}=${encodeURIComponent(value)}`;
179
-
180
- if (opts.maxAge) cookie += `; max-age=${opts.maxAge}`;
181
- if (opts.domain) cookie += `; domain=${opts.domain}`;
182
- if (opts.path !== undefined) cookie += `; path=${opts.path}`;
183
- else cookie += '; path=/';
184
- if (opts.secure) cookie += '; secure';
185
- if (opts.sameSite) cookie += `; samesite=${opts.sameSite}`;
186
-
187
- assignCookie(cookie);
188
- };
189
-
190
- const deleteCookie = (name: string) => {
191
- if (!isBrowser) return;
192
- assignCookie(`${name}=; max-age=0; path=/`);
193
- };
194
-
195
- return {
196
- get: async () => {
197
- try {
198
- const raw = getCookie(key);
199
- const value = await deserialize(raw);
200
- fallback.set(value);
201
- return value;
202
- } catch (error) {
203
- console.warn('[auth2][storage] Failed to read from cookie', error);
204
- return fallback.get();
205
- }
206
- },
207
- set: async (value) => {
208
- try {
209
- const serialized = await serialize(value);
210
- if (serialized === null) {
211
- deleteCookie(key);
212
- } else {
213
- setCookie(key, serialized, cookieOpts);
214
- }
215
- fallback.set(value);
216
- } catch (error) {
217
- console.warn('[auth2][storage] Failed to write to cookie', error);
218
- fallback.set(value);
219
- }
220
- },
221
- clear: () => {
222
- try {
223
- deleteCookie(key);
224
- } catch (error) {
225
- console.warn('[auth2][storage] Failed to clear cookie', error);
226
- } finally {
227
- fallback.clear();
228
- }
229
- },
230
- };
231
- };
232
-
233
- // ============================================================================
234
- // Browser Storage (localStorage/sessionStorage)
235
- // ============================================================================
236
-
237
- export type BrowserStorageOptions<T> = {
238
- key: string;
239
- serialize?: (value: T | null) => Promise<string | null> | string | null;
240
- deserialize?: (value: string | null) => Promise<T | null> | T | null;
241
- storage?: Storage;
242
- encrypt?: boolean;
243
- encryptionKey?: string;
244
- };
245
-
246
- export const createBrowserStorage = <T>(
247
- options: BrowserStorageOptions<T>,
248
- ): AuthStorage<T> => {
249
- const fallback = createMemoryStorage<T>();
250
- const targetStorage =
251
- options.storage ?? (isBrowser ? window.localStorage : undefined);
252
-
253
- const encryption =
254
- options.encrypt && options.encryptionKey
255
- ? new SimpleEncryption(options.encryptionKey)
256
- : null;
257
-
258
- const defaultSerialize = async (value: T | null): Promise<string | null> => {
259
- if (value === null) return null;
260
- const json = JSON.stringify(value);
261
- return encryption ? await encryption.encrypt(json) : json;
262
- };
263
-
264
- const defaultDeserialize = async (
265
- value: string | null,
266
- ): Promise<T | null> => {
267
- if (!value) return null;
268
- try {
269
- const decrypted = encryption ? await encryption.decrypt(value) : value;
270
- return JSON.parse(decrypted) as T;
271
- } catch {
272
- return null;
273
- }
274
- };
275
-
276
- const serialize = options.serialize
277
- ? async (value: T | null) => {
278
- const result = await options.serialize?.(value);
279
- return result ?? null;
280
- }
281
- : defaultSerialize;
282
-
283
- const deserialize = options.deserialize
284
- ? async (value: string | null) => {
285
- const result = await options.deserialize?.(value);
286
- return result ?? null;
287
- }
288
- : defaultDeserialize;
289
-
290
- if (!targetStorage) {
291
- return fallback;
292
- }
293
-
294
- return {
295
- get: async () => {
296
- try {
297
- const raw = targetStorage.getItem(options.key);
298
- const value = await deserialize(raw);
299
- fallback.set(value);
300
- return value;
301
- } catch (error) {
302
- console.warn('[auth2][storage] Failed to read from storage', error);
303
- return fallback.get();
304
- }
305
- },
306
- set: async (value) => {
307
- try {
308
- const serialized = await serialize(value);
309
- if (serialized === null) {
310
- targetStorage.removeItem(options.key);
311
- } else {
312
- targetStorage.setItem(options.key, serialized);
313
- }
314
- fallback.set(value);
315
- } catch (error) {
316
- console.warn('[auth2][storage] Failed to write to storage', error);
317
- fallback.set(value);
318
- }
319
- },
320
- clear: () => {
321
- try {
322
- targetStorage.removeItem(options.key);
323
- } catch (error) {
324
- console.warn('[auth2][storage] Failed to clear storage', error);
325
- } finally {
326
- fallback.clear();
327
- }
328
- },
329
- };
330
- };
331
-
332
- // ============================================================================
333
- // Factory Function
334
- // ============================================================================
335
-
336
- /**
337
- * Creates appropriate storage based on options
338
- */
339
- export function createStorage<T>(options: StorageOptions = {}): AuthStorage<T> {
340
- const storageType = options.storage ?? 'local';
341
- const key = options.key ?? 'auth2_session';
342
-
343
- switch (storageType) {
344
- case 'cookie':
345
- return createCookieStorage<T>(options);
346
-
347
- case 'session':
348
- return createBrowserStorage<T>({
349
- key,
350
- storage: isBrowser ? window.sessionStorage : undefined,
351
- encrypt: options.encrypt,
352
- encryptionKey: options.encryptionKey,
353
- });
354
-
355
- case 'local':
356
- return createBrowserStorage<T>({
357
- key,
358
- storage: isBrowser ? window.localStorage : undefined,
359
- encrypt: options.encrypt,
360
- encryptionKey: options.encryptionKey,
361
- });
362
-
363
- default:
364
- return createMemoryStorage<T>();
365
- }
366
- }