@angular-libs/signal-storage 0.0.1-beta.1 → 0.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
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
# @angular-libs/signal-storage
|
|
2
2
|
|
|
3
|
-
A
|
|
3
|
+
A lightweight Angular library for reactive state management using Signals, featuring built-in persistence (`localStorage`/`sessionStorage`) and flexible in-memory storage.
|
|
4
|
+
|
|
5
|
+
[StackBlitz demo](https://stackblitz.com/edit/signal-storage?file=src%2Fmain.ts)
|
|
4
6
|
|
|
5
7
|
## Installation
|
|
6
8
|
|
|
@@ -65,7 +67,9 @@ export class AppComponent {
|
|
|
65
67
|
|
|
66
68
|
## Custom Storage & Server-Side Rendering (SSR)
|
|
67
69
|
|
|
68
|
-
By default, the library uses `localStorage`. If you want to use `sessionStorage` instead, or if you need to provide a custom storage implementation
|
|
70
|
+
By default, the library uses `localStorage`. If you want to use `sessionStorage` instead, or if you need to provide a custom storage implementation, you can override the storage provider using `SIGNAL_STORAGE_TOKEN`.
|
|
71
|
+
|
|
72
|
+
_Note: The library automatically falls back to a safe `MemoryStorage` when rendering on the server (SSR) where `window` is undefined._
|
|
69
73
|
|
|
70
74
|
### Using `sessionStorage`
|
|
71
75
|
|
|
@@ -73,18 +77,32 @@ You can easily switch to `sessionStorage` by providing the token in your `app.co
|
|
|
73
77
|
|
|
74
78
|
```typescript
|
|
75
79
|
import { ApplicationConfig } from '@angular/core';
|
|
76
|
-
import { SIGNAL_STORAGE_TOKEN } from '@angular-libs/signal-storage';
|
|
80
|
+
import { SIGNAL_STORAGE_TOKEN, MemoryStorage } from '@angular-libs/signal-storage';
|
|
77
81
|
|
|
78
82
|
export const appConfig: ApplicationConfig = {
|
|
79
83
|
providers: [
|
|
80
84
|
{
|
|
81
85
|
provide: SIGNAL_STORAGE_TOKEN,
|
|
82
|
-
useFactory: () =>
|
|
86
|
+
useFactory: () =>
|
|
87
|
+
typeof window !== 'undefined' ? window.sessionStorage : new MemoryStorage(),
|
|
83
88
|
},
|
|
84
89
|
],
|
|
85
90
|
};
|
|
86
91
|
```
|
|
87
92
|
|
|
93
|
+
### In-Memory App State (No Persistence)
|
|
94
|
+
|
|
95
|
+
If you want to use the library purely for reactive, lightweight in-memory state management comparable to `@ngrx/signals` (without persisting to `localStorage`), you can explicitly provide the built-in `MemoryStorage`:
|
|
96
|
+
|
|
97
|
+
```typescript
|
|
98
|
+
import { ApplicationConfig } from '@angular/core';
|
|
99
|
+
import { SIGNAL_STORAGE_TOKEN, MemoryStorage } from '@angular-libs/signal-storage';
|
|
100
|
+
|
|
101
|
+
export const appConfig: ApplicationConfig = {
|
|
102
|
+
providers: [{ provide: SIGNAL_STORAGE_TOKEN, useClass: MemoryStorage }],
|
|
103
|
+
};
|
|
104
|
+
```
|
|
105
|
+
|
|
88
106
|
## License
|
|
89
107
|
|
|
90
108
|
MIT
|
|
@@ -3,7 +3,7 @@ import { InjectionToken, signal, Optional, Inject, Injectable } from '@angular/c
|
|
|
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
8
|
class SignalStorageService {
|
|
9
9
|
storage;
|
|
@@ -13,7 +13,8 @@ class SignalStorageService {
|
|
|
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;
|
|
@@ -110,6 +111,33 @@ 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 SignalStorageService 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
|
+
}
|
|
113
141
|
|
|
114
142
|
/*
|
|
115
143
|
* Public API Surface of signal-storage
|
|
@@ -119,5 +147,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.0", ngImpor
|
|
|
119
147
|
* Generated bundle index. Do not edit.
|
|
120
148
|
*/
|
|
121
149
|
|
|
122
|
-
export { SIGNAL_STORAGE_TOKEN, SignalStorageService };
|
|
150
|
+
export { MemoryStorage, SIGNAL_STORAGE_TOKEN, SignalStorageService };
|
|
123
151
|
//# 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 : (
|
|
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 : new MemoryStorage()),\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 =\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 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/**\n * An in-memory implementation of the Storage interface.\n * Can be provided to SIGNAL_STORAGE_TOKEN to use SignalStorageService 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","/*\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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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@angular-libs/signal-storage",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.2",
|
|
4
4
|
"peerDependencies": {
|
|
5
5
|
"@angular/common": ">=18.0.0",
|
|
6
6
|
"@angular/core": ">=18.0.0"
|
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
"dependencies": {
|
|
12
12
|
"tslib": "^2.3.0"
|
|
13
13
|
},
|
|
14
|
+
"license": "MIT",
|
|
14
15
|
"sideEffects": false,
|
|
15
16
|
"module": "fesm2022/angular-libs-signal-storage.mjs",
|
|
16
17
|
"typings": "types/angular-libs-signal-storage.d.ts",
|
|
@@ -59,5 +59,18 @@ declare class SignalStorageService<T extends Record<string, any> = {}> {
|
|
|
59
59
|
static ɵfac: i0.ɵɵFactoryDeclaration<SignalStorageService<any>, [{ optional: true; }]>;
|
|
60
60
|
static ɵprov: i0.ɵɵInjectableDeclaration<SignalStorageService<any>>;
|
|
61
61
|
}
|
|
62
|
+
/**
|
|
63
|
+
* An in-memory implementation of the Storage interface.
|
|
64
|
+
* Can be provided to SIGNAL_STORAGE_TOKEN to use SignalStorageService 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
|
+
}
|
|
62
75
|
|
|
63
|
-
export { SIGNAL_STORAGE_TOKEN, SignalStorageService };
|
|
76
|
+
export { MemoryStorage, SIGNAL_STORAGE_TOKEN, SignalStorageService };
|