@ahoo-wang/fetcher-storage 3.13.15 → 3.15.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/dist/index.es.js CHANGED
@@ -1,226 +1,90 @@
1
- import { nameGenerator as i, SerialTypedEventBus as a } from "@ahoo-wang/fetcher-eventbus";
2
- class n {
3
- constructor() {
4
- this.store = /* @__PURE__ */ new Map();
5
- }
6
- /**
7
- * Gets the number of items stored in the storage.
8
- */
9
- get length() {
10
- return this.store.size;
11
- }
12
- /**
13
- * Clears all items from the storage.
14
- */
15
- clear() {
16
- this.store.clear();
17
- }
18
- /**
19
- * Gets an item from the storage.
20
- * @param key - The key of the item to retrieve
21
- * @returns The value of the item, or null if the item does not exist
22
- */
23
- getItem(e) {
24
- const t = this.store.get(e);
25
- return t !== void 0 ? t : null;
26
- }
27
- /**
28
- * Gets the key at the specified index.
29
- * @param index - The index of the key to retrieve
30
- * @returns The key at the specified index, or null if the index is out of bounds
31
- */
32
- key(e) {
33
- return Array.from(this.store.keys())[e] || null;
34
- }
35
- /**
36
- * Removes an item from the storage.
37
- * @param key - The key of the item to remove
38
- */
39
- removeItem(e) {
40
- this.store.delete(e);
41
- }
42
- /**
43
- * Sets an item in the storage.
44
- * @param key - The key of the item to set
45
- * @param value - The value to set
46
- */
47
- setItem(e, t) {
48
- this.store.set(e, t);
49
- }
1
+ import { SerialTypedEventBus as e, nameGenerator as t } from "@ahoo-wang/fetcher-eventbus";
2
+ //#region src/inMemoryStorage.ts
3
+ var n = class {
4
+ constructor() {
5
+ this.store = /* @__PURE__ */ new Map();
6
+ }
7
+ get length() {
8
+ return this.store.size;
9
+ }
10
+ clear() {
11
+ this.store.clear();
12
+ }
13
+ getItem(e) {
14
+ let t = this.store.get(e);
15
+ return t === void 0 ? null : t;
16
+ }
17
+ key(e) {
18
+ return Array.from(this.store.keys())[e] || null;
19
+ }
20
+ removeItem(e) {
21
+ this.store.delete(e);
22
+ }
23
+ setItem(e, t) {
24
+ this.store.set(e, t);
25
+ }
26
+ };
27
+ //#endregion
28
+ //#region src/env.ts
29
+ function r() {
30
+ return typeof window < "u";
50
31
  }
32
+ var i = () => r() ? window.localStorage : new n(), a = class {
33
+ serialize(e) {
34
+ return JSON.stringify(e);
35
+ }
36
+ deserialize(e) {
37
+ return JSON.parse(e);
38
+ }
39
+ }, o = class {
40
+ serialize(e) {
41
+ return e;
42
+ }
43
+ deserialize(e) {
44
+ return e;
45
+ }
46
+ }, s = new a(), c = new o();
51
47
  function l() {
52
- return typeof window < "u";
53
- }
54
- const u = () => l() ? window.localStorage : new n();
55
- class h {
56
- /**
57
- * Serializes a value to a JSON string
58
- * @param value The value to serialize
59
- * @returns The JSON string representation of the value
60
- */
61
- serialize(e) {
62
- return JSON.stringify(e);
63
- }
64
- /**
65
- * Deserializes a JSON string to a value
66
- * @param value The JSON string to deserialize
67
- * @returns The deserialized value
68
- */
69
- deserialize(e) {
70
- return JSON.parse(e);
71
- }
72
- }
73
- class o {
74
- /**
75
- * Returns the value as-is without serialization
76
- * @param value The value to pass through
77
- * @returns The same value that was passed in
78
- */
79
- serialize(e) {
80
- return e;
81
- }
82
- /**
83
- * Returns the value as-is without deserialization
84
- * @param value The value to pass through
85
- * @returns The same value that was passed in
86
- */
87
- deserialize(e) {
88
- return e;
89
- }
90
- }
91
- const c = new h(), d = new o();
92
- function g() {
93
- return d;
94
- }
95
- class m {
96
- /**
97
- * Creates a new KeyStorage instance
98
- * @param options Configuration options for the storage
99
- */
100
- constructor(e) {
101
- this.defaultValue = null, this.cacheValue = null, this.keyStorageHandler = {
102
- name: i.generate("KeyStorage"),
103
- handle: (t) => {
104
- this.cacheValue = t.newValue ?? null;
105
- }
106
- }, this.key = e.key, this.serializer = e.serializer ?? c, this.storage = e.storage ?? u(), this.eventBus = e.eventBus ?? new a(
107
- `KeyStorage:${this.key}`
108
- ), this.defaultValue = e.defaultValue ?? null, this.eventBus.on(this.keyStorageHandler);
109
- }
110
- /**
111
- * Adds a listener for storage changes.
112
- *
113
- * The listener will be called whenever the storage value changes,
114
- * either locally or from other tabs/windows.
115
- *
116
- * @param listener - The event handler to be called when storage changes
117
- * @returns A function that can be called to remove the listener
118
- *
119
- * @example
120
- * ```typescript
121
- * const storage = new KeyStorage<string>({ key: 'userName' });
122
- * const removeListener = storage.addListener({
123
- * name: 'userNameChange',
124
- * handle: (event) => {
125
- * console.log('User name changed:', event.newValue);
126
- * }
127
- * });
128
- *
129
- * // Later, to remove the listener
130
- * removeListener();
131
- * ```
132
- */
133
- addListener(e) {
134
- return this.eventBus.on(e), () => this.eventBus.off(e.name);
135
- }
136
- /**
137
- * Retrieves the current value from storage.
138
- *
139
- * Uses caching to avoid repeated deserialization. If the value is not in cache,
140
- * it retrieves it from the underlying storage and deserializes it.
141
- *
142
- * @returns The deserialized value, or null if no value exists in storage
143
- *
144
- * @example
145
- * ```typescript
146
- * const storage = new KeyStorage<string>({ key: 'userName' });
147
- * const userName = storage.get();
148
- * console.log(userName); // 'John Doe' or null
149
- * ```
150
- */
151
- get() {
152
- if (this.cacheValue !== null && this.cacheValue !== void 0)
153
- return this.cacheValue;
154
- const e = this.storage.getItem(this.key);
155
- return e == null ? this.defaultValue : (this.cacheValue = this.serializer.deserialize(e), this.cacheValue);
156
- }
157
- /**
158
- * Stores a value in storage and notifies all listeners.
159
- *
160
- * Serializes the value, stores it in the underlying storage, updates the cache,
161
- * and emits a change event to all registered listeners.
162
- *
163
- * @param value - The value to store (will be serialized before storage)
164
- *
165
- * @example
166
- * ```typescript
167
- * const storage = new KeyStorage<string>({ key: 'userName' });
168
- * storage.set('John Doe');
169
- * ```
170
- */
171
- set(e) {
172
- const t = this.get(), r = this.serializer.serialize(e);
173
- this.storage.setItem(this.key, r), this.cacheValue = e, this.eventBus.emit({
174
- newValue: e,
175
- oldValue: t
176
- });
177
- }
178
- /**
179
- * Removes the value from storage and notifies all listeners.
180
- *
181
- * Removes the item from the underlying storage, clears the cache,
182
- * and emits a change event indicating the value was removed.
183
- *
184
- * @example
185
- * ```typescript
186
- * const storage = new KeyStorage<string>({ key: 'userName' });
187
- * storage.remove(); // Removes the stored value
188
- * ```
189
- */
190
- remove() {
191
- const e = this.get();
192
- this.storage.removeItem(this.key), this.cacheValue = null, this.eventBus.emit({
193
- oldValue: e,
194
- newValue: null
195
- });
196
- }
197
- /**
198
- * Cleans up resources used by the KeyStorage instance.
199
- *
200
- * Removes the internal event handler from the event bus.
201
- * Should be called when the KeyStorage instance is no longer needed
202
- * to prevent memory leaks.
203
- *
204
- * @example
205
- * ```typescript
206
- * const storage = new KeyStorage<string>({ key: 'userName' });
207
- * // ... use storage ...
208
- * storage.destroy(); // Clean up resources
209
- * ```
210
- */
211
- destroy() {
212
- this.eventBus.off(this.keyStorageHandler.name);
213
- }
48
+ return c;
214
49
  }
215
- export {
216
- o as IdentitySerializer,
217
- n as InMemoryStorage,
218
- h as JsonSerializer,
219
- m as KeyStorage,
220
- u as getStorage,
221
- d as identitySerializer,
222
- l as isBrowser,
223
- c as jsonSerializer,
224
- g as typedIdentitySerializer
50
+ //#endregion
51
+ //#region src/keyStorage.ts
52
+ var u = class {
53
+ constructor(n) {
54
+ this.defaultValue = null, this.cacheValue = null, this.keyStorageHandler = {
55
+ name: t.generate("KeyStorage"),
56
+ handle: (e) => {
57
+ this.cacheValue = e.newValue ?? null;
58
+ }
59
+ }, this.key = n.key, this.serializer = n.serializer ?? s, this.storage = n.storage ?? i(), this.eventBus = n.eventBus ?? new e(`KeyStorage:${this.key}`), this.defaultValue = n.defaultValue ?? null, this.eventBus.on(this.keyStorageHandler);
60
+ }
61
+ addListener(e) {
62
+ return this.eventBus.on(e), () => this.eventBus.off(e.name);
63
+ }
64
+ get() {
65
+ if (this.cacheValue !== null && this.cacheValue !== void 0) return this.cacheValue;
66
+ let e = this.storage.getItem(this.key);
67
+ return e == null ? this.defaultValue : (this.cacheValue = this.serializer.deserialize(e), this.cacheValue);
68
+ }
69
+ set(e) {
70
+ let t = this.get(), n = this.serializer.serialize(e);
71
+ this.storage.setItem(this.key, n), this.cacheValue = e, this.eventBus.emit({
72
+ newValue: e,
73
+ oldValue: t
74
+ });
75
+ }
76
+ remove() {
77
+ let e = this.get();
78
+ this.storage.removeItem(this.key), this.cacheValue = null, this.eventBus.emit({
79
+ oldValue: e,
80
+ newValue: null
81
+ });
82
+ }
83
+ destroy() {
84
+ this.eventBus.off(this.keyStorageHandler.name);
85
+ }
225
86
  };
226
- //# sourceMappingURL=index.es.js.map
87
+ //#endregion
88
+ export { o as IdentitySerializer, n as InMemoryStorage, a as JsonSerializer, u as KeyStorage, i as getStorage, c as identitySerializer, r as isBrowser, s as jsonSerializer, l as typedIdentitySerializer };
89
+
90
+ //# sourceMappingURL=index.es.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.es.js","sources":["../src/inMemoryStorage.ts","../src/env.ts","../src/serializer.ts","../src/keyStorage.ts"],"sourcesContent":["/*\n * Copyright [2021-present] [ahoo wang <ahoowang@qq.com> (https://github.com/Ahoo-Wang)].\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * http://www.apache.org/licenses/LICENSE-2.0\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nexport class InMemoryStorage implements Storage {\n private readonly store: Map<string, string> = new Map();\n\n /**\n * Gets the number of items stored in the storage.\n */\n get length(): number {\n return this.store.size;\n }\n\n /**\n * Clears all items from the storage.\n */\n clear(): void {\n this.store.clear();\n }\n\n /**\n * Gets an item from the storage.\n * @param key - The key of the item to retrieve\n * @returns The value of the item, or null if the item does not exist\n */\n getItem(key: string): string | null {\n const value = this.store.get(key);\n return value !== undefined ? value : null;\n }\n\n /**\n * Gets the key at the specified index.\n * @param index - The index of the key to retrieve\n * @returns The key at the specified index, or null if the index is out of bounds\n */\n key(index: number): string | null {\n const keys = Array.from(this.store.keys());\n return keys[index] || null;\n }\n\n /**\n * Removes an item from the storage.\n * @param key - The key of the item to remove\n */\n removeItem(key: string): void {\n this.store.delete(key);\n }\n\n /**\n * Sets an item in the storage.\n * @param key - The key of the item to set\n * @param value - The value to set\n */\n setItem(key: string, value: string): void {\n this.store.set(key, value);\n }\n}\n","/*\n * Copyright [2021-present] [ahoo wang <ahoowang@qq.com> (https://github.com/Ahoo-Wang)].\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * http://www.apache.org/licenses/LICENSE-2.0\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { InMemoryStorage } from './inMemoryStorage';\n\n/**\n * Checks if the current environment is a browser.\n * @returns True if running in a browser environment, false otherwise\n */\nexport function isBrowser(): boolean {\n return typeof window !== 'undefined';\n}\n\n/**\n * Gets the appropriate storage implementation based on the environment.\n * Returns localStorage in browser environments if available, InMemoryStorage otherwise.\n * @returns A Storage-compatible object\n */\nexport const getStorage = (): Storage => {\n if (isBrowser()) {\n return window.localStorage;\n }\n return new InMemoryStorage();\n};\n","/*\n * Copyright [2021-present] [ahoo wang <ahoowang@qq.com> (https://github.com/Ahoo-Wang)].\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * http://www.apache.org/licenses/LICENSE-2.0\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Interface for serializing and deserializing values\n * @template Serialized The type of the serialized value\n * @template Deserialized The type of the deserialized value\n */\nexport interface Serializer<Serialized, Deserialized> {\n /**\n * Serializes a value to the specified format\n * @param value The value to serialize\n * @returns The serialized value\n */\n serialize(value: any): Serialized;\n\n /**\n * Deserializes a value from the specified format\n * @param value The value to deserialize\n * @returns The deserialized value\n */\n deserialize(value: Serialized): Deserialized;\n}\n\n/**\n * Implementation of Serializer that uses JSON for serialization\n */\nexport class JsonSerializer implements Serializer<string, any> {\n /**\n * Serializes a value to a JSON string\n * @param value The value to serialize\n * @returns The JSON string representation of the value\n */\n serialize(value: any): string {\n return JSON.stringify(value);\n }\n\n /**\n * Deserializes a JSON string to a value\n * @param value The JSON string to deserialize\n * @returns The deserialized value\n */\n deserialize(value: string): any {\n return JSON.parse(value);\n }\n}\n\n/**\n * Implementation of Serializer that performs no actual serialization\n * @template T The type of the value to pass through\n */\nexport class IdentitySerializer<T> implements Serializer<T, T> {\n /**\n * Returns the value as-is without serialization\n * @param value The value to pass through\n * @returns The same value that was passed in\n */\n serialize(value: T): T {\n return value;\n }\n\n /**\n * Returns the value as-is without deserialization\n * @param value The value to pass through\n * @returns The same value that was passed in\n */\n deserialize(value: T): T {\n return value;\n }\n}\n\n/**\n * Global instance of JsonSerializer\n */\nexport const jsonSerializer = new JsonSerializer();\n/**\n * Global instance of IdentitySerializer\n */\nexport const identitySerializer = new IdentitySerializer<any>();\n\nexport function typedIdentitySerializer<T>(): IdentitySerializer<T> {\n return identitySerializer as IdentitySerializer<T>;\n}\n","/*\n * Copyright [2021-present] [ahoo wang <ahoowang@qq.com> (https://github.com/Ahoo-Wang)].\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * http://www.apache.org/licenses/LICENSE-2.0\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { jsonSerializer, Serializer } from './serializer';\nimport {\n EventHandler,\n nameGenerator,\n SerialTypedEventBus,\n TypedEventBus,\n} from '@ahoo-wang/fetcher-eventbus';\nimport { getStorage } from './env';\n\nexport interface StorageEvent<Deserialized> {\n newValue?: Deserialized | null;\n oldValue?: Deserialized | null;\n}\n\n/**\n * A function that removes a storage listener when called.\n */\nexport type RemoveStorageListener = () => void;\n\nexport interface StorageListenable<Deserialized> {\n /**\n * Adds a listener for storage changes.\n * @param listener - The listener function to be called when storage changes\n * @returns A function that can be called to remove the listener\n */\n addListener(\n listener: EventHandler<StorageEvent<Deserialized>>,\n ): RemoveStorageListener;\n}\n\n/**\n * Options for configuring KeyStorage\n */\nexport interface KeyStorageOptions<Deserialized> {\n /**\n * The key used to store and retrieve values from storage\n */\n key: string;\n\n /**\n * Optional serializer for converting values to and from storage format\n * Defaults to IdentitySerializer if not provided\n */\n serializer?: Serializer<string, Deserialized>;\n\n /**\n * Optional storage instance. Defaults to localStorage\n */\n storage?: Storage;\n\n /**\n * Optional event bus for cross-tab communication. Defaults to SerialTypedEventBus\n */\n eventBus?: TypedEventBus<StorageEvent<Deserialized>>;\n\n /**\n * Optional default value to return when no value exists in storage\n */\n defaultValue?: Deserialized;\n}\n\n/**\n * A storage wrapper that manages a single value associated with a specific key\n * Provides caching and automatic cache invalidation when the storage value changes\n * @template Deserialized The type of the value being stored\n */\nexport class KeyStorage<Deserialized>\n implements StorageListenable<Deserialized> {\n private readonly key: string;\n private readonly serializer: Serializer<string, Deserialized>;\n private readonly storage: Storage;\n public readonly eventBus: TypedEventBus<StorageEvent<Deserialized>>;\n private readonly defaultValue: Deserialized | null = null;\n private cacheValue: Deserialized | null = null;\n private readonly keyStorageHandler: EventHandler<StorageEvent<Deserialized>> =\n {\n name: nameGenerator.generate('KeyStorage'),\n handle: (event: StorageEvent<Deserialized>) => {\n this.cacheValue = event.newValue ?? null;\n },\n };\n\n /**\n * Creates a new KeyStorage instance\n * @param options Configuration options for the storage\n */\n constructor(options: KeyStorageOptions<Deserialized>) {\n this.key = options.key;\n this.serializer = options.serializer ?? jsonSerializer;\n this.storage = options.storage ?? getStorage();\n this.eventBus =\n options.eventBus ??\n new SerialTypedEventBus<StorageEvent<Deserialized>>(\n `KeyStorage:${this.key}`,\n );\n this.defaultValue = options.defaultValue ?? null;\n this.eventBus.on(this.keyStorageHandler);\n }\n\n /**\n * Adds a listener for storage changes.\n *\n * The listener will be called whenever the storage value changes,\n * either locally or from other tabs/windows.\n *\n * @param listener - The event handler to be called when storage changes\n * @returns A function that can be called to remove the listener\n *\n * @example\n * ```typescript\n * const storage = new KeyStorage<string>({ key: 'userName' });\n * const removeListener = storage.addListener({\n * name: 'userNameChange',\n * handle: (event) => {\n * console.log('User name changed:', event.newValue);\n * }\n * });\n *\n * // Later, to remove the listener\n * removeListener();\n * ```\n */\n addListener(\n listener: EventHandler<StorageEvent<Deserialized>>,\n ): RemoveStorageListener {\n this.eventBus.on(listener);\n return () => this.eventBus.off(listener.name);\n }\n\n /**\n * Retrieves the current value from storage.\n *\n * Uses caching to avoid repeated deserialization. If the value is not in cache,\n * it retrieves it from the underlying storage and deserializes it.\n *\n * @returns The deserialized value, or null if no value exists in storage\n *\n * @example\n * ```typescript\n * const storage = new KeyStorage<string>({ key: 'userName' });\n * const userName = storage.get();\n * console.log(userName); // 'John Doe' or null\n * ```\n */\n get(): Deserialized | null {\n if (this.cacheValue !== null && this.cacheValue !== undefined) {\n return this.cacheValue;\n }\n const value = this.storage.getItem(this.key);\n if (value === null || value === undefined) {\n return this.defaultValue;\n }\n this.cacheValue = this.serializer.deserialize(value);\n return this.cacheValue;\n }\n\n /**\n * Stores a value in storage and notifies all listeners.\n *\n * Serializes the value, stores it in the underlying storage, updates the cache,\n * and emits a change event to all registered listeners.\n *\n * @param value - The value to store (will be serialized before storage)\n *\n * @example\n * ```typescript\n * const storage = new KeyStorage<string>({ key: 'userName' });\n * storage.set('John Doe');\n * ```\n */\n set(value: Deserialized): void {\n const oldValue = this.get();\n const serialized = this.serializer.serialize(value);\n this.storage.setItem(this.key, serialized);\n this.cacheValue = value;\n this.eventBus.emit({\n newValue: value,\n oldValue: oldValue,\n });\n }\n\n /**\n * Removes the value from storage and notifies all listeners.\n *\n * Removes the item from the underlying storage, clears the cache,\n * and emits a change event indicating the value was removed.\n *\n * @example\n * ```typescript\n * const storage = new KeyStorage<string>({ key: 'userName' });\n * storage.remove(); // Removes the stored value\n * ```\n */\n remove(): void {\n const oldValue = this.get();\n this.storage.removeItem(this.key);\n this.cacheValue = null;\n this.eventBus.emit({\n oldValue: oldValue,\n newValue: null,\n });\n }\n\n /**\n * Cleans up resources used by the KeyStorage instance.\n *\n * Removes the internal event handler from the event bus.\n * Should be called when the KeyStorage instance is no longer needed\n * to prevent memory leaks.\n *\n * @example\n * ```typescript\n * const storage = new KeyStorage<string>({ key: 'userName' });\n * // ... use storage ...\n * storage.destroy(); // Clean up resources\n * ```\n */\n destroy() {\n this.eventBus.off(this.keyStorageHandler.name);\n }\n}\n"],"names":["InMemoryStorage","key","value","index","isBrowser","getStorage","JsonSerializer","IdentitySerializer","jsonSerializer","identitySerializer","typedIdentitySerializer","KeyStorage","options","nameGenerator","event","SerialTypedEventBus","listener","oldValue","serialized"],"mappings":";AAaO,MAAMA,EAAmC;AAAA,EAAzC,cAAA;AACL,SAAiB,4BAAiC,IAAA;AAAA,EAAI;AAAA;AAAA;AAAA;AAAA,EAKtD,IAAI,SAAiB;AACnB,WAAO,KAAK,MAAM;AAAA,EACpB;AAAA;AAAA;AAAA;AAAA,EAKA,QAAc;AACZ,SAAK,MAAM,MAAA;AAAA,EACb;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,QAAQC,GAA4B;AAClC,UAAMC,IAAQ,KAAK,MAAM,IAAID,CAAG;AAChC,WAAOC,MAAU,SAAYA,IAAQ;AAAA,EACvC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,IAAIC,GAA8B;AAEhC,WADa,MAAM,KAAK,KAAK,MAAM,MAAM,EAC7BA,CAAK,KAAK;AAAA,EACxB;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,WAAWF,GAAmB;AAC5B,SAAK,MAAM,OAAOA,CAAG;AAAA,EACvB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,QAAQA,GAAaC,GAAqB;AACxC,SAAK,MAAM,IAAID,GAAKC,CAAK;AAAA,EAC3B;AACF;AC/CO,SAASE,IAAqB;AACnC,SAAO,OAAO,SAAW;AAC3B;AAOO,MAAMC,IAAa,MACpBD,MACK,OAAO,eAET,IAAIJ,EAAA;ACKN,MAAMM,EAAkD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAM7D,UAAUJ,GAAoB;AAC5B,WAAO,KAAK,UAAUA,CAAK;AAAA,EAC7B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,YAAYA,GAAoB;AAC9B,WAAO,KAAK,MAAMA,CAAK;AAAA,EACzB;AACF;AAMO,MAAMK,EAAkD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAM7D,UAAUL,GAAa;AACrB,WAAOA;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,YAAYA,GAAa;AACvB,WAAOA;AAAA,EACT;AACF;AAKO,MAAMM,IAAiB,IAAIF,EAAA,GAIrBG,IAAqB,IAAIF,EAAA;AAE/B,SAASG,IAAoD;AAClE,SAAOD;AACT;ACbO,MAAME,EACgC;AAAA;AAAA;AAAA;AAAA;AAAA,EAmB3C,YAAYC,GAA0C;AAdtD,SAAiB,eAAoC,MACrD,KAAQ,aAAkC,MAC1C,KAAiB,oBACf;AAAA,MACE,MAAMC,EAAc,SAAS,YAAY;AAAA,MACzC,QAAQ,CAACC,MAAsC;AAC7C,aAAK,aAAaA,EAAM,YAAY;AAAA,MACtC;AAAA,IAAA,GAQF,KAAK,MAAMF,EAAQ,KACnB,KAAK,aAAaA,EAAQ,cAAcJ,GACxC,KAAK,UAAUI,EAAQ,WAAWP,EAAA,GAClC,KAAK,WACHO,EAAQ,YACR,IAAIG;AAAA,MACF,cAAc,KAAK,GAAG;AAAA,IAAA,GAE1B,KAAK,eAAeH,EAAQ,gBAAgB,MAC5C,KAAK,SAAS,GAAG,KAAK,iBAAiB;AAAA,EACzC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAyBA,YACEI,GACuB;AACvB,gBAAK,SAAS,GAAGA,CAAQ,GAClB,MAAM,KAAK,SAAS,IAAIA,EAAS,IAAI;AAAA,EAC9C;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAiBA,MAA2B;AACzB,QAAI,KAAK,eAAe,QAAQ,KAAK,eAAe;AAClD,aAAO,KAAK;AAEd,UAAMd,IAAQ,KAAK,QAAQ,QAAQ,KAAK,GAAG;AAC3C,WAAIA,KAAU,OACL,KAAK,gBAEd,KAAK,aAAa,KAAK,WAAW,YAAYA,CAAK,GAC5C,KAAK;AAAA,EACd;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAgBA,IAAIA,GAA2B;AAC7B,UAAMe,IAAW,KAAK,IAAA,GAChBC,IAAa,KAAK,WAAW,UAAUhB,CAAK;AAClD,SAAK,QAAQ,QAAQ,KAAK,KAAKgB,CAAU,GACzC,KAAK,aAAahB,GAClB,KAAK,SAAS,KAAK;AAAA,MACjB,UAAUA;AAAA,MACV,UAAAe;AAAA,IAAA,CACD;AAAA,EACH;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAcA,SAAe;AACb,UAAMA,IAAW,KAAK,IAAA;AACtB,SAAK,QAAQ,WAAW,KAAK,GAAG,GAChC,KAAK,aAAa,MAClB,KAAK,SAAS,KAAK;AAAA,MACjB,UAAAA;AAAA,MACA,UAAU;AAAA,IAAA,CACX;AAAA,EACH;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAgBA,UAAU;AACR,SAAK,SAAS,IAAI,KAAK,kBAAkB,IAAI;AAAA,EAC/C;AACF;"}
1
+ {"version":3,"file":"index.es.js","names":[],"sources":["../src/inMemoryStorage.ts","../src/env.ts","../src/serializer.ts","../src/keyStorage.ts"],"sourcesContent":["/*\n * Copyright [2021-present] [ahoo wang <ahoowang@qq.com> (https://github.com/Ahoo-Wang)].\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * http://www.apache.org/licenses/LICENSE-2.0\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nexport class InMemoryStorage implements Storage {\n private readonly store: Map<string, string> = new Map();\n\n /**\n * Gets the number of items stored in the storage.\n */\n get length(): number {\n return this.store.size;\n }\n\n /**\n * Clears all items from the storage.\n */\n clear(): void {\n this.store.clear();\n }\n\n /**\n * Gets an item from the storage.\n * @param key - The key of the item to retrieve\n * @returns The value of the item, or null if the item does not exist\n */\n getItem(key: string): string | null {\n const value = this.store.get(key);\n return value !== undefined ? value : null;\n }\n\n /**\n * Gets the key at the specified index.\n * @param index - The index of the key to retrieve\n * @returns The key at the specified index, or null if the index is out of bounds\n */\n key(index: number): string | null {\n const keys = Array.from(this.store.keys());\n return keys[index] || null;\n }\n\n /**\n * Removes an item from the storage.\n * @param key - The key of the item to remove\n */\n removeItem(key: string): void {\n this.store.delete(key);\n }\n\n /**\n * Sets an item in the storage.\n * @param key - The key of the item to set\n * @param value - The value to set\n */\n setItem(key: string, value: string): void {\n this.store.set(key, value);\n }\n}\n","/*\n * Copyright [2021-present] [ahoo wang <ahoowang@qq.com> (https://github.com/Ahoo-Wang)].\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * http://www.apache.org/licenses/LICENSE-2.0\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { InMemoryStorage } from './inMemoryStorage';\n\n/**\n * Checks if the current environment is a browser.\n * @returns True if running in a browser environment, false otherwise\n */\nexport function isBrowser(): boolean {\n return typeof window !== 'undefined';\n}\n\n/**\n * Gets the appropriate storage implementation based on the environment.\n * Returns localStorage in browser environments if available, InMemoryStorage otherwise.\n * @returns A Storage-compatible object\n */\nexport const getStorage = (): Storage => {\n if (isBrowser()) {\n return window.localStorage;\n }\n return new InMemoryStorage();\n};\n","/*\n * Copyright [2021-present] [ahoo wang <ahoowang@qq.com> (https://github.com/Ahoo-Wang)].\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * http://www.apache.org/licenses/LICENSE-2.0\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Interface for serializing and deserializing values\n * @template Serialized The type of the serialized value\n * @template Deserialized The type of the deserialized value\n */\nexport interface Serializer<Serialized, Deserialized> {\n /**\n * Serializes a value to the specified format\n * @param value The value to serialize\n * @returns The serialized value\n */\n serialize(value: any): Serialized;\n\n /**\n * Deserializes a value from the specified format\n * @param value The value to deserialize\n * @returns The deserialized value\n */\n deserialize(value: Serialized): Deserialized;\n}\n\n/**\n * Implementation of Serializer that uses JSON for serialization\n */\nexport class JsonSerializer implements Serializer<string, any> {\n /**\n * Serializes a value to a JSON string\n * @param value The value to serialize\n * @returns The JSON string representation of the value\n */\n serialize(value: any): string {\n return JSON.stringify(value);\n }\n\n /**\n * Deserializes a JSON string to a value\n * @param value The JSON string to deserialize\n * @returns The deserialized value\n */\n deserialize(value: string): any {\n return JSON.parse(value);\n }\n}\n\n/**\n * Implementation of Serializer that performs no actual serialization\n * @template T The type of the value to pass through\n */\nexport class IdentitySerializer<T> implements Serializer<T, T> {\n /**\n * Returns the value as-is without serialization\n * @param value The value to pass through\n * @returns The same value that was passed in\n */\n serialize(value: T): T {\n return value;\n }\n\n /**\n * Returns the value as-is without deserialization\n * @param value The value to pass through\n * @returns The same value that was passed in\n */\n deserialize(value: T): T {\n return value;\n }\n}\n\n/**\n * Global instance of JsonSerializer\n */\nexport const jsonSerializer = new JsonSerializer();\n/**\n * Global instance of IdentitySerializer\n */\nexport const identitySerializer = new IdentitySerializer<any>();\n\nexport function typedIdentitySerializer<T>(): IdentitySerializer<T> {\n return identitySerializer as IdentitySerializer<T>;\n}\n","/*\n * Copyright [2021-present] [ahoo wang <ahoowang@qq.com> (https://github.com/Ahoo-Wang)].\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * http://www.apache.org/licenses/LICENSE-2.0\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport type { Serializer } from './serializer';\nimport { jsonSerializer } from './serializer';\nimport type {\n EventHandler,\n TypedEventBus} from '@ahoo-wang/fetcher-eventbus';\nimport {\n nameGenerator,\n SerialTypedEventBus\n} from '@ahoo-wang/fetcher-eventbus';\nimport { getStorage } from './env';\n\nexport interface StorageEvent<Deserialized> {\n newValue?: Deserialized | null;\n oldValue?: Deserialized | null;\n}\n\n/**\n * A function that removes a storage listener when called.\n */\nexport type RemoveStorageListener = () => void;\n\nexport interface StorageListenable<Deserialized> {\n /**\n * Adds a listener for storage changes.\n * @param listener - The listener function to be called when storage changes\n * @returns A function that can be called to remove the listener\n */\n addListener(\n listener: EventHandler<StorageEvent<Deserialized>>,\n ): RemoveStorageListener;\n}\n\n/**\n * Options for configuring KeyStorage\n */\nexport interface KeyStorageOptions<Deserialized> {\n /**\n * The key used to store and retrieve values from storage\n */\n key: string;\n\n /**\n * Optional serializer for converting values to and from storage format\n * Defaults to IdentitySerializer if not provided\n */\n serializer?: Serializer<string, Deserialized>;\n\n /**\n * Optional storage instance. Defaults to localStorage\n */\n storage?: Storage;\n\n /**\n * Optional event bus for cross-tab communication. Defaults to SerialTypedEventBus\n */\n eventBus?: TypedEventBus<StorageEvent<Deserialized>>;\n\n /**\n * Optional default value to return when no value exists in storage\n */\n defaultValue?: Deserialized;\n}\n\n/**\n * A storage wrapper that manages a single value associated with a specific key\n * Provides caching and automatic cache invalidation when the storage value changes\n * @template Deserialized The type of the value being stored\n */\nexport class KeyStorage<Deserialized>\n implements StorageListenable<Deserialized> {\n private readonly key: string;\n private readonly serializer: Serializer<string, Deserialized>;\n private readonly storage: Storage;\n public readonly eventBus: TypedEventBus<StorageEvent<Deserialized>>;\n private readonly defaultValue: Deserialized | null = null;\n private cacheValue: Deserialized | null = null;\n private readonly keyStorageHandler: EventHandler<StorageEvent<Deserialized>> =\n {\n name: nameGenerator.generate('KeyStorage'),\n handle: (event: StorageEvent<Deserialized>) => {\n this.cacheValue = event.newValue ?? null;\n },\n };\n\n /**\n * Creates a new KeyStorage instance\n * @param options Configuration options for the storage\n */\n constructor(options: KeyStorageOptions<Deserialized>) {\n this.key = options.key;\n this.serializer = options.serializer ?? jsonSerializer;\n this.storage = options.storage ?? getStorage();\n this.eventBus =\n options.eventBus ??\n new SerialTypedEventBus<StorageEvent<Deserialized>>(\n `KeyStorage:${this.key}`,\n );\n this.defaultValue = options.defaultValue ?? null;\n this.eventBus.on(this.keyStorageHandler);\n }\n\n /**\n * Adds a listener for storage changes.\n *\n * The listener will be called whenever the storage value changes,\n * either locally or from other tabs/windows.\n *\n * @param listener - The event handler to be called when storage changes\n * @returns A function that can be called to remove the listener\n *\n * @example\n * ```typescript\n * const storage = new KeyStorage<string>({ key: 'userName' });\n * const removeListener = storage.addListener({\n * name: 'userNameChange',\n * handle: (event) => {\n * console.log('User name changed:', event.newValue);\n * }\n * });\n *\n * // Later, to remove the listener\n * removeListener();\n * ```\n */\n addListener(\n listener: EventHandler<StorageEvent<Deserialized>>,\n ): RemoveStorageListener {\n this.eventBus.on(listener);\n return () => this.eventBus.off(listener.name);\n }\n\n /**\n * Retrieves the current value from storage.\n *\n * Uses caching to avoid repeated deserialization. If the value is not in cache,\n * it retrieves it from the underlying storage and deserializes it.\n *\n * @returns The deserialized value, or null if no value exists in storage\n *\n * @example\n * ```typescript\n * const storage = new KeyStorage<string>({ key: 'userName' });\n * const userName = storage.get();\n * console.log(userName); // 'John Doe' or null\n * ```\n */\n get(): Deserialized | null {\n if (this.cacheValue !== null && this.cacheValue !== undefined) {\n return this.cacheValue;\n }\n const value = this.storage.getItem(this.key);\n if (value === null || value === undefined) {\n return this.defaultValue;\n }\n this.cacheValue = this.serializer.deserialize(value);\n return this.cacheValue;\n }\n\n /**\n * Stores a value in storage and notifies all listeners.\n *\n * Serializes the value, stores it in the underlying storage, updates the cache,\n * and emits a change event to all registered listeners.\n *\n * @param value - The value to store (will be serialized before storage)\n *\n * @example\n * ```typescript\n * const storage = new KeyStorage<string>({ key: 'userName' });\n * storage.set('John Doe');\n * ```\n */\n set(value: Deserialized): void {\n const oldValue = this.get();\n const serialized = this.serializer.serialize(value);\n this.storage.setItem(this.key, serialized);\n this.cacheValue = value;\n this.eventBus.emit({\n newValue: value,\n oldValue: oldValue,\n });\n }\n\n /**\n * Removes the value from storage and notifies all listeners.\n *\n * Removes the item from the underlying storage, clears the cache,\n * and emits a change event indicating the value was removed.\n *\n * @example\n * ```typescript\n * const storage = new KeyStorage<string>({ key: 'userName' });\n * storage.remove(); // Removes the stored value\n * ```\n */\n remove(): void {\n const oldValue = this.get();\n this.storage.removeItem(this.key);\n this.cacheValue = null;\n this.eventBus.emit({\n oldValue: oldValue,\n newValue: null,\n });\n }\n\n /**\n * Cleans up resources used by the KeyStorage instance.\n *\n * Removes the internal event handler from the event bus.\n * Should be called when the KeyStorage instance is no longer needed\n * to prevent memory leaks.\n *\n * @example\n * ```typescript\n * const storage = new KeyStorage<string>({ key: 'userName' });\n * // ... use storage ...\n * storage.destroy(); // Clean up resources\n * ```\n */\n destroy() {\n this.eventBus.off(this.keyStorageHandler.name);\n }\n}\n"],"mappings":";;AAaA,IAAa,IAAb,MAAgD;;+BACA,IAAI,KAAK;;CAKvD,IAAI,SAAiB;AACnB,SAAO,KAAK,MAAM;;CAMpB,QAAc;AACZ,OAAK,MAAM,OAAO;;CAQpB,QAAQ,GAA4B;EAClC,IAAM,IAAQ,KAAK,MAAM,IAAI,EAAI;AACjC,SAAO,MAAU,KAAA,IAAoB,OAAR;;CAQ/B,IAAI,GAA8B;AAEhC,SADa,MAAM,KAAK,KAAK,MAAM,MAAM,CAAC,CAC9B,MAAU;;CAOxB,WAAW,GAAmB;AAC5B,OAAK,MAAM,OAAO,EAAI;;CAQxB,QAAQ,GAAa,GAAqB;AACxC,OAAK,MAAM,IAAI,GAAK,EAAM;;;;;AC7C9B,SAAgB,IAAqB;AACnC,QAAO,OAAO,SAAW;;AAQ3B,IAAa,UACP,GAAW,GACN,OAAO,eAET,IAAI,GAAiB,ECKjB,IAAb,MAA+D;CAM7D,UAAU,GAAoB;AAC5B,SAAO,KAAK,UAAU,EAAM;;CAQ9B,YAAY,GAAoB;AAC9B,SAAO,KAAK,MAAM,EAAM;;GAQf,IAAb,MAA+D;CAM7D,UAAU,GAAa;AACrB,SAAO;;CAQT,YAAY,GAAa;AACvB,SAAO;;GAOE,IAAiB,IAAI,GAAgB,EAIrC,IAAqB,IAAI,GAAyB;AAE/D,SAAgB,IAAoD;AAClE,QAAO;;;;ACVT,IAAa,IAAb,MAC6C;CAmB3C,YAAY,GAA0C;AAUpD,sBAxBmD,wBACX,+BAExC;GACE,MAAM,EAAc,SAAS,aAAa;GAC1C,SAAS,MAAsC;AAC7C,SAAK,aAAa,EAAM,YAAY;;GAEvC,EAOD,KAAK,MAAM,EAAQ,KACnB,KAAK,aAAa,EAAQ,cAAc,GACxC,KAAK,UAAU,EAAQ,WAAW,GAAY,EAC9C,KAAK,WACH,EAAQ,YACR,IAAI,EACF,cAAc,KAAK,MACpB,EACH,KAAK,eAAe,EAAQ,gBAAgB,MAC5C,KAAK,SAAS,GAAG,KAAK,kBAAkB;;CA0B1C,YACE,GACuB;AAEvB,SADA,KAAK,SAAS,GAAG,EAAS,QACb,KAAK,SAAS,IAAI,EAAS,KAAK;;CAkB/C,MAA2B;AACzB,MAAI,KAAK,eAAe,QAAQ,KAAK,eAAe,KAAA,EAClD,QAAO,KAAK;EAEd,IAAM,IAAQ,KAAK,QAAQ,QAAQ,KAAK,IAAI;AAK5C,SAJI,KAAU,OACL,KAAK,gBAEd,KAAK,aAAa,KAAK,WAAW,YAAY,EAAM,EAC7C,KAAK;;CAiBd,IAAI,GAA2B;EAC7B,IAAM,IAAW,KAAK,KAAK,EACrB,IAAa,KAAK,WAAW,UAAU,EAAM;AAGnD,EAFA,KAAK,QAAQ,QAAQ,KAAK,KAAK,EAAW,EAC1C,KAAK,aAAa,GAClB,KAAK,SAAS,KAAK;GACjB,UAAU;GACA;GACX,CAAC;;CAeJ,SAAe;EACb,IAAM,IAAW,KAAK,KAAK;AAG3B,EAFA,KAAK,QAAQ,WAAW,KAAK,IAAI,EACjC,KAAK,aAAa,MAClB,KAAK,SAAS,KAAK;GACP;GACV,UAAU;GACX,CAAC;;CAiBJ,UAAU;AACR,OAAK,SAAS,IAAI,KAAK,kBAAkB,KAAK"}
package/dist/index.umd.js CHANGED
@@ -1,2 +1,2 @@
1
- (function(t,r){typeof exports=="object"&&typeof module<"u"?r(exports,require("@ahoo-wang/fetcher-eventbus")):typeof define=="function"&&define.amd?define(["exports","@ahoo-wang/fetcher-eventbus"],r):(t=typeof globalThis<"u"?globalThis:t||self,r(t.Fetcher={},t.FetcherEventBus))})(this,(function(t,r){"use strict";class n{constructor(){this.store=new Map}get length(){return this.store.size}clear(){this.store.clear()}getItem(e){const i=this.store.get(e);return i!==void 0?i:null}key(e){return Array.from(this.store.keys())[e]||null}removeItem(e){this.store.delete(e)}setItem(e,i){this.store.set(e,i)}}function a(){return typeof window<"u"}const l=()=>a()?window.localStorage:new n;class u{serialize(e){return JSON.stringify(e)}deserialize(e){return JSON.parse(e)}}class o{serialize(e){return e}deserialize(e){return e}}const h=new u,c=new o;function d(){return c}class y{constructor(e){this.defaultValue=null,this.cacheValue=null,this.keyStorageHandler={name:r.nameGenerator.generate("KeyStorage"),handle:i=>{this.cacheValue=i.newValue??null}},this.key=e.key,this.serializer=e.serializer??h,this.storage=e.storage??l(),this.eventBus=e.eventBus??new r.SerialTypedEventBus(`KeyStorage:${this.key}`),this.defaultValue=e.defaultValue??null,this.eventBus.on(this.keyStorageHandler)}addListener(e){return this.eventBus.on(e),()=>this.eventBus.off(e.name)}get(){if(this.cacheValue!==null&&this.cacheValue!==void 0)return this.cacheValue;const e=this.storage.getItem(this.key);return e==null?this.defaultValue:(this.cacheValue=this.serializer.deserialize(e),this.cacheValue)}set(e){const i=this.get(),g=this.serializer.serialize(e);this.storage.setItem(this.key,g),this.cacheValue=e,this.eventBus.emit({newValue:e,oldValue:i})}remove(){const e=this.get();this.storage.removeItem(this.key),this.cacheValue=null,this.eventBus.emit({oldValue:e,newValue:null})}destroy(){this.eventBus.off(this.keyStorageHandler.name)}}t.IdentitySerializer=o,t.InMemoryStorage=n,t.JsonSerializer=u,t.KeyStorage=y,t.getStorage=l,t.identitySerializer=c,t.isBrowser=a,t.jsonSerializer=h,t.typedIdentitySerializer=d,Object.defineProperty(t,Symbol.toStringTag,{value:"Module"})}));
2
- //# sourceMappingURL=index.umd.js.map
1
+ (function(e,t){typeof exports==`object`&&typeof module<`u`?t(exports,require(`@ahoo-wang/fetcher-eventbus`)):typeof define==`function`&&define.amd?define([`exports`,`@ahoo-wang/fetcher-eventbus`],t):(e=typeof globalThis<`u`?globalThis:e||self,t(e.Fetcher={},e.FetcherEventBus))})(this,function(e,t){Object.defineProperty(e,Symbol.toStringTag,{value:`Module`});var n=class{constructor(){this.store=new Map}get length(){return this.store.size}clear(){this.store.clear()}getItem(e){let t=this.store.get(e);return t===void 0?null:t}key(e){return Array.from(this.store.keys())[e]||null}removeItem(e){this.store.delete(e)}setItem(e,t){this.store.set(e,t)}};function r(){return typeof window<`u`}var i=()=>r()?window.localStorage:new n,a=class{serialize(e){return JSON.stringify(e)}deserialize(e){return JSON.parse(e)}},o=class{serialize(e){return e}deserialize(e){return e}},s=new a,c=new o;function l(){return c}var u=class{constructor(e){this.defaultValue=null,this.cacheValue=null,this.keyStorageHandler={name:t.nameGenerator.generate(`KeyStorage`),handle:e=>{this.cacheValue=e.newValue??null}},this.key=e.key,this.serializer=e.serializer??s,this.storage=e.storage??i(),this.eventBus=e.eventBus??new t.SerialTypedEventBus(`KeyStorage:${this.key}`),this.defaultValue=e.defaultValue??null,this.eventBus.on(this.keyStorageHandler)}addListener(e){return this.eventBus.on(e),()=>this.eventBus.off(e.name)}get(){if(this.cacheValue!==null&&this.cacheValue!==void 0)return this.cacheValue;let e=this.storage.getItem(this.key);return e==null?this.defaultValue:(this.cacheValue=this.serializer.deserialize(e),this.cacheValue)}set(e){let t=this.get(),n=this.serializer.serialize(e);this.storage.setItem(this.key,n),this.cacheValue=e,this.eventBus.emit({newValue:e,oldValue:t})}remove(){let e=this.get();this.storage.removeItem(this.key),this.cacheValue=null,this.eventBus.emit({oldValue:e,newValue:null})}destroy(){this.eventBus.off(this.keyStorageHandler.name)}};e.IdentitySerializer=o,e.InMemoryStorage=n,e.JsonSerializer=a,e.KeyStorage=u,e.getStorage=i,e.identitySerializer=c,e.isBrowser=r,e.jsonSerializer=s,e.typedIdentitySerializer=l});
2
+ //# sourceMappingURL=index.umd.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.umd.js","sources":["../src/inMemoryStorage.ts","../src/env.ts","../src/serializer.ts","../src/keyStorage.ts"],"sourcesContent":["/*\n * Copyright [2021-present] [ahoo wang <ahoowang@qq.com> (https://github.com/Ahoo-Wang)].\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * http://www.apache.org/licenses/LICENSE-2.0\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nexport class InMemoryStorage implements Storage {\n private readonly store: Map<string, string> = new Map();\n\n /**\n * Gets the number of items stored in the storage.\n */\n get length(): number {\n return this.store.size;\n }\n\n /**\n * Clears all items from the storage.\n */\n clear(): void {\n this.store.clear();\n }\n\n /**\n * Gets an item from the storage.\n * @param key - The key of the item to retrieve\n * @returns The value of the item, or null if the item does not exist\n */\n getItem(key: string): string | null {\n const value = this.store.get(key);\n return value !== undefined ? value : null;\n }\n\n /**\n * Gets the key at the specified index.\n * @param index - The index of the key to retrieve\n * @returns The key at the specified index, or null if the index is out of bounds\n */\n key(index: number): string | null {\n const keys = Array.from(this.store.keys());\n return keys[index] || null;\n }\n\n /**\n * Removes an item from the storage.\n * @param key - The key of the item to remove\n */\n removeItem(key: string): void {\n this.store.delete(key);\n }\n\n /**\n * Sets an item in the storage.\n * @param key - The key of the item to set\n * @param value - The value to set\n */\n setItem(key: string, value: string): void {\n this.store.set(key, value);\n }\n}\n","/*\n * Copyright [2021-present] [ahoo wang <ahoowang@qq.com> (https://github.com/Ahoo-Wang)].\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * http://www.apache.org/licenses/LICENSE-2.0\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { InMemoryStorage } from './inMemoryStorage';\n\n/**\n * Checks if the current environment is a browser.\n * @returns True if running in a browser environment, false otherwise\n */\nexport function isBrowser(): boolean {\n return typeof window !== 'undefined';\n}\n\n/**\n * Gets the appropriate storage implementation based on the environment.\n * Returns localStorage in browser environments if available, InMemoryStorage otherwise.\n * @returns A Storage-compatible object\n */\nexport const getStorage = (): Storage => {\n if (isBrowser()) {\n return window.localStorage;\n }\n return new InMemoryStorage();\n};\n","/*\n * Copyright [2021-present] [ahoo wang <ahoowang@qq.com> (https://github.com/Ahoo-Wang)].\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * http://www.apache.org/licenses/LICENSE-2.0\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Interface for serializing and deserializing values\n * @template Serialized The type of the serialized value\n * @template Deserialized The type of the deserialized value\n */\nexport interface Serializer<Serialized, Deserialized> {\n /**\n * Serializes a value to the specified format\n * @param value The value to serialize\n * @returns The serialized value\n */\n serialize(value: any): Serialized;\n\n /**\n * Deserializes a value from the specified format\n * @param value The value to deserialize\n * @returns The deserialized value\n */\n deserialize(value: Serialized): Deserialized;\n}\n\n/**\n * Implementation of Serializer that uses JSON for serialization\n */\nexport class JsonSerializer implements Serializer<string, any> {\n /**\n * Serializes a value to a JSON string\n * @param value The value to serialize\n * @returns The JSON string representation of the value\n */\n serialize(value: any): string {\n return JSON.stringify(value);\n }\n\n /**\n * Deserializes a JSON string to a value\n * @param value The JSON string to deserialize\n * @returns The deserialized value\n */\n deserialize(value: string): any {\n return JSON.parse(value);\n }\n}\n\n/**\n * Implementation of Serializer that performs no actual serialization\n * @template T The type of the value to pass through\n */\nexport class IdentitySerializer<T> implements Serializer<T, T> {\n /**\n * Returns the value as-is without serialization\n * @param value The value to pass through\n * @returns The same value that was passed in\n */\n serialize(value: T): T {\n return value;\n }\n\n /**\n * Returns the value as-is without deserialization\n * @param value The value to pass through\n * @returns The same value that was passed in\n */\n deserialize(value: T): T {\n return value;\n }\n}\n\n/**\n * Global instance of JsonSerializer\n */\nexport const jsonSerializer = new JsonSerializer();\n/**\n * Global instance of IdentitySerializer\n */\nexport const identitySerializer = new IdentitySerializer<any>();\n\nexport function typedIdentitySerializer<T>(): IdentitySerializer<T> {\n return identitySerializer as IdentitySerializer<T>;\n}\n","/*\n * Copyright [2021-present] [ahoo wang <ahoowang@qq.com> (https://github.com/Ahoo-Wang)].\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * http://www.apache.org/licenses/LICENSE-2.0\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { jsonSerializer, Serializer } from './serializer';\nimport {\n EventHandler,\n nameGenerator,\n SerialTypedEventBus,\n TypedEventBus,\n} from '@ahoo-wang/fetcher-eventbus';\nimport { getStorage } from './env';\n\nexport interface StorageEvent<Deserialized> {\n newValue?: Deserialized | null;\n oldValue?: Deserialized | null;\n}\n\n/**\n * A function that removes a storage listener when called.\n */\nexport type RemoveStorageListener = () => void;\n\nexport interface StorageListenable<Deserialized> {\n /**\n * Adds a listener for storage changes.\n * @param listener - The listener function to be called when storage changes\n * @returns A function that can be called to remove the listener\n */\n addListener(\n listener: EventHandler<StorageEvent<Deserialized>>,\n ): RemoveStorageListener;\n}\n\n/**\n * Options for configuring KeyStorage\n */\nexport interface KeyStorageOptions<Deserialized> {\n /**\n * The key used to store and retrieve values from storage\n */\n key: string;\n\n /**\n * Optional serializer for converting values to and from storage format\n * Defaults to IdentitySerializer if not provided\n */\n serializer?: Serializer<string, Deserialized>;\n\n /**\n * Optional storage instance. Defaults to localStorage\n */\n storage?: Storage;\n\n /**\n * Optional event bus for cross-tab communication. Defaults to SerialTypedEventBus\n */\n eventBus?: TypedEventBus<StorageEvent<Deserialized>>;\n\n /**\n * Optional default value to return when no value exists in storage\n */\n defaultValue?: Deserialized;\n}\n\n/**\n * A storage wrapper that manages a single value associated with a specific key\n * Provides caching and automatic cache invalidation when the storage value changes\n * @template Deserialized The type of the value being stored\n */\nexport class KeyStorage<Deserialized>\n implements StorageListenable<Deserialized> {\n private readonly key: string;\n private readonly serializer: Serializer<string, Deserialized>;\n private readonly storage: Storage;\n public readonly eventBus: TypedEventBus<StorageEvent<Deserialized>>;\n private readonly defaultValue: Deserialized | null = null;\n private cacheValue: Deserialized | null = null;\n private readonly keyStorageHandler: EventHandler<StorageEvent<Deserialized>> =\n {\n name: nameGenerator.generate('KeyStorage'),\n handle: (event: StorageEvent<Deserialized>) => {\n this.cacheValue = event.newValue ?? null;\n },\n };\n\n /**\n * Creates a new KeyStorage instance\n * @param options Configuration options for the storage\n */\n constructor(options: KeyStorageOptions<Deserialized>) {\n this.key = options.key;\n this.serializer = options.serializer ?? jsonSerializer;\n this.storage = options.storage ?? getStorage();\n this.eventBus =\n options.eventBus ??\n new SerialTypedEventBus<StorageEvent<Deserialized>>(\n `KeyStorage:${this.key}`,\n );\n this.defaultValue = options.defaultValue ?? null;\n this.eventBus.on(this.keyStorageHandler);\n }\n\n /**\n * Adds a listener for storage changes.\n *\n * The listener will be called whenever the storage value changes,\n * either locally or from other tabs/windows.\n *\n * @param listener - The event handler to be called when storage changes\n * @returns A function that can be called to remove the listener\n *\n * @example\n * ```typescript\n * const storage = new KeyStorage<string>({ key: 'userName' });\n * const removeListener = storage.addListener({\n * name: 'userNameChange',\n * handle: (event) => {\n * console.log('User name changed:', event.newValue);\n * }\n * });\n *\n * // Later, to remove the listener\n * removeListener();\n * ```\n */\n addListener(\n listener: EventHandler<StorageEvent<Deserialized>>,\n ): RemoveStorageListener {\n this.eventBus.on(listener);\n return () => this.eventBus.off(listener.name);\n }\n\n /**\n * Retrieves the current value from storage.\n *\n * Uses caching to avoid repeated deserialization. If the value is not in cache,\n * it retrieves it from the underlying storage and deserializes it.\n *\n * @returns The deserialized value, or null if no value exists in storage\n *\n * @example\n * ```typescript\n * const storage = new KeyStorage<string>({ key: 'userName' });\n * const userName = storage.get();\n * console.log(userName); // 'John Doe' or null\n * ```\n */\n get(): Deserialized | null {\n if (this.cacheValue !== null && this.cacheValue !== undefined) {\n return this.cacheValue;\n }\n const value = this.storage.getItem(this.key);\n if (value === null || value === undefined) {\n return this.defaultValue;\n }\n this.cacheValue = this.serializer.deserialize(value);\n return this.cacheValue;\n }\n\n /**\n * Stores a value in storage and notifies all listeners.\n *\n * Serializes the value, stores it in the underlying storage, updates the cache,\n * and emits a change event to all registered listeners.\n *\n * @param value - The value to store (will be serialized before storage)\n *\n * @example\n * ```typescript\n * const storage = new KeyStorage<string>({ key: 'userName' });\n * storage.set('John Doe');\n * ```\n */\n set(value: Deserialized): void {\n const oldValue = this.get();\n const serialized = this.serializer.serialize(value);\n this.storage.setItem(this.key, serialized);\n this.cacheValue = value;\n this.eventBus.emit({\n newValue: value,\n oldValue: oldValue,\n });\n }\n\n /**\n * Removes the value from storage and notifies all listeners.\n *\n * Removes the item from the underlying storage, clears the cache,\n * and emits a change event indicating the value was removed.\n *\n * @example\n * ```typescript\n * const storage = new KeyStorage<string>({ key: 'userName' });\n * storage.remove(); // Removes the stored value\n * ```\n */\n remove(): void {\n const oldValue = this.get();\n this.storage.removeItem(this.key);\n this.cacheValue = null;\n this.eventBus.emit({\n oldValue: oldValue,\n newValue: null,\n });\n }\n\n /**\n * Cleans up resources used by the KeyStorage instance.\n *\n * Removes the internal event handler from the event bus.\n * Should be called when the KeyStorage instance is no longer needed\n * to prevent memory leaks.\n *\n * @example\n * ```typescript\n * const storage = new KeyStorage<string>({ key: 'userName' });\n * // ... use storage ...\n * storage.destroy(); // Clean up resources\n * ```\n */\n destroy() {\n this.eventBus.off(this.keyStorageHandler.name);\n }\n}\n"],"names":["InMemoryStorage","key","value","index","isBrowser","getStorage","JsonSerializer","IdentitySerializer","jsonSerializer","identitySerializer","typedIdentitySerializer","KeyStorage","options","nameGenerator","event","SerialTypedEventBus","listener","oldValue","serialized"],"mappings":"yTAaO,MAAMA,CAAmC,CAAzC,aAAA,CACL,KAAiB,UAAiC,GAAI,CAKtD,IAAI,QAAiB,CACnB,OAAO,KAAK,MAAM,IACpB,CAKA,OAAc,CACZ,KAAK,MAAM,MAAA,CACb,CAOA,QAAQC,EAA4B,CAClC,MAAMC,EAAQ,KAAK,MAAM,IAAID,CAAG,EAChC,OAAOC,IAAU,OAAYA,EAAQ,IACvC,CAOA,IAAIC,EAA8B,CAEhC,OADa,MAAM,KAAK,KAAK,MAAM,MAAM,EAC7BA,CAAK,GAAK,IACxB,CAMA,WAAWF,EAAmB,CAC5B,KAAK,MAAM,OAAOA,CAAG,CACvB,CAOA,QAAQA,EAAaC,EAAqB,CACxC,KAAK,MAAM,IAAID,EAAKC,CAAK,CAC3B,CACF,CC/CO,SAASE,GAAqB,CACnC,OAAO,OAAO,OAAW,GAC3B,CAOO,MAAMC,EAAa,IACpBD,IACK,OAAO,aAET,IAAIJ,ECKN,MAAMM,CAAkD,CAM7D,UAAUJ,EAAoB,CAC5B,OAAO,KAAK,UAAUA,CAAK,CAC7B,CAOA,YAAYA,EAAoB,CAC9B,OAAO,KAAK,MAAMA,CAAK,CACzB,CACF,CAMO,MAAMK,CAAkD,CAM7D,UAAUL,EAAa,CACrB,OAAOA,CACT,CAOA,YAAYA,EAAa,CACvB,OAAOA,CACT,CACF,CAKO,MAAMM,EAAiB,IAAIF,EAIrBG,EAAqB,IAAIF,EAE/B,SAASG,GAAoD,CAClE,OAAOD,CACT,CCbO,MAAME,CACgC,CAmB3C,YAAYC,EAA0C,CAdtD,KAAiB,aAAoC,KACrD,KAAQ,WAAkC,KAC1C,KAAiB,kBACf,CACE,KAAMC,EAAAA,cAAc,SAAS,YAAY,EACzC,OAASC,GAAsC,CAC7C,KAAK,WAAaA,EAAM,UAAY,IACtC,CAAA,EAQF,KAAK,IAAMF,EAAQ,IACnB,KAAK,WAAaA,EAAQ,YAAcJ,EACxC,KAAK,QAAUI,EAAQ,SAAWP,EAAA,EAClC,KAAK,SACHO,EAAQ,UACR,IAAIG,EAAAA,oBACF,cAAc,KAAK,GAAG,EAAA,EAE1B,KAAK,aAAeH,EAAQ,cAAgB,KAC5C,KAAK,SAAS,GAAG,KAAK,iBAAiB,CACzC,CAyBA,YACEI,EACuB,CACvB,YAAK,SAAS,GAAGA,CAAQ,EAClB,IAAM,KAAK,SAAS,IAAIA,EAAS,IAAI,CAC9C,CAiBA,KAA2B,CACzB,GAAI,KAAK,aAAe,MAAQ,KAAK,aAAe,OAClD,OAAO,KAAK,WAEd,MAAMd,EAAQ,KAAK,QAAQ,QAAQ,KAAK,GAAG,EAC3C,OAAIA,GAAU,KACL,KAAK,cAEd,KAAK,WAAa,KAAK,WAAW,YAAYA,CAAK,EAC5C,KAAK,WACd,CAgBA,IAAIA,EAA2B,CAC7B,MAAMe,EAAW,KAAK,IAAA,EAChBC,EAAa,KAAK,WAAW,UAAUhB,CAAK,EAClD,KAAK,QAAQ,QAAQ,KAAK,IAAKgB,CAAU,EACzC,KAAK,WAAahB,EAClB,KAAK,SAAS,KAAK,CACjB,SAAUA,EACV,SAAAe,CAAA,CACD,CACH,CAcA,QAAe,CACb,MAAMA,EAAW,KAAK,IAAA,EACtB,KAAK,QAAQ,WAAW,KAAK,GAAG,EAChC,KAAK,WAAa,KAClB,KAAK,SAAS,KAAK,CACjB,SAAAA,EACA,SAAU,IAAA,CACX,CACH,CAgBA,SAAU,CACR,KAAK,SAAS,IAAI,KAAK,kBAAkB,IAAI,CAC/C,CACF"}
1
+ {"version":3,"file":"index.umd.js","names":[],"sources":["../src/inMemoryStorage.ts","../src/env.ts","../src/serializer.ts","../src/keyStorage.ts"],"sourcesContent":["/*\n * Copyright [2021-present] [ahoo wang <ahoowang@qq.com> (https://github.com/Ahoo-Wang)].\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * http://www.apache.org/licenses/LICENSE-2.0\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nexport class InMemoryStorage implements Storage {\n private readonly store: Map<string, string> = new Map();\n\n /**\n * Gets the number of items stored in the storage.\n */\n get length(): number {\n return this.store.size;\n }\n\n /**\n * Clears all items from the storage.\n */\n clear(): void {\n this.store.clear();\n }\n\n /**\n * Gets an item from the storage.\n * @param key - The key of the item to retrieve\n * @returns The value of the item, or null if the item does not exist\n */\n getItem(key: string): string | null {\n const value = this.store.get(key);\n return value !== undefined ? value : null;\n }\n\n /**\n * Gets the key at the specified index.\n * @param index - The index of the key to retrieve\n * @returns The key at the specified index, or null if the index is out of bounds\n */\n key(index: number): string | null {\n const keys = Array.from(this.store.keys());\n return keys[index] || null;\n }\n\n /**\n * Removes an item from the storage.\n * @param key - The key of the item to remove\n */\n removeItem(key: string): void {\n this.store.delete(key);\n }\n\n /**\n * Sets an item in the storage.\n * @param key - The key of the item to set\n * @param value - The value to set\n */\n setItem(key: string, value: string): void {\n this.store.set(key, value);\n }\n}\n","/*\n * Copyright [2021-present] [ahoo wang <ahoowang@qq.com> (https://github.com/Ahoo-Wang)].\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * http://www.apache.org/licenses/LICENSE-2.0\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { InMemoryStorage } from './inMemoryStorage';\n\n/**\n * Checks if the current environment is a browser.\n * @returns True if running in a browser environment, false otherwise\n */\nexport function isBrowser(): boolean {\n return typeof window !== 'undefined';\n}\n\n/**\n * Gets the appropriate storage implementation based on the environment.\n * Returns localStorage in browser environments if available, InMemoryStorage otherwise.\n * @returns A Storage-compatible object\n */\nexport const getStorage = (): Storage => {\n if (isBrowser()) {\n return window.localStorage;\n }\n return new InMemoryStorage();\n};\n","/*\n * Copyright [2021-present] [ahoo wang <ahoowang@qq.com> (https://github.com/Ahoo-Wang)].\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * http://www.apache.org/licenses/LICENSE-2.0\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Interface for serializing and deserializing values\n * @template Serialized The type of the serialized value\n * @template Deserialized The type of the deserialized value\n */\nexport interface Serializer<Serialized, Deserialized> {\n /**\n * Serializes a value to the specified format\n * @param value The value to serialize\n * @returns The serialized value\n */\n serialize(value: any): Serialized;\n\n /**\n * Deserializes a value from the specified format\n * @param value The value to deserialize\n * @returns The deserialized value\n */\n deserialize(value: Serialized): Deserialized;\n}\n\n/**\n * Implementation of Serializer that uses JSON for serialization\n */\nexport class JsonSerializer implements Serializer<string, any> {\n /**\n * Serializes a value to a JSON string\n * @param value The value to serialize\n * @returns The JSON string representation of the value\n */\n serialize(value: any): string {\n return JSON.stringify(value);\n }\n\n /**\n * Deserializes a JSON string to a value\n * @param value The JSON string to deserialize\n * @returns The deserialized value\n */\n deserialize(value: string): any {\n return JSON.parse(value);\n }\n}\n\n/**\n * Implementation of Serializer that performs no actual serialization\n * @template T The type of the value to pass through\n */\nexport class IdentitySerializer<T> implements Serializer<T, T> {\n /**\n * Returns the value as-is without serialization\n * @param value The value to pass through\n * @returns The same value that was passed in\n */\n serialize(value: T): T {\n return value;\n }\n\n /**\n * Returns the value as-is without deserialization\n * @param value The value to pass through\n * @returns The same value that was passed in\n */\n deserialize(value: T): T {\n return value;\n }\n}\n\n/**\n * Global instance of JsonSerializer\n */\nexport const jsonSerializer = new JsonSerializer();\n/**\n * Global instance of IdentitySerializer\n */\nexport const identitySerializer = new IdentitySerializer<any>();\n\nexport function typedIdentitySerializer<T>(): IdentitySerializer<T> {\n return identitySerializer as IdentitySerializer<T>;\n}\n","/*\n * Copyright [2021-present] [ahoo wang <ahoowang@qq.com> (https://github.com/Ahoo-Wang)].\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * http://www.apache.org/licenses/LICENSE-2.0\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport type { Serializer } from './serializer';\nimport { jsonSerializer } from './serializer';\nimport type {\n EventHandler,\n TypedEventBus} from '@ahoo-wang/fetcher-eventbus';\nimport {\n nameGenerator,\n SerialTypedEventBus\n} from '@ahoo-wang/fetcher-eventbus';\nimport { getStorage } from './env';\n\nexport interface StorageEvent<Deserialized> {\n newValue?: Deserialized | null;\n oldValue?: Deserialized | null;\n}\n\n/**\n * A function that removes a storage listener when called.\n */\nexport type RemoveStorageListener = () => void;\n\nexport interface StorageListenable<Deserialized> {\n /**\n * Adds a listener for storage changes.\n * @param listener - The listener function to be called when storage changes\n * @returns A function that can be called to remove the listener\n */\n addListener(\n listener: EventHandler<StorageEvent<Deserialized>>,\n ): RemoveStorageListener;\n}\n\n/**\n * Options for configuring KeyStorage\n */\nexport interface KeyStorageOptions<Deserialized> {\n /**\n * The key used to store and retrieve values from storage\n */\n key: string;\n\n /**\n * Optional serializer for converting values to and from storage format\n * Defaults to IdentitySerializer if not provided\n */\n serializer?: Serializer<string, Deserialized>;\n\n /**\n * Optional storage instance. Defaults to localStorage\n */\n storage?: Storage;\n\n /**\n * Optional event bus for cross-tab communication. Defaults to SerialTypedEventBus\n */\n eventBus?: TypedEventBus<StorageEvent<Deserialized>>;\n\n /**\n * Optional default value to return when no value exists in storage\n */\n defaultValue?: Deserialized;\n}\n\n/**\n * A storage wrapper that manages a single value associated with a specific key\n * Provides caching and automatic cache invalidation when the storage value changes\n * @template Deserialized The type of the value being stored\n */\nexport class KeyStorage<Deserialized>\n implements StorageListenable<Deserialized> {\n private readonly key: string;\n private readonly serializer: Serializer<string, Deserialized>;\n private readonly storage: Storage;\n public readonly eventBus: TypedEventBus<StorageEvent<Deserialized>>;\n private readonly defaultValue: Deserialized | null = null;\n private cacheValue: Deserialized | null = null;\n private readonly keyStorageHandler: EventHandler<StorageEvent<Deserialized>> =\n {\n name: nameGenerator.generate('KeyStorage'),\n handle: (event: StorageEvent<Deserialized>) => {\n this.cacheValue = event.newValue ?? null;\n },\n };\n\n /**\n * Creates a new KeyStorage instance\n * @param options Configuration options for the storage\n */\n constructor(options: KeyStorageOptions<Deserialized>) {\n this.key = options.key;\n this.serializer = options.serializer ?? jsonSerializer;\n this.storage = options.storage ?? getStorage();\n this.eventBus =\n options.eventBus ??\n new SerialTypedEventBus<StorageEvent<Deserialized>>(\n `KeyStorage:${this.key}`,\n );\n this.defaultValue = options.defaultValue ?? null;\n this.eventBus.on(this.keyStorageHandler);\n }\n\n /**\n * Adds a listener for storage changes.\n *\n * The listener will be called whenever the storage value changes,\n * either locally or from other tabs/windows.\n *\n * @param listener - The event handler to be called when storage changes\n * @returns A function that can be called to remove the listener\n *\n * @example\n * ```typescript\n * const storage = new KeyStorage<string>({ key: 'userName' });\n * const removeListener = storage.addListener({\n * name: 'userNameChange',\n * handle: (event) => {\n * console.log('User name changed:', event.newValue);\n * }\n * });\n *\n * // Later, to remove the listener\n * removeListener();\n * ```\n */\n addListener(\n listener: EventHandler<StorageEvent<Deserialized>>,\n ): RemoveStorageListener {\n this.eventBus.on(listener);\n return () => this.eventBus.off(listener.name);\n }\n\n /**\n * Retrieves the current value from storage.\n *\n * Uses caching to avoid repeated deserialization. If the value is not in cache,\n * it retrieves it from the underlying storage and deserializes it.\n *\n * @returns The deserialized value, or null if no value exists in storage\n *\n * @example\n * ```typescript\n * const storage = new KeyStorage<string>({ key: 'userName' });\n * const userName = storage.get();\n * console.log(userName); // 'John Doe' or null\n * ```\n */\n get(): Deserialized | null {\n if (this.cacheValue !== null && this.cacheValue !== undefined) {\n return this.cacheValue;\n }\n const value = this.storage.getItem(this.key);\n if (value === null || value === undefined) {\n return this.defaultValue;\n }\n this.cacheValue = this.serializer.deserialize(value);\n return this.cacheValue;\n }\n\n /**\n * Stores a value in storage and notifies all listeners.\n *\n * Serializes the value, stores it in the underlying storage, updates the cache,\n * and emits a change event to all registered listeners.\n *\n * @param value - The value to store (will be serialized before storage)\n *\n * @example\n * ```typescript\n * const storage = new KeyStorage<string>({ key: 'userName' });\n * storage.set('John Doe');\n * ```\n */\n set(value: Deserialized): void {\n const oldValue = this.get();\n const serialized = this.serializer.serialize(value);\n this.storage.setItem(this.key, serialized);\n this.cacheValue = value;\n this.eventBus.emit({\n newValue: value,\n oldValue: oldValue,\n });\n }\n\n /**\n * Removes the value from storage and notifies all listeners.\n *\n * Removes the item from the underlying storage, clears the cache,\n * and emits a change event indicating the value was removed.\n *\n * @example\n * ```typescript\n * const storage = new KeyStorage<string>({ key: 'userName' });\n * storage.remove(); // Removes the stored value\n * ```\n */\n remove(): void {\n const oldValue = this.get();\n this.storage.removeItem(this.key);\n this.cacheValue = null;\n this.eventBus.emit({\n oldValue: oldValue,\n newValue: null,\n });\n }\n\n /**\n * Cleans up resources used by the KeyStorage instance.\n *\n * Removes the internal event handler from the event bus.\n * Should be called when the KeyStorage instance is no longer needed\n * to prevent memory leaks.\n *\n * @example\n * ```typescript\n * const storage = new KeyStorage<string>({ key: 'userName' });\n * // ... use storage ...\n * storage.destroy(); // Clean up resources\n * ```\n */\n destroy() {\n this.eventBus.off(this.keyStorageHandler.name);\n }\n}\n"],"mappings":"wWAaA,IAAa,EAAb,KAAgD,0BACA,IAAI,IAKlD,IAAI,QAAiB,CACnB,OAAO,KAAK,MAAM,KAMpB,OAAc,CACZ,KAAK,MAAM,OAAO,CAQpB,QAAQ,EAA4B,CAClC,IAAM,EAAQ,KAAK,MAAM,IAAI,EAAI,CACjC,OAAO,IAAU,IAAA,GAAoB,KAAR,EAQ/B,IAAI,EAA8B,CAEhC,OADa,MAAM,KAAK,KAAK,MAAM,MAAM,CAAC,CAC9B,IAAU,KAOxB,WAAW,EAAmB,CAC5B,KAAK,MAAM,OAAO,EAAI,CAQxB,QAAQ,EAAa,EAAqB,CACxC,KAAK,MAAM,IAAI,EAAK,EAAM,GC7C9B,SAAgB,GAAqB,CACnC,OAAO,OAAO,OAAW,IAQ3B,IAAa,MACP,GAAW,CACN,OAAO,aAET,IAAI,ECKA,EAAb,KAA+D,CAM7D,UAAU,EAAoB,CAC5B,OAAO,KAAK,UAAU,EAAM,CAQ9B,YAAY,EAAoB,CAC9B,OAAO,KAAK,MAAM,EAAM,GAQf,EAAb,KAA+D,CAM7D,UAAU,EAAa,CACrB,OAAO,EAQT,YAAY,EAAa,CACvB,OAAO,IAOE,EAAiB,IAAI,EAIrB,EAAqB,IAAI,EAEtC,SAAgB,GAAoD,CAClE,OAAO,ECVT,IAAa,EAAb,KAC6C,CAmB3C,YAAY,EAA0C,mBAdD,qBACX,4BAExC,CACE,KAAM,EAAA,cAAc,SAAS,aAAa,CAC1C,OAAS,GAAsC,CAC7C,KAAK,WAAa,EAAM,UAAY,MAEvC,CAOD,KAAK,IAAM,EAAQ,IACnB,KAAK,WAAa,EAAQ,YAAc,EACxC,KAAK,QAAU,EAAQ,SAAW,GAAY,CAC9C,KAAK,SACH,EAAQ,UACR,IAAI,EAAA,oBACF,cAAc,KAAK,MACpB,CACH,KAAK,aAAe,EAAQ,cAAgB,KAC5C,KAAK,SAAS,GAAG,KAAK,kBAAkB,CA0B1C,YACE,EACuB,CAEvB,OADA,KAAK,SAAS,GAAG,EAAS,KACb,KAAK,SAAS,IAAI,EAAS,KAAK,CAkB/C,KAA2B,CACzB,GAAI,KAAK,aAAe,MAAQ,KAAK,aAAe,IAAA,GAClD,OAAO,KAAK,WAEd,IAAM,EAAQ,KAAK,QAAQ,QAAQ,KAAK,IAAI,CAK5C,OAJI,GAAU,KACL,KAAK,cAEd,KAAK,WAAa,KAAK,WAAW,YAAY,EAAM,CAC7C,KAAK,YAiBd,IAAI,EAA2B,CAC7B,IAAM,EAAW,KAAK,KAAK,CACrB,EAAa,KAAK,WAAW,UAAU,EAAM,CACnD,KAAK,QAAQ,QAAQ,KAAK,IAAK,EAAW,CAC1C,KAAK,WAAa,EAClB,KAAK,SAAS,KAAK,CACjB,SAAU,EACA,WACX,CAAC,CAeJ,QAAe,CACb,IAAM,EAAW,KAAK,KAAK,CAC3B,KAAK,QAAQ,WAAW,KAAK,IAAI,CACjC,KAAK,WAAa,KAClB,KAAK,SAAS,KAAK,CACP,WACV,SAAU,KACX,CAAC,CAiBJ,SAAU,CACR,KAAK,SAAS,IAAI,KAAK,kBAAkB,KAAK"}
@@ -1 +1 @@
1
- {"version":3,"file":"keyStorage.d.ts","sourceRoot":"","sources":["../src/keyStorage.ts"],"names":[],"mappings":"AAaA,OAAO,EAAkB,UAAU,EAAE,MAAM,cAAc,CAAC;AAC1D,OAAO,EACL,YAAY,EAGZ,aAAa,EACd,MAAM,6BAA6B,CAAC;AAGrC,MAAM,WAAW,YAAY,CAAC,YAAY;IACxC,QAAQ,CAAC,EAAE,YAAY,GAAG,IAAI,CAAC;IAC/B,QAAQ,CAAC,EAAE,YAAY,GAAG,IAAI,CAAC;CAChC;AAED;;GAEG;AACH,MAAM,MAAM,qBAAqB,GAAG,MAAM,IAAI,CAAC;AAE/C,MAAM,WAAW,iBAAiB,CAAC,YAAY;IAC7C;;;;OAIG;IACH,WAAW,CACT,QAAQ,EAAE,YAAY,CAAC,YAAY,CAAC,YAAY,CAAC,CAAC,GACjD,qBAAqB,CAAC;CAC1B;AAED;;GAEG;AACH,MAAM,WAAW,iBAAiB,CAAC,YAAY;IAC7C;;OAEG;IACH,GAAG,EAAE,MAAM,CAAC;IAEZ;;;OAGG;IACH,UAAU,CAAC,EAAE,UAAU,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC;IAE9C;;OAEG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;IAElB;;OAEG;IACH,QAAQ,CAAC,EAAE,aAAa,CAAC,YAAY,CAAC,YAAY,CAAC,CAAC,CAAC;IAErD;;OAEG;IACH,YAAY,CAAC,EAAE,YAAY,CAAC;CAC7B;AAED;;;;GAIG;AACH,qBAAa,UAAU,CAAC,YAAY,CAClC,YAAW,iBAAiB,CAAC,YAAY,CAAC;IAC1C,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAS;IAC7B,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAmC;IAC9D,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAU;IAClC,SAAgB,QAAQ,EAAE,aAAa,CAAC,YAAY,CAAC,YAAY,CAAC,CAAC,CAAC;IACpE,OAAO,CAAC,QAAQ,CAAC,YAAY,CAA6B;IAC1D,OAAO,CAAC,UAAU,CAA6B;IAC/C,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAM9B;IAEJ;;;OAGG;gBACS,OAAO,EAAE,iBAAiB,CAAC,YAAY,CAAC;IAapD;;;;;;;;;;;;;;;;;;;;;;OAsBG;IACH,WAAW,CACT,QAAQ,EAAE,YAAY,CAAC,YAAY,CAAC,YAAY,CAAC,CAAC,GACjD,qBAAqB;IAKxB;;;;;;;;;;;;;;OAcG;IACH,GAAG,IAAI,YAAY,GAAG,IAAI;IAY1B;;;;;;;;;;;;;OAaG;IACH,GAAG,CAAC,KAAK,EAAE,YAAY,GAAG,IAAI;IAW9B;;;;;;;;;;;OAWG;IACH,MAAM,IAAI,IAAI;IAUd;;;;;;;;;;;;;OAaG;IACH,OAAO;CAGR"}
1
+ {"version":3,"file":"keyStorage.d.ts","sourceRoot":"","sources":["../src/keyStorage.ts"],"names":[],"mappings":"AAaA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAE/C,OAAO,KAAK,EACV,YAAY,EACZ,aAAa,EAAC,MAAM,6BAA6B,CAAC;AAOpD,MAAM,WAAW,YAAY,CAAC,YAAY;IACxC,QAAQ,CAAC,EAAE,YAAY,GAAG,IAAI,CAAC;IAC/B,QAAQ,CAAC,EAAE,YAAY,GAAG,IAAI,CAAC;CAChC;AAED;;GAEG;AACH,MAAM,MAAM,qBAAqB,GAAG,MAAM,IAAI,CAAC;AAE/C,MAAM,WAAW,iBAAiB,CAAC,YAAY;IAC7C;;;;OAIG;IACH,WAAW,CACT,QAAQ,EAAE,YAAY,CAAC,YAAY,CAAC,YAAY,CAAC,CAAC,GACjD,qBAAqB,CAAC;CAC1B;AAED;;GAEG;AACH,MAAM,WAAW,iBAAiB,CAAC,YAAY;IAC7C;;OAEG;IACH,GAAG,EAAE,MAAM,CAAC;IAEZ;;;OAGG;IACH,UAAU,CAAC,EAAE,UAAU,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC;IAE9C;;OAEG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;IAElB;;OAEG;IACH,QAAQ,CAAC,EAAE,aAAa,CAAC,YAAY,CAAC,YAAY,CAAC,CAAC,CAAC;IAErD;;OAEG;IACH,YAAY,CAAC,EAAE,YAAY,CAAC;CAC7B;AAED;;;;GAIG;AACH,qBAAa,UAAU,CAAC,YAAY,CAClC,YAAW,iBAAiB,CAAC,YAAY,CAAC;IAC1C,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAS;IAC7B,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAmC;IAC9D,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAU;IAClC,SAAgB,QAAQ,EAAE,aAAa,CAAC,YAAY,CAAC,YAAY,CAAC,CAAC,CAAC;IACpE,OAAO,CAAC,QAAQ,CAAC,YAAY,CAA6B;IAC1D,OAAO,CAAC,UAAU,CAA6B;IAC/C,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAM9B;IAEJ;;;OAGG;gBACS,OAAO,EAAE,iBAAiB,CAAC,YAAY,CAAC;IAapD;;;;;;;;;;;;;;;;;;;;;;OAsBG;IACH,WAAW,CACT,QAAQ,EAAE,YAAY,CAAC,YAAY,CAAC,YAAY,CAAC,CAAC,GACjD,qBAAqB;IAKxB;;;;;;;;;;;;;;OAcG;IACH,GAAG,IAAI,YAAY,GAAG,IAAI;IAY1B;;;;;;;;;;;;;OAaG;IACH,GAAG,CAAC,KAAK,EAAE,YAAY,GAAG,IAAI;IAW9B;;;;;;;;;;;OAWG;IACH,MAAM,IAAI,IAAI;IAUd;;;;;;;;;;;;;OAaG;IACH,OAAO;CAGR"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ahoo-wang/fetcher-storage",
3
- "version": "3.13.15",
3
+ "version": "3.15.0",
4
4
  "description": "A lightweight, cross-environment storage library with key-based storage and automatic environment detection. Provides consistent API for browser localStorage and in-memory storage with change notifications.",
5
5
  "keywords": [
6
6
  "storage",
@@ -51,11 +51,11 @@
51
51
  "globals": "^17.4.0",
52
52
  "prettier": "^3.8.1",
53
53
  "typescript": "^5.9.3",
54
- "typescript-eslint": "^8.58.0",
54
+ "typescript-eslint": "^8.58.1",
55
55
  "unplugin-dts": "1.0.0-beta.6",
56
- "vite": "^7.3.1",
57
- "vite-bundle-analyzer": "^1.3.6",
58
- "vitest": "^4.1.2"
56
+ "vite": "^8.0.7",
57
+ "vite-bundle-analyzer": "^1.3.7",
58
+ "vitest": "^4.1.3"
59
59
  },
60
60
  "scripts": {
61
61
  "build": "vite build",