-
Notifications
You must be signed in to change notification settings - Fork 6
Expand file tree
/
Copy pathgetStorage.mjs
More file actions
176 lines (171 loc) · 6.65 KB
/
getStorage.mjs
File metadata and controls
176 lines (171 loc) · 6.65 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
import "./lib/argument.mjs";
import { Console } from "./polyfill/Console.mjs";
import { Lodash as _ } from "./polyfill/Lodash.mjs";
import { Storage } from "./polyfill/Storage.mjs";
/**
* 存储配置读取与合并结果。
* Merged storage result object.
*
* @typedef {object} StorageProfile
* @property {Record<string, any>} Settings 运行设置 / Runtime settings.
* @property {Record<string, any>} Configs 静态配置 / Static configs.
* @property {Record<string, any>} Caches 缓存数据 / Runtime caches.
*/
/**
* 读取并合并默认配置、持久化配置与 `$argument`。
* Read and merge default config, persisted config and `$argument`.
*
* 注意:`Configs` 与 `Caches` 始终按每个 profile(`names`)合并;`Settings` 的合并顺序由 `$argument.Storage` 控制(支持别名)。
* Note: `Configs` and `Caches` are always merged per-profile (`names`); the merge order for `Settings` is controlled by `$argument.Storage` (aliases supported).
*
* 合并来源与顺序由 `$argument.Storage` 控制(支持以下值 / 别名):
* Merge source order is controlled by `$argument.Storage` (accepted values / aliases):
* - `undefined`: `database[name]` -> `$argument` -> `PersistentStore[name]`
* - `Argument` / `$argument`: `database[name]` -> `PersistentStore[name]` -> `$argument`
* - `PersistentStore` / `BoxJs` / `boxjs` / `$persistentStore`(默认):`database[name]` -> `PersistentStore[name]`
* - `database`: 仅 `database[name]`
*
* 注意:字符串比较为精确匹配(区分大小写)。
*
* @since 2.1.3
* @link https://github.com/NanoCat-Me/utils/blob/main/getStorage.mjs
* @author VirgilClyne
* @param {string} key 持久化主键 / Persistent store key.
* @param {string|string[]|Array<string|string[]>} names 目标配置名 / Target profile names.
* @param {Record<string, any>} database 默认数据库 / Default database object.
* @returns {StorageProfile}
*
* @module getStorage
* @default
*/
export default function getStorage(key, names, database) {
if (database?.Default?.Settings?.LogLevel) Console.logLevel = database.Default.Settings.LogLevel;
Console.debug("☑️ getStorage");
names = [names].flat(Number.POSITIVE_INFINITY);
/***************** Default *****************/
const Root = { Settings: database?.Default?.Settings || {}, Configs: database?.Default?.Configs || {}, Caches: {} };
Console.debug("Default", `Root.Settings类型: ${typeof Root.Settings}`, `Root.Settings: ${JSON.stringify(Root.Settings)}`);
/***************** PersistentStore *****************/
// 包装为局部变量,用完释放内存
// BoxJs 的清空操作返回假值空字符串, 逻辑或操作符会在左侧操作数为假值时返回右侧操作数。
const PersistentStore = Storage.getItem(key, {});
if (PersistentStore) {
Console.debug("☑️ PersistentStore", `PersistentStore类型: ${typeof PersistentStore}`, `PersistentStore内容: ${JSON.stringify(PersistentStore || {})}`);
names.forEach(name => {
if (typeof PersistentStore?.[name]?.Settings === "string") {
PersistentStore[name].Settings = JSON.parse(PersistentStore[name].Settings || "{}");
}
if (typeof PersistentStore?.[name]?.Caches === "string") {
PersistentStore[name].Caches = JSON.parse(PersistentStore[name].Caches || "{}");
}
});
if (PersistentStore.LogLevel) Console.logLevel = PersistentStore.LogLevel;
Console.debug("✅ PersistentStore", `Root.Settings类型: ${typeof Root.Settings}`, `Root.Settings: ${JSON.stringify(Root.Settings)}`);
}
/***************** Merge *****************/
names.forEach(name => {
_.merge(Root.Configs, database?.[name]?.Configs);
_.merge(Root.Caches, PersistentStore?.[name]?.Caches);
});
switch ($argument.Storage) {
case "Argument":
case "$argument":
names.forEach(name => {
_.merge(Root.Settings, database?.[name]?.Settings, PersistentStore?.[name]?.Settings);
});
_.merge(Root.Settings, $argument);
break;
default:
case "BoxJs":
case "boxjs":
case "PersistentStore":
case "$persistentStore":
names.forEach(name => {
_.merge(Root.Settings, database?.[name]?.Settings, PersistentStore?.[name]?.Settings);
});
break;
case "database":
names.forEach(name => {
_.merge(Root.Settings, database?.[name]?.Settings);
});
break;
case undefined:
names.forEach(name => {
_.merge(Root.Settings, database?.[name]?.Settings);
});
_.merge(Root.Settings, $argument);
names.forEach(name => {
_.merge(Root.Settings, PersistentStore?.[name]?.Settings);
});
break;
}
if (Root.Settings.LogLevel) Console.logLevel = Root.Settings.LogLevel;
Console.debug("✅ Merge", `Root.Settings类型: ${typeof Root.Settings}`, `Root.Settings: ${JSON.stringify(Root.Settings)}`);
/***************** traverseObject *****************/
traverseObject(Root.Settings, (key, value) => {
Console.debug("☑️ traverseObject", `${key}: ${typeof value}`, `${key}: ${JSON.stringify(value)}`);
switch (typeof value) {
case "string":
switch (value) {
case "true":
case "false":
case "[]":
value = JSON.parse(value); // 字符串转Boolean/空数组
break;
default:
if (value.includes(","))
value = value2array(value).map(item => string2number(item)); // 字符串转数组转数字
else value = string2number(value); // 字符串转数字
}
break;
}
return value;
});
Console.debug("✅ traverseObject", `Root.Settings类型: ${typeof Root.Settings}`, `Root.Settings: ${JSON.stringify(Root.Settings)}`);
Console.debug("✅ getStorage");
return Root;
}
/**
* 深度遍历对象并用回调替换叶子值。
* Deep-walk an object and replace leaf values using callback.
*
* @param {Record<string, any>} o 目标对象 / Target object.
* @param {(key: string, value: any) => any} c 处理回调 / Transformer callback.
* @returns {Record<string, any>}
*/
export function traverseObject(o, c) {
for (const t in o) {
const n = o[t];
o[t] = "object" === typeof n && null !== n ? traverseObject(n, c) : c(t, n);
}
return o;
}
/**
* 将纯数字字符串转换为数字。
* Convert integer-like string into number.
*
* @param {string} string 输入字符串 / Input string.
* @returns {string|number}
*/
export function string2number(string) {
if (/^\d+$/.test(string)) string = Number.parseInt(string, 10);
return string;
}
/**
* 将值包装为数组。
* Split value into array.
*
* @param {string|number|boolean|string[]|null|undefined} value 输入值 / Input value.
* @returns {(string|number|boolean)[]}
*/
export function value2array(value) {
switch (typeof value) {
case "string":
return value.split(",");
case "number":
case "boolean":
return [value];
default:
return value || [];
}
}