@proveanything/smartlinks-auth-ui 0.5.22 → 0.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.
@@ -0,0 +1,349 @@
1
+ # Account Information Caching
2
+
3
+ The auth module provides intelligent caching for Smartlinks account information, reducing API calls and improving performance.
4
+
5
+ ## Basic Usage
6
+
7
+ ### Get Account Information
8
+
9
+ ```typescript
10
+ import { useAuth } from '@smartlinks/auth-ui';
11
+
12
+ function MyComponent() {
13
+ const auth = useAuth();
14
+
15
+ useEffect(() => {
16
+ const loadAccount = async () => {
17
+ try {
18
+ // Smart caching - uses cache if fresh, fetches if stale
19
+ const account = await auth.getAccount();
20
+ console.log('Account:', account);
21
+ } catch (error) {
22
+ console.error('Failed to load account:', error);
23
+ }
24
+ };
25
+
26
+ if (auth.isAuthenticated) {
27
+ loadAccount();
28
+ }
29
+ }, [auth.isAuthenticated]);
30
+ }
31
+ ```
32
+
33
+ ### Force Refresh
34
+
35
+ ```typescript
36
+ // Bypass cache and fetch fresh data
37
+ const account = await auth.getAccount(true);
38
+
39
+ // Or use convenience method
40
+ const account = await auth.refreshAccount();
41
+ ```
42
+
43
+ ### Access Cached Data Synchronously
44
+
45
+ ```typescript
46
+ // Access cached account info without triggering API call
47
+ const { accountInfo } = useAuth();
48
+
49
+ if (accountInfo) {
50
+ console.log('Cached account:', accountInfo);
51
+ }
52
+ ```
53
+
54
+ ## Configuration
55
+
56
+ ### Cache TTL
57
+
58
+ Configure how long account data is cached:
59
+
60
+ ```typescript
61
+ <AuthProvider accountCacheTTL={10 * 60 * 1000}> {/* 10 minutes */}
62
+ <App />
63
+ </AuthProvider>
64
+ ```
65
+
66
+ ### Preload on Login
67
+
68
+ Automatically fetch account info on login:
69
+
70
+ ```typescript
71
+ <AuthProvider preloadAccountInfo={true}>
72
+ <App />
73
+ </AuthProvider>
74
+ ```
75
+
76
+ ## Caching Strategy
77
+
78
+ **When cache is used:**
79
+ - Account info cached for 5 minutes by default (configurable)
80
+ - Subsequent calls return cached data without API request
81
+ - Cache persists across page refreshes (IndexedDB/localStorage)
82
+ - Cache synchronized across browser tabs
83
+
84
+ **When API is called:**
85
+ - First `getAccount()` call after login
86
+ - Cache has expired (past TTL)
87
+ - `forceRefresh` parameter is true
88
+ - Cache is cleared or missing
89
+
90
+ **Fallback behavior:**
91
+ - If API call fails, returns stale cached data if available
92
+ - Logs warning when returning stale data
93
+ - Throws error only if no cache exists and API fails
94
+
95
+ ## Cross-Tab Synchronization
96
+
97
+ When one tab fetches fresh account info, all other tabs automatically receive the updated data:
98
+
99
+ ```typescript
100
+ useEffect(() => {
101
+ const unsubscribe = auth.onAuthStateChange((event) => {
102
+ if (event.type === 'ACCOUNT_REFRESH') {
103
+ console.log('Account refreshed:', event.accountInfo);
104
+ // Update UI with fresh account data
105
+ }
106
+ });
107
+
108
+ return unsubscribe;
109
+ }, []);
110
+ ```
111
+
112
+ ## Best Practices
113
+
114
+ ### 1. Use Cached Data for Frequent Access
115
+
116
+ ```typescript
117
+ // Good - uses cached data
118
+ const { accountInfo } = useAuth();
119
+ const userName = accountInfo?.name;
120
+
121
+ // Avoid - triggers API call on every render if cache expired
122
+ useEffect(() => {
123
+ auth.getAccount().then(account => setName(account.name));
124
+ }, []);
125
+ ```
126
+
127
+ ### 2. Refresh on User-Initiated Actions
128
+
129
+ ```typescript
130
+ function RefreshButton() {
131
+ const auth = useAuth();
132
+
133
+ const handleRefresh = async () => {
134
+ await auth.refreshAccount(); // Force fresh fetch
135
+ toast.success('Account refreshed');
136
+ };
137
+
138
+ return <button onClick={handleRefresh}>Refresh Account</button>;
139
+ }
140
+ ```
141
+
142
+ ### 3. Clear Cache on Sensitive Operations
143
+
144
+ ```typescript
145
+ // After updating user profile via separate API
146
+ await updateUserProfile({ displayName: 'New Name' });
147
+
148
+ // Clear account cache to ensure fresh data on next fetch
149
+ await auth.clearAccountCache();
150
+
151
+ // Fetch fresh data
152
+ const updatedAccount = await auth.getAccount();
153
+ ```
154
+
155
+ ### 4. Handle Loading and Error States
156
+
157
+ ```typescript
158
+ function AccountDisplay() {
159
+ const auth = useAuth();
160
+ const [account, setAccount] = useState(null);
161
+ const [loading, setLoading] = useState(true);
162
+ const [error, setError] = useState(null);
163
+
164
+ useEffect(() => {
165
+ if (auth.isAuthenticated) {
166
+ auth.getAccount()
167
+ .then(setAccount)
168
+ .catch(setError)
169
+ .finally(() => setLoading(false));
170
+ }
171
+ }, [auth.isAuthenticated]);
172
+
173
+ if (loading) return <div>Loading...</div>;
174
+ if (error) return <div>Error: {error.message}</div>;
175
+ if (!account) return <div>No account data</div>;
176
+
177
+ return <div>Welcome, {account.name}!</div>;
178
+ }
179
+ ```
180
+
181
+ ## Common Patterns
182
+
183
+ ### Dashboard Component
184
+
185
+ ```typescript
186
+ function Dashboard() {
187
+ const auth = useAuth();
188
+ const [account, setAccount] = useState(auth.accountInfo); // Start with cached
189
+
190
+ useEffect(() => {
191
+ // Load account info, using cache if available
192
+ auth.getAccount().then(setAccount);
193
+ }, []);
194
+
195
+ return (
196
+ <div>
197
+ <h1>Welcome, {account?.name}</h1>
198
+ <p>Email: {account?.email}</p>
199
+ <p>Collections: {Object.keys(account?.sites || {}).length}</p>
200
+ </div>
201
+ );
202
+ }
203
+ ```
204
+
205
+ ### Permission Check
206
+
207
+ ```typescript
208
+ function AdminPanel() {
209
+ const auth = useAuth();
210
+ const [hasAccess, setHasAccess] = useState(false);
211
+
212
+ useEffect(() => {
213
+ auth.getAccount().then(account => {
214
+ const isAdmin = account.features?.adminCollections === true;
215
+ setHasAccess(isAdmin);
216
+ });
217
+ }, []);
218
+
219
+ if (!hasAccess) return <div>Access Denied</div>;
220
+
221
+ return <div>Admin Content</div>;
222
+ }
223
+ ```
224
+
225
+ ## Migration Guide
226
+
227
+ ### Before (Direct Smartlinks SDK)
228
+
229
+ ```typescript
230
+ import * as smartlinks from '@proveanything/smartlinks';
231
+
232
+ function MyComponent() {
233
+ useEffect(() => {
234
+ // Every call hits the API
235
+ smartlinks.auth.getAccount().then(account => {
236
+ console.log(account);
237
+ });
238
+ }, []);
239
+ }
240
+ ```
241
+
242
+ ### After (Auth Module with Caching)
243
+
244
+ ```typescript
245
+ import { useAuth } from '@smartlinks/auth-ui';
246
+
247
+ function MyComponent() {
248
+ const auth = useAuth();
249
+
250
+ useEffect(() => {
251
+ // Smart caching - much faster on subsequent calls
252
+ auth.getAccount().then(account => {
253
+ console.log(account);
254
+ });
255
+ }, []);
256
+ }
257
+ ```
258
+
259
+ ## Performance Benefits
260
+
261
+ - **Reduced API calls**: 5-minute cache means 1 API call instead of potentially dozens
262
+ - **Faster response**: Cached data returns in <1ms vs 100-500ms API latency
263
+ - **Better UX**: Instant data display on page navigation
264
+ - **Cross-tab efficiency**: One tab fetches, all tabs benefit
265
+ - **Offline resilience**: Stale cache returned when API unavailable
266
+
267
+ ## Troubleshooting
268
+
269
+ ### Cache Not Updating
270
+
271
+ ```typescript
272
+ // Force refresh to bypass cache
273
+ await auth.refreshAccount();
274
+ ```
275
+
276
+ ### Cross-Tab Sync Not Working
277
+
278
+ Ensure BroadcastChannel is supported (modern browsers):
279
+
280
+ ```typescript
281
+ if ('BroadcastChannel' in window) {
282
+ // Cross-tab sync available
283
+ } else {
284
+ // Fallback: cache still works, just no cross-tab sync
285
+ }
286
+ ```
287
+
288
+ ### Memory Concerns
289
+
290
+ Account info is stored in IndexedDB/localStorage, not memory:
291
+ - Minimal memory footprint
292
+ - Persists across page refreshes
293
+ - Automatically cleared on logout
294
+
295
+ ## API Reference
296
+
297
+ ### `getAccount(forceRefresh?: boolean): Promise<Record<string, any>>`
298
+
299
+ Retrieves account information with intelligent caching.
300
+
301
+ **Parameters:**
302
+ - `forceRefresh` (optional): Set to `true` to bypass cache and fetch fresh data
303
+
304
+ **Returns:** Promise resolving to account info object
305
+
306
+ **Throws:** Error if not authenticated or API fails with no cache available
307
+
308
+ ### `refreshAccount(): Promise<Record<string, any>>`
309
+
310
+ Convenience method to force refresh account info. Equivalent to `getAccount(true)`.
311
+
312
+ **Returns:** Promise resolving to fresh account info
313
+
314
+ ### `clearAccountCache(): Promise<void>`
315
+
316
+ Clears the cached account information. Next `getAccount()` call will fetch fresh data.
317
+
318
+ **Returns:** Promise that resolves when cache is cleared
319
+
320
+ ### `accountInfo: Record<string, any> | null`
321
+
322
+ Synchronously accessible cached account information. Returns `null` if no cache exists.
323
+
324
+ ## Implementation Details
325
+
326
+ ### Storage Architecture
327
+
328
+ - **Primary storage**: IndexedDB for structured data and better capacity
329
+ - **Fallback storage**: localStorage for browsers with limited IndexedDB support
330
+ - **In-memory cache**: React state for synchronous access within components
331
+
332
+ ### Cache Structure
333
+
334
+ ```typescript
335
+ {
336
+ data: Record<string, any>, // The actual account info
337
+ cachedAt: number, // Timestamp when data was cached
338
+ expiresAt: number // Timestamp when cache expires
339
+ }
340
+ ```
341
+
342
+ ### Cross-Tab Synchronization
343
+
344
+ Uses BroadcastChannel API and storage events to notify all browser tabs when:
345
+ - Account info is fetched/refreshed in any tab
346
+ - Account cache is cleared
347
+ - User logs out (clears all caches)
348
+
349
+ All tabs automatically update their in-memory state without additional API calls.