@lytjs/common-storage 6.5.0 → 6.6.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.
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts"],"names":[],"mappings":"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","file":"index.cjs","sourcesContent":["/**\r\n * @lytjs/common-storage\r\n * 轻量级类型安全的存储工具\r\n */\r\n\r\ndeclare const __DEV__: boolean;\r\n\r\nexport interface StorageOptions<T> {\r\n key: string;\r\n storage?: Storage;\r\n serializer?: (value: T) => string;\r\n deserializer?: (value: string) => T;\r\n default?: T;\r\n}\r\n\r\nexport interface StorageAdapter<T> {\r\n get(): T;\r\n set(value: T): void;\r\n remove(): void;\r\n has(): boolean;\r\n onChange(callback: (value: T | null) => void): () => void;\r\n}\r\n\r\n/**\r\n * 安全的 JSON 解析,解析失败返回 fallback\r\n */\r\nexport function parseJSON<T>(value: string, fallback: T): T {\r\n try {\r\n return JSON.parse(value) as T;\r\n } catch {\r\n return fallback;\r\n }\r\n}\r\n\r\n/**\r\n * 检查存储是否可用(处理隐私模式等场景)\r\n */\r\nexport function isStorageAvailable(storage?: Storage): boolean {\r\n if (!storage) {\r\n try {\r\n storage = window.localStorage;\r\n } catch {\r\n return false;\r\n }\r\n }\r\n\r\n try {\r\n const testKey = '__lytjs_storage_test__';\r\n storage.setItem(testKey, '1');\r\n storage.removeItem(testKey);\r\n return true;\r\n } catch {\r\n return false;\r\n }\r\n}\r\n\r\n/**\r\n * 创建类型安全的存储适配器\r\n *\r\n * @param options - 存储配置\r\n * @returns StorageAdapter 实例\r\n */\r\nexport function createStorage<T>(options: StorageOptions<T>): StorageAdapter<T> {\r\n const {\r\n key,\r\n storage,\r\n serializer = JSON.stringify,\r\n deserializer = (v: string) => parseJSON<T>(v, options.default as T),\r\n default: defaultValue,\r\n } = options;\r\n\r\n const listeners = new Set<(value: T | null) => void>();\r\n\r\n const getStorage = (): Storage | undefined => {\r\n if (storage) return storage;\r\n if (typeof window !== 'undefined') {\r\n return window.localStorage;\r\n }\r\n return undefined;\r\n };\r\n\r\n const notifyListeners = (value: T | null) => {\r\n for (const listener of listeners) {\r\n try {\r\n listener(value);\r\n } catch {\r\n // Ignore listener errors\r\n }\r\n }\r\n };\r\n\r\n const handleStorageEvent = (event: StorageEvent) => {\r\n if (event.key === key) {\r\n const newValue = event.newValue\r\n ? deserializer(event.newValue)\r\n : null;\r\n notifyListeners(newValue);\r\n }\r\n };\r\n\r\n const adapter: StorageAdapter<T> = {\r\n get(): T {\r\n const s = getStorage();\r\n if (!s) return defaultValue as T;\r\n try {\r\n const raw = s.getItem(key);\r\n if (raw === null) return defaultValue as T;\r\n return deserializer(raw);\r\n } catch {\r\n return defaultValue as T;\r\n }\r\n },\r\n\r\n set(value: T): void {\r\n const s = getStorage();\r\n if (!s) return;\r\n try {\r\n s.setItem(key, serializer(value));\r\n notifyListeners(value);\r\n } catch {\r\n if (__DEV__) {\r\n console.warn(`[storage] Failed to set item \"${key}\"`);\r\n }\r\n }\r\n },\r\n\r\n remove(): void {\r\n const s = getStorage();\r\n if (!s) return;\r\n try {\r\n s.removeItem(key);\r\n notifyListeners(null);\r\n } catch {\r\n // Ignore\r\n }\r\n },\r\n\r\n has(): boolean {\r\n const s = getStorage();\r\n if (!s) return false;\r\n try {\r\n return s.getItem(key) !== null;\r\n } catch {\r\n return false;\r\n }\r\n },\r\n\r\n onChange(callback: (value: T | null) => void): () => void {\r\n listeners.add(callback);\r\n if (typeof window !== 'undefined' && listeners.size === 1) {\r\n window.addEventListener('storage', handleStorageEvent);\r\n }\r\n return () => {\r\n listeners.delete(callback);\r\n if (typeof window !== 'undefined' && listeners.size === 0) {\r\n window.removeEventListener('storage', handleStorageEvent);\r\n }\r\n };\r\n },\r\n };\r\n\r\n return adapter;\r\n}\r\n\r\n/**\r\n * 使用 sessionStorage 的快捷方式\r\n *\r\n * @param options - 存储配置(不含 storage 字段)\r\n * @returns StorageAdapter 实例\r\n */\r\nexport function createSessionStorage<T>(\r\n options: Omit<StorageOptions<T>, 'storage'>,\r\n): StorageAdapter<T> {\r\n const getSessionStorage = (): Storage | undefined => {\r\n if (typeof window !== 'undefined') {\r\n return window.sessionStorage;\r\n }\r\n return undefined;\r\n };\r\n\r\n return createStorage<T>({\r\n ...options,\r\n storage: getSessionStorage(),\r\n });\r\n}\r\n"]}
1
+ {"version":3,"sources":["../src/index.ts"],"names":[],"mappings":"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","file":"index.cjs","sourcesContent":["/**\n * @lytjs/common-storage\n * 轻量级类型安全的存储工具\n */\n\ndeclare const __DEV__: boolean;\n\nexport interface StorageOptions<T> {\n key: string;\n storage?: Storage;\n serializer?: (value: T) => string;\n deserializer?: (value: string) => T;\n default?: T;\n}\n\nexport interface StorageAdapter<T> {\n get(): T;\n set(value: T): void;\n remove(): void;\n has(): boolean;\n onChange(callback: (value: T | null) => void): () => void;\n}\n\n/**\n * 安全的 JSON 解析,解析失败返回 fallback\n */\nexport function parseJSON<T>(value: string, fallback: T): T {\n try {\n return JSON.parse(value) as T;\n } catch {\n return fallback;\n }\n}\n\n/**\n * 检查存储是否可用(处理隐私模式等场景)\n */\nexport function isStorageAvailable(storage?: Storage): boolean {\n if (!storage) {\n try {\n storage = window.localStorage;\n } catch {\n return false;\n }\n }\n\n try {\n const testKey = '__lytjs_storage_test__';\n storage.setItem(testKey, '1');\n storage.removeItem(testKey);\n return true;\n } catch {\n return false;\n }\n}\n\n/**\n * 创建类型安全的存储适配器\n *\n * @param options - 存储配置\n * @returns StorageAdapter 实例\n */\nexport function createStorage<T>(options: StorageOptions<T>): StorageAdapter<T> {\n const {\n key,\n storage,\n serializer = JSON.stringify,\n deserializer = (v: string) => parseJSON<T>(v, options.default as T),\n default: defaultValue,\n } = options;\n\n const listeners = new Set<(value: T | null) => void>();\n\n const getStorage = (): Storage | undefined => {\n if (storage) return storage;\n if (typeof window !== 'undefined') {\n return window.localStorage;\n }\n return undefined;\n };\n\n const notifyListeners = (value: T | null) => {\n for (const listener of listeners) {\n try {\n listener(value);\n } catch {\n // Ignore listener errors\n }\n }\n };\n\n const handleStorageEvent = (event: StorageEvent) => {\n if (event.key === key) {\n const newValue = event.newValue ? deserializer(event.newValue) : null;\n notifyListeners(newValue);\n }\n };\n\n const adapter: StorageAdapter<T> = {\n get(): T {\n const s = getStorage();\n if (!s) return defaultValue as T;\n try {\n const raw = s.getItem(key);\n if (raw === null) return defaultValue as T;\n return deserializer(raw);\n } catch {\n return defaultValue as T;\n }\n },\n\n set(value: T): void {\n const s = getStorage();\n if (!s) return;\n try {\n s.setItem(key, serializer(value));\n notifyListeners(value);\n } catch {\n if (__DEV__) {\n console.warn(`[storage] Failed to set item \"${key}\"`);\n }\n }\n },\n\n remove(): void {\n const s = getStorage();\n if (!s) return;\n try {\n s.removeItem(key);\n notifyListeners(null);\n } catch {\n // Ignore\n }\n },\n\n has(): boolean {\n const s = getStorage();\n if (!s) return false;\n try {\n return s.getItem(key) !== null;\n } catch {\n return false;\n }\n },\n\n onChange(callback: (value: T | null) => void): () => void {\n listeners.add(callback);\n if (typeof window !== 'undefined' && listeners.size === 1) {\n window.addEventListener('storage', handleStorageEvent);\n }\n return () => {\n listeners.delete(callback);\n if (typeof window !== 'undefined' && listeners.size === 0) {\n window.removeEventListener('storage', handleStorageEvent);\n }\n };\n },\n };\n\n return adapter;\n}\n\n/**\n * 使用 sessionStorage 的快捷方式\n *\n * @param options - 存储配置(不含 storage 字段)\n * @returns StorageAdapter 实例\n */\nexport function createSessionStorage<T>(\n options: Omit<StorageOptions<T>, 'storage'>,\n): StorageAdapter<T> {\n const getSessionStorage = (): Storage | undefined => {\n if (typeof window !== 'undefined') {\n return window.sessionStorage;\n }\n return undefined;\n };\n\n return createStorage<T>({\n ...options,\n storage: getSessionStorage(),\n });\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts"],"names":[],"mappings":"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","file":"index.mjs","sourcesContent":["/**\r\n * @lytjs/common-storage\r\n * 轻量级类型安全的存储工具\r\n */\r\n\r\ndeclare const __DEV__: boolean;\r\n\r\nexport interface StorageOptions<T> {\r\n key: string;\r\n storage?: Storage;\r\n serializer?: (value: T) => string;\r\n deserializer?: (value: string) => T;\r\n default?: T;\r\n}\r\n\r\nexport interface StorageAdapter<T> {\r\n get(): T;\r\n set(value: T): void;\r\n remove(): void;\r\n has(): boolean;\r\n onChange(callback: (value: T | null) => void): () => void;\r\n}\r\n\r\n/**\r\n * 安全的 JSON 解析,解析失败返回 fallback\r\n */\r\nexport function parseJSON<T>(value: string, fallback: T): T {\r\n try {\r\n return JSON.parse(value) as T;\r\n } catch {\r\n return fallback;\r\n }\r\n}\r\n\r\n/**\r\n * 检查存储是否可用(处理隐私模式等场景)\r\n */\r\nexport function isStorageAvailable(storage?: Storage): boolean {\r\n if (!storage) {\r\n try {\r\n storage = window.localStorage;\r\n } catch {\r\n return false;\r\n }\r\n }\r\n\r\n try {\r\n const testKey = '__lytjs_storage_test__';\r\n storage.setItem(testKey, '1');\r\n storage.removeItem(testKey);\r\n return true;\r\n } catch {\r\n return false;\r\n }\r\n}\r\n\r\n/**\r\n * 创建类型安全的存储适配器\r\n *\r\n * @param options - 存储配置\r\n * @returns StorageAdapter 实例\r\n */\r\nexport function createStorage<T>(options: StorageOptions<T>): StorageAdapter<T> {\r\n const {\r\n key,\r\n storage,\r\n serializer = JSON.stringify,\r\n deserializer = (v: string) => parseJSON<T>(v, options.default as T),\r\n default: defaultValue,\r\n } = options;\r\n\r\n const listeners = new Set<(value: T | null) => void>();\r\n\r\n const getStorage = (): Storage | undefined => {\r\n if (storage) return storage;\r\n if (typeof window !== 'undefined') {\r\n return window.localStorage;\r\n }\r\n return undefined;\r\n };\r\n\r\n const notifyListeners = (value: T | null) => {\r\n for (const listener of listeners) {\r\n try {\r\n listener(value);\r\n } catch {\r\n // Ignore listener errors\r\n }\r\n }\r\n };\r\n\r\n const handleStorageEvent = (event: StorageEvent) => {\r\n if (event.key === key) {\r\n const newValue = event.newValue\r\n ? deserializer(event.newValue)\r\n : null;\r\n notifyListeners(newValue);\r\n }\r\n };\r\n\r\n const adapter: StorageAdapter<T> = {\r\n get(): T {\r\n const s = getStorage();\r\n if (!s) return defaultValue as T;\r\n try {\r\n const raw = s.getItem(key);\r\n if (raw === null) return defaultValue as T;\r\n return deserializer(raw);\r\n } catch {\r\n return defaultValue as T;\r\n }\r\n },\r\n\r\n set(value: T): void {\r\n const s = getStorage();\r\n if (!s) return;\r\n try {\r\n s.setItem(key, serializer(value));\r\n notifyListeners(value);\r\n } catch {\r\n if (__DEV__) {\r\n console.warn(`[storage] Failed to set item \"${key}\"`);\r\n }\r\n }\r\n },\r\n\r\n remove(): void {\r\n const s = getStorage();\r\n if (!s) return;\r\n try {\r\n s.removeItem(key);\r\n notifyListeners(null);\r\n } catch {\r\n // Ignore\r\n }\r\n },\r\n\r\n has(): boolean {\r\n const s = getStorage();\r\n if (!s) return false;\r\n try {\r\n return s.getItem(key) !== null;\r\n } catch {\r\n return false;\r\n }\r\n },\r\n\r\n onChange(callback: (value: T | null) => void): () => void {\r\n listeners.add(callback);\r\n if (typeof window !== 'undefined' && listeners.size === 1) {\r\n window.addEventListener('storage', handleStorageEvent);\r\n }\r\n return () => {\r\n listeners.delete(callback);\r\n if (typeof window !== 'undefined' && listeners.size === 0) {\r\n window.removeEventListener('storage', handleStorageEvent);\r\n }\r\n };\r\n },\r\n };\r\n\r\n return adapter;\r\n}\r\n\r\n/**\r\n * 使用 sessionStorage 的快捷方式\r\n *\r\n * @param options - 存储配置(不含 storage 字段)\r\n * @returns StorageAdapter 实例\r\n */\r\nexport function createSessionStorage<T>(\r\n options: Omit<StorageOptions<T>, 'storage'>,\r\n): StorageAdapter<T> {\r\n const getSessionStorage = (): Storage | undefined => {\r\n if (typeof window !== 'undefined') {\r\n return window.sessionStorage;\r\n }\r\n return undefined;\r\n };\r\n\r\n return createStorage<T>({\r\n ...options,\r\n storage: getSessionStorage(),\r\n });\r\n}\r\n"]}
1
+ {"version":3,"sources":["../src/index.ts"],"names":[],"mappings":"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","file":"index.mjs","sourcesContent":["/**\n * @lytjs/common-storage\n * 轻量级类型安全的存储工具\n */\n\ndeclare const __DEV__: boolean;\n\nexport interface StorageOptions<T> {\n key: string;\n storage?: Storage;\n serializer?: (value: T) => string;\n deserializer?: (value: string) => T;\n default?: T;\n}\n\nexport interface StorageAdapter<T> {\n get(): T;\n set(value: T): void;\n remove(): void;\n has(): boolean;\n onChange(callback: (value: T | null) => void): () => void;\n}\n\n/**\n * 安全的 JSON 解析,解析失败返回 fallback\n */\nexport function parseJSON<T>(value: string, fallback: T): T {\n try {\n return JSON.parse(value) as T;\n } catch {\n return fallback;\n }\n}\n\n/**\n * 检查存储是否可用(处理隐私模式等场景)\n */\nexport function isStorageAvailable(storage?: Storage): boolean {\n if (!storage) {\n try {\n storage = window.localStorage;\n } catch {\n return false;\n }\n }\n\n try {\n const testKey = '__lytjs_storage_test__';\n storage.setItem(testKey, '1');\n storage.removeItem(testKey);\n return true;\n } catch {\n return false;\n }\n}\n\n/**\n * 创建类型安全的存储适配器\n *\n * @param options - 存储配置\n * @returns StorageAdapter 实例\n */\nexport function createStorage<T>(options: StorageOptions<T>): StorageAdapter<T> {\n const {\n key,\n storage,\n serializer = JSON.stringify,\n deserializer = (v: string) => parseJSON<T>(v, options.default as T),\n default: defaultValue,\n } = options;\n\n const listeners = new Set<(value: T | null) => void>();\n\n const getStorage = (): Storage | undefined => {\n if (storage) return storage;\n if (typeof window !== 'undefined') {\n return window.localStorage;\n }\n return undefined;\n };\n\n const notifyListeners = (value: T | null) => {\n for (const listener of listeners) {\n try {\n listener(value);\n } catch {\n // Ignore listener errors\n }\n }\n };\n\n const handleStorageEvent = (event: StorageEvent) => {\n if (event.key === key) {\n const newValue = event.newValue ? deserializer(event.newValue) : null;\n notifyListeners(newValue);\n }\n };\n\n const adapter: StorageAdapter<T> = {\n get(): T {\n const s = getStorage();\n if (!s) return defaultValue as T;\n try {\n const raw = s.getItem(key);\n if (raw === null) return defaultValue as T;\n return deserializer(raw);\n } catch {\n return defaultValue as T;\n }\n },\n\n set(value: T): void {\n const s = getStorage();\n if (!s) return;\n try {\n s.setItem(key, serializer(value));\n notifyListeners(value);\n } catch {\n if (__DEV__) {\n console.warn(`[storage] Failed to set item \"${key}\"`);\n }\n }\n },\n\n remove(): void {\n const s = getStorage();\n if (!s) return;\n try {\n s.removeItem(key);\n notifyListeners(null);\n } catch {\n // Ignore\n }\n },\n\n has(): boolean {\n const s = getStorage();\n if (!s) return false;\n try {\n return s.getItem(key) !== null;\n } catch {\n return false;\n }\n },\n\n onChange(callback: (value: T | null) => void): () => void {\n listeners.add(callback);\n if (typeof window !== 'undefined' && listeners.size === 1) {\n window.addEventListener('storage', handleStorageEvent);\n }\n return () => {\n listeners.delete(callback);\n if (typeof window !== 'undefined' && listeners.size === 0) {\n window.removeEventListener('storage', handleStorageEvent);\n }\n };\n },\n };\n\n return adapter;\n}\n\n/**\n * 使用 sessionStorage 的快捷方式\n *\n * @param options - 存储配置(不含 storage 字段)\n * @returns StorageAdapter 实例\n */\nexport function createSessionStorage<T>(\n options: Omit<StorageOptions<T>, 'storage'>,\n): StorageAdapter<T> {\n const getSessionStorage = (): Storage | undefined => {\n if (typeof window !== 'undefined') {\n return window.sessionStorage;\n }\n return undefined;\n };\n\n return createStorage<T>({\n ...options,\n storage: getSessionStorage(),\n });\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lytjs/common-storage",
3
- "version": "6.5.0",
3
+ "version": "6.6.0",
4
4
  "description": "Lightweight type-safe storage utilities for LytJS",
5
5
  "main": "./dist/index.cjs",
6
6
  "module": "./dist/index.mjs",
package/src/index.ts CHANGED
@@ -1,185 +1,183 @@
1
- /**
2
- * @lytjs/common-storage
3
- * 轻量级类型安全的存储工具
4
- */
5
-
6
- declare const __DEV__: boolean;
7
-
8
- export interface StorageOptions<T> {
9
- key: string;
10
- storage?: Storage;
11
- serializer?: (value: T) => string;
12
- deserializer?: (value: string) => T;
13
- default?: T;
14
- }
15
-
16
- export interface StorageAdapter<T> {
17
- get(): T;
18
- set(value: T): void;
19
- remove(): void;
20
- has(): boolean;
21
- onChange(callback: (value: T | null) => void): () => void;
22
- }
23
-
24
- /**
25
- * 安全的 JSON 解析,解析失败返回 fallback
26
- */
27
- export function parseJSON<T>(value: string, fallback: T): T {
28
- try {
29
- return JSON.parse(value) as T;
30
- } catch {
31
- return fallback;
32
- }
33
- }
34
-
35
- /**
36
- * 检查存储是否可用(处理隐私模式等场景)
37
- */
38
- export function isStorageAvailable(storage?: Storage): boolean {
39
- if (!storage) {
40
- try {
41
- storage = window.localStorage;
42
- } catch {
43
- return false;
44
- }
45
- }
46
-
47
- try {
48
- const testKey = '__lytjs_storage_test__';
49
- storage.setItem(testKey, '1');
50
- storage.removeItem(testKey);
51
- return true;
52
- } catch {
53
- return false;
54
- }
55
- }
56
-
57
- /**
58
- * 创建类型安全的存储适配器
59
- *
60
- * @param options - 存储配置
61
- * @returns StorageAdapter 实例
62
- */
63
- export function createStorage<T>(options: StorageOptions<T>): StorageAdapter<T> {
64
- const {
65
- key,
66
- storage,
67
- serializer = JSON.stringify,
68
- deserializer = (v: string) => parseJSON<T>(v, options.default as T),
69
- default: defaultValue,
70
- } = options;
71
-
72
- const listeners = new Set<(value: T | null) => void>();
73
-
74
- const getStorage = (): Storage | undefined => {
75
- if (storage) return storage;
76
- if (typeof window !== 'undefined') {
77
- return window.localStorage;
78
- }
79
- return undefined;
80
- };
81
-
82
- const notifyListeners = (value: T | null) => {
83
- for (const listener of listeners) {
84
- try {
85
- listener(value);
86
- } catch {
87
- // Ignore listener errors
88
- }
89
- }
90
- };
91
-
92
- const handleStorageEvent = (event: StorageEvent) => {
93
- if (event.key === key) {
94
- const newValue = event.newValue
95
- ? deserializer(event.newValue)
96
- : null;
97
- notifyListeners(newValue);
98
- }
99
- };
100
-
101
- const adapter: StorageAdapter<T> = {
102
- get(): T {
103
- const s = getStorage();
104
- if (!s) return defaultValue as T;
105
- try {
106
- const raw = s.getItem(key);
107
- if (raw === null) return defaultValue as T;
108
- return deserializer(raw);
109
- } catch {
110
- return defaultValue as T;
111
- }
112
- },
113
-
114
- set(value: T): void {
115
- const s = getStorage();
116
- if (!s) return;
117
- try {
118
- s.setItem(key, serializer(value));
119
- notifyListeners(value);
120
- } catch {
121
- if (__DEV__) {
122
- console.warn(`[storage] Failed to set item "${key}"`);
123
- }
124
- }
125
- },
126
-
127
- remove(): void {
128
- const s = getStorage();
129
- if (!s) return;
130
- try {
131
- s.removeItem(key);
132
- notifyListeners(null);
133
- } catch {
134
- // Ignore
135
- }
136
- },
137
-
138
- has(): boolean {
139
- const s = getStorage();
140
- if (!s) return false;
141
- try {
142
- return s.getItem(key) !== null;
143
- } catch {
144
- return false;
145
- }
146
- },
147
-
148
- onChange(callback: (value: T | null) => void): () => void {
149
- listeners.add(callback);
150
- if (typeof window !== 'undefined' && listeners.size === 1) {
151
- window.addEventListener('storage', handleStorageEvent);
152
- }
153
- return () => {
154
- listeners.delete(callback);
155
- if (typeof window !== 'undefined' && listeners.size === 0) {
156
- window.removeEventListener('storage', handleStorageEvent);
157
- }
158
- };
159
- },
160
- };
161
-
162
- return adapter;
163
- }
164
-
165
- /**
166
- * 使用 sessionStorage 的快捷方式
167
- *
168
- * @param options - 存储配置(不含 storage 字段)
169
- * @returns StorageAdapter 实例
170
- */
171
- export function createSessionStorage<T>(
172
- options: Omit<StorageOptions<T>, 'storage'>,
173
- ): StorageAdapter<T> {
174
- const getSessionStorage = (): Storage | undefined => {
175
- if (typeof window !== 'undefined') {
176
- return window.sessionStorage;
177
- }
178
- return undefined;
179
- };
180
-
181
- return createStorage<T>({
182
- ...options,
183
- storage: getSessionStorage(),
184
- });
185
- }
1
+ /**
2
+ * @lytjs/common-storage
3
+ * 轻量级类型安全的存储工具
4
+ */
5
+
6
+ declare const __DEV__: boolean;
7
+
8
+ export interface StorageOptions<T> {
9
+ key: string;
10
+ storage?: Storage;
11
+ serializer?: (value: T) => string;
12
+ deserializer?: (value: string) => T;
13
+ default?: T;
14
+ }
15
+
16
+ export interface StorageAdapter<T> {
17
+ get(): T;
18
+ set(value: T): void;
19
+ remove(): void;
20
+ has(): boolean;
21
+ onChange(callback: (value: T | null) => void): () => void;
22
+ }
23
+
24
+ /**
25
+ * 安全的 JSON 解析,解析失败返回 fallback
26
+ */
27
+ export function parseJSON<T>(value: string, fallback: T): T {
28
+ try {
29
+ return JSON.parse(value) as T;
30
+ } catch {
31
+ return fallback;
32
+ }
33
+ }
34
+
35
+ /**
36
+ * 检查存储是否可用(处理隐私模式等场景)
37
+ */
38
+ export function isStorageAvailable(storage?: Storage): boolean {
39
+ if (!storage) {
40
+ try {
41
+ storage = window.localStorage;
42
+ } catch {
43
+ return false;
44
+ }
45
+ }
46
+
47
+ try {
48
+ const testKey = '__lytjs_storage_test__';
49
+ storage.setItem(testKey, '1');
50
+ storage.removeItem(testKey);
51
+ return true;
52
+ } catch {
53
+ return false;
54
+ }
55
+ }
56
+
57
+ /**
58
+ * 创建类型安全的存储适配器
59
+ *
60
+ * @param options - 存储配置
61
+ * @returns StorageAdapter 实例
62
+ */
63
+ export function createStorage<T>(options: StorageOptions<T>): StorageAdapter<T> {
64
+ const {
65
+ key,
66
+ storage,
67
+ serializer = JSON.stringify,
68
+ deserializer = (v: string) => parseJSON<T>(v, options.default as T),
69
+ default: defaultValue,
70
+ } = options;
71
+
72
+ const listeners = new Set<(value: T | null) => void>();
73
+
74
+ const getStorage = (): Storage | undefined => {
75
+ if (storage) return storage;
76
+ if (typeof window !== 'undefined') {
77
+ return window.localStorage;
78
+ }
79
+ return undefined;
80
+ };
81
+
82
+ const notifyListeners = (value: T | null) => {
83
+ for (const listener of listeners) {
84
+ try {
85
+ listener(value);
86
+ } catch {
87
+ // Ignore listener errors
88
+ }
89
+ }
90
+ };
91
+
92
+ const handleStorageEvent = (event: StorageEvent) => {
93
+ if (event.key === key) {
94
+ const newValue = event.newValue ? deserializer(event.newValue) : null;
95
+ notifyListeners(newValue);
96
+ }
97
+ };
98
+
99
+ const adapter: StorageAdapter<T> = {
100
+ get(): T {
101
+ const s = getStorage();
102
+ if (!s) return defaultValue as T;
103
+ try {
104
+ const raw = s.getItem(key);
105
+ if (raw === null) return defaultValue as T;
106
+ return deserializer(raw);
107
+ } catch {
108
+ return defaultValue as T;
109
+ }
110
+ },
111
+
112
+ set(value: T): void {
113
+ const s = getStorage();
114
+ if (!s) return;
115
+ try {
116
+ s.setItem(key, serializer(value));
117
+ notifyListeners(value);
118
+ } catch {
119
+ if (__DEV__) {
120
+ console.warn(`[storage] Failed to set item "${key}"`);
121
+ }
122
+ }
123
+ },
124
+
125
+ remove(): void {
126
+ const s = getStorage();
127
+ if (!s) return;
128
+ try {
129
+ s.removeItem(key);
130
+ notifyListeners(null);
131
+ } catch {
132
+ // Ignore
133
+ }
134
+ },
135
+
136
+ has(): boolean {
137
+ const s = getStorage();
138
+ if (!s) return false;
139
+ try {
140
+ return s.getItem(key) !== null;
141
+ } catch {
142
+ return false;
143
+ }
144
+ },
145
+
146
+ onChange(callback: (value: T | null) => void): () => void {
147
+ listeners.add(callback);
148
+ if (typeof window !== 'undefined' && listeners.size === 1) {
149
+ window.addEventListener('storage', handleStorageEvent);
150
+ }
151
+ return () => {
152
+ listeners.delete(callback);
153
+ if (typeof window !== 'undefined' && listeners.size === 0) {
154
+ window.removeEventListener('storage', handleStorageEvent);
155
+ }
156
+ };
157
+ },
158
+ };
159
+
160
+ return adapter;
161
+ }
162
+
163
+ /**
164
+ * 使用 sessionStorage 的快捷方式
165
+ *
166
+ * @param options - 存储配置(不含 storage 字段)
167
+ * @returns StorageAdapter 实例
168
+ */
169
+ export function createSessionStorage<T>(
170
+ options: Omit<StorageOptions<T>, 'storage'>,
171
+ ): StorageAdapter<T> {
172
+ const getSessionStorage = (): Storage | undefined => {
173
+ if (typeof window !== 'undefined') {
174
+ return window.sessionStorage;
175
+ }
176
+ return undefined;
177
+ };
178
+
179
+ return createStorage<T>({
180
+ ...options,
181
+ storage: getSessionStorage(),
182
+ });
183
+ }