@lytjs/common-storage 6.0.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.cjs +133 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.mts +42 -0
- package/dist/index.d.ts +42 -0
- package/dist/index.mjs +128 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +36 -0
- package/src/index.ts +185 -0
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// src/index.ts
|
|
4
|
+
function parseJSON(value, fallback) {
|
|
5
|
+
try {
|
|
6
|
+
return JSON.parse(value);
|
|
7
|
+
} catch {
|
|
8
|
+
return fallback;
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
function isStorageAvailable(storage) {
|
|
12
|
+
if (!storage) {
|
|
13
|
+
try {
|
|
14
|
+
storage = window.localStorage;
|
|
15
|
+
} catch {
|
|
16
|
+
return false;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
try {
|
|
20
|
+
const testKey = "__lytjs_storage_test__";
|
|
21
|
+
storage.setItem(testKey, "1");
|
|
22
|
+
storage.removeItem(testKey);
|
|
23
|
+
return true;
|
|
24
|
+
} catch {
|
|
25
|
+
return false;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
function createStorage(options) {
|
|
29
|
+
const {
|
|
30
|
+
key,
|
|
31
|
+
storage,
|
|
32
|
+
serializer = JSON.stringify,
|
|
33
|
+
deserializer = (v) => parseJSON(v, options.default),
|
|
34
|
+
default: defaultValue
|
|
35
|
+
} = options;
|
|
36
|
+
const listeners = /* @__PURE__ */ new Set();
|
|
37
|
+
const getStorage = () => {
|
|
38
|
+
if (storage) return storage;
|
|
39
|
+
if (typeof window !== "undefined") {
|
|
40
|
+
return window.localStorage;
|
|
41
|
+
}
|
|
42
|
+
return void 0;
|
|
43
|
+
};
|
|
44
|
+
const notifyListeners = (value) => {
|
|
45
|
+
for (const listener of listeners) {
|
|
46
|
+
try {
|
|
47
|
+
listener(value);
|
|
48
|
+
} catch {
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
};
|
|
52
|
+
const handleStorageEvent = (event) => {
|
|
53
|
+
if (event.key === key) {
|
|
54
|
+
const newValue = event.newValue ? deserializer(event.newValue) : null;
|
|
55
|
+
notifyListeners(newValue);
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
const adapter = {
|
|
59
|
+
get() {
|
|
60
|
+
const s = getStorage();
|
|
61
|
+
if (!s) return defaultValue;
|
|
62
|
+
try {
|
|
63
|
+
const raw = s.getItem(key);
|
|
64
|
+
if (raw === null) return defaultValue;
|
|
65
|
+
return deserializer(raw);
|
|
66
|
+
} catch {
|
|
67
|
+
return defaultValue;
|
|
68
|
+
}
|
|
69
|
+
},
|
|
70
|
+
set(value) {
|
|
71
|
+
const s = getStorage();
|
|
72
|
+
if (!s) return;
|
|
73
|
+
try {
|
|
74
|
+
s.setItem(key, serializer(value));
|
|
75
|
+
notifyListeners(value);
|
|
76
|
+
} catch {
|
|
77
|
+
if (__DEV__) {
|
|
78
|
+
console.warn(`[storage] Failed to set item "${key}"`);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
},
|
|
82
|
+
remove() {
|
|
83
|
+
const s = getStorage();
|
|
84
|
+
if (!s) return;
|
|
85
|
+
try {
|
|
86
|
+
s.removeItem(key);
|
|
87
|
+
notifyListeners(null);
|
|
88
|
+
} catch {
|
|
89
|
+
}
|
|
90
|
+
},
|
|
91
|
+
has() {
|
|
92
|
+
const s = getStorage();
|
|
93
|
+
if (!s) return false;
|
|
94
|
+
try {
|
|
95
|
+
return s.getItem(key) !== null;
|
|
96
|
+
} catch {
|
|
97
|
+
return false;
|
|
98
|
+
}
|
|
99
|
+
},
|
|
100
|
+
onChange(callback) {
|
|
101
|
+
listeners.add(callback);
|
|
102
|
+
if (typeof window !== "undefined" && listeners.size === 1) {
|
|
103
|
+
window.addEventListener("storage", handleStorageEvent);
|
|
104
|
+
}
|
|
105
|
+
return () => {
|
|
106
|
+
listeners.delete(callback);
|
|
107
|
+
if (typeof window !== "undefined" && listeners.size === 0) {
|
|
108
|
+
window.removeEventListener("storage", handleStorageEvent);
|
|
109
|
+
}
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
};
|
|
113
|
+
return adapter;
|
|
114
|
+
}
|
|
115
|
+
function createSessionStorage(options) {
|
|
116
|
+
const getSessionStorage = () => {
|
|
117
|
+
if (typeof window !== "undefined") {
|
|
118
|
+
return window.sessionStorage;
|
|
119
|
+
}
|
|
120
|
+
return void 0;
|
|
121
|
+
};
|
|
122
|
+
return createStorage({
|
|
123
|
+
...options,
|
|
124
|
+
storage: getSessionStorage()
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
exports.createSessionStorage = createSessionStorage;
|
|
129
|
+
exports.createStorage = createStorage;
|
|
130
|
+
exports.isStorageAvailable = isStorageAvailable;
|
|
131
|
+
exports.parseJSON = parseJSON;
|
|
132
|
+
//# sourceMappingURL=index.cjs.map
|
|
133
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +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"]}
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @lytjs/common-storage
|
|
3
|
+
* 轻量级类型安全的存储工具
|
|
4
|
+
*/
|
|
5
|
+
interface StorageOptions<T> {
|
|
6
|
+
key: string;
|
|
7
|
+
storage?: Storage;
|
|
8
|
+
serializer?: (value: T) => string;
|
|
9
|
+
deserializer?: (value: string) => T;
|
|
10
|
+
default?: T;
|
|
11
|
+
}
|
|
12
|
+
interface StorageAdapter<T> {
|
|
13
|
+
get(): T;
|
|
14
|
+
set(value: T): void;
|
|
15
|
+
remove(): void;
|
|
16
|
+
has(): boolean;
|
|
17
|
+
onChange(callback: (value: T | null) => void): () => void;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* 安全的 JSON 解析,解析失败返回 fallback
|
|
21
|
+
*/
|
|
22
|
+
declare function parseJSON<T>(value: string, fallback: T): T;
|
|
23
|
+
/**
|
|
24
|
+
* 检查存储是否可用(处理隐私模式等场景)
|
|
25
|
+
*/
|
|
26
|
+
declare function isStorageAvailable(storage?: Storage): boolean;
|
|
27
|
+
/**
|
|
28
|
+
* 创建类型安全的存储适配器
|
|
29
|
+
*
|
|
30
|
+
* @param options - 存储配置
|
|
31
|
+
* @returns StorageAdapter 实例
|
|
32
|
+
*/
|
|
33
|
+
declare function createStorage<T>(options: StorageOptions<T>): StorageAdapter<T>;
|
|
34
|
+
/**
|
|
35
|
+
* 使用 sessionStorage 的快捷方式
|
|
36
|
+
*
|
|
37
|
+
* @param options - 存储配置(不含 storage 字段)
|
|
38
|
+
* @returns StorageAdapter 实例
|
|
39
|
+
*/
|
|
40
|
+
declare function createSessionStorage<T>(options: Omit<StorageOptions<T>, 'storage'>): StorageAdapter<T>;
|
|
41
|
+
|
|
42
|
+
export { type StorageAdapter, type StorageOptions, createSessionStorage, createStorage, isStorageAvailable, parseJSON };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @lytjs/common-storage
|
|
3
|
+
* 轻量级类型安全的存储工具
|
|
4
|
+
*/
|
|
5
|
+
interface StorageOptions<T> {
|
|
6
|
+
key: string;
|
|
7
|
+
storage?: Storage;
|
|
8
|
+
serializer?: (value: T) => string;
|
|
9
|
+
deserializer?: (value: string) => T;
|
|
10
|
+
default?: T;
|
|
11
|
+
}
|
|
12
|
+
interface StorageAdapter<T> {
|
|
13
|
+
get(): T;
|
|
14
|
+
set(value: T): void;
|
|
15
|
+
remove(): void;
|
|
16
|
+
has(): boolean;
|
|
17
|
+
onChange(callback: (value: T | null) => void): () => void;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* 安全的 JSON 解析,解析失败返回 fallback
|
|
21
|
+
*/
|
|
22
|
+
declare function parseJSON<T>(value: string, fallback: T): T;
|
|
23
|
+
/**
|
|
24
|
+
* 检查存储是否可用(处理隐私模式等场景)
|
|
25
|
+
*/
|
|
26
|
+
declare function isStorageAvailable(storage?: Storage): boolean;
|
|
27
|
+
/**
|
|
28
|
+
* 创建类型安全的存储适配器
|
|
29
|
+
*
|
|
30
|
+
* @param options - 存储配置
|
|
31
|
+
* @returns StorageAdapter 实例
|
|
32
|
+
*/
|
|
33
|
+
declare function createStorage<T>(options: StorageOptions<T>): StorageAdapter<T>;
|
|
34
|
+
/**
|
|
35
|
+
* 使用 sessionStorage 的快捷方式
|
|
36
|
+
*
|
|
37
|
+
* @param options - 存储配置(不含 storage 字段)
|
|
38
|
+
* @returns StorageAdapter 实例
|
|
39
|
+
*/
|
|
40
|
+
declare function createSessionStorage<T>(options: Omit<StorageOptions<T>, 'storage'>): StorageAdapter<T>;
|
|
41
|
+
|
|
42
|
+
export { type StorageAdapter, type StorageOptions, createSessionStorage, createStorage, isStorageAvailable, parseJSON };
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
// src/index.ts
|
|
2
|
+
function parseJSON(value, fallback) {
|
|
3
|
+
try {
|
|
4
|
+
return JSON.parse(value);
|
|
5
|
+
} catch {
|
|
6
|
+
return fallback;
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
function isStorageAvailable(storage) {
|
|
10
|
+
if (!storage) {
|
|
11
|
+
try {
|
|
12
|
+
storage = window.localStorage;
|
|
13
|
+
} catch {
|
|
14
|
+
return false;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
try {
|
|
18
|
+
const testKey = "__lytjs_storage_test__";
|
|
19
|
+
storage.setItem(testKey, "1");
|
|
20
|
+
storage.removeItem(testKey);
|
|
21
|
+
return true;
|
|
22
|
+
} catch {
|
|
23
|
+
return false;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
function createStorage(options) {
|
|
27
|
+
const {
|
|
28
|
+
key,
|
|
29
|
+
storage,
|
|
30
|
+
serializer = JSON.stringify,
|
|
31
|
+
deserializer = (v) => parseJSON(v, options.default),
|
|
32
|
+
default: defaultValue
|
|
33
|
+
} = options;
|
|
34
|
+
const listeners = /* @__PURE__ */ new Set();
|
|
35
|
+
const getStorage = () => {
|
|
36
|
+
if (storage) return storage;
|
|
37
|
+
if (typeof window !== "undefined") {
|
|
38
|
+
return window.localStorage;
|
|
39
|
+
}
|
|
40
|
+
return void 0;
|
|
41
|
+
};
|
|
42
|
+
const notifyListeners = (value) => {
|
|
43
|
+
for (const listener of listeners) {
|
|
44
|
+
try {
|
|
45
|
+
listener(value);
|
|
46
|
+
} catch {
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
const handleStorageEvent = (event) => {
|
|
51
|
+
if (event.key === key) {
|
|
52
|
+
const newValue = event.newValue ? deserializer(event.newValue) : null;
|
|
53
|
+
notifyListeners(newValue);
|
|
54
|
+
}
|
|
55
|
+
};
|
|
56
|
+
const adapter = {
|
|
57
|
+
get() {
|
|
58
|
+
const s = getStorage();
|
|
59
|
+
if (!s) return defaultValue;
|
|
60
|
+
try {
|
|
61
|
+
const raw = s.getItem(key);
|
|
62
|
+
if (raw === null) return defaultValue;
|
|
63
|
+
return deserializer(raw);
|
|
64
|
+
} catch {
|
|
65
|
+
return defaultValue;
|
|
66
|
+
}
|
|
67
|
+
},
|
|
68
|
+
set(value) {
|
|
69
|
+
const s = getStorage();
|
|
70
|
+
if (!s) return;
|
|
71
|
+
try {
|
|
72
|
+
s.setItem(key, serializer(value));
|
|
73
|
+
notifyListeners(value);
|
|
74
|
+
} catch {
|
|
75
|
+
if (__DEV__) {
|
|
76
|
+
console.warn(`[storage] Failed to set item "${key}"`);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
},
|
|
80
|
+
remove() {
|
|
81
|
+
const s = getStorage();
|
|
82
|
+
if (!s) return;
|
|
83
|
+
try {
|
|
84
|
+
s.removeItem(key);
|
|
85
|
+
notifyListeners(null);
|
|
86
|
+
} catch {
|
|
87
|
+
}
|
|
88
|
+
},
|
|
89
|
+
has() {
|
|
90
|
+
const s = getStorage();
|
|
91
|
+
if (!s) return false;
|
|
92
|
+
try {
|
|
93
|
+
return s.getItem(key) !== null;
|
|
94
|
+
} catch {
|
|
95
|
+
return false;
|
|
96
|
+
}
|
|
97
|
+
},
|
|
98
|
+
onChange(callback) {
|
|
99
|
+
listeners.add(callback);
|
|
100
|
+
if (typeof window !== "undefined" && listeners.size === 1) {
|
|
101
|
+
window.addEventListener("storage", handleStorageEvent);
|
|
102
|
+
}
|
|
103
|
+
return () => {
|
|
104
|
+
listeners.delete(callback);
|
|
105
|
+
if (typeof window !== "undefined" && listeners.size === 0) {
|
|
106
|
+
window.removeEventListener("storage", handleStorageEvent);
|
|
107
|
+
}
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
};
|
|
111
|
+
return adapter;
|
|
112
|
+
}
|
|
113
|
+
function createSessionStorage(options) {
|
|
114
|
+
const getSessionStorage = () => {
|
|
115
|
+
if (typeof window !== "undefined") {
|
|
116
|
+
return window.sessionStorage;
|
|
117
|
+
}
|
|
118
|
+
return void 0;
|
|
119
|
+
};
|
|
120
|
+
return createStorage({
|
|
121
|
+
...options,
|
|
122
|
+
storage: getSessionStorage()
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
export { createSessionStorage, createStorage, isStorageAvailable, parseJSON };
|
|
127
|
+
//# sourceMappingURL=index.mjs.map
|
|
128
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +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"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@lytjs/common-storage",
|
|
3
|
+
"version": "6.0.0",
|
|
4
|
+
"description": "Lightweight type-safe storage utilities for LytJS",
|
|
5
|
+
"main": "./dist/index.cjs",
|
|
6
|
+
"module": "./dist/index.mjs",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"import": "./dist/index.mjs",
|
|
12
|
+
"require": "./dist/index.cjs"
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
"files": [
|
|
16
|
+
"dist",
|
|
17
|
+
"src"
|
|
18
|
+
],
|
|
19
|
+
"scripts": {
|
|
20
|
+
"test": "vitest run",
|
|
21
|
+
"test:watch": "vitest",
|
|
22
|
+
"test:coverage": "vitest run --coverage",
|
|
23
|
+
"lint": "eslint \"src/**/*.ts\"",
|
|
24
|
+
"build": "tsup",
|
|
25
|
+
"type-check": "tsc --noEmit",
|
|
26
|
+
"clean": "rm -rf dist"
|
|
27
|
+
},
|
|
28
|
+
"sideEffects": false,
|
|
29
|
+
"license": "MIT",
|
|
30
|
+
"devDependencies": {
|
|
31
|
+
"jsdom": "^26.0.0",
|
|
32
|
+
"tsup": "^8.4.0",
|
|
33
|
+
"typescript": "^5.8.2",
|
|
34
|
+
"vitest": "^3.0.7"
|
|
35
|
+
}
|
|
36
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,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
|
|
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
|
+
}
|