ngx-webstore 1.0.0 → 1.0.2
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 +66 -726
- package/fesm2022/ngx-webstore.mjs +2033 -0
- package/fesm2022/ngx-webstore.mjs.map +1 -0
- package/index.d.ts +5 -0
- package/lib/core/encryption.service.d.ts +58 -0
- package/lib/core/namespace-manager.service.d.ts +33 -0
- package/lib/core/serialization.service.d.ts +25 -0
- package/lib/core/storage-service.interface.d.ts +23 -0
- package/lib/core/ttl-manager.service.d.ts +33 -0
- package/lib/models/storage-config.model.d.ts +55 -0
- package/lib/models/storage-item.model.d.ts +25 -0
- package/lib/models/storage-options.model.d.ts +38 -0
- package/lib/ngx-webstore.module.d.ts +36 -0
- package/lib/services/cookie.service.d.ts +71 -0
- package/lib/services/global-state.service.d.ts +59 -0
- package/lib/services/indexeddb.service.d.ts +72 -0
- package/lib/services/local-storage.service.d.ts +69 -0
- package/lib/services/session-storage.service.d.ts +61 -0
- package/lib/services/storage-manager.service.d.ts +88 -0
- package/lib/utils/storage-availability.d.ts +23 -0
- package/lib/utils/type-guards.d.ts +31 -0
- package/package.json +48 -31
- package/{projects/ngx-webstore/src/public-api.ts → public-api.d.ts} +17 -34
- package/LICENSE +0 -21
- package/angular.json +0 -37
- package/projects/ngx-webstore/README.md +0 -122
- package/projects/ngx-webstore/ng-package.json +0 -7
- package/projects/ngx-webstore/package.json +0 -37
- package/projects/ngx-webstore/src/lib/core/encryption.service.ts +0 -217
- package/projects/ngx-webstore/src/lib/core/index.ts +0 -5
- package/projects/ngx-webstore/src/lib/core/namespace-manager.service.ts +0 -68
- package/projects/ngx-webstore/src/lib/core/serialization.service.ts +0 -145
- package/projects/ngx-webstore/src/lib/core/storage-service.interface.ts +0 -31
- package/projects/ngx-webstore/src/lib/core/ttl-manager.service.ts +0 -77
- package/projects/ngx-webstore/src/lib/models/index.ts +0 -3
- package/projects/ngx-webstore/src/lib/models/storage-config.model.ts +0 -85
- package/projects/ngx-webstore/src/lib/models/storage-item.model.ts +0 -26
- package/projects/ngx-webstore/src/lib/models/storage-options.model.ts +0 -41
- package/projects/ngx-webstore/src/lib/ngx-webstore.module.ts +0 -63
- package/projects/ngx-webstore/src/lib/services/cookie.service.ts +0 -281
- package/projects/ngx-webstore/src/lib/services/global-state.service.ts +0 -229
- package/projects/ngx-webstore/src/lib/services/index.ts +0 -6
- package/projects/ngx-webstore/src/lib/services/indexeddb.service.ts +0 -329
- package/projects/ngx-webstore/src/lib/services/local-storage.service.ts +0 -268
- package/projects/ngx-webstore/src/lib/services/session-storage.service.ts +0 -217
- package/projects/ngx-webstore/src/lib/services/storage-manager.service.ts +0 -183
- package/projects/ngx-webstore/src/lib/utils/index.ts +0 -2
- package/projects/ngx-webstore/src/lib/utils/storage-availability.ts +0 -73
- package/projects/ngx-webstore/src/lib/utils/type-guards.ts +0 -64
- package/projects/ngx-webstore/tsconfig.lib.json +0 -15
- package/projects/ngx-webstore/tsconfig.lib.prod.json +0 -11
- package/projects/ngx-webstore/tsconfig.spec.json +0 -15
- package/tsconfig.json +0 -32
package/README.md
CHANGED
|
@@ -1,18 +1,20 @@
|
|
|
1
1
|
# ngx-webstore
|
|
2
2
|
|
|
3
|
-
A comprehensive Angular library for browser storage management with TypeScript support
|
|
3
|
+
A comprehensive Angular library for browser storage management with TypeScript support.
|
|
4
|
+
|
|
5
|
+
[](https://www.npmjs.com/package/ngx-webstore)
|
|
6
|
+
[](https://opensource.org/licenses/MIT)
|
|
4
7
|
|
|
5
8
|
## Features
|
|
6
9
|
|
|
10
|
+
- 🗄️ **Multiple Storage Types** - LocalStorage, SessionStorage, Cookies, IndexedDB
|
|
11
|
+
- ⏰ **TTL Support** - Auto-expiring data with Time-To-Live
|
|
7
12
|
- 🔐 **Encryption** - AES-GCM encryption via Web Crypto API
|
|
8
|
-
-
|
|
9
|
-
- 🔄 **Reactive** -
|
|
10
|
-
-
|
|
11
|
-
-
|
|
12
|
-
-
|
|
13
|
-
- 🔑 **Namespacing** - Prefix keys for isolation
|
|
14
|
-
- 🛡️ **Fallback Strategy** - Automatic storage fallback
|
|
15
|
-
- 📊 **Smart Serialization** - Handles Date, Map, Set, BigInt
|
|
13
|
+
- 📛 **Namespacing** - Isolate storage by namespace/prefix
|
|
14
|
+
- 🔄 **Reactive** - RxJS Observables for real-time updates
|
|
15
|
+
- 🔗 **Cross-Tab Sync** - Synchronize state across browser tabs
|
|
16
|
+
- 🌐 **Global State** - Reactive in-memory state management
|
|
17
|
+
- 📦 **Unified API** - Single interface for all storage types
|
|
16
18
|
|
|
17
19
|
## Installation
|
|
18
20
|
|
|
@@ -22,18 +24,18 @@ npm install ngx-webstore
|
|
|
22
24
|
|
|
23
25
|
## Quick Start
|
|
24
26
|
|
|
27
|
+
### 1. Import the module
|
|
28
|
+
|
|
25
29
|
```typescript
|
|
26
|
-
// app.module.ts
|
|
27
30
|
import { NgxWebStoreModule } from 'ngx-webstore';
|
|
28
31
|
|
|
29
32
|
@NgModule({
|
|
30
33
|
imports: [
|
|
31
34
|
NgxWebStoreModule.forRoot({
|
|
32
35
|
namespace: 'myApp',
|
|
33
|
-
defaultTTL: 86400000, // 24 hours
|
|
34
36
|
encryption: {
|
|
35
37
|
enabled: true,
|
|
36
|
-
secret:
|
|
38
|
+
secret: 'your-secret-key'
|
|
37
39
|
}
|
|
38
40
|
})
|
|
39
41
|
]
|
|
@@ -41,742 +43,80 @@ import { NgxWebStoreModule } from 'ngx-webstore';
|
|
|
41
43
|
export class AppModule {}
|
|
42
44
|
```
|
|
43
45
|
|
|
46
|
+
### 2. Use the services
|
|
47
|
+
|
|
44
48
|
```typescript
|
|
45
|
-
|
|
46
|
-
import { LocalStorageService } from 'ngx-webstore';
|
|
49
|
+
import { LocalStorageService, GlobalStateService } from 'ngx-webstore';
|
|
47
50
|
|
|
48
51
|
@Component({...})
|
|
49
52
|
export class MyComponent {
|
|
50
|
-
constructor(private localStorage: LocalStorageService) {}
|
|
51
|
-
|
|
52
|
-
async saveUser() {
|
|
53
|
-
await this.localStorage.set('user', { name: 'John', age: 30 });
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
async getUser() {
|
|
57
|
-
const user = await this.localStorage.get<User>('user');
|
|
58
|
-
console.log(user);
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
```
|
|
62
|
-
|
|
63
|
-
---
|
|
64
|
-
|
|
65
|
-
## API Documentation
|
|
66
|
-
|
|
67
|
-
### Services
|
|
68
|
-
|
|
69
|
-
#### LocalStorageService
|
|
70
|
-
|
|
71
|
-
Injectable service for browser localStorage with cross-tab synchronization.
|
|
72
|
-
|
|
73
|
-
```typescript
|
|
74
|
-
import { LocalStorageService } from 'ngx-webstore';
|
|
75
|
-
|
|
76
|
-
constructor(private localStorage: LocalStorageService) {}
|
|
77
|
-
```
|
|
78
|
-
|
|
79
|
-
**Methods:**
|
|
80
|
-
|
|
81
|
-
| Method | Return Type | Description |
|
|
82
|
-
|--------|------------|-------------|
|
|
83
|
-
| `get<T>(key: string)` | `Promise<T \| null>` | Get value by key |
|
|
84
|
-
| `set<T>(key: string, value: T, options?)` | `Promise<void>` | Set value with options |
|
|
85
|
-
| `remove(key: string)` | `Promise<void>` | Remove a key |
|
|
86
|
-
| `clear()` | `Promise<void>` | Clear all keys in namespace |
|
|
87
|
-
| `keys()` | `Promise<string[]>` | Get all keys |
|
|
88
|
-
| `has(key: string)` | `Promise<boolean>` | Check if key exists |
|
|
89
|
-
| `watch<T>(key: string)` | `Observable<T \| null>` | Watch for changes |
|
|
90
|
-
|
|
91
|
-
**Properties:**
|
|
92
|
-
- `isAvailable: boolean` - Check if localStorage is available
|
|
93
|
-
|
|
94
|
-
**Examples:**
|
|
95
|
-
|
|
96
|
-
```typescript
|
|
97
|
-
// Basic usage
|
|
98
|
-
await this.localStorage.set('theme', 'dark');
|
|
99
|
-
const theme = await this.localStorage.get<string>('theme');
|
|
100
|
-
|
|
101
|
-
// With TTL (1 hour)
|
|
102
|
-
await this.localStorage.set('token', 'abc123', { ttl: 3600000 });
|
|
103
|
-
|
|
104
|
-
// With encryption
|
|
105
|
-
await this.localStorage.set('sensitive', { ssn: '123-45-6789' }, { encrypt: true });
|
|
106
|
-
|
|
107
|
-
// Reactive updates
|
|
108
|
-
this.localStorage.watch<string>('theme').subscribe(theme => {
|
|
109
|
-
console.log('Theme changed:', theme);
|
|
110
|
-
});
|
|
111
|
-
|
|
112
|
-
// Check if exists
|
|
113
|
-
const exists = await this.localStorage.has('user');
|
|
114
|
-
|
|
115
|
-
// Get all keys
|
|
116
|
-
const keys = await this.localStorage.keys();
|
|
117
|
-
```
|
|
118
|
-
|
|
119
|
-
---
|
|
120
|
-
|
|
121
|
-
#### SessionStorageService
|
|
122
|
-
|
|
123
|
-
Injectable service for browser sessionStorage. Same API as LocalStorageService but data persists only for session duration.
|
|
124
|
-
|
|
125
|
-
```typescript
|
|
126
|
-
import { SessionStorageService } from 'ngx-webstore';
|
|
127
|
-
|
|
128
|
-
constructor(private sessionStorage: SessionStorageService) {}
|
|
129
|
-
|
|
130
|
-
// API identical to LocalStorageService
|
|
131
|
-
await this.sessionStorage.set('tempData', { id: 123 });
|
|
132
|
-
```
|
|
133
|
-
|
|
134
|
-
---
|
|
135
|
-
|
|
136
|
-
#### CookieService
|
|
137
|
-
|
|
138
|
-
Injectable service for HTTP cookies with full cookie options support.
|
|
139
|
-
|
|
140
|
-
```typescript
|
|
141
|
-
import { CookieService } from 'ngx-webstore';
|
|
142
|
-
|
|
143
|
-
constructor(private cookieService: CookieService) {}
|
|
144
|
-
```
|
|
145
|
-
|
|
146
|
-
**Extended Options:**
|
|
147
|
-
|
|
148
|
-
```typescript
|
|
149
|
-
interface CookieStorageOptions {
|
|
150
|
-
ttl?: number; // Time-to-live in milliseconds
|
|
151
|
-
encrypt?: boolean; // Encrypt cookie value
|
|
152
|
-
path?: string; // Cookie path (default: '/')
|
|
153
|
-
domain?: string; // Cookie domain
|
|
154
|
-
secure?: boolean; // Secure flag
|
|
155
|
-
sameSite?: 'Strict' | 'Lax' | 'None'; // SameSite attribute
|
|
156
|
-
}
|
|
157
|
-
```
|
|
158
|
-
|
|
159
|
-
**Examples:**
|
|
160
|
-
|
|
161
|
-
```typescript
|
|
162
|
-
// Basic usage
|
|
163
|
-
await this.cookieService.set('user', { id: 1 });
|
|
164
|
-
|
|
165
|
-
// With all options
|
|
166
|
-
await this.cookieService.set('authToken', 'abc123', {
|
|
167
|
-
ttl: 3600000, // 1 hour
|
|
168
|
-
secure: true, // HTTPS only
|
|
169
|
-
sameSite: 'Strict', // CSRF protection
|
|
170
|
-
path: '/admin', // Specific path
|
|
171
|
-
encrypt: true // Encrypted
|
|
172
|
-
});
|
|
173
|
-
|
|
174
|
-
// Get cookie
|
|
175
|
-
const token = await this.cookieService.get<string>('authToken');
|
|
176
|
-
```
|
|
177
|
-
|
|
178
|
-
**Note:** Cookies have a ~4KB size limit. The service will throw an error if the data exceeds this limit.
|
|
179
|
-
|
|
180
|
-
---
|
|
181
|
-
|
|
182
|
-
#### IndexedDBService
|
|
183
|
-
|
|
184
|
-
Injectable service for IndexedDB storage. Best for large data storage.
|
|
185
|
-
|
|
186
|
-
```typescript
|
|
187
|
-
import { IndexedDBService } from 'ngx-webstore';
|
|
188
|
-
|
|
189
|
-
constructor(private indexedDB: IndexedDBService) {}
|
|
190
|
-
```
|
|
191
|
-
|
|
192
|
-
**Examples:**
|
|
193
|
-
|
|
194
|
-
```typescript
|
|
195
|
-
// Store large objects
|
|
196
|
-
await this.indexedDB.set('largeDataset', {
|
|
197
|
-
items: [...], // Large array
|
|
198
|
-
metadata: {...}
|
|
199
|
-
});
|
|
200
|
-
|
|
201
|
-
// Retrieve
|
|
202
|
-
const data = await this.indexedDB.get<Dataset>('largeDataset');
|
|
203
|
-
|
|
204
|
-
// Same API as other storage services
|
|
205
|
-
await this.indexedDB.remove('key');
|
|
206
|
-
const keys = await this.indexedDB.keys();
|
|
207
|
-
```
|
|
208
|
-
|
|
209
|
-
**Configuration:**
|
|
210
|
-
|
|
211
|
-
```typescript
|
|
212
|
-
NgxWebStoreModule.forRoot({
|
|
213
|
-
indexedDB: {
|
|
214
|
-
dbName: 'my-app-db',
|
|
215
|
-
storeName: 'storage',
|
|
216
|
-
version: 1
|
|
217
|
-
}
|
|
218
|
-
})
|
|
219
|
-
```
|
|
220
|
-
|
|
221
|
-
---
|
|
222
|
-
|
|
223
|
-
#### GlobalStateService
|
|
224
|
-
|
|
225
|
-
Injectable service for reactive global application state with optional persistence.
|
|
226
|
-
|
|
227
|
-
```typescript
|
|
228
|
-
import { GlobalStateService } from 'ngx-webstore';
|
|
229
|
-
|
|
230
|
-
constructor(private globalState: GlobalStateService) {}
|
|
231
|
-
```
|
|
232
|
-
|
|
233
|
-
**Methods:**
|
|
234
|
-
|
|
235
|
-
| Method | Return Type | Description |
|
|
236
|
-
|--------|------------|-------------|
|
|
237
|
-
| `createVar<T>(key, defaultValue, options?)` | `GlobalVariable<T>` | Create reactive variable |
|
|
238
|
-
| `getVar<T>(key)` | `GlobalVariable<T> \| undefined` | Get existing variable |
|
|
239
|
-
| `createSignal<T>(key, defaultValue, options?)` | `WritableSignal<T>` | Create Angular signal |
|
|
240
|
-
|
|
241
|
-
**GlobalVariable API:**
|
|
242
|
-
|
|
243
|
-
```typescript
|
|
244
|
-
interface GlobalVariable<T> {
|
|
245
|
-
readonly key: string;
|
|
246
|
-
readonly value$: Observable<T>;
|
|
247
|
-
get(): T;
|
|
248
|
-
set(value: T): Promise<void>;
|
|
249
|
-
update(fn: (current: T) => T): Promise<void>;
|
|
250
|
-
reset(): Promise<void>;
|
|
251
|
-
destroy(): void;
|
|
252
|
-
}
|
|
253
|
-
```
|
|
254
|
-
|
|
255
|
-
**Examples:**
|
|
256
|
-
|
|
257
|
-
```typescript
|
|
258
|
-
// Create reactive variable
|
|
259
|
-
theme = this.globalState.createVar('theme', 'light', {
|
|
260
|
-
storage: 'localStorage',
|
|
261
|
-
persist: true,
|
|
262
|
-
ttl: 86400000 // 24 hours
|
|
263
|
-
});
|
|
264
|
-
|
|
265
|
-
// Use in template with async pipe
|
|
266
|
-
theme$ = this.theme.value$;
|
|
267
|
-
|
|
268
|
-
// Update value
|
|
269
|
-
await this.theme.set('dark');
|
|
270
|
-
|
|
271
|
-
// Update with function
|
|
272
|
-
await this.theme.update(t => t === 'light' ? 'dark' : 'light');
|
|
273
|
-
|
|
274
|
-
// Angular 16+ Signals
|
|
275
|
-
darkMode = this.globalState.createSignal('darkMode', false, {
|
|
276
|
-
storage: 'localStorage',
|
|
277
|
-
persist: true
|
|
278
|
-
});
|
|
279
|
-
|
|
280
|
-
// Use signal in template
|
|
281
|
-
// {{ darkMode() }}
|
|
282
|
-
|
|
283
|
-
// Update signal
|
|
284
|
-
this.darkMode.set(true);
|
|
285
|
-
this.darkMode.update(val => !val);
|
|
286
|
-
```
|
|
287
|
-
|
|
288
|
-
**Options:**
|
|
289
|
-
|
|
290
|
-
```typescript
|
|
291
|
-
interface GlobalVarOptions {
|
|
292
|
-
storage?: 'localStorage' | 'sessionStorage'; // Storage type
|
|
293
|
-
persist?: boolean; // Save to storage (default: true)
|
|
294
|
-
ttl?: number; // Time-to-live
|
|
295
|
-
encrypt?: boolean; // Encrypt value
|
|
296
|
-
}
|
|
297
|
-
```
|
|
298
|
-
|
|
299
|
-
---
|
|
300
|
-
|
|
301
|
-
#### StorageManagerService
|
|
302
|
-
|
|
303
|
-
Unified service for dynamic storage selection and fallback support.
|
|
304
|
-
|
|
305
|
-
```typescript
|
|
306
|
-
import { StorageManagerService } from 'ngx-webstore';
|
|
307
|
-
|
|
308
|
-
constructor(private storage: StorageManagerService) {}
|
|
309
|
-
```
|
|
310
|
-
|
|
311
|
-
**Direct Access:**
|
|
312
|
-
|
|
313
|
-
```typescript
|
|
314
|
-
// Access specific storage directly
|
|
315
|
-
await this.storage.local.set('key', value);
|
|
316
|
-
await this.storage.session.set('key', value);
|
|
317
|
-
await this.storage.cookie.set('key', value);
|
|
318
|
-
await this.storage.indexedDB.set('key', value);
|
|
319
|
-
```
|
|
320
|
-
|
|
321
|
-
**Dynamic Storage Selection:**
|
|
322
|
-
|
|
323
|
-
```typescript
|
|
324
|
-
// Specify storage at runtime
|
|
325
|
-
await this.storage.set('key', value, {
|
|
326
|
-
storage: 'indexedDB',
|
|
327
|
-
ttl: 3600000
|
|
328
|
-
});
|
|
329
|
-
|
|
330
|
-
const data = await this.storage.get('key', { storage: 'indexedDB' });
|
|
331
|
-
```
|
|
332
|
-
|
|
333
|
-
**Fallback Methods:**
|
|
334
|
-
|
|
335
|
-
```typescript
|
|
336
|
-
// Try each storage in fallback order
|
|
337
|
-
await this.storage.setWithFallback('important', value);
|
|
338
|
-
const data = await this.storage.getWithFallback<Data>('important');
|
|
339
|
-
|
|
340
|
-
// Remove from all storages
|
|
341
|
-
await this.storage.removeFromAll('key');
|
|
342
|
-
|
|
343
|
-
// Check available storages
|
|
344
|
-
const available = this.storage.getAvailableStorages();
|
|
345
|
-
// Returns: ['localStorage', 'sessionStorage', 'cookie', 'indexedDB']
|
|
346
|
-
|
|
347
|
-
const isAvailable = this.storage.isStorageAvailable('indexedDB');
|
|
348
|
-
```
|
|
349
|
-
|
|
350
|
-
---
|
|
351
|
-
|
|
352
|
-
## Configuration
|
|
353
|
-
|
|
354
|
-
### Module Configuration
|
|
355
|
-
|
|
356
|
-
```typescript
|
|
357
|
-
NgxWebStoreModule.forRoot({
|
|
358
|
-
// Default storage type
|
|
359
|
-
defaultStorage: 'localStorage',
|
|
360
|
-
|
|
361
|
-
// Global namespace prefix
|
|
362
|
-
namespace: 'myApp',
|
|
363
|
-
|
|
364
|
-
// Default TTL for all items (milliseconds)
|
|
365
|
-
defaultTTL: 86400000, // 24 hours
|
|
366
|
-
|
|
367
|
-
// Storage fallback order
|
|
368
|
-
fallbackOrder: ['localStorage', 'indexedDB', 'sessionStorage', 'cookie'],
|
|
369
|
-
|
|
370
|
-
// Encryption configuration
|
|
371
|
-
encryption: {
|
|
372
|
-
enabled: true,
|
|
373
|
-
secret: environment.storageSecret,
|
|
374
|
-
keyDerivationIterations: 100000
|
|
375
|
-
},
|
|
376
|
-
|
|
377
|
-
// IndexedDB configuration
|
|
378
|
-
indexedDB: {
|
|
379
|
-
dbName: 'my-app-db',
|
|
380
|
-
storeName: 'storage',
|
|
381
|
-
version: 1
|
|
382
|
-
},
|
|
383
|
-
|
|
384
|
-
// Cookie defaults
|
|
385
|
-
cookie: {
|
|
386
|
-
path: '/',
|
|
387
|
-
secure: true,
|
|
388
|
-
sameSite: 'Lax'
|
|
389
|
-
}
|
|
390
|
-
})
|
|
391
|
-
```
|
|
392
|
-
|
|
393
|
-
### Storage Options
|
|
394
|
-
|
|
395
|
-
```typescript
|
|
396
|
-
interface StorageOptions {
|
|
397
|
-
ttl?: number; // Time-to-live in milliseconds
|
|
398
|
-
encrypt?: boolean; // Override global encryption setting
|
|
399
|
-
}
|
|
400
|
-
|
|
401
|
-
interface UnifiedStorageOptions extends StorageOptions {
|
|
402
|
-
storage?: 'localStorage' | 'sessionStorage' | 'cookie' | 'indexedDB';
|
|
403
|
-
}
|
|
404
|
-
```
|
|
405
|
-
|
|
406
|
-
---
|
|
407
|
-
|
|
408
|
-
## Usage Examples
|
|
409
|
-
|
|
410
|
-
### User Authentication
|
|
411
|
-
|
|
412
|
-
```typescript
|
|
413
|
-
@Injectable({ providedIn: 'root' })
|
|
414
|
-
export class AuthService {
|
|
415
53
|
constructor(
|
|
416
54
|
private localStorage: LocalStorageService,
|
|
417
|
-
private
|
|
418
|
-
) {}
|
|
419
|
-
|
|
420
|
-
async login(credentials: Credentials) {
|
|
421
|
-
const response = await this.api.login(credentials);
|
|
422
|
-
|
|
423
|
-
// Store access token in cookie (httpOnly would be better on server)
|
|
424
|
-
await this.cookieService.set('accessToken', response.accessToken, {
|
|
425
|
-
ttl: 900000, // 15 minutes
|
|
426
|
-
secure: true,
|
|
427
|
-
sameSite: 'Strict',
|
|
428
|
-
encrypt: true
|
|
429
|
-
});
|
|
430
|
-
|
|
431
|
-
// Store refresh token in localStorage
|
|
432
|
-
await this.localStorage.set('refreshToken', response.refreshToken, {
|
|
433
|
-
ttl: 2592000000, // 30 days
|
|
434
|
-
encrypt: true
|
|
435
|
-
});
|
|
436
|
-
|
|
437
|
-
// Store user info
|
|
438
|
-
await this.localStorage.set('user', response.user);
|
|
439
|
-
}
|
|
440
|
-
|
|
441
|
-
async logout() {
|
|
442
|
-
await this.cookieService.remove('accessToken');
|
|
443
|
-
await this.localStorage.remove('refreshToken');
|
|
444
|
-
await this.localStorage.remove('user');
|
|
445
|
-
}
|
|
446
|
-
|
|
447
|
-
watchUser(): Observable<User | null> {
|
|
448
|
-
return this.localStorage.watch<User>('user');
|
|
449
|
-
}
|
|
450
|
-
}
|
|
451
|
-
```
|
|
452
|
-
|
|
453
|
-
### Application Settings
|
|
454
|
-
|
|
455
|
-
```typescript
|
|
456
|
-
@Component({
|
|
457
|
-
selector: 'app-settings',
|
|
458
|
-
template: `
|
|
459
|
-
<div>
|
|
460
|
-
<label>
|
|
461
|
-
<input type="checkbox" [checked]="darkMode()"
|
|
462
|
-
(change)="toggleDarkMode()">
|
|
463
|
-
Dark Mode
|
|
464
|
-
</label>
|
|
465
|
-
|
|
466
|
-
<select [value]="language()" (change)="setLanguage($event)">
|
|
467
|
-
<option value="en">English</option>
|
|
468
|
-
<option value="es">Spanish</option>
|
|
469
|
-
<option value="fr">French</option>
|
|
470
|
-
</select>
|
|
471
|
-
</div>
|
|
472
|
-
`
|
|
473
|
-
})
|
|
474
|
-
export class SettingsComponent {
|
|
475
|
-
// Using signals (Angular 16+)
|
|
476
|
-
darkMode = this.globalState.createSignal('darkMode', false, {
|
|
477
|
-
storage: 'localStorage',
|
|
478
|
-
persist: true
|
|
479
|
-
});
|
|
480
|
-
|
|
481
|
-
language = this.globalState.createSignal('language', 'en', {
|
|
482
|
-
storage: 'localStorage',
|
|
483
|
-
persist: true
|
|
484
|
-
});
|
|
485
|
-
|
|
486
|
-
constructor(private globalState: GlobalStateService) {}
|
|
487
|
-
|
|
488
|
-
toggleDarkMode() {
|
|
489
|
-
this.darkMode.update(val => !val);
|
|
490
|
-
}
|
|
491
|
-
|
|
492
|
-
setLanguage(event: Event) {
|
|
493
|
-
const value = (event.target as HTMLSelectElement).value;
|
|
494
|
-
this.language.set(value);
|
|
495
|
-
}
|
|
496
|
-
}
|
|
497
|
-
```
|
|
498
|
-
|
|
499
|
-
### Caching API Responses
|
|
500
|
-
|
|
501
|
-
```typescript
|
|
502
|
-
@Injectable({ providedIn: 'root' })
|
|
503
|
-
export class DataService {
|
|
504
|
-
constructor(
|
|
505
|
-
private indexedDB: IndexedDBService,
|
|
506
|
-
private http: HttpClient
|
|
55
|
+
private globalState: GlobalStateService
|
|
507
56
|
) {}
|
|
508
57
|
|
|
509
|
-
async
|
|
510
|
-
//
|
|
511
|
-
|
|
512
|
-
if (cached) {
|
|
513
|
-
return cached;
|
|
514
|
-
}
|
|
515
|
-
|
|
516
|
-
// Fetch from API
|
|
517
|
-
const products = await firstValueFrom(this.http.get<Product[]>('/api/products'));
|
|
518
|
-
|
|
519
|
-
// Cache for 1 hour
|
|
520
|
-
await this.indexedDB.set('products', products, { ttl: 3600000 });
|
|
521
|
-
|
|
522
|
-
return products;
|
|
58
|
+
async saveData() {
|
|
59
|
+
// Save with 1 hour TTL
|
|
60
|
+
await this.localStorage.set('user', { name: 'John' }, { ttl: 3600000 });
|
|
523
61
|
}
|
|
524
62
|
|
|
525
|
-
async
|
|
526
|
-
await this.
|
|
63
|
+
async getData() {
|
|
64
|
+
const user = await this.localStorage.get<{ name: string }>('user');
|
|
527
65
|
}
|
|
528
|
-
}
|
|
529
|
-
```
|
|
530
|
-
|
|
531
|
-
### Form State Persistence
|
|
532
66
|
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
})
|
|
538
|
-
export class FormComponent implements OnInit, OnDestroy {
|
|
539
|
-
form = this.fb.group({
|
|
540
|
-
name: [''],
|
|
541
|
-
email: [''],
|
|
542
|
-
message: ['']
|
|
543
|
-
});
|
|
544
|
-
|
|
545
|
-
constructor(
|
|
546
|
-
private fb: FormBuilder,
|
|
547
|
-
private sessionStorage: SessionStorageService
|
|
548
|
-
) {}
|
|
549
|
-
|
|
550
|
-
async ngOnInit() {
|
|
551
|
-
// Restore form state
|
|
552
|
-
const saved = await this.sessionStorage.get<any>('formDraft');
|
|
553
|
-
if (saved) {
|
|
554
|
-
this.form.patchValue(saved);
|
|
555
|
-
}
|
|
556
|
-
|
|
557
|
-
// Auto-save on changes
|
|
558
|
-
this.form.valueChanges.pipe(
|
|
559
|
-
debounceTime(500)
|
|
560
|
-
).subscribe(async value => {
|
|
561
|
-
await this.sessionStorage.set('formDraft', value);
|
|
67
|
+
// Reactive updates
|
|
68
|
+
watchData() {
|
|
69
|
+
this.localStorage.watch<{ name: string }>('user').subscribe(user => {
|
|
70
|
+
console.log('User changed:', user);
|
|
562
71
|
});
|
|
563
72
|
}
|
|
564
|
-
|
|
565
|
-
async onSubmit() {
|
|
566
|
-
// Clear draft after submission
|
|
567
|
-
await this.sessionStorage.remove('formDraft');
|
|
568
|
-
}
|
|
569
73
|
}
|
|
570
74
|
```
|
|
571
75
|
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
// Tab 2 - automatically receives update
|
|
609
|
-
this.localStorage.watch<number>('counter').subscribe(value => {
|
|
610
|
-
console.log('Counter updated:', value); // 5
|
|
611
|
-
});
|
|
612
|
-
```
|
|
613
|
-
|
|
614
|
-
### TTL and Expiration
|
|
615
|
-
|
|
616
|
-
```typescript
|
|
617
|
-
// Item expires after 1 hour
|
|
618
|
-
await this.localStorage.set('temp', data, { ttl: 3600000 });
|
|
619
|
-
|
|
620
|
-
// Check if expired
|
|
621
|
-
const data = await this.localStorage.get('temp'); // null if expired
|
|
622
|
-
```
|
|
623
|
-
|
|
624
|
-
### Namespacing
|
|
625
|
-
|
|
626
|
-
Prevent key collisions between different apps or modules:
|
|
627
|
-
|
|
628
|
-
```typescript
|
|
629
|
-
NgxWebStoreModule.forRoot({
|
|
630
|
-
namespace: 'myApp'
|
|
631
|
-
})
|
|
632
|
-
|
|
633
|
-
// Keys are prefixed: "myApp:user", "myApp:settings"
|
|
634
|
-
await this.localStorage.set('user', data);
|
|
635
|
-
```
|
|
636
|
-
|
|
637
|
-
### Type Safety
|
|
638
|
-
|
|
639
|
-
```typescript
|
|
640
|
-
interface User {
|
|
641
|
-
id: number;
|
|
642
|
-
name: string;
|
|
643
|
-
email: string;
|
|
76
|
+
## Available Services
|
|
77
|
+
|
|
78
|
+
| Service | Description |
|
|
79
|
+
|---------|-------------|
|
|
80
|
+
| `LocalStorageService` | Browser localStorage with TTL & encryption |
|
|
81
|
+
| `SessionStorageService` | Browser sessionStorage with TTL & encryption |
|
|
82
|
+
| `CookieService` | Cookie management with options |
|
|
83
|
+
| `IndexedDBService` | IndexedDB with async operations |
|
|
84
|
+
| `GlobalStateService` | Reactive in-memory state |
|
|
85
|
+
| `StorageManagerService` | Unified API with fallback strategy |
|
|
86
|
+
|
|
87
|
+
## Configuration Options
|
|
88
|
+
|
|
89
|
+
```typescript
|
|
90
|
+
interface StorageConfig {
|
|
91
|
+
namespace?: string; // Key prefix (default: '')
|
|
92
|
+
defaultTTL?: number; // Default TTL in ms
|
|
93
|
+
defaultStorage?: StorageType; // 'localStorage' | 'sessionStorage' | 'cookie' | 'indexedDB'
|
|
94
|
+
fallbackOrder?: StorageType[];// Fallback order when primary fails
|
|
95
|
+
encryption?: {
|
|
96
|
+
enabled: boolean;
|
|
97
|
+
secret?: string;
|
|
98
|
+
keyDerivationIterations?: number; // PBKDF2 iterations (default: 100000)
|
|
99
|
+
};
|
|
100
|
+
indexedDB?: {
|
|
101
|
+
dbName?: string;
|
|
102
|
+
storeName?: string;
|
|
103
|
+
version?: number;
|
|
104
|
+
};
|
|
105
|
+
cookie?: {
|
|
106
|
+
path?: string;
|
|
107
|
+
domain?: string;
|
|
108
|
+
secure?: boolean;
|
|
109
|
+
sameSite?: 'Strict' | 'Lax' | 'None';
|
|
110
|
+
};
|
|
644
111
|
}
|
|
645
|
-
|
|
646
|
-
// Type-safe get
|
|
647
|
-
const user = await this.localStorage.get<User>('user');
|
|
648
|
-
// user is typed as User | null
|
|
649
|
-
|
|
650
|
-
// Type-safe watch
|
|
651
|
-
this.localStorage.watch<User>('user').subscribe(user => {
|
|
652
|
-
// user is typed as User | null
|
|
653
|
-
});
|
|
654
112
|
```
|
|
655
113
|
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
## Browser Compatibility
|
|
659
|
-
|
|
660
|
-
- Chrome/Edge: ✅ Full support
|
|
661
|
-
- Firefox: ✅ Full support
|
|
662
|
-
- Safari: ✅ Full support
|
|
663
|
-
- IE11: ❌ Not supported (requires modern browser APIs)
|
|
664
|
-
|
|
665
|
-
**Required APIs:**
|
|
666
|
-
- Web Storage API (localStorage, sessionStorage)
|
|
667
|
-
- IndexedDB API
|
|
668
|
-
- Web Crypto API (for encryption)
|
|
114
|
+
## Compatibility
|
|
669
115
|
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
```typescript
|
|
675
|
-
import { LocalStorageService } from 'ngx-webstore';
|
|
676
|
-
|
|
677
|
-
try {
|
|
678
|
-
await this.localStorage.set('key', largeData);
|
|
679
|
-
} catch (error) {
|
|
680
|
-
if (error.message.includes('quota exceeded')) {
|
|
681
|
-
// Handle storage quota exceeded
|
|
682
|
-
console.error('Storage quota exceeded');
|
|
683
|
-
}
|
|
684
|
-
}
|
|
685
|
-
|
|
686
|
-
// Check availability first
|
|
687
|
-
if (this.localStorage.isAvailable) {
|
|
688
|
-
await this.localStorage.set('key', data);
|
|
689
|
-
}
|
|
690
|
-
```
|
|
691
|
-
|
|
692
|
-
---
|
|
693
|
-
|
|
694
|
-
## Best Practices
|
|
695
|
-
|
|
696
|
-
1. **Use appropriate storage types**
|
|
697
|
-
- `localStorage` - User preferences, app state
|
|
698
|
-
- `sessionStorage` - Temporary data, form drafts
|
|
699
|
-
- `cookies` - Auth tokens (with httpOnly on server)
|
|
700
|
-
- `indexedDB` - Large datasets, offline data
|
|
701
|
-
|
|
702
|
-
2. **Set reasonable TTLs**
|
|
703
|
-
```typescript
|
|
704
|
-
// Sensitive data - short TTL
|
|
705
|
-
await storage.set('token', token, { ttl: 900000 }); // 15 min
|
|
706
|
-
|
|
707
|
-
// User preferences - long TTL
|
|
708
|
-
await storage.set('theme', theme, { ttl: 2592000000 }); // 30 days
|
|
709
|
-
```
|
|
710
|
-
|
|
711
|
-
3. **Encrypt sensitive data**
|
|
712
|
-
```typescript
|
|
713
|
-
await storage.set('ssn', data, { encrypt: true });
|
|
714
|
-
```
|
|
715
|
-
|
|
716
|
-
4. **Use reactive patterns**
|
|
717
|
-
```typescript
|
|
718
|
-
// Instead of polling
|
|
719
|
-
theme$ = this.localStorage.watch<string>('theme');
|
|
720
|
-
```
|
|
721
|
-
|
|
722
|
-
5. **Namespace your app**
|
|
723
|
-
```typescript
|
|
724
|
-
NgxWebStoreModule.forRoot({
|
|
725
|
-
namespace: 'myApp'
|
|
726
|
-
})
|
|
727
|
-
```
|
|
728
|
-
|
|
729
|
-
---
|
|
730
|
-
|
|
731
|
-
## Performance Considerations
|
|
732
|
-
|
|
733
|
-
- **localStorage/sessionStorage**: Synchronous operations wrapped in Promises
|
|
734
|
-
- **IndexedDB**: Fully asynchronous, best for large data
|
|
735
|
-
- **Cookies**: Limited to ~4KB, sent with every HTTP request
|
|
736
|
-
- **Serialization**: Automatic for complex types (Date, Map, Set)
|
|
737
|
-
|
|
738
|
-
---
|
|
739
|
-
|
|
740
|
-
## Troubleshooting
|
|
741
|
-
|
|
742
|
-
### Storage not available
|
|
743
|
-
|
|
744
|
-
```typescript
|
|
745
|
-
if (!this.localStorage.isAvailable) {
|
|
746
|
-
console.warn('localStorage not available, using fallback');
|
|
747
|
-
// Use alternative storage
|
|
748
|
-
}
|
|
749
|
-
```
|
|
750
|
-
|
|
751
|
-
### Quota exceeded
|
|
752
|
-
|
|
753
|
-
```typescript
|
|
754
|
-
try {
|
|
755
|
-
await this.localStorage.set('key', data);
|
|
756
|
-
} catch (error) {
|
|
757
|
-
// Clear old data or use IndexedDB
|
|
758
|
-
await this.localStorage.clear();
|
|
759
|
-
}
|
|
760
|
-
```
|
|
761
|
-
|
|
762
|
-
### Encryption not working
|
|
763
|
-
|
|
764
|
-
```typescript
|
|
765
|
-
// Make sure encryption is initialized
|
|
766
|
-
NgxWebStoreModule.forRoot({
|
|
767
|
-
encryption: {
|
|
768
|
-
enabled: true,
|
|
769
|
-
secret: 'your-secret' // Required!
|
|
770
|
-
}
|
|
771
|
-
})
|
|
772
|
-
```
|
|
773
|
-
|
|
774
|
-
---
|
|
116
|
+
- Angular 18.x, 19.x
|
|
117
|
+
- RxJS 7.8+
|
|
118
|
+
- TypeScript 5.4+
|
|
775
119
|
|
|
776
120
|
## License
|
|
777
121
|
|
|
778
122
|
MIT
|
|
779
|
-
|
|
780
|
-
## Support
|
|
781
|
-
|
|
782
|
-
For issues and feature requests, please use the GitHub issue tracker.
|