@angular-libs/signal-storage 0.0.1 → 0.1.0-beta.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
CHANGED
|
@@ -1,92 +1,167 @@
|
|
|
1
1
|
# @angular-libs/signal-storage
|
|
2
2
|
|
|
3
|
-
A
|
|
3
|
+
A lightweight, type-safe Angular library for reactive state management using Signals. It provides seamless syncing with **localStorage**, **sessionStorage**, **IndexedDB** (via Angular Resources), and **In-Memory** storage.
|
|
4
4
|
|
|
5
|
-
[StackBlitz demo](https://stackblitz.com/edit/signal-storage?file=src%2Fmain.ts)
|
|
5
|
+
[StackBlitz demo](https://stackblitz.com/edit/signal-storage?file=src%2Fmain.ts) | `npm install @angular-libs/signal-storage`
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
---
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
## 🚀 1. Sync Storage (localStorage / sessionStorage)
|
|
10
10
|
|
|
11
|
-
|
|
12
|
-
|
|
11
|
+
Perfect for lightweight, synchronous key-value storage. By default, it uses `localStorage` (falling back to memory for SSR).
|
|
12
|
+
|
|
13
|
+
```typescript
|
|
14
|
+
import { Component, inject } from '@angular/core';
|
|
15
|
+
import { SignalStorage } from '@angular-libs/signal-storage';
|
|
16
|
+
|
|
17
|
+
interface AppState {
|
|
18
|
+
theme: 'light' | 'dark';
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
@Component({
|
|
22
|
+
standalone: true,
|
|
23
|
+
template: `<button (click)="toggle()">{{ theme() }}</button>`,
|
|
24
|
+
})
|
|
25
|
+
export class AppComponent {
|
|
26
|
+
private storage = inject(SignalStorage<AppState>);
|
|
27
|
+
theme = this.storage.getSignal('theme', 'light');
|
|
28
|
+
|
|
29
|
+
toggle() {
|
|
30
|
+
this.storage.update('theme', (t) => (t === 'light' ? 'dark' : 'light'));
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
### Changing the Sync Storage Provider
|
|
36
|
+
|
|
37
|
+
You can switch to `sessionStorage` or a purely `MemoryStorage` (similar to NgRx SignalStore without persistence) via providers:
|
|
38
|
+
|
|
39
|
+
```typescript
|
|
40
|
+
import { SIGNAL_STORAGE_TOKEN, MemoryStorage } from '@angular-libs/signal-storage';
|
|
41
|
+
|
|
42
|
+
// In app.config.ts
|
|
43
|
+
providers: [
|
|
44
|
+
// For Session Storage:
|
|
45
|
+
{ provide: SIGNAL_STORAGE_TOKEN, useFactory: () => window.sessionStorage },
|
|
46
|
+
|
|
47
|
+
// OR for Pure Memory Storage:
|
|
48
|
+
{ provide: SIGNAL_STORAGE_TOKEN, useClass: MemoryStorage },
|
|
49
|
+
];
|
|
13
50
|
```
|
|
14
51
|
|
|
15
|
-
|
|
52
|
+
---
|
|
16
53
|
|
|
17
|
-
|
|
18
|
-
- **Persistent Storage**: Automatically saves to `localStorage` or `sessionStorage`.
|
|
19
|
-
- **Type-safe**: Built with TypeScript to ensure type safety for your stored data.
|
|
20
|
-
- **Lightweight**: Zero external dependencies other than Angular.
|
|
54
|
+
## ⚡ 2. Async Storage (IndexedDB + Angular Resources)
|
|
21
55
|
|
|
22
|
-
|
|
56
|
+
Ideal for larger amounts of data. It uses Angular's new `resource` API and supports automatic cross-tab synchronization.
|
|
23
57
|
|
|
24
|
-
|
|
58
|
+
**1. Provide the storage:**
|
|
25
59
|
|
|
26
60
|
```typescript
|
|
27
|
-
import {
|
|
28
|
-
import { SignalStorageService } from '@angular-libs/signal-storage';
|
|
61
|
+
import { provideSignalIndexedDb } from '@angular-libs/signal-storage';
|
|
29
62
|
|
|
30
|
-
//
|
|
31
|
-
|
|
32
|
-
|
|
63
|
+
// In app.config.ts
|
|
64
|
+
providers: [
|
|
65
|
+
provideSignalIndexedDb({ dbName: 'MyAppDb' }), // config is optional
|
|
66
|
+
];
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
**2. Use with `ResourceRef`:**
|
|
70
|
+
|
|
71
|
+
```typescript
|
|
72
|
+
import { Component, inject } from '@angular/core';
|
|
73
|
+
import { SignalIndexedDb } from '@angular-libs/signal-storage';
|
|
74
|
+
|
|
75
|
+
interface AppData {
|
|
76
|
+
user: { name: string };
|
|
33
77
|
}
|
|
34
78
|
|
|
35
79
|
@Component({
|
|
36
|
-
selector: 'app-root',
|
|
37
80
|
standalone: true,
|
|
38
|
-
providers: [SignalStorageService],
|
|
39
81
|
template: `
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
82
|
+
@if (profile.isLoading()) {
|
|
83
|
+
Loading...
|
|
84
|
+
} @else {
|
|
85
|
+
{{ profile.value()?.name }}
|
|
86
|
+
}
|
|
44
87
|
`,
|
|
45
88
|
})
|
|
46
|
-
export class
|
|
47
|
-
private
|
|
89
|
+
export class ProfileComponent {
|
|
90
|
+
private db = inject(SignalIndexedDb<AppData>);
|
|
48
91
|
|
|
49
|
-
//
|
|
50
|
-
|
|
92
|
+
// Automatically fetches from IndexedDB as a ResourceRef
|
|
93
|
+
profile = this.db.getResource('user');
|
|
51
94
|
|
|
95
|
+
async save() {
|
|
96
|
+
await this.db.set('user', { name: 'Alice' });
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
---
|
|
102
|
+
|
|
103
|
+
## 🛠 3. Advanced: Multiple Storage Instances
|
|
104
|
+
|
|
105
|
+
Need `localStorage` AND `MemoryStorage` in the same app? Extend the base class:
|
|
106
|
+
|
|
107
|
+
```typescript
|
|
108
|
+
import { Injectable } from '@angular/core';
|
|
109
|
+
import { SignalStorage, MemoryStorage } from '@angular-libs/signal-storage';
|
|
110
|
+
|
|
111
|
+
interface LocalState {
|
|
112
|
+
user: string;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
@Injectable({ providedIn: 'root' })
|
|
116
|
+
export class LocalStore extends SignalStorage<LocalState> {
|
|
52
117
|
constructor() {
|
|
53
|
-
|
|
54
|
-
console.log('Theme changed to:', this.theme());
|
|
55
|
-
});
|
|
118
|
+
super(typeof window !== 'undefined' ? window.localStorage : new MemoryStorage());
|
|
56
119
|
}
|
|
120
|
+
}
|
|
57
121
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
122
|
+
interface TempState {
|
|
123
|
+
tempId: string;
|
|
124
|
+
}
|
|
61
125
|
|
|
62
|
-
|
|
63
|
-
|
|
126
|
+
@Injectable({ providedIn: 'root' })
|
|
127
|
+
export class TempStore extends SignalStorage<TempState> {
|
|
128
|
+
constructor() {
|
|
129
|
+
super(new MemoryStorage());
|
|
64
130
|
}
|
|
65
131
|
}
|
|
66
132
|
```
|
|
67
133
|
|
|
68
|
-
|
|
134
|
+
The same applies for `SignalIndexedDb` if you need multiple different databases:
|
|
69
135
|
|
|
70
|
-
|
|
136
|
+
```typescript
|
|
137
|
+
import { Injectable, Injector, inject } from '@angular/core';
|
|
138
|
+
import { SignalIndexedDb } from '@angular-libs/signal-storage';
|
|
71
139
|
|
|
72
|
-
|
|
140
|
+
interface UserDbState {
|
|
141
|
+
user: { name: string; email: string };
|
|
142
|
+
}
|
|
73
143
|
|
|
74
|
-
|
|
144
|
+
@Injectable({ providedIn: 'root' })
|
|
145
|
+
export class UserDb extends SignalIndexedDb<UserDbState> {
|
|
146
|
+
constructor() {
|
|
147
|
+
super(inject(Injector), { dbName: 'UserDb', storeName: 'keyValue', version: 1 });
|
|
148
|
+
}
|
|
149
|
+
}
|
|
75
150
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
],
|
|
87
|
-
};
|
|
151
|
+
interface SettingsDbState {
|
|
152
|
+
settings: { theme: 'light' | 'dark' };
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
@Injectable({ providedIn: 'root' })
|
|
156
|
+
export class SettingsDb extends SignalIndexedDb<SettingsDbState> {
|
|
157
|
+
constructor() {
|
|
158
|
+
super(inject(Injector), { dbName: 'SettingsDb', storeName: 'keyValue', version: 1 });
|
|
159
|
+
}
|
|
160
|
+
}
|
|
88
161
|
```
|
|
89
162
|
|
|
163
|
+
---
|
|
164
|
+
|
|
90
165
|
## License
|
|
91
166
|
|
|
92
167
|
MIT
|
|
@@ -1,19 +1,20 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
|
-
import { InjectionToken, signal, Optional, Inject, Injectable } from '@angular/core';
|
|
2
|
+
import { InjectionToken, signal, Optional, Inject, Injectable, resource } from '@angular/core';
|
|
3
3
|
|
|
4
4
|
const SIGNAL_STORAGE_TOKEN = new InjectionToken('SIGNAL_STORAGE_TOKEN', {
|
|
5
5
|
providedIn: 'root',
|
|
6
|
-
factory: () => (typeof window !== 'undefined' ? window.localStorage :
|
|
6
|
+
factory: () => (typeof window !== 'undefined' ? window.localStorage : new MemoryStorage()),
|
|
7
7
|
});
|
|
8
|
-
class
|
|
8
|
+
class SignalStorage {
|
|
9
9
|
storage;
|
|
10
10
|
signals = new Map();
|
|
11
11
|
/**
|
|
12
|
-
* Create a new
|
|
12
|
+
* Create a new SignalStorage instance
|
|
13
13
|
* @param storage The storage to use (localStorage or sessionStorage). Defaults to localStorage.
|
|
14
14
|
*/
|
|
15
15
|
constructor(storage) {
|
|
16
|
-
this.storage =
|
|
16
|
+
this.storage =
|
|
17
|
+
storage || (typeof window !== 'undefined' ? window.localStorage : new MemoryStorage());
|
|
17
18
|
if (typeof window !== 'undefined') {
|
|
18
19
|
window.addEventListener('storage', (event) => {
|
|
19
20
|
const { key, newValue, storageArea } = event;
|
|
@@ -99,10 +100,10 @@ class SignalStorageService {
|
|
|
99
100
|
sig.set(null);
|
|
100
101
|
}
|
|
101
102
|
}
|
|
102
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type:
|
|
103
|
-
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type:
|
|
103
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: SignalStorage, deps: [{ token: SIGNAL_STORAGE_TOKEN, optional: true }], target: i0.ɵɵFactoryTarget.Injectable });
|
|
104
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: SignalStorage });
|
|
104
105
|
}
|
|
105
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type:
|
|
106
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: SignalStorage, decorators: [{
|
|
106
107
|
type: Injectable
|
|
107
108
|
}], ctorParameters: () => [{ type: Storage, decorators: [{
|
|
108
109
|
type: Optional
|
|
@@ -110,6 +111,273 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.0", ngImpor
|
|
|
110
111
|
type: Inject,
|
|
111
112
|
args: [SIGNAL_STORAGE_TOKEN]
|
|
112
113
|
}] }] });
|
|
114
|
+
/**
|
|
115
|
+
* An in-memory implementation of the Storage interface.
|
|
116
|
+
* Can be provided to SIGNAL_STORAGE_TOKEN to use SignalStorage for purely in-memory app state.
|
|
117
|
+
*/
|
|
118
|
+
class MemoryStorage {
|
|
119
|
+
data = new Map();
|
|
120
|
+
get length() {
|
|
121
|
+
return this.data.size;
|
|
122
|
+
}
|
|
123
|
+
clear() {
|
|
124
|
+
this.data.clear();
|
|
125
|
+
}
|
|
126
|
+
getItem(key) {
|
|
127
|
+
const value = this.data.get(key);
|
|
128
|
+
return value === undefined ? null : value;
|
|
129
|
+
}
|
|
130
|
+
removeItem(key) {
|
|
131
|
+
this.data.delete(key);
|
|
132
|
+
}
|
|
133
|
+
setItem(key, value) {
|
|
134
|
+
this.data.set(key, String(value));
|
|
135
|
+
}
|
|
136
|
+
key(index) {
|
|
137
|
+
const keys = Array.from(this.data.keys());
|
|
138
|
+
return keys[index] === undefined ? null : keys[index];
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
const DEFAULT_CONFIG = {
|
|
143
|
+
dbName: 'SignalStorageDb',
|
|
144
|
+
storeName: 'keyValue',
|
|
145
|
+
version: 1,
|
|
146
|
+
};
|
|
147
|
+
const SIGNAL_INDEXEDDB_CONFIG = new InjectionToken('SIGNAL_INDEXEDDB_CONFIG', {
|
|
148
|
+
providedIn: 'root',
|
|
149
|
+
factory: () => DEFAULT_CONFIG,
|
|
150
|
+
});
|
|
151
|
+
function provideSignalIndexedDb(config) {
|
|
152
|
+
return [
|
|
153
|
+
{
|
|
154
|
+
provide: SIGNAL_INDEXEDDB_CONFIG,
|
|
155
|
+
useValue: { ...DEFAULT_CONFIG, ...config },
|
|
156
|
+
},
|
|
157
|
+
// Explicitly provide the storage instance here!
|
|
158
|
+
SignalIndexedDb,
|
|
159
|
+
];
|
|
160
|
+
}
|
|
161
|
+
class SignalIndexedDb {
|
|
162
|
+
injector;
|
|
163
|
+
config;
|
|
164
|
+
dbPromise;
|
|
165
|
+
fallbackStorage = new Map();
|
|
166
|
+
isFallback = false;
|
|
167
|
+
resources = new Map();
|
|
168
|
+
broadcastChannel;
|
|
169
|
+
constructor(injector, config) {
|
|
170
|
+
this.injector = injector;
|
|
171
|
+
this.config = config || DEFAULT_CONFIG;
|
|
172
|
+
this.dbPromise = this.initDB().catch((err) => {
|
|
173
|
+
console.warn('IndexedDB initialization failed. Falling back to in-memory storage.', err);
|
|
174
|
+
this.isFallback = true;
|
|
175
|
+
return null;
|
|
176
|
+
});
|
|
177
|
+
if (typeof window !== 'undefined' && 'BroadcastChannel' in window) {
|
|
178
|
+
try {
|
|
179
|
+
this.broadcastChannel = new BroadcastChannel(`${this.config.dbName}-sync`);
|
|
180
|
+
this.broadcastChannel.onmessage = (event) => {
|
|
181
|
+
const { key, value } = event.data;
|
|
182
|
+
if (this.resources.has(key)) {
|
|
183
|
+
this.resources.get(key).value.set(value);
|
|
184
|
+
}
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
catch {
|
|
188
|
+
// Ignore broadcast channel errors inside environments that do not fully support it
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
initDB() {
|
|
193
|
+
return new Promise((resolve, reject) => {
|
|
194
|
+
if (typeof window === 'undefined' || !window.indexedDB) {
|
|
195
|
+
return reject('IndexedDB is not supported');
|
|
196
|
+
}
|
|
197
|
+
const request = indexedDB.open(this.config.dbName, this.config.version);
|
|
198
|
+
request.onerror = () => reject(request.error);
|
|
199
|
+
request.onsuccess = () => resolve(request.result);
|
|
200
|
+
request.onupgradeneeded = (event) => {
|
|
201
|
+
const db = event.target.result;
|
|
202
|
+
if (!db.objectStoreNames.contains(this.config.storeName)) {
|
|
203
|
+
db.createObjectStore(this.config.storeName);
|
|
204
|
+
}
|
|
205
|
+
};
|
|
206
|
+
});
|
|
207
|
+
}
|
|
208
|
+
getResource(key, defaultValue) {
|
|
209
|
+
if (!this.resources.has(key)) {
|
|
210
|
+
const dbResource = resource({ ...(ngDevMode ? { debugName: "dbResource" } : {}), loader: async () => {
|
|
211
|
+
const val = await this.get(key);
|
|
212
|
+
return val !== undefined ? val : defaultValue;
|
|
213
|
+
},
|
|
214
|
+
injector: this.injector });
|
|
215
|
+
this.resources.set(key, dbResource);
|
|
216
|
+
}
|
|
217
|
+
return this.resources.get(key);
|
|
218
|
+
}
|
|
219
|
+
/**
|
|
220
|
+
* Get data asynchronously from IndexedDB
|
|
221
|
+
*/
|
|
222
|
+
async get(key) {
|
|
223
|
+
if (this.isFallback)
|
|
224
|
+
return this.fallbackStorage.get(key);
|
|
225
|
+
const db = await this.dbPromise;
|
|
226
|
+
if (!db)
|
|
227
|
+
return this.fallbackStorage.get(key);
|
|
228
|
+
return new Promise((resolve, reject) => {
|
|
229
|
+
try {
|
|
230
|
+
const transaction = db.transaction(this.config.storeName, 'readonly');
|
|
231
|
+
const store = transaction.objectStore(this.config.storeName);
|
|
232
|
+
const request = store.get(key);
|
|
233
|
+
request.onerror = () => reject(request.error);
|
|
234
|
+
request.onsuccess = () => resolve(request.result);
|
|
235
|
+
}
|
|
236
|
+
catch (e) {
|
|
237
|
+
reject(e);
|
|
238
|
+
}
|
|
239
|
+
});
|
|
240
|
+
}
|
|
241
|
+
/**
|
|
242
|
+
* Set data asynchronously in IndexedDB and update the resource
|
|
243
|
+
*/
|
|
244
|
+
async set(key, value) {
|
|
245
|
+
if (this.isFallback) {
|
|
246
|
+
this.fallbackStorage.set(key, value);
|
|
247
|
+
this.updateLocalState(key, value);
|
|
248
|
+
return;
|
|
249
|
+
}
|
|
250
|
+
const db = await this.dbPromise;
|
|
251
|
+
if (!db) {
|
|
252
|
+
this.fallbackStorage.set(key, value);
|
|
253
|
+
this.updateLocalState(key, value);
|
|
254
|
+
return;
|
|
255
|
+
}
|
|
256
|
+
return new Promise((resolve, reject) => {
|
|
257
|
+
try {
|
|
258
|
+
const transaction = db.transaction(this.config.storeName, 'readwrite');
|
|
259
|
+
const store = transaction.objectStore(this.config.storeName);
|
|
260
|
+
const request = store.put(value, key);
|
|
261
|
+
request.onerror = () => reject(request.error);
|
|
262
|
+
request.onsuccess = () => {
|
|
263
|
+
this.updateLocalState(key, value);
|
|
264
|
+
resolve();
|
|
265
|
+
};
|
|
266
|
+
}
|
|
267
|
+
catch (e) {
|
|
268
|
+
reject(e);
|
|
269
|
+
}
|
|
270
|
+
});
|
|
271
|
+
}
|
|
272
|
+
/**
|
|
273
|
+
* Safely update a value based on its previous state
|
|
274
|
+
*/
|
|
275
|
+
async update(key, updater) {
|
|
276
|
+
const current = await this.get(key);
|
|
277
|
+
const newValue = updater(current);
|
|
278
|
+
await this.set(key, newValue);
|
|
279
|
+
}
|
|
280
|
+
/**
|
|
281
|
+
* Remove a key asynchronously from IndexedDB
|
|
282
|
+
*/
|
|
283
|
+
async remove(key) {
|
|
284
|
+
if (this.isFallback) {
|
|
285
|
+
this.fallbackStorage.delete(key);
|
|
286
|
+
this.updateLocalState(key, undefined);
|
|
287
|
+
return;
|
|
288
|
+
}
|
|
289
|
+
const db = await this.dbPromise;
|
|
290
|
+
if (!db) {
|
|
291
|
+
this.fallbackStorage.delete(key);
|
|
292
|
+
this.updateLocalState(key, undefined);
|
|
293
|
+
return;
|
|
294
|
+
}
|
|
295
|
+
return new Promise((resolve, reject) => {
|
|
296
|
+
try {
|
|
297
|
+
const transaction = db.transaction(this.config.storeName, 'readwrite');
|
|
298
|
+
const store = transaction.objectStore(this.config.storeName);
|
|
299
|
+
const request = store.delete(key);
|
|
300
|
+
request.onerror = () => reject(request.error);
|
|
301
|
+
request.onsuccess = () => {
|
|
302
|
+
this.updateLocalState(key, undefined);
|
|
303
|
+
resolve();
|
|
304
|
+
};
|
|
305
|
+
}
|
|
306
|
+
catch (e) {
|
|
307
|
+
reject(e);
|
|
308
|
+
}
|
|
309
|
+
});
|
|
310
|
+
}
|
|
311
|
+
/**
|
|
312
|
+
* Clear the entire object store
|
|
313
|
+
*/
|
|
314
|
+
async clear() {
|
|
315
|
+
if (this.isFallback) {
|
|
316
|
+
this.fallbackStorage.clear();
|
|
317
|
+
this.resources.forEach((_, key) => this.updateLocalState(key, undefined));
|
|
318
|
+
return;
|
|
319
|
+
}
|
|
320
|
+
const db = await this.dbPromise;
|
|
321
|
+
if (!db) {
|
|
322
|
+
this.fallbackStorage.clear();
|
|
323
|
+
this.resources.forEach((_, key) => this.updateLocalState(key, undefined));
|
|
324
|
+
return;
|
|
325
|
+
}
|
|
326
|
+
return new Promise((resolve, reject) => {
|
|
327
|
+
try {
|
|
328
|
+
const transaction = db.transaction(this.config.storeName, 'readwrite');
|
|
329
|
+
const store = transaction.objectStore(this.config.storeName);
|
|
330
|
+
const request = store.clear();
|
|
331
|
+
request.onerror = () => reject(request.error);
|
|
332
|
+
request.onsuccess = () => {
|
|
333
|
+
this.resources.forEach((_, key) => this.updateLocalState(key, undefined));
|
|
334
|
+
resolve();
|
|
335
|
+
};
|
|
336
|
+
}
|
|
337
|
+
catch (e) {
|
|
338
|
+
reject(e);
|
|
339
|
+
}
|
|
340
|
+
});
|
|
341
|
+
}
|
|
342
|
+
/**
|
|
343
|
+
* Get all keys from the object store
|
|
344
|
+
*/
|
|
345
|
+
async keys() {
|
|
346
|
+
if (this.isFallback)
|
|
347
|
+
return Array.from(this.fallbackStorage.keys());
|
|
348
|
+
const db = await this.dbPromise;
|
|
349
|
+
if (!db)
|
|
350
|
+
return Array.from(this.fallbackStorage.keys());
|
|
351
|
+
return new Promise((resolve, reject) => {
|
|
352
|
+
try {
|
|
353
|
+
const transaction = db.transaction(this.config.storeName, 'readonly');
|
|
354
|
+
const store = transaction.objectStore(this.config.storeName);
|
|
355
|
+
const request = store.getAllKeys();
|
|
356
|
+
request.onerror = () => reject(request.error);
|
|
357
|
+
request.onsuccess = () => resolve(request.result);
|
|
358
|
+
}
|
|
359
|
+
catch (e) {
|
|
360
|
+
reject(e);
|
|
361
|
+
}
|
|
362
|
+
});
|
|
363
|
+
}
|
|
364
|
+
updateLocalState(key, value) {
|
|
365
|
+
if (this.resources.has(key)) {
|
|
366
|
+
this.resources.get(key).value.set(value);
|
|
367
|
+
}
|
|
368
|
+
this.broadcastChannel?.postMessage({ key, value });
|
|
369
|
+
}
|
|
370
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: SignalIndexedDb, deps: [{ token: i0.Injector }, { token: SIGNAL_INDEXEDDB_CONFIG, optional: true }], target: i0.ɵɵFactoryTarget.Injectable });
|
|
371
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: SignalIndexedDb });
|
|
372
|
+
}
|
|
373
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: SignalIndexedDb, decorators: [{
|
|
374
|
+
type: Injectable
|
|
375
|
+
}], ctorParameters: () => [{ type: i0.Injector }, { type: undefined, decorators: [{
|
|
376
|
+
type: Optional
|
|
377
|
+
}, {
|
|
378
|
+
type: Inject,
|
|
379
|
+
args: [SIGNAL_INDEXEDDB_CONFIG]
|
|
380
|
+
}] }] });
|
|
113
381
|
|
|
114
382
|
/*
|
|
115
383
|
* Public API Surface of signal-storage
|
|
@@ -119,5 +387,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.0", ngImpor
|
|
|
119
387
|
* Generated bundle index. Do not edit.
|
|
120
388
|
*/
|
|
121
389
|
|
|
122
|
-
export { SIGNAL_STORAGE_TOKEN,
|
|
390
|
+
export { MemoryStorage, SIGNAL_INDEXEDDB_CONFIG, SIGNAL_STORAGE_TOKEN, SignalIndexedDb, SignalStorage, provideSignalIndexedDb };
|
|
123
391
|
//# sourceMappingURL=angular-libs-signal-storage.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"angular-libs-signal-storage.mjs","sources":["../../../../projects/angular-libs/signal-storage/src/lib/signal-storage.ts","../../../../projects/angular-libs/signal-storage/src/public-api.ts","../../../../projects/angular-libs/signal-storage/src/angular-libs-signal-storage.ts"],"sourcesContent":["import {\n signal,\n WritableSignal,\n Signal,\n Injectable,\n InjectionToken,\n Optional,\n Inject,\n} from '@angular/core';\n\nexport const SIGNAL_STORAGE_TOKEN = new InjectionToken<Storage>('SIGNAL_STORAGE_TOKEN', {\n providedIn: 'root',\n factory: () => (typeof window !== 'undefined' ? window.localStorage : ({} as Storage)),\n});\n\n@Injectable()\nexport class SignalStorageService<T extends Record<string, any> = {}> {\n private storage: Storage;\n private signals = new Map<keyof T, WritableSignal<any>>();\n\n /**\n * Create a new SignalStorageService instance\n * @param storage The storage to use (localStorage or sessionStorage). Defaults to localStorage.\n */\n constructor(@Optional() @Inject(SIGNAL_STORAGE_TOKEN) storage?: Storage) {\n this.storage = storage || (typeof window !== 'undefined' ? window.localStorage : ({} as Storage));\n if (typeof window !== 'undefined') {\n window.addEventListener('storage', (event: StorageEvent) => {\n const { key, newValue, storageArea } = event;\n if (storageArea === this.storage && key && this.signals.has(key as keyof T)) {\n try {\n const value = newValue ? JSON.parse(newValue) : null;\n this.signals.get(key as keyof T)!.set(value);\n } catch {\n // Ignore parse errors from external changes\n }\n }\n });\n }\n }\n /**\n * Get typed data from SignalStorageService\n * @param key The key to retrieve\n * @returns The typed data or null if not found\n */\n get<K extends keyof T>(key: K): T[K] | null;\n /**\n * Get typed data from SignalStorageService with a default value\n * @param key The key to retrieve\n * @param defaultValue The default value to return if key not found\n * @returns The stored data or the default value\n */\n get<K extends keyof T>(key: K, defaultValue: T[K]): T[K];\n get<K extends keyof T>(key: K, defaultValue?: T[K]): T[K] | null {\n const item = this.storage.getItem(key as string);\n if (item === null) {\n return defaultValue ?? null;\n }\n try {\n return JSON.parse(item) as T[K];\n } catch {\n return defaultValue ?? null;\n }\n }\n\n /**\n * Get a reactive Angular signal for a key\n * @param key The key to retrieve\n * @param defaultValue The default value to return if key not found\n * @returns A read-only Signal containing the stored data\n */\n getSignal<K extends keyof T>(key: K): Signal<T[K] | null>;\n getSignal<K extends keyof T>(key: K, defaultValue: T[K]): Signal<T[K]>;\n getSignal<K extends keyof T>(key: K, defaultValue?: T[K]): Signal<any> {\n if (!this.signals.has(key)) {\n const initialValue = this.get(key, defaultValue as T[K]);\n this.signals.set(key, signal(initialValue));\n }\n return this.signals.get(key)!.asReadonly();\n }\n\n /**\n * Set typed data in localStorage\n * @param key The key to set\n * @param value The value to store\n */\n set<K extends keyof T>(key: K, value: T[K]): void {\n try {\n this.storage.setItem(key as string, JSON.stringify(value));\n } catch (e) {\n console.error(\n `Error saving to storage for key \"${String(key)}\". Storage quota may be exceeded.`,\n e,\n );\n }\n if (this.signals.has(key)) {\n this.signals.get(key)!.set(value);\n }\n }\n\n /**\n * Update typed data based on current value using a callback\n * @param key The key to update\n * @param updateFn Callback function that receives the current value and returns the new value\n */\n update<K extends keyof T>(key: K, updateFn: (currentValue: T[K] | null) => T[K]): void {\n const currentValue = this.get(key);\n const newValue = updateFn(currentValue);\n this.set(key, newValue);\n }\n\n /**\n * Remove an item from localStorage\n * @param key The key to remove\n */\n remove<K extends keyof T>(key: K): void {\n this.storage.removeItem(key as string);\n if (this.signals.has(key)) {\n this.signals.get(key)!.set(null);\n }\n }\n\n /**\n * Check if a key exists in localStorage\n * @param key The key to check\n * @returns true if the key exists, false otherwise\n */\n has<K extends keyof T>(key: K): boolean {\n return this.storage.getItem(key as string) !== null;\n }\n\n /** Clear all localStorage */\n clear(): void {\n this.storage.clear();\n for (const sig of this.signals.values()) {\n sig.set(null);\n }\n }\n}\n","/*\n * Public API Surface of signal-storage\n */\n\nexport * from './lib/signal-storage';\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"angular-libs-signal-storage.mjs","sources":["../../../../projects/angular-libs/signal-storage/src/lib/signal-storage.ts","../../../../projects/angular-libs/signal-storage/src/lib/signal-indexeddb.ts","../../../../projects/angular-libs/signal-storage/src/public-api.ts","../../../../projects/angular-libs/signal-storage/src/angular-libs-signal-storage.ts"],"sourcesContent":["import {\n signal,\n WritableSignal,\n Signal,\n Injectable,\n InjectionToken,\n Optional,\n Inject,\n} from '@angular/core';\n\nexport const SIGNAL_STORAGE_TOKEN = new InjectionToken<Storage>('SIGNAL_STORAGE_TOKEN', {\n providedIn: 'root',\n factory: () => (typeof window !== 'undefined' ? window.localStorage : new MemoryStorage()),\n});\n\n@Injectable()\nexport class SignalStorage<T extends Record<string, any> = {}> {\n private storage: Storage;\n private signals = new Map<keyof T, WritableSignal<any>>();\n\n /**\n * Create a new SignalStorage instance\n * @param storage The storage to use (localStorage or sessionStorage). Defaults to localStorage.\n */\n constructor(@Optional() @Inject(SIGNAL_STORAGE_TOKEN) storage?: Storage) {\n this.storage =\n storage || (typeof window !== 'undefined' ? window.localStorage : new MemoryStorage());\n if (typeof window !== 'undefined') {\n window.addEventListener('storage', (event: StorageEvent) => {\n const { key, newValue, storageArea } = event;\n if (storageArea === this.storage && key && this.signals.has(key as keyof T)) {\n try {\n const value = newValue ? JSON.parse(newValue) : null;\n this.signals.get(key as keyof T)!.set(value);\n } catch {\n // Ignore parse errors from external changes\n }\n }\n });\n }\n }\n /**\n * Get typed data from SignalStorage\n * @param key The key to retrieve\n * @returns The typed data or null if not found\n */\n get<K extends keyof T>(key: K): T[K] | null;\n /**\n * Get typed data from SignalStorage with a default value\n * @param key The key to retrieve\n * @param defaultValue The default value to return if key not found\n * @returns The stored data or the default value\n */\n get<K extends keyof T>(key: K, defaultValue: T[K]): T[K];\n get<K extends keyof T>(key: K, defaultValue?: T[K]): T[K] | null {\n const item = this.storage.getItem(key as string);\n if (item === null) {\n return defaultValue ?? null;\n }\n try {\n return JSON.parse(item) as T[K];\n } catch {\n return defaultValue ?? null;\n }\n }\n\n /**\n * Get a reactive Angular signal for a key\n * @param key The key to retrieve\n * @param defaultValue The default value to return if key not found\n * @returns A read-only Signal containing the stored data\n */\n getSignal<K extends keyof T>(key: K): Signal<T[K] | null>;\n getSignal<K extends keyof T>(key: K, defaultValue: T[K]): Signal<T[K]>;\n getSignal<K extends keyof T>(key: K, defaultValue?: T[K]): Signal<any> {\n if (!this.signals.has(key)) {\n const initialValue = this.get(key, defaultValue as T[K]);\n this.signals.set(key, signal(initialValue));\n }\n return this.signals.get(key)!.asReadonly();\n }\n\n /**\n * Set typed data in localStorage\n * @param key The key to set\n * @param value The value to store\n */\n set<K extends keyof T>(key: K, value: T[K]): void {\n try {\n this.storage.setItem(key as string, JSON.stringify(value));\n } catch (e) {\n console.error(\n `Error saving to storage for key \"${String(key)}\". Storage quota may be exceeded.`,\n e,\n );\n }\n if (this.signals.has(key)) {\n this.signals.get(key)!.set(value);\n }\n }\n\n /**\n * Update typed data based on current value using a callback\n * @param key The key to update\n * @param updateFn Callback function that receives the current value and returns the new value\n */\n update<K extends keyof T>(key: K, updateFn: (currentValue: T[K] | null) => T[K]): void {\n const currentValue = this.get(key);\n const newValue = updateFn(currentValue);\n this.set(key, newValue);\n }\n\n /**\n * Remove an item from localStorage\n * @param key The key to remove\n */\n remove<K extends keyof T>(key: K): void {\n this.storage.removeItem(key as string);\n if (this.signals.has(key)) {\n this.signals.get(key)!.set(null);\n }\n }\n\n /**\n * Check if a key exists in localStorage\n * @param key The key to check\n * @returns true if the key exists, false otherwise\n */\n has<K extends keyof T>(key: K): boolean {\n return this.storage.getItem(key as string) !== null;\n }\n\n /** Clear all localStorage */\n clear(): void {\n this.storage.clear();\n for (const sig of this.signals.values()) {\n sig.set(null);\n }\n }\n}\n\n/**\n * An in-memory implementation of the Storage interface.\n * Can be provided to SIGNAL_STORAGE_TOKEN to use SignalStorage for purely in-memory app state.\n */\nexport class MemoryStorage implements Storage {\n private data = new Map<string, string>();\n\n get length(): number {\n return this.data.size;\n }\n\n clear(): void {\n this.data.clear();\n }\n\n getItem(key: string): string | null {\n const value = this.data.get(key);\n return value === undefined ? null : value;\n }\n\n removeItem(key: string): void {\n this.data.delete(key);\n }\n\n setItem(key: string, value: string): void {\n this.data.set(key, String(value));\n }\n\n key(index: number): string | null {\n const keys = Array.from(this.data.keys());\n return keys[index] === undefined ? null : keys[index];\n }\n}\n","import {\n Injectable,\n InjectionToken,\n Injector,\n Optional,\n Inject,\n resource,\n ResourceRef,\n} from '@angular/core';\n\nexport interface SignalIndexedDbConfig {\n /** Default: `SignalStorageDb` */\n dbName: string;\n /** Default: `keyValue` */\n storeName: string;\n /** Default: `1` */\n version: number;\n}\n\nconst DEFAULT_CONFIG: SignalIndexedDbConfig = {\n dbName: 'SignalStorageDb',\n storeName: 'keyValue',\n version: 1,\n};\n\nexport const SIGNAL_INDEXEDDB_CONFIG = new InjectionToken<SignalIndexedDbConfig>(\n 'SIGNAL_INDEXEDDB_CONFIG',\n {\n providedIn: 'root',\n factory: () => DEFAULT_CONFIG,\n },\n);\n\nexport function provideSignalIndexedDb(config?: Partial<SignalIndexedDbConfig>) {\n return [\n {\n provide: SIGNAL_INDEXEDDB_CONFIG,\n useValue: { ...DEFAULT_CONFIG, ...config },\n },\n // Explicitly provide the storage instance here!\n SignalIndexedDb,\n ];\n}\n\n@Injectable()\nexport class SignalIndexedDb<T extends Record<string, any> = {}> {\n private config: SignalIndexedDbConfig;\n private dbPromise: Promise<IDBDatabase | null>;\n private fallbackStorage = new Map<string, any>();\n private isFallback = false;\n\n private resources = new Map<keyof T, ResourceRef<any>>();\n private broadcastChannel?: BroadcastChannel;\n\n constructor(\n private injector: Injector,\n @Optional() @Inject(SIGNAL_INDEXEDDB_CONFIG) config?: SignalIndexedDbConfig,\n ) {\n this.config = config || DEFAULT_CONFIG;\n this.dbPromise = this.initDB().catch((err) => {\n console.warn('IndexedDB initialization failed. Falling back to in-memory storage.', err);\n this.isFallback = true;\n return null;\n });\n\n if (typeof window !== 'undefined' && 'BroadcastChannel' in window) {\n try {\n this.broadcastChannel = new BroadcastChannel(`${this.config.dbName}-sync`);\n this.broadcastChannel.onmessage = (event) => {\n const { key, value } = event.data;\n if (this.resources.has(key as keyof T)) {\n this.resources.get(key as keyof T)!.value.set(value);\n }\n };\n } catch {\n // Ignore broadcast channel errors inside environments that do not fully support it\n }\n }\n }\n\n private initDB(): Promise<IDBDatabase> {\n return new Promise((resolve, reject) => {\n if (typeof window === 'undefined' || !window.indexedDB) {\n return reject('IndexedDB is not supported');\n }\n\n const request = indexedDB.open(this.config.dbName, this.config.version);\n\n request.onerror = () => reject(request.error);\n request.onsuccess = () => resolve(request.result);\n\n request.onupgradeneeded = (event: IDBVersionChangeEvent) => {\n const db = (event.target as IDBOpenDBRequest).result;\n if (!db.objectStoreNames.contains(this.config.storeName)) {\n db.createObjectStore(this.config.storeName);\n }\n };\n });\n }\n\n /**\n * Get an Angular Resource for a key\n */\n getResource<K extends keyof T>(key: K): ResourceRef<T[K] | undefined>;\n getResource<K extends keyof T>(key: K, defaultValue: T[K]): ResourceRef<T[K]>;\n getResource<K extends keyof T>(key: K, defaultValue?: T[K]): ResourceRef<any> {\n if (!this.resources.has(key)) {\n const dbResource = resource({\n loader: async () => {\n const val = await this.get(key);\n return val !== undefined ? val : defaultValue;\n },\n injector: this.injector,\n });\n\n this.resources.set(key, dbResource);\n }\n\n return this.resources.get(key)!;\n }\n\n /**\n * Get data asynchronously from IndexedDB\n */\n async get<K extends keyof T>(key: K): Promise<T[K] | undefined> {\n if (this.isFallback) return this.fallbackStorage.get(key as string);\n\n const db = await this.dbPromise;\n if (!db) return this.fallbackStorage.get(key as string);\n\n return new Promise((resolve, reject) => {\n try {\n const transaction = db.transaction(this.config.storeName, 'readonly');\n const store = transaction.objectStore(this.config.storeName);\n const request = store.get(key as string);\n\n request.onerror = () => reject(request.error);\n request.onsuccess = () => resolve(request.result);\n } catch (e) {\n reject(e);\n }\n });\n }\n\n /**\n * Set data asynchronously in IndexedDB and update the resource\n */\n async set<K extends keyof T>(key: K, value: T[K]): Promise<void> {\n if (this.isFallback) {\n this.fallbackStorage.set(key as string, value);\n this.updateLocalState(key, value);\n return;\n }\n\n const db = await this.dbPromise;\n if (!db) {\n this.fallbackStorage.set(key as string, value);\n this.updateLocalState(key, value);\n return;\n }\n\n return new Promise((resolve, reject) => {\n try {\n const transaction = db.transaction(this.config.storeName, 'readwrite');\n const store = transaction.objectStore(this.config.storeName);\n const request = store.put(value, key as string);\n\n request.onerror = () => reject(request.error);\n request.onsuccess = () => {\n this.updateLocalState(key, value);\n resolve();\n };\n } catch (e) {\n reject(e);\n }\n });\n }\n\n /**\n * Safely update a value based on its previous state\n */\n async update<K extends keyof T>(\n key: K,\n updater: (current: T[K] | undefined) => T[K],\n ): Promise<void> {\n const current = await this.get(key);\n const newValue = updater(current);\n await this.set(key, newValue);\n }\n\n /**\n * Remove a key asynchronously from IndexedDB\n */\n async remove<K extends keyof T>(key: K): Promise<void> {\n if (this.isFallback) {\n this.fallbackStorage.delete(key as string);\n this.updateLocalState(key, undefined);\n return;\n }\n\n const db = await this.dbPromise;\n if (!db) {\n this.fallbackStorage.delete(key as string);\n this.updateLocalState(key, undefined);\n return;\n }\n\n return new Promise((resolve, reject) => {\n try {\n const transaction = db.transaction(this.config.storeName, 'readwrite');\n const store = transaction.objectStore(this.config.storeName);\n const request = store.delete(key as string);\n\n request.onerror = () => reject(request.error);\n request.onsuccess = () => {\n this.updateLocalState(key, undefined);\n resolve();\n };\n } catch (e) {\n reject(e);\n }\n });\n }\n\n /**\n * Clear the entire object store\n */\n async clear(): Promise<void> {\n if (this.isFallback) {\n this.fallbackStorage.clear();\n this.resources.forEach((_, key) => this.updateLocalState(key as keyof T, undefined));\n return;\n }\n\n const db = await this.dbPromise;\n if (!db) {\n this.fallbackStorage.clear();\n this.resources.forEach((_, key) => this.updateLocalState(key as keyof T, undefined));\n return;\n }\n\n return new Promise((resolve, reject) => {\n try {\n const transaction = db.transaction(this.config.storeName, 'readwrite');\n const store = transaction.objectStore(this.config.storeName);\n const request = store.clear();\n\n request.onerror = () => reject(request.error);\n request.onsuccess = () => {\n this.resources.forEach((_, key) => this.updateLocalState(key as keyof T, undefined));\n resolve();\n };\n } catch (e) {\n reject(e);\n }\n });\n }\n\n /**\n * Get all keys from the object store\n */\n async keys(): Promise<string[]> {\n if (this.isFallback) return Array.from(this.fallbackStorage.keys());\n\n const db = await this.dbPromise;\n if (!db) return Array.from(this.fallbackStorage.keys());\n\n return new Promise((resolve, reject) => {\n try {\n const transaction = db.transaction(this.config.storeName, 'readonly');\n const store = transaction.objectStore(this.config.storeName);\n const request = store.getAllKeys();\n\n request.onerror = () => reject(request.error);\n request.onsuccess = () => resolve(request.result as string[]);\n } catch (e) {\n reject(e);\n }\n });\n }\n\n private updateLocalState(key: keyof T, value: any) {\n if (this.resources.has(key)) {\n this.resources.get(key)!.value.set(value);\n }\n this.broadcastChannel?.postMessage({ key, value });\n }\n}\n","/*\n * Public API Surface of signal-storage\n */\n\nexport * from './lib/signal-storage';\nexport * from './lib/signal-indexeddb';\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":";;;MAUa,oBAAoB,GAAG,IAAI,cAAc,CAAU,sBAAsB,EAAE;AACtF,IAAA,UAAU,EAAE,MAAM;IAClB,OAAO,EAAE,OAAO,OAAO,MAAM,KAAK,WAAW,GAAG,MAAM,CAAC,YAAY,GAAG,IAAI,aAAa,EAAE,CAAC;AAC3F,CAAA;MAGY,aAAa,CAAA;AAChB,IAAA,OAAO;AACP,IAAA,OAAO,GAAG,IAAI,GAAG,EAAgC;AAEzD;;;AAGG;AACH,IAAA,WAAA,CAAsD,OAAiB,EAAA;AACrE,QAAA,IAAI,CAAC,OAAO;AACV,YAAA,OAAO,KAAK,OAAO,MAAM,KAAK,WAAW,GAAG,MAAM,CAAC,YAAY,GAAG,IAAI,aAAa,EAAE,CAAC;AACxF,QAAA,IAAI,OAAO,MAAM,KAAK,WAAW,EAAE;YACjC,MAAM,CAAC,gBAAgB,CAAC,SAAS,EAAE,CAAC,KAAmB,KAAI;gBACzD,MAAM,EAAE,GAAG,EAAE,QAAQ,EAAE,WAAW,EAAE,GAAG,KAAK;AAC5C,gBAAA,IAAI,WAAW,KAAK,IAAI,CAAC,OAAO,IAAI,GAAG,IAAI,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,GAAc,CAAC,EAAE;AAC3E,oBAAA,IAAI;AACF,wBAAA,MAAM,KAAK,GAAG,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,GAAG,IAAI;AACpD,wBAAA,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,GAAc,CAAE,CAAC,GAAG,CAAC,KAAK,CAAC;oBAC9C;AAAE,oBAAA,MAAM;;oBAER;gBACF;AACF,YAAA,CAAC,CAAC;QACJ;IACF;IAcA,GAAG,CAAoB,GAAM,EAAE,YAAmB,EAAA;QAChD,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,GAAa,CAAC;AAChD,QAAA,IAAI,IAAI,KAAK,IAAI,EAAE;YACjB,OAAO,YAAY,IAAI,IAAI;QAC7B;AACA,QAAA,IAAI;AACF,YAAA,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAAS;QACjC;AAAE,QAAA,MAAM;YACN,OAAO,YAAY,IAAI,IAAI;QAC7B;IACF;IAUA,SAAS,CAAoB,GAAM,EAAE,YAAmB,EAAA;QACtD,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE;YAC1B,MAAM,YAAY,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,YAAoB,CAAC;AACxD,YAAA,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,EAAE,MAAM,CAAC,YAAY,CAAC,CAAC;QAC7C;QACA,OAAO,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,CAAE,CAAC,UAAU,EAAE;IAC5C;AAEA;;;;AAIG;IACH,GAAG,CAAoB,GAAM,EAAE,KAAW,EAAA;AACxC,QAAA,IAAI;AACF,YAAA,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,GAAa,EAAE,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;QAC5D;QAAE,OAAO,CAAC,EAAE;AACV,YAAA,OAAO,CAAC,KAAK,CACX,CAAA,iCAAA,EAAoC,MAAM,CAAC,GAAG,CAAC,CAAA,iCAAA,CAAmC,EAClF,CAAC,CACF;QACH;QACA,IAAI,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE;AACzB,YAAA,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,CAAE,CAAC,GAAG,CAAC,KAAK,CAAC;QACnC;IACF;AAEA;;;;AAIG;IACH,MAAM,CAAoB,GAAM,EAAE,QAA6C,EAAA;QAC7E,MAAM,YAAY,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC;AAClC,QAAA,MAAM,QAAQ,GAAG,QAAQ,CAAC,YAAY,CAAC;AACvC,QAAA,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,QAAQ,CAAC;IACzB;AAEA;;;AAGG;AACH,IAAA,MAAM,CAAoB,GAAM,EAAA;AAC9B,QAAA,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,GAAa,CAAC;QACtC,IAAI,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE;AACzB,YAAA,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,CAAE,CAAC,GAAG,CAAC,IAAI,CAAC;QAClC;IACF;AAEA;;;;AAIG;AACH,IAAA,GAAG,CAAoB,GAAM,EAAA;QAC3B,OAAO,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,GAAa,CAAC,KAAK,IAAI;IACrD;;IAGA,KAAK,GAAA;AACH,QAAA,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE;QACpB,KAAK,MAAM,GAAG,IAAI,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE;AACvC,YAAA,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC;QACf;IACF;AA1HW,IAAA,OAAA,IAAA,GAAA,EAAA,CAAA,kBAAA,CAAA,EAAA,UAAA,EAAA,QAAA,EAAA,OAAA,EAAA,QAAA,EAAA,QAAA,EAAA,EAAA,EAAA,IAAA,EAAA,aAAa,kBAQQ,oBAAoB,EAAA,QAAA,EAAA,IAAA,EAAA,CAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,UAAA,EAAA,CAAA;2GARzC,aAAa,EAAA,CAAA;;2FAAb,aAAa,EAAA,UAAA,EAAA,CAAA;kBADzB;;0BASc;;0BAAY,MAAM;2BAAC,oBAAoB;;AAqHtD;;;AAGG;MACU,aAAa,CAAA;AAChB,IAAA,IAAI,GAAG,IAAI,GAAG,EAAkB;AAExC,IAAA,IAAI,MAAM,GAAA;AACR,QAAA,OAAO,IAAI,CAAC,IAAI,CAAC,IAAI;IACvB;IAEA,KAAK,GAAA;AACH,QAAA,IAAI,CAAC,IAAI,CAAC,KAAK,EAAE;IACnB;AAEA,IAAA,OAAO,CAAC,GAAW,EAAA;QACjB,MAAM,KAAK,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC;QAChC,OAAO,KAAK,KAAK,SAAS,GAAG,IAAI,GAAG,KAAK;IAC3C;AAEA,IAAA,UAAU,CAAC,GAAW,EAAA;AACpB,QAAA,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC;IACvB;IAEA,OAAO,CAAC,GAAW,EAAE,KAAa,EAAA;AAChC,QAAA,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC;IACnC;AAEA,IAAA,GAAG,CAAC,KAAa,EAAA;AACf,QAAA,MAAM,IAAI,GAAG,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC;AACzC,QAAA,OAAO,IAAI,CAAC,KAAK,CAAC,KAAK,SAAS,GAAG,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC;IACvD;AACD;;AC1JD,MAAM,cAAc,GAA0B;AAC5C,IAAA,MAAM,EAAE,iBAAiB;AACzB,IAAA,SAAS,EAAE,UAAU;AACrB,IAAA,OAAO,EAAE,CAAC;CACX;MAEY,uBAAuB,GAAG,IAAI,cAAc,CACvD,yBAAyB,EACzB;AACE,IAAA,UAAU,EAAE,MAAM;AAClB,IAAA,OAAO,EAAE,MAAM,cAAc;AAC9B,CAAA;AAGG,SAAU,sBAAsB,CAAC,MAAuC,EAAA;IAC5E,OAAO;AACL,QAAA;AACE,YAAA,OAAO,EAAE,uBAAuB;AAChC,YAAA,QAAQ,EAAE,EAAE,GAAG,cAAc,EAAE,GAAG,MAAM,EAAE;AAC3C,SAAA;;QAED,eAAe;KAChB;AACH;MAGa,eAAe,CAAA;AAUhB,IAAA,QAAA;AATF,IAAA,MAAM;AACN,IAAA,SAAS;AACT,IAAA,eAAe,GAAG,IAAI,GAAG,EAAe;IACxC,UAAU,GAAG,KAAK;AAElB,IAAA,SAAS,GAAG,IAAI,GAAG,EAA6B;AAChD,IAAA,gBAAgB;IAExB,WAAA,CACU,QAAkB,EACmB,MAA8B,EAAA;QADnE,IAAA,CAAA,QAAQ,GAAR,QAAQ;AAGhB,QAAA,IAAI,CAAC,MAAM,GAAG,MAAM,IAAI,cAAc;AACtC,QAAA,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC,KAAK,CAAC,CAAC,GAAG,KAAI;AAC3C,YAAA,OAAO,CAAC,IAAI,CAAC,qEAAqE,EAAE,GAAG,CAAC;AACxF,YAAA,IAAI,CAAC,UAAU,GAAG,IAAI;AACtB,YAAA,OAAO,IAAI;AACb,QAAA,CAAC,CAAC;QAEF,IAAI,OAAO,MAAM,KAAK,WAAW,IAAI,kBAAkB,IAAI,MAAM,EAAE;AACjE,YAAA,IAAI;AACF,gBAAA,IAAI,CAAC,gBAAgB,GAAG,IAAI,gBAAgB,CAAC,CAAA,EAAG,IAAI,CAAC,MAAM,CAAC,MAAM,CAAA,KAAA,CAAO,CAAC;gBAC1E,IAAI,CAAC,gBAAgB,CAAC,SAAS,GAAG,CAAC,KAAK,KAAI;oBAC1C,MAAM,EAAE,GAAG,EAAE,KAAK,EAAE,GAAG,KAAK,CAAC,IAAI;oBACjC,IAAI,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,GAAc,CAAC,EAAE;AACtC,wBAAA,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,GAAc,CAAE,CAAC,KAAK,CAAC,GAAG,CAAC,KAAK,CAAC;oBACtD;AACF,gBAAA,CAAC;YACH;AAAE,YAAA,MAAM;;YAER;QACF;IACF;IAEQ,MAAM,GAAA;QACZ,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,KAAI;YACrC,IAAI,OAAO,MAAM,KAAK,WAAW,IAAI,CAAC,MAAM,CAAC,SAAS,EAAE;AACtD,gBAAA,OAAO,MAAM,CAAC,4BAA4B,CAAC;YAC7C;AAEA,YAAA,MAAM,OAAO,GAAG,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC;AAEvE,YAAA,OAAO,CAAC,OAAO,GAAG,MAAM,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC;AAC7C,YAAA,OAAO,CAAC,SAAS,GAAG,MAAM,OAAO,CAAC,OAAO,CAAC,MAAM,CAAC;AAEjD,YAAA,OAAO,CAAC,eAAe,GAAG,CAAC,KAA4B,KAAI;AACzD,gBAAA,MAAM,EAAE,GAAI,KAAK,CAAC,MAA2B,CAAC,MAAM;AACpD,gBAAA,IAAI,CAAC,EAAE,CAAC,gBAAgB,CAAC,QAAQ,CAAC,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,EAAE;oBACxD,EAAE,CAAC,iBAAiB,CAAC,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC;gBAC7C;AACF,YAAA,CAAC;AACH,QAAA,CAAC,CAAC;IACJ;IAOA,WAAW,CAAoB,GAAM,EAAE,YAAmB,EAAA;QACxD,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE;YAC5B,MAAM,UAAU,GAAG,QAAQ,CAAA,EAAA,IAAA,SAAA,GAAA,EAAA,SAAA,EAAA,YAAA,EAAA,GAAA,EAAA,CAAA,EACzB,MAAM,EAAE,YAAW;oBACjB,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC;oBAC/B,OAAO,GAAG,KAAK,SAAS,GAAG,GAAG,GAAG,YAAY;gBAC/C,CAAC;AACD,gBAAA,QAAQ,EAAE,IAAI,CAAC,QAAQ,GACvB;YAEF,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,GAAG,EAAE,UAAU,CAAC;QACrC;QAEA,OAAO,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,GAAG,CAAE;IACjC;AAEA;;AAEG;IACH,MAAM,GAAG,CAAoB,GAAM,EAAA;QACjC,IAAI,IAAI,CAAC,UAAU;YAAE,OAAO,IAAI,CAAC,eAAe,CAAC,GAAG,CAAC,GAAa,CAAC;AAEnE,QAAA,MAAM,EAAE,GAAG,MAAM,IAAI,CAAC,SAAS;AAC/B,QAAA,IAAI,CAAC,EAAE;YAAE,OAAO,IAAI,CAAC,eAAe,CAAC,GAAG,CAAC,GAAa,CAAC;QAEvD,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,KAAI;AACrC,YAAA,IAAI;AACF,gBAAA,MAAM,WAAW,GAAG,EAAE,CAAC,WAAW,CAAC,IAAI,CAAC,MAAM,CAAC,SAAS,EAAE,UAAU,CAAC;AACrE,gBAAA,MAAM,KAAK,GAAG,WAAW,CAAC,WAAW,CAAC,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC;gBAC5D,MAAM,OAAO,GAAG,KAAK,CAAC,GAAG,CAAC,GAAa,CAAC;AAExC,gBAAA,OAAO,CAAC,OAAO,GAAG,MAAM,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC;AAC7C,gBAAA,OAAO,CAAC,SAAS,GAAG,MAAM,OAAO,CAAC,OAAO,CAAC,MAAM,CAAC;YACnD;YAAE,OAAO,CAAC,EAAE;gBACV,MAAM,CAAC,CAAC,CAAC;YACX;AACF,QAAA,CAAC,CAAC;IACJ;AAEA;;AAEG;AACH,IAAA,MAAM,GAAG,CAAoB,GAAM,EAAE,KAAW,EAAA;AAC9C,QAAA,IAAI,IAAI,CAAC,UAAU,EAAE;YACnB,IAAI,CAAC,eAAe,CAAC,GAAG,CAAC,GAAa,EAAE,KAAK,CAAC;AAC9C,YAAA,IAAI,CAAC,gBAAgB,CAAC,GAAG,EAAE,KAAK,CAAC;YACjC;QACF;AAEA,QAAA,MAAM,EAAE,GAAG,MAAM,IAAI,CAAC,SAAS;QAC/B,IAAI,CAAC,EAAE,EAAE;YACP,IAAI,CAAC,eAAe,CAAC,GAAG,CAAC,GAAa,EAAE,KAAK,CAAC;AAC9C,YAAA,IAAI,CAAC,gBAAgB,CAAC,GAAG,EAAE,KAAK,CAAC;YACjC;QACF;QAEA,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,KAAI;AACrC,YAAA,IAAI;AACF,gBAAA,MAAM,WAAW,GAAG,EAAE,CAAC,WAAW,CAAC,IAAI,CAAC,MAAM,CAAC,SAAS,EAAE,WAAW,CAAC;AACtE,gBAAA,MAAM,KAAK,GAAG,WAAW,CAAC,WAAW,CAAC,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC;gBAC5D,MAAM,OAAO,GAAG,KAAK,CAAC,GAAG,CAAC,KAAK,EAAE,GAAa,CAAC;AAE/C,gBAAA,OAAO,CAAC,OAAO,GAAG,MAAM,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC;AAC7C,gBAAA,OAAO,CAAC,SAAS,GAAG,MAAK;AACvB,oBAAA,IAAI,CAAC,gBAAgB,CAAC,GAAG,EAAE,KAAK,CAAC;AACjC,oBAAA,OAAO,EAAE;AACX,gBAAA,CAAC;YACH;YAAE,OAAO,CAAC,EAAE;gBACV,MAAM,CAAC,CAAC,CAAC;YACX;AACF,QAAA,CAAC,CAAC;IACJ;AAEA;;AAEG;AACH,IAAA,MAAM,MAAM,CACV,GAAM,EACN,OAA4C,EAAA;QAE5C,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC;AACnC,QAAA,MAAM,QAAQ,GAAG,OAAO,CAAC,OAAO,CAAC;QACjC,MAAM,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,QAAQ,CAAC;IAC/B;AAEA;;AAEG;IACH,MAAM,MAAM,CAAoB,GAAM,EAAA;AACpC,QAAA,IAAI,IAAI,CAAC,UAAU,EAAE;AACnB,YAAA,IAAI,CAAC,eAAe,CAAC,MAAM,CAAC,GAAa,CAAC;AAC1C,YAAA,IAAI,CAAC,gBAAgB,CAAC,GAAG,EAAE,SAAS,CAAC;YACrC;QACF;AAEA,QAAA,MAAM,EAAE,GAAG,MAAM,IAAI,CAAC,SAAS;QAC/B,IAAI,CAAC,EAAE,EAAE;AACP,YAAA,IAAI,CAAC,eAAe,CAAC,MAAM,CAAC,GAAa,CAAC;AAC1C,YAAA,IAAI,CAAC,gBAAgB,CAAC,GAAG,EAAE,SAAS,CAAC;YACrC;QACF;QAEA,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,KAAI;AACrC,YAAA,IAAI;AACF,gBAAA,MAAM,WAAW,GAAG,EAAE,CAAC,WAAW,CAAC,IAAI,CAAC,MAAM,CAAC,SAAS,EAAE,WAAW,CAAC;AACtE,gBAAA,MAAM,KAAK,GAAG,WAAW,CAAC,WAAW,CAAC,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC;gBAC5D,MAAM,OAAO,GAAG,KAAK,CAAC,MAAM,CAAC,GAAa,CAAC;AAE3C,gBAAA,OAAO,CAAC,OAAO,GAAG,MAAM,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC;AAC7C,gBAAA,OAAO,CAAC,SAAS,GAAG,MAAK;AACvB,oBAAA,IAAI,CAAC,gBAAgB,CAAC,GAAG,EAAE,SAAS,CAAC;AACrC,oBAAA,OAAO,EAAE;AACX,gBAAA,CAAC;YACH;YAAE,OAAO,CAAC,EAAE;gBACV,MAAM,CAAC,CAAC,CAAC;YACX;AACF,QAAA,CAAC,CAAC;IACJ;AAEA;;AAEG;AACH,IAAA,MAAM,KAAK,GAAA;AACT,QAAA,IAAI,IAAI,CAAC,UAAU,EAAE;AACnB,YAAA,IAAI,CAAC,eAAe,CAAC,KAAK,EAAE;YAC5B,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,GAAG,KAAK,IAAI,CAAC,gBAAgB,CAAC,GAAc,EAAE,SAAS,CAAC,CAAC;YACpF;QACF;AAEA,QAAA,MAAM,EAAE,GAAG,MAAM,IAAI,CAAC,SAAS;QAC/B,IAAI,CAAC,EAAE,EAAE;AACP,YAAA,IAAI,CAAC,eAAe,CAAC,KAAK,EAAE;YAC5B,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,GAAG,KAAK,IAAI,CAAC,gBAAgB,CAAC,GAAc,EAAE,SAAS,CAAC,CAAC;YACpF;QACF;QAEA,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,KAAI;AACrC,YAAA,IAAI;AACF,gBAAA,MAAM,WAAW,GAAG,EAAE,CAAC,WAAW,CAAC,IAAI,CAAC,MAAM,CAAC,SAAS,EAAE,WAAW,CAAC;AACtE,gBAAA,MAAM,KAAK,GAAG,WAAW,CAAC,WAAW,CAAC,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC;AAC5D,gBAAA,MAAM,OAAO,GAAG,KAAK,CAAC,KAAK,EAAE;AAE7B,gBAAA,OAAO,CAAC,OAAO,GAAG,MAAM,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC;AAC7C,gBAAA,OAAO,CAAC,SAAS,GAAG,MAAK;oBACvB,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,GAAG,KAAK,IAAI,CAAC,gBAAgB,CAAC,GAAc,EAAE,SAAS,CAAC,CAAC;AACpF,oBAAA,OAAO,EAAE;AACX,gBAAA,CAAC;YACH;YAAE,OAAO,CAAC,EAAE;gBACV,MAAM,CAAC,CAAC,CAAC;YACX;AACF,QAAA,CAAC,CAAC;IACJ;AAEA;;AAEG;AACH,IAAA,MAAM,IAAI,GAAA;QACR,IAAI,IAAI,CAAC,UAAU;YAAE,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,eAAe,CAAC,IAAI,EAAE,CAAC;AAEnE,QAAA,MAAM,EAAE,GAAG,MAAM,IAAI,CAAC,SAAS;AAC/B,QAAA,IAAI,CAAC,EAAE;YAAE,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,eAAe,CAAC,IAAI,EAAE,CAAC;QAEvD,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,KAAI;AACrC,YAAA,IAAI;AACF,gBAAA,MAAM,WAAW,GAAG,EAAE,CAAC,WAAW,CAAC,IAAI,CAAC,MAAM,CAAC,SAAS,EAAE,UAAU,CAAC;AACrE,gBAAA,MAAM,KAAK,GAAG,WAAW,CAAC,WAAW,CAAC,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC;AAC5D,gBAAA,MAAM,OAAO,GAAG,KAAK,CAAC,UAAU,EAAE;AAElC,gBAAA,OAAO,CAAC,OAAO,GAAG,MAAM,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC;AAC7C,gBAAA,OAAO,CAAC,SAAS,GAAG,MAAM,OAAO,CAAC,OAAO,CAAC,MAAkB,CAAC;YAC/D;YAAE,OAAO,CAAC,EAAE;gBACV,MAAM,CAAC,CAAC,CAAC;YACX;AACF,QAAA,CAAC,CAAC;IACJ;IAEQ,gBAAgB,CAAC,GAAY,EAAE,KAAU,EAAA;QAC/C,IAAI,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE;AAC3B,YAAA,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,GAAG,CAAE,CAAC,KAAK,CAAC,GAAG,CAAC,KAAK,CAAC;QAC3C;QACA,IAAI,CAAC,gBAAgB,EAAE,WAAW,CAAC,EAAE,GAAG,EAAE,KAAK,EAAE,CAAC;IACpD;AAjPW,IAAA,OAAA,IAAA,GAAA,EAAA,CAAA,kBAAA,CAAA,EAAA,UAAA,EAAA,QAAA,EAAA,OAAA,EAAA,QAAA,EAAA,QAAA,EAAA,EAAA,EAAA,IAAA,EAAA,eAAe,0CAWJ,uBAAuB,EAAA,QAAA,EAAA,IAAA,EAAA,CAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,UAAA,EAAA,CAAA;2GAXlC,eAAe,EAAA,CAAA;;2FAAf,eAAe,EAAA,UAAA,EAAA,CAAA;kBAD3B;;0BAYI;;0BAAY,MAAM;2BAAC,uBAAuB;;;ACxD/C;;AAEG;;ACFH;;AAEG;;;;"}
|
package/package.json
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@angular-libs/signal-storage",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.1.0-beta.0",
|
|
4
4
|
"peerDependencies": {
|
|
5
|
-
"@angular/common": ">=
|
|
6
|
-
"@angular/core": ">=
|
|
5
|
+
"@angular/common": ">=19.0.0",
|
|
6
|
+
"@angular/core": ">=19.0.0"
|
|
7
7
|
},
|
|
8
8
|
"publishConfig": {
|
|
9
9
|
"access": "public"
|
|
@@ -1,23 +1,23 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
|
-
import { InjectionToken, Signal } from '@angular/core';
|
|
2
|
+
import { InjectionToken, Signal, Injector, ResourceRef } from '@angular/core';
|
|
3
3
|
|
|
4
4
|
declare const SIGNAL_STORAGE_TOKEN: InjectionToken<Storage>;
|
|
5
|
-
declare class
|
|
5
|
+
declare class SignalStorage<T extends Record<string, any> = {}> {
|
|
6
6
|
private storage;
|
|
7
7
|
private signals;
|
|
8
8
|
/**
|
|
9
|
-
* Create a new
|
|
9
|
+
* Create a new SignalStorage instance
|
|
10
10
|
* @param storage The storage to use (localStorage or sessionStorage). Defaults to localStorage.
|
|
11
11
|
*/
|
|
12
12
|
constructor(storage?: Storage);
|
|
13
13
|
/**
|
|
14
|
-
* Get typed data from
|
|
14
|
+
* Get typed data from SignalStorage
|
|
15
15
|
* @param key The key to retrieve
|
|
16
16
|
* @returns The typed data or null if not found
|
|
17
17
|
*/
|
|
18
18
|
get<K extends keyof T>(key: K): T[K] | null;
|
|
19
19
|
/**
|
|
20
|
-
* Get typed data from
|
|
20
|
+
* Get typed data from SignalStorage with a default value
|
|
21
21
|
* @param key The key to retrieve
|
|
22
22
|
* @param defaultValue The default value to return if key not found
|
|
23
23
|
* @returns The stored data or the default value
|
|
@@ -56,8 +56,83 @@ declare class SignalStorageService<T extends Record<string, any> = {}> {
|
|
|
56
56
|
has<K extends keyof T>(key: K): boolean;
|
|
57
57
|
/** Clear all localStorage */
|
|
58
58
|
clear(): void;
|
|
59
|
-
static ɵfac: i0.ɵɵFactoryDeclaration<
|
|
60
|
-
static ɵprov: i0.ɵɵInjectableDeclaration<
|
|
59
|
+
static ɵfac: i0.ɵɵFactoryDeclaration<SignalStorage<any>, [{ optional: true; }]>;
|
|
60
|
+
static ɵprov: i0.ɵɵInjectableDeclaration<SignalStorage<any>>;
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* An in-memory implementation of the Storage interface.
|
|
64
|
+
* Can be provided to SIGNAL_STORAGE_TOKEN to use SignalStorage for purely in-memory app state.
|
|
65
|
+
*/
|
|
66
|
+
declare class MemoryStorage implements Storage {
|
|
67
|
+
private data;
|
|
68
|
+
get length(): number;
|
|
69
|
+
clear(): void;
|
|
70
|
+
getItem(key: string): string | null;
|
|
71
|
+
removeItem(key: string): void;
|
|
72
|
+
setItem(key: string, value: string): void;
|
|
73
|
+
key(index: number): string | null;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
interface SignalIndexedDbConfig {
|
|
77
|
+
/** Default: `SignalStorageDb` */
|
|
78
|
+
dbName: string;
|
|
79
|
+
/** Default: `keyValue` */
|
|
80
|
+
storeName: string;
|
|
81
|
+
/** Default: `1` */
|
|
82
|
+
version: number;
|
|
83
|
+
}
|
|
84
|
+
declare const SIGNAL_INDEXEDDB_CONFIG: InjectionToken<SignalIndexedDbConfig>;
|
|
85
|
+
declare function provideSignalIndexedDb(config?: Partial<SignalIndexedDbConfig>): (typeof SignalIndexedDb | {
|
|
86
|
+
provide: InjectionToken<SignalIndexedDbConfig>;
|
|
87
|
+
useValue: {
|
|
88
|
+
dbName: string;
|
|
89
|
+
storeName: string;
|
|
90
|
+
version: number;
|
|
91
|
+
};
|
|
92
|
+
})[];
|
|
93
|
+
declare class SignalIndexedDb<T extends Record<string, any> = {}> {
|
|
94
|
+
private injector;
|
|
95
|
+
private config;
|
|
96
|
+
private dbPromise;
|
|
97
|
+
private fallbackStorage;
|
|
98
|
+
private isFallback;
|
|
99
|
+
private resources;
|
|
100
|
+
private broadcastChannel?;
|
|
101
|
+
constructor(injector: Injector, config?: SignalIndexedDbConfig);
|
|
102
|
+
private initDB;
|
|
103
|
+
/**
|
|
104
|
+
* Get an Angular Resource for a key
|
|
105
|
+
*/
|
|
106
|
+
getResource<K extends keyof T>(key: K): ResourceRef<T[K] | undefined>;
|
|
107
|
+
getResource<K extends keyof T>(key: K, defaultValue: T[K]): ResourceRef<T[K]>;
|
|
108
|
+
/**
|
|
109
|
+
* Get data asynchronously from IndexedDB
|
|
110
|
+
*/
|
|
111
|
+
get<K extends keyof T>(key: K): Promise<T[K] | undefined>;
|
|
112
|
+
/**
|
|
113
|
+
* Set data asynchronously in IndexedDB and update the resource
|
|
114
|
+
*/
|
|
115
|
+
set<K extends keyof T>(key: K, value: T[K]): Promise<void>;
|
|
116
|
+
/**
|
|
117
|
+
* Safely update a value based on its previous state
|
|
118
|
+
*/
|
|
119
|
+
update<K extends keyof T>(key: K, updater: (current: T[K] | undefined) => T[K]): Promise<void>;
|
|
120
|
+
/**
|
|
121
|
+
* Remove a key asynchronously from IndexedDB
|
|
122
|
+
*/
|
|
123
|
+
remove<K extends keyof T>(key: K): Promise<void>;
|
|
124
|
+
/**
|
|
125
|
+
* Clear the entire object store
|
|
126
|
+
*/
|
|
127
|
+
clear(): Promise<void>;
|
|
128
|
+
/**
|
|
129
|
+
* Get all keys from the object store
|
|
130
|
+
*/
|
|
131
|
+
keys(): Promise<string[]>;
|
|
132
|
+
private updateLocalState;
|
|
133
|
+
static ɵfac: i0.ɵɵFactoryDeclaration<SignalIndexedDb<any>, [null, { optional: true; }]>;
|
|
134
|
+
static ɵprov: i0.ɵɵInjectableDeclaration<SignalIndexedDb<any>>;
|
|
61
135
|
}
|
|
62
136
|
|
|
63
|
-
export { SIGNAL_STORAGE_TOKEN,
|
|
137
|
+
export { MemoryStorage, SIGNAL_INDEXEDDB_CONFIG, SIGNAL_STORAGE_TOKEN, SignalIndexedDb, SignalStorage, provideSignalIndexedDb };
|
|
138
|
+
export type { SignalIndexedDbConfig };
|