UNPKG

@nsnanocat/util

Version:

Pure JS's util module for well-known iOS network tools

177 lines (171 loc) 6.81 kB
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 || []; } }