@dariushstony/smart-storage 0.1.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.
package/README.md ADDED
@@ -0,0 +1,764 @@
1
+ # @dariushstony/smart-storage
2
+
3
+ [![npm version](https://badge.fury.io/js/@dariushstony%2Fsmart-storage.svg)](https://www.npmjs.com/package/@dariushstony/smart-storage)
4
+ [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](./LICENSE)
5
+ [![TypeScript](https://img.shields.io/badge/TypeScript-5.9-blue.svg)](https://www.typescriptlang.org/)
6
+
7
+ A **robust, SSR-safe, and production-ready wrapper** around Web Storage (`localStorage` / `sessionStorage` / `in-memory`) with:
8
+
9
+ - ๐Ÿ—„๏ธ Three storage types: `'local'`, `'session'`, `'in-memory'`
10
+ - โฑ๏ธ TTL-based expiration
11
+ - โšก Debounced writes
12
+ - ๐Ÿงน Automatic cleanup
13
+ - ๐Ÿ”„ SSR-safe with automatic fallback
14
+ - ๐Ÿ›ก๏ธ Strong safety guarantees and detailed diagnostics
15
+ - ๐Ÿ“ฆ Dual package: ESM and CommonJS support
16
+
17
+ Designed for **real-world frontend applications** where correctness, performance, and edge-case handling matter.
18
+
19
+ ---
20
+
21
+ ## ๐Ÿ“ฆ Installation
22
+
23
+ ```bash
24
+ npm install @dariushstony/smart-storage
25
+ # or
26
+ yarn add @dariushstony/smart-storage
27
+ # or
28
+ pnpm add @dariushstony/smart-storage
29
+ ```
30
+
31
+ **Works with:**
32
+
33
+ - โœ… ESM (`import`)
34
+ - โœ… CommonJS (`require`)
35
+ - โœ… TypeScript
36
+ - โœ… Node.js 18+
37
+ - โœ… Modern browsers
38
+ - โœ… SSR frameworks (Next.js, Nuxt, etc.)
39
+
40
+ ---
41
+
42
+ ## โš ๏ธ Security Warning (Read This First)
43
+
44
+ **Web Storage is NOT secure.**
45
+
46
+ - Data is fully accessible via JavaScript
47
+ - Vulnerable to XSS
48
+ - Easily inspectable by users
49
+
50
+ โŒ **Do NOT store**:
51
+
52
+ - Auth tokens
53
+ - Passwords
54
+ - Sensitive user data
55
+
56
+ โœ… **Use instead**:
57
+
58
+ - `httpOnly` cookies
59
+ - Secure server-side sessions
60
+
61
+ Treat **all stored data as potentially compromised** and validate on read.
62
+
63
+ ---
64
+
65
+ ## โœจ Features
66
+
67
+ - โœ… Three storage types: `'local'`, `'session'`, `'in-memory'`
68
+ - โœ… Safe SSR support (no `window` access on server)
69
+ - โœ… TTL (time-to-live) expiration
70
+ - โœ… Automatic expired-item cleanup
71
+ - โœ… Debounced writes (default: 100ms)
72
+ - โœ… Read-after-write consistency
73
+ - โœ… QuotaExceeded recovery logic
74
+ - โœ… Prototype-pollution protection
75
+ - โœ… Singleton per storage slice + type
76
+ - โœ… Detailed stats & diagnostics
77
+ - โœ… Zero dependencies
78
+
79
+ ---
80
+
81
+ ## ๐Ÿ“ค Exports
82
+
83
+ ```typescript
84
+ // Main exports
85
+ import {
86
+ getStorageSlice, // Create custom storage slices
87
+ disposeStorageSlice, // Clean up temporary slices
88
+ StorageVault, // Class for advanced usage
89
+ } from '@dariushstony/smart-storage';
90
+
91
+ // Type exports
92
+ import type {
93
+ StorageLogger,
94
+ StorageType, // 'local' | 'session' | 'in-memory'
95
+ StorageVaultOptions,
96
+ StorageStats,
97
+ StorageTransform,
98
+ StoredData,
99
+ DataRecord,
100
+ } from '@dariushstony/smart-storage';
101
+ ```
102
+
103
+ ---
104
+
105
+ ## ๐Ÿ—„๏ธ Storage Types
106
+
107
+ StorageVault supports three storage backends:
108
+
109
+ ```typescript
110
+ import { getStorageSlice } from '@dariushstony/smart-storage';
111
+
112
+ // 'local' - localStorage (persists across browser sessions)
113
+ const persistent = getStorageSlice('USER_DATA', {
114
+ storageType: 'local', // Default
115
+ });
116
+
117
+ // 'session' - sessionStorage (cleared when tab closes)
118
+ const temporary = getStorageSlice('WIZARD_STATE', {
119
+ storageType: 'session',
120
+ });
121
+
122
+ // 'in-memory' - Map (cleared on page reload, great for testing)
123
+ const testing = getStorageSlice('TEST_DATA', {
124
+ storageType: 'in-memory',
125
+ });
126
+ ```
127
+
128
+ ### When to use each type
129
+
130
+ | Type | Persistence | Use Case |
131
+ | ----------------- | ----------------- | --------------------------------------- |
132
+ | **`'local'`** | Across sessions | User preferences, cart, long-term cache |
133
+ | **`'session'`** | Until tab closes | Wizard flows, temporary form data |
134
+ | **`'in-memory'`** | Until page reload | Testing, SSR fallback, temporary data |
135
+
136
+ ---
137
+
138
+ ## ๐Ÿš€ Quick Start
139
+
140
+ ### ESM (Modern JavaScript)
141
+
142
+ ```typescript
143
+ import { getStorageSlice } from '@dariushstony/smart-storage';
144
+
145
+ // Create a storage slice (localStorage by default)
146
+ const storage = getStorageSlice('MY_APP');
147
+
148
+ // Store data
149
+ storage.setItem('theme', 'dark');
150
+
151
+ // Retrieve data
152
+ const theme = storage.getItem<string>('theme');
153
+ console.log(theme); // โ†’ "dark"
154
+ ```
155
+
156
+ ### CommonJS (Node.js)
157
+
158
+ ```javascript
159
+ const { getStorageSlice } = require('@dariushstony/smart-storage');
160
+
161
+ // Create a storage slice
162
+ const storage = getStorageSlice('MY_APP');
163
+
164
+ // Works the same way!
165
+ storage.setItem('user', { name: 'dariush', id: 123 });
166
+ ```
167
+
168
+ ### TypeScript
169
+
170
+ ```typescript
171
+ import { getStorageSlice } from '@dariushstony/smart-storage';
172
+
173
+ interface User {
174
+ name: string;
175
+ id: number;
176
+ }
177
+
178
+ const storage = getStorageSlice('MY_APP');
179
+
180
+ // Store with TTL (auto-expiry)
181
+ storage.setItem<User>('user', { name: 'dariush', id: 123 });
182
+
183
+ // Retrieve with type safety
184
+ const user = storage.getItem<User>('user');
185
+ if (user) {
186
+ console.log(user.name); // TypeScript knows the shape!
187
+ }
188
+ ```
189
+
190
+ ---
191
+
192
+ ## ๐Ÿงฉ Storage Slices (Recommended)
193
+
194
+ All data is stored as one JSON blob per slice. Slices help reduce re-serialization costs and isolate concerns.
195
+
196
+ ```typescript
197
+ import { getStorageSlice } from '@dariushstony/smart-storage';
198
+
199
+ // Persistent user preferences
200
+ const userPrefs = getStorageSlice('USER_PREFERENCES', {
201
+ storageType: 'local', // Default
202
+ });
203
+
204
+ // Temporary session cache
205
+ const tempCache = getStorageSlice('TEMP_CACHE', {
206
+ storageType: 'session',
207
+ });
208
+
209
+ // Testing data
210
+ const testData = getStorageSlice('TEST_DATA', {
211
+ storageType: 'in-memory',
212
+ });
213
+
214
+ userPrefs.setItem('theme', 'dark');
215
+ tempCache.setItem('data', { value: 123 }, 5 * 60 * 1000); // 5 min TTL
216
+ ```
217
+
218
+ ### When to use slices
219
+
220
+ - โœ… Split data by update frequency
221
+ - โœ… Isolate large or experimental data
222
+ - โœ… Avoid rewriting unrelated data on each update
223
+
224
+ ### Examples
225
+
226
+ โŒ **Bad** - Too granular:
227
+
228
+ ```typescript
229
+ getStorageSlice('USER_NAME');
230
+ getStorageSlice('USER_EMAIL');
231
+ ```
232
+
233
+ โœ… **Good** - Grouped logically:
234
+
235
+ ```typescript
236
+ getStorageSlice('USER_DATA');
237
+ ```
238
+
239
+ ---
240
+
241
+ ## โฑ TTL (Time-to-Live)
242
+
243
+ ```typescript
244
+ import { getStorageSlice } from '@dariushstony/smart-storage';
245
+
246
+ const storage = getStorageSlice('MY_APP');
247
+
248
+ // Expires in 1 hour
249
+ storage.setItem('token', 'abc123', 60 * 60 * 1000);
250
+
251
+ // Never expires
252
+ storage.setItem('config', { theme: 'dark' });
253
+
254
+ // Immediately deletes (TTL = 0)
255
+ storage.setItem('temp', 'data', 0);
256
+ ```
257
+
258
+ ### Get remaining TTL
259
+
260
+ ```typescript
261
+ const remainingMs = storage.getRemainingTTL('token');
262
+ if (remainingMs) {
263
+ console.log(`Expires in ${Math.floor(remainingMs / 1000)} seconds`);
264
+ }
265
+ ```
266
+
267
+ ### Update value without changing TTL
268
+
269
+ ```typescript
270
+ storage.setItem('counter', 0, 60000); // Expires in 1 minute
271
+ storage.updateItem('counter', 5); // Updates value, keeps same expiry
272
+ ```
273
+
274
+ ### Extend TTL
275
+
276
+ ```typescript
277
+ storage.extendTTL('token', 30 * 60 * 1000); // +30 minutes
278
+ ```
279
+
280
+ **Note:** If the item had no expiry, `extendTTL` will add one starting from now.
281
+
282
+ ---
283
+
284
+ ## ๐Ÿงน Cleanup
285
+
286
+ ### Automatic
287
+
288
+ - Expired items are removed on read
289
+ - Cleanup runs automatically on quota errors
290
+
291
+ ### Manual
292
+
293
+ ```typescript
294
+ const storage = getStorageSlice('MY_APP');
295
+ const removedCount = storage.cleanupExpiredItems();
296
+ console.log(`Removed ${removedCount} expired items`);
297
+ ```
298
+
299
+ ---
300
+
301
+ ## โšก Debounced Writes (Performance)
302
+
303
+ Writes are debounced (default: 100ms) to batch rapid updates.
304
+
305
+ - **Reads always see pending writes** (read-after-write consistency)
306
+ - **Pending writes flush automatically on page unload** (no data loss)
307
+
308
+ ### Force immediate persistence
309
+
310
+ ```typescript
311
+ const storage = getStorageSlice('MY_APP');
312
+ storage.flush();
313
+ ```
314
+
315
+ ### Disable debouncing
316
+
317
+ ```typescript
318
+ const criticalStorage = getStorageSlice('CRITICAL_DATA', { debounceMs: 0 });
319
+ ```
320
+
321
+ ### Custom debounce timing
322
+
323
+ ```typescript
324
+ const analyticsStorage = getStorageSlice('ANALYTICS', { debounceMs: 500 });
325
+ ```
326
+
327
+ ---
328
+
329
+ ## ๐Ÿ–ฅ SSR Behavior
330
+
331
+ - **Server:** Automatically uses in-memory storage (Map)
332
+ - **Client:** Uses Web Storage (localStorage/sessionStorage based on `storageType`)
333
+ - **Important:** Server data is NOT hydrated automatically
334
+
335
+ ### Recommended pattern
336
+
337
+ ```typescript
338
+ import { getStorageSlice } from '@dariushstony/smart-storage';
339
+
340
+ // Server โ†’ pass initial data via props
341
+ // Client โ†’ re-store in useEffect or client-side code
342
+
343
+ const storage = getStorageSlice('MY_APP');
344
+
345
+ // In your client-side initialization:
346
+ storage.setItem('data', initialData);
347
+ ```
348
+
349
+ ### Explicit in-memory for testing
350
+
351
+ ```typescript
352
+ const testStorage = getStorageSlice('TEST', {
353
+ storageType: 'in-memory', // No real storage, perfect for tests
354
+ debounceMs: 0, // Immediate writes for predictable tests
355
+ });
356
+ ```
357
+
358
+ ---
359
+
360
+ ## ๐Ÿง  Serialization Rules (JSON)
361
+
362
+ Uses `JSON.stringify()` internally.
363
+
364
+ ### Limitations
365
+
366
+ - โŒ `Functions`, `undefined`, `Symbol` โ†’ silently dropped
367
+ - โŒ Circular references โ†’ throws error
368
+ - โš ๏ธ `Date` โ†’ becomes string (must convert back manually)
369
+ - โš ๏ธ `Map`, `Set`, class instances โ†’ lose type information
370
+
371
+ ๐Ÿ‘‰ **Serialize complex types manually before storing.**
372
+
373
+ ---
374
+
375
+ ## ๐Ÿ“Š Stats & Debugging
376
+
377
+ ```typescript
378
+ import { getStorageSlice } from '@dariushstony/smart-storage';
379
+
380
+ const storage = getStorageSlice('MY_APP');
381
+ const stats = storage.getStats();
382
+ console.log(stats);
383
+ ```
384
+
385
+ ### Returns
386
+
387
+ ```typescript
388
+ {
389
+ itemCount: number;
390
+ sizeBytes: number;
391
+ stringLength: number;
392
+ maxSizeBytes: number;
393
+ quotaPercentage: number;
394
+ storageType: 'localStorage' | 'sessionStorage' | 'memory' | 'unavailable';
395
+ }
396
+ ```
397
+
398
+ ### Example usage
399
+
400
+ ```typescript
401
+ const storage = getStorageSlice('MY_APP');
402
+ const stats = storage.getStats();
403
+ console.log(`Using ${stats.itemCount} items`);
404
+ console.log(`Size: ${(stats.sizeBytes / 1024).toFixed(2)} KB`);
405
+ console.log(`Quota: ${stats.quotaPercentage.toFixed(1)}%`);
406
+
407
+ if (stats.quotaPercentage > 80) {
408
+ console.warn('Storage is over 80% full!');
409
+ storage.cleanupExpiredItems();
410
+ }
411
+ ```
412
+
413
+ ---
414
+
415
+ ## ๐Ÿ—‘ Clearing Data
416
+
417
+ ```typescript
418
+ import { getStorageSlice } from '@dariushstony/smart-storage';
419
+
420
+ const storage = getStorageSlice('MY_APP');
421
+ storage.clear(); // Clears only this slice
422
+ ```
423
+
424
+ **Note:** Other slices are unaffected.
425
+
426
+ ---
427
+
428
+ ## โ™ป๏ธ Disposing Slices
429
+
430
+ Useful for temporary or short-lived slices. Removes the instance from the singleton cache and cleans up event listeners.
431
+
432
+ ```typescript
433
+ import {
434
+ getStorageSlice,
435
+ disposeStorageSlice,
436
+ } from '@dariushstony/smart-storage';
437
+
438
+ const tempStorage = getStorageSlice('TEMP_SESSION', {
439
+ storageType: 'session',
440
+ });
441
+
442
+ // ... use storage ...
443
+
444
+ // Clean up when done (must match storageType used when creating)
445
+ disposeStorageSlice('TEMP_SESSION', {
446
+ storageType: 'session',
447
+ });
448
+ ```
449
+
450
+ **Important:** The options passed to `disposeStorageSlice` must match exactly with those used in `getStorageSlice`.
451
+
452
+ ---
453
+
454
+ ## ๐Ÿงช Testing Utilities
455
+
456
+ ### Clear all instances
457
+
458
+ ```typescript
459
+ import { StorageVault } from '@dariushstony/smart-storage';
460
+
461
+ // In test teardown:
462
+ afterEach(() => {
463
+ StorageVault.clearAllInstances();
464
+ });
465
+ ```
466
+
467
+ ### Use in-memory storage for tests
468
+
469
+ ```typescript
470
+ import { getStorageSlice } from '@dariushstony/smart-storage';
471
+
472
+ describe('My tests', () => {
473
+ const testVault = getStorageSlice('TEST_DATA', {
474
+ storageType: 'in-memory', // Isolated, no real storage
475
+ debounceMs: 0, // Immediate writes
476
+ });
477
+
478
+ afterEach(() => {
479
+ testVault.clear(); // Clean up after each test
480
+ });
481
+
482
+ it('should store data', () => {
483
+ testVault.setItem('key', 'value');
484
+ expect(testVault.getItem('key')).toBe('value');
485
+ });
486
+ });
487
+ ```
488
+
489
+ Flushes, cleans up, and removes all vault instances.
490
+
491
+ ---
492
+
493
+ ## ๐Ÿงฑ Error Handling & Logging
494
+
495
+ Inject your own logger (e.g., Sentry):
496
+
497
+ ```typescript
498
+ import { getStorageSlice } from '@dariushstony/smart-storage';
499
+ import type { StorageLogger } from '@dariushstony/smart-storage';
500
+
501
+ const customLogger: StorageLogger = {
502
+ log: (message, error) => {
503
+ console.error('[Storage]', message, error);
504
+ // Send to Sentry or your logging service
505
+ // Sentry.captureException(error, { extra: { message } });
506
+ },
507
+ };
508
+
509
+ const vault = getStorageSlice('APP_DATA', {
510
+ storageType: 'local',
511
+ logger: customLogger,
512
+ });
513
+ ```
514
+
515
+ ---
516
+
517
+ ## ๐Ÿ”ง Advanced Configuration
518
+
519
+ ```typescript
520
+ const customStorage = getStorageSlice('CUSTOM', {
521
+ storageType: 'session', // 'local' | 'session' | 'in-memory'
522
+ debounceMs: 200, // Custom debounce delay
523
+ maxSizeBytes: 10_000_000, // 10MB quota warning threshold
524
+ maxItemsInMemory: 2000, // Max items for in-memory fallback
525
+ logger: customLogger, // Custom logger integration
526
+ });
527
+ ```
528
+
529
+ ### Configuration Options
530
+
531
+ | Option | Type | Default | Description |
532
+ | ------------------ | ------------------------------------- | ------------ | -------------------------------------- |
533
+ | `storageType` | `'local' \| 'session' \| 'in-memory'` | `'local'` | Storage backend to use |
534
+ | `storageKey` | `string` | `'APP_DATA'` | Key under which to store data |
535
+ | `debounceMs` | `number` | `100` | Write debouncing delay (0 = immediate) |
536
+ | `maxSizeBytes` | `number` | `4_000_000` | Quota warning threshold (~4MB) |
537
+ | `maxItemsInMemory` | `number` | `1000` | Max items for in-memory storage |
538
+ | `logger` | `StorageLogger` | `undefined` | Custom error logger |
539
+
540
+ ---
541
+
542
+ ## ๐Ÿ“š API Reference
543
+
544
+ ### Write Operations
545
+
546
+ | Method | Returns | Description |
547
+ | ------------------------------- | --------- | -------------------------------------- |
548
+ | `setItem(key, value, ttl?)` | `boolean` | Stores a value with optional TTL |
549
+ | `updateItem(key, newValue)` | `boolean` | Updates value without changing TTL |
550
+ | `removeItem(key)` | `boolean` | Removes an item |
551
+ | `clear()` | `boolean` | Clears all data for this slice |
552
+ | `extendTTL(key, additionalTTL)` | `boolean` | Extends TTL or adds one if none exists |
553
+
554
+ ### Read Operations
555
+
556
+ | Method | Returns | Description |
557
+ | ---------------------- | ------------------------- | ---------------------------------------- |
558
+ | `getItem<T>(key)` | `T \| null` | Retrieves a value |
559
+ | `hasItem(key)` | `boolean` | Checks if item exists and is not expired |
560
+ | `getRemainingTTL(key)` | `number \| null` | Returns remaining TTL in milliseconds |
561
+ | `getAllKeys()` | `string[]` | Returns all valid keys |
562
+ | `getAll()` | `Record<string, unknown>` | Returns all valid items |
563
+
564
+ ### Maintenance Operations
565
+
566
+ | Method | Returns | Description |
567
+ | ----------------------- | -------- | ------------------------------------ |
568
+ | `cleanupExpiredItems()` | `number` | Removes expired items, returns count |
569
+ | `flush()` | `void` | Flushes pending debounced writes |
570
+ | `getCurrentSize()` | `number` | Returns storage size in bytes |
571
+ | `getStats()` | `object` | Returns detailed storage statistics |
572
+
573
+ ---
574
+
575
+ ## ๐Ÿ”Œ Transform Pipeline
576
+
577
+ You can chain multiple transforms (compression, encryption, encoding) to process data before storage:
578
+
579
+ ```typescript
580
+ import { getStorageSlice } from '@dariushstony/smart-storage';
581
+ import type { StorageTransform } from '@dariushstony/smart-storage';
582
+
583
+ // Example: Compression transform (requires lz-string package)
584
+ const compressionTransform: StorageTransform = {
585
+ serialize: (data: string) => LZString.compress(data),
586
+ deserialize: (data: string) => LZString.decompress(data) || '',
587
+ };
588
+
589
+ const vault = getStorageSlice('LARGE_DATA', {
590
+ transforms: [compressionTransform],
591
+ });
592
+
593
+ // Data is automatically compressed before storage and decompressed on read
594
+ vault.setItem('bigObject', {
595
+ /* large data */
596
+ });
597
+ ```
598
+
599
+ Transforms are applied in order during writes and reversed during reads.
600
+
601
+ ---
602
+
603
+ ## ๐Ÿ”Œ Setup with Custom Logger
604
+
605
+ If you want to integrate with your logging service (Sentry, LogRocket, etc.):
606
+
607
+ ```typescript
608
+ import { getStorageSlice } from '@dariushstony/smart-storage';
609
+ import type { StorageLogger } from '@dariushstony/smart-storage';
610
+
611
+ const customLogger: StorageLogger = {
612
+ log: (message, error) => {
613
+ console.error('[Storage]', message, error);
614
+ // Send to your logging service
615
+ // Sentry.captureException(error, { extra: { message } });
616
+ },
617
+ };
618
+
619
+ const vault = getStorageSlice('APP_DATA', {
620
+ storageType: 'local',
621
+ logger: customLogger,
622
+ });
623
+ ```
624
+
625
+ ---
626
+
627
+ ## ๐Ÿ’ก Common Patterns
628
+
629
+ ### Feature Flags with Auto-Expiry
630
+
631
+ ```typescript
632
+ const featureFlags = getStorageSlice('FEATURE_FLAGS', {
633
+ storageType: 'local',
634
+ });
635
+
636
+ function enableFeature(name: string, durationMs = 24 * 60 * 60 * 1000) {
637
+ featureFlags.setItem(`feature:${name}`, true, durationMs);
638
+ }
639
+
640
+ function isFeatureEnabled(name: string): boolean {
641
+ return featureFlags.hasItem(`feature:${name}`);
642
+ }
643
+
644
+ enableFeature('new-checkout', 7 * 24 * 60 * 60 * 1000); // 7 days
645
+ ```
646
+
647
+ ### Rate Limiting
648
+
649
+ ```typescript
650
+ const rateLimiter = getStorageSlice('RATE_LIMIT', {
651
+ storageType: 'local',
652
+ });
653
+
654
+ function canPerformAction(action: string, limitMs = 60000): boolean {
655
+ const key = `action:${action}`;
656
+ if (rateLimiter.hasItem(key)) {
657
+ return false; // Rate-limited
658
+ }
659
+
660
+ rateLimiter.setItem(key, true, limitMs);
661
+ return true;
662
+ }
663
+
664
+ if (canPerformAction('send-email', 5 * 60 * 1000)) {
665
+ console.log('Sending email...');
666
+ }
667
+ ```
668
+
669
+ ### Form Draft Auto-Save
670
+
671
+ ```typescript
672
+ const draftStorage = getStorageSlice('FORM_DRAFTS', {
673
+ storageType: 'local',
674
+ debounceMs: 1000, // Save 1 second after typing stops
675
+ });
676
+
677
+ function saveDraft(formId: string, data: Record<string, unknown>) {
678
+ draftStorage.setItem(`draft:${formId}`, data, 24 * 60 * 60 * 1000);
679
+ }
680
+
681
+ function loadDraft(formId: string) {
682
+ return draftStorage.getItem<Record<string, unknown>>(`draft:${formId}`);
683
+ }
684
+ ```
685
+
686
+ ### Temporary Wizard Flow
687
+
688
+ ```typescript
689
+ const wizardStorage = getStorageSlice('CHECKOUT_WIZARD', {
690
+ storageType: 'session', // Auto-cleared when tab closes
691
+ });
692
+
693
+ function saveWizardStep(step: number, data: any) {
694
+ wizardStorage.setItem('currentStep', step);
695
+ wizardStorage.setItem('formData', data);
696
+ }
697
+
698
+ // Data automatically cleared when user closes tab - no cleanup needed!
699
+ ```
700
+
701
+ ---
702
+
703
+ ## ๐Ÿ Design Philosophy
704
+
705
+ - **Prefer correctness over cleverness**
706
+ - **Defensive by default**
707
+ - **Explicit trade-offs**
708
+ - **Optimized for real production constraints**
709
+
710
+ This is infrastructure code, not a toy utility.
711
+
712
+ ---
713
+
714
+ ## ๐Ÿ“š Additional Documentation
715
+
716
+ - **Architecture**: `docs/ARCHITECTURE.md` - System design and technical decisions
717
+ - **Storage Architecture**: `docs/STORAGE_ARCHITECTURE.md` - Deep dive into storage mechanisms
718
+ - **How to Use**: `docs/HOW_TO_USE_STORAGE.md` - Simple examples and patterns
719
+ - **Project Structure**: `docs/STRUCTURE.md` - Codebase organization
720
+
721
+ ## ๐Ÿค Contributing
722
+
723
+ Contributions are welcome! Please feel free to submit a Pull Request.
724
+
725
+ 1. Fork the repository
726
+ 2. Create your feature branch (`git checkout -b feature/amazing-feature`)
727
+ 3. Commit your changes (`git commit -m 'feat: add amazing feature'`)
728
+ 4. Push to the branch (`git push origin feature/amazing-feature`)
729
+ 5. Open a Pull Request
730
+
731
+ ## ๐Ÿ“ License
732
+
733
+ This project is licensed under the MIT License - see the [LICENSE](./LICENSE) file for details.
734
+
735
+ ## ๐Ÿ‘ค Author
736
+
737
+ **Dariush Hadipour**
738
+
739
+ - GitHub: [@DariushStony](https://github.com/DariushStony)
740
+ - Package: [@dariushstony/smart-storage](https://www.npmjs.com/package/@dariushstony/smart-storage)
741
+
742
+ ---
743
+
744
+ ## ๐ŸŽ“ Quick Reference
745
+
746
+ ### Storage Types
747
+
748
+ ```typescript
749
+ 'local'; // localStorage - persists across sessions
750
+ 'session'; // sessionStorage - cleared on tab close
751
+ 'in-memory'; // Map - cleared on reload, great for tests
752
+ ```
753
+
754
+ ### Common Use Cases
755
+
756
+ | Use Case | Storage Type | TTL | Debounce |
757
+ | ---------------- | ------------------------ | -------- | --------------- |
758
+ | User preferences | `'local'` | None | 0ms (immediate) |
759
+ | Shopping cart | `'local'` | None | 100ms |
760
+ | API cache | `'local'` | 5-10 min | 200ms |
761
+ | Feature flags | `'local'` | 7 days | 100ms |
762
+ | Wizard flow | `'session'` | None | 100ms |
763
+ | Form drafts | `'local'` or `'session'` | 24 hours | 1000ms |
764
+ | Testing | `'in-memory'` | Varies | 0ms |