@angular-libs/signal-storage 0.0.1-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 ADDED
@@ -0,0 +1,67 @@
1
+ # @angular-libs/signal-storage
2
+
3
+ A simple and robust Angular library for managing state using Angular Signals with built-in persistence to `localStorage` and `sessionStorage`.
4
+
5
+ ## Installation
6
+
7
+ Install the library using npm:
8
+
9
+ ```bash
10
+ npm install @angular-libs/signal-storage
11
+ ```
12
+
13
+ ## Features
14
+
15
+ - **Angular Signals Integration**: Seamlessly syncs your storage state with Angular Signals.
16
+ - **Persistent Storage**: Automatically saves to `localStorage` or `sessionStorage`.
17
+ - **Type-safe**: Built with TypeScript to ensure type safety for your stored data.
18
+ - **Lightweight**: Zero external dependencies other than Angular.
19
+
20
+ ## Usage
21
+
22
+ Here is a basic example of how to use `@angular-libs/signal-storage` in your Angular components or services.
23
+
24
+ ```typescript
25
+ import { Component, effect, inject } from '@angular/core';
26
+ import { SignalStorageService } from '@angular-libs/signal-storage';
27
+
28
+ // Define your storage state structure using an interface
29
+ interface AppStorage {
30
+ appTheme: 'light' | 'dark';
31
+ }
32
+
33
+ @Component({
34
+ selector: 'app-root',
35
+ standalone: true,
36
+ template: `
37
+ <div>
38
+ <p>Current Theme: {{ theme() }}</p>
39
+ <button (click)="toggleTheme()">Toggle Theme</button>
40
+ </div>
41
+ `,
42
+ })
43
+ export class AppComponent {
44
+ private storage = inject(SignalStorageService<AppStorage>);
45
+
46
+ // Initialize storage signal with a key and default value
47
+ theme = this.storage.getSignal('appTheme', 'light');
48
+
49
+ constructor() {
50
+ effect(() => {
51
+ console.log('Theme changed to:', this.theme());
52
+ });
53
+ }
54
+
55
+ toggleTheme() {
56
+ // To update, use the storage service's set or update methods
57
+ this.storage.set('appTheme', this.theme() === 'light' ? 'dark' : 'light');
58
+
59
+ // Alternatively, using update:
60
+ // this.storage.update('appTheme', theme => theme === 'light' ? 'dark' : 'light');
61
+ }
62
+ }
63
+ ```
64
+
65
+ ## License
66
+
67
+ MIT
@@ -0,0 +1,111 @@
1
+ import { signal } from '@angular/core';
2
+
3
+ class SignalStorageService {
4
+ storage;
5
+ signals = new Map();
6
+ /**
7
+ * Create a new SignalStorageService instance
8
+ * @param storage The storage to use (localStorage or sessionStorage). Defaults to localStorage.
9
+ */
10
+ constructor(storage = localStorage) {
11
+ this.storage = storage;
12
+ if (typeof window !== 'undefined') {
13
+ window.addEventListener('storage', (event) => {
14
+ if (event.storageArea === this.storage &&
15
+ event.key &&
16
+ this.signals.has(event.key)) {
17
+ try {
18
+ const newValue = event.newValue ? JSON.parse(event.newValue) : null;
19
+ this.signals.get(event.key).set(newValue);
20
+ }
21
+ catch {
22
+ // Ignore parse errors from external changes
23
+ }
24
+ }
25
+ });
26
+ }
27
+ }
28
+ get(key, defaultValue) {
29
+ const item = this.storage.getItem(key);
30
+ if (item === null) {
31
+ return defaultValue ?? null;
32
+ }
33
+ try {
34
+ return JSON.parse(item);
35
+ }
36
+ catch {
37
+ return defaultValue ?? null;
38
+ }
39
+ }
40
+ getSignal(key, defaultValue) {
41
+ if (!this.signals.has(key)) {
42
+ const initialValue = this.get(key, defaultValue);
43
+ this.signals.set(key, signal(initialValue));
44
+ }
45
+ return this.signals.get(key).asReadonly();
46
+ }
47
+ /**
48
+ * Set typed data in localStorage
49
+ * @param key The key to set
50
+ * @param value The value to store
51
+ */
52
+ set(key, value) {
53
+ try {
54
+ this.storage.setItem(key, JSON.stringify(value));
55
+ }
56
+ catch (e) {
57
+ console.error(`Error saving to storage for key "${String(key)}". Storage quota may be exceeded.`, e);
58
+ }
59
+ if (this.signals.has(key)) {
60
+ this.signals.get(key).set(value);
61
+ }
62
+ }
63
+ /**
64
+ * Update typed data based on current value using a callback
65
+ * @param key The key to update
66
+ * @param updateFn Callback function that receives the current value and returns the new value
67
+ */
68
+ update(key, updateFn) {
69
+ const currentValue = this.get(key);
70
+ const newValue = updateFn(currentValue);
71
+ this.set(key, newValue);
72
+ }
73
+ /**
74
+ * Remove an item from localStorage
75
+ * @param key The key to remove
76
+ */
77
+ remove(key) {
78
+ this.storage.removeItem(key);
79
+ if (this.signals.has(key)) {
80
+ this.signals.get(key).set(null);
81
+ }
82
+ }
83
+ /**
84
+ * Check if a key exists in localStorage
85
+ * @param key The key to check
86
+ * @returns true if the key exists, false otherwise
87
+ */
88
+ has(key) {
89
+ return this.storage.getItem(key) !== null;
90
+ }
91
+ /**
92
+ * Clear all localStorage
93
+ */
94
+ clear() {
95
+ this.storage.clear();
96
+ for (const sig of this.signals.values()) {
97
+ sig.set(null);
98
+ }
99
+ }
100
+ }
101
+
102
+ /*
103
+ * Public API Surface of signal-storage
104
+ */
105
+
106
+ /**
107
+ * Generated bundle index. Do not edit.
108
+ */
109
+
110
+ export { SignalStorageService };
111
+ //# sourceMappingURL=angular-libs-signal-storage.mjs.map
@@ -0,0 +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 { signal, WritableSignal, Signal } from '@angular/core';\n\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(storage: Storage = localStorage) {\n this.storage = storage;\n if (typeof window !== 'undefined') {\n window.addEventListener('storage', (event: StorageEvent) => {\n if (\n event.storageArea === this.storage &&\n event.key &&\n this.signals.has(event.key as keyof T)\n ) {\n try {\n const newValue = event.newValue ? JSON.parse(event.newValue) : null;\n this.signals.get(event.key as keyof T)!.set(newValue);\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 /**\n * Clear all localStorage\n */\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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"}
package/package.json ADDED
@@ -0,0 +1,26 @@
1
+ {
2
+ "name": "@angular-libs/signal-storage",
3
+ "version": "0.0.1-beta.0",
4
+ "peerDependencies": {
5
+ "@angular/common": ">=18.0.0",
6
+ "@angular/core": ">=18.0.0"
7
+ },
8
+ "publishConfig": {
9
+ "access": "public"
10
+ },
11
+ "dependencies": {
12
+ "tslib": "^2.3.0"
13
+ },
14
+ "sideEffects": false,
15
+ "module": "fesm2022/angular-libs-signal-storage.mjs",
16
+ "typings": "types/angular-libs-signal-storage.d.ts",
17
+ "exports": {
18
+ "./package.json": {
19
+ "default": "./package.json"
20
+ },
21
+ ".": {
22
+ "types": "./types/angular-libs-signal-storage.d.ts",
23
+ "default": "./fesm2022/angular-libs-signal-storage.mjs"
24
+ }
25
+ }
26
+ }
@@ -0,0 +1,61 @@
1
+ import { Signal } from '@angular/core';
2
+
3
+ declare class SignalStorageService<T extends Record<string, any> = {}> {
4
+ private storage;
5
+ private signals;
6
+ /**
7
+ * Create a new SignalStorageService instance
8
+ * @param storage The storage to use (localStorage or sessionStorage). Defaults to localStorage.
9
+ */
10
+ constructor(storage?: Storage);
11
+ /**
12
+ * Get typed data from SignalStorageService
13
+ * @param key The key to retrieve
14
+ * @returns The typed data or null if not found
15
+ */
16
+ get<K extends keyof T>(key: K): T[K] | null;
17
+ /**
18
+ * Get typed data from SignalStorageService with a default value
19
+ * @param key The key to retrieve
20
+ * @param defaultValue The default value to return if key not found
21
+ * @returns The stored data or the default value
22
+ */
23
+ get<K extends keyof T>(key: K, defaultValue: T[K]): T[K];
24
+ /**
25
+ * Get a reactive Angular signal for a key
26
+ * @param key The key to retrieve
27
+ * @param defaultValue The default value to return if key not found
28
+ * @returns A read-only Signal containing the stored data
29
+ */
30
+ getSignal<K extends keyof T>(key: K): Signal<T[K] | null>;
31
+ getSignal<K extends keyof T>(key: K, defaultValue: T[K]): Signal<T[K]>;
32
+ /**
33
+ * Set typed data in localStorage
34
+ * @param key The key to set
35
+ * @param value The value to store
36
+ */
37
+ set<K extends keyof T>(key: K, value: T[K]): void;
38
+ /**
39
+ * Update typed data based on current value using a callback
40
+ * @param key The key to update
41
+ * @param updateFn Callback function that receives the current value and returns the new value
42
+ */
43
+ update<K extends keyof T>(key: K, updateFn: (currentValue: T[K] | null) => T[K]): void;
44
+ /**
45
+ * Remove an item from localStorage
46
+ * @param key The key to remove
47
+ */
48
+ remove<K extends keyof T>(key: K): void;
49
+ /**
50
+ * Check if a key exists in localStorage
51
+ * @param key The key to check
52
+ * @returns true if the key exists, false otherwise
53
+ */
54
+ has<K extends keyof T>(key: K): boolean;
55
+ /**
56
+ * Clear all localStorage
57
+ */
58
+ clear(): void;
59
+ }
60
+
61
+ export { SignalStorageService };