327 lines
8.7 KiB
JavaScript
327 lines
8.7 KiB
JavaScript
/** @type {Function} 日志输出 */
|
|
var log = console.warn;
|
|
|
|
/**
|
|
* 判断值是否为 String 类型
|
|
* @param {*} str - 待检测值
|
|
* @returns {boolean}
|
|
* @private
|
|
*/
|
|
var isString = function (str) {
|
|
return Object.getTypeString(str) === "String";
|
|
};
|
|
|
|
/**
|
|
* IndexedDB 存储实现(大容量存储优先选择)
|
|
*
|
|
* @class IDBStorage
|
|
* @example
|
|
* const storage = new IDBStorage('myDb', 'myStore', 1);
|
|
* await storage.set('key', { data: 123 });
|
|
* await storage.get('key', null);
|
|
*/
|
|
class IDBStorage {
|
|
/**
|
|
* 创建 IndexedDB 存储实例
|
|
* @param {string} [dbName='dbName'] - 数据库名称
|
|
* @param {string} [storeName='storeName'] - 对象存储名称
|
|
* @param {number} [version=1] - 数据库版本号
|
|
*/
|
|
constructor(dbName, storeName, version) {
|
|
/** @type {number} 数据库版本号 */
|
|
this.version = version || 1;
|
|
/** @type {string} 数据库名称 */
|
|
this.dbName = dbName || "dbName";
|
|
/** @type {string} 对象存储名称 */
|
|
this.storeName = storeName || "storeName";
|
|
|
|
var request = indexedDB.open(this.dbName, this.version);
|
|
/** @type {Promise<IDBDatabase>} 数据库连接 Promise */
|
|
this.requestPromise = new Promise(
|
|
function (resolve, reject) {
|
|
request.onupgradeneeded = function (e) {
|
|
if (
|
|
e &&
|
|
e.target &&
|
|
!request.result.objectStoreNames.contains(this.storeName)
|
|
) {
|
|
request.result
|
|
.createObjectStore(this.storeName, { keyPath: "key" })
|
|
.createIndex("key", "key", { unique: false });
|
|
}
|
|
log("[Index][open]-upgrade");
|
|
resolve(request.result);
|
|
}.bind(this);
|
|
request.onsuccess = function () {
|
|
log("[IndexDB][open]-success");
|
|
resolve(request.result);
|
|
};
|
|
request.onerror = function (e) {
|
|
log("[IndexDB][open]-error", 2);
|
|
reject(e);
|
|
};
|
|
}.bind(this),
|
|
);
|
|
}
|
|
|
|
/**
|
|
* 获取存储值
|
|
* @template T
|
|
* @param {string} key - 键名
|
|
* @param {T} initValue - 默认值(未找到时返回)
|
|
* @returns {Promise<T>} 存储的值或默认值
|
|
*/
|
|
get(key, initValue) {
|
|
var _this = this;
|
|
return new Promise(async function (resolve) {
|
|
var db = await _this.requestPromise;
|
|
var transaction = db.transaction(_this.storeName, "readonly");
|
|
var objectStore = transaction.objectStore(_this.storeName);
|
|
var req = objectStore.get(key);
|
|
req.onsuccess = function () {
|
|
resolve(req.result ? req.result.value : initValue || null);
|
|
};
|
|
req.onerror = function (e) {
|
|
log("[indexDB][get]-error", 2, e);
|
|
resolve(initValue || null);
|
|
};
|
|
});
|
|
}
|
|
|
|
/**
|
|
* 设置存储值
|
|
* @template T
|
|
* @param {string} key - 键名
|
|
* @param {T} value - 要存储的值
|
|
* @returns {Promise<T>} 返回写入的值
|
|
*/
|
|
set(key, value) {
|
|
var _this = this;
|
|
return new Promise(async function (resolve) {
|
|
var db = await _this.requestPromise;
|
|
var transaction = db.transaction(_this.storeName, "readwrite");
|
|
var objectStore = transaction.objectStore(_this.storeName);
|
|
var req = objectStore.put({ key: key, value: value });
|
|
req.onsuccess = function () {
|
|
resolve(value);
|
|
};
|
|
req.onerror = function (e) {
|
|
log("[indexDB][set]-error", 2, e);
|
|
resolve(value);
|
|
};
|
|
});
|
|
}
|
|
}
|
|
|
|
/**
|
|
* localStorage 存储实现(兼容性好,带内存缓存)
|
|
*
|
|
* @class ILocalStorage
|
|
* @example
|
|
* const storage = new ILocalStorage();
|
|
* await storage.set('key', { data: 456 });
|
|
* await storage.get('key', null);
|
|
*/
|
|
class ILocalStorage {
|
|
constructor() {
|
|
/** @type {Map<string, *>} 内存缓存 */
|
|
this.cache = new Map();
|
|
this.load();
|
|
}
|
|
|
|
/** 从 localStorage 全量加载到缓存 */
|
|
load() {
|
|
var _this = this;
|
|
Object.entries(localStorage).forEach(function (entry) {
|
|
var key = entry[0],
|
|
value = entry[1];
|
|
try {
|
|
_this.cache.set(key, JSON.parse(value));
|
|
} catch (error) {
|
|
log("[localStorage][load]-error", 2, error, key, value);
|
|
}
|
|
});
|
|
}
|
|
|
|
/**
|
|
* 从缓存获取值
|
|
* @template T
|
|
* @param {string} key - 键名
|
|
* @param {T} initValue - 默认值
|
|
* @returns {Promise<T>} 缓存的值或默认值
|
|
*/
|
|
get(key, initValue) {
|
|
return new Promise(
|
|
function (resolve) {
|
|
resolve(
|
|
this.cache.get(key) !== undefined ? this.cache.get(key) : initValue,
|
|
);
|
|
}.bind(this),
|
|
);
|
|
}
|
|
|
|
/**
|
|
* 写入 localStorage 并更新缓存
|
|
* @template T
|
|
* @param {string} key - 键名
|
|
* @param {T} value - 要存储的值
|
|
* @returns {Promise<T>} 返回写入的值
|
|
*/
|
|
set(key, value) {
|
|
return new Promise(
|
|
function (resolve) {
|
|
var _value = isString(value) ? value : "";
|
|
try {
|
|
_value = JSON.stringify(value);
|
|
} catch (e) {
|
|
log("[localStorage][set]-error", 2, e);
|
|
}
|
|
localStorage.setItem(key, _value);
|
|
this.cache.set(key, _value);
|
|
resolve(value);
|
|
}.bind(this),
|
|
);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 纯内存存储实现(无持久化,适用于所有环境兜底)
|
|
*
|
|
* @class MemoryStorage
|
|
* @example
|
|
* const storage = new MemoryStorage();
|
|
* await storage.set('key', { data: 789 });
|
|
* await storage.get('key', null);
|
|
*/
|
|
class MemoryStorage {
|
|
constructor() {
|
|
/** @type {Map<string, *>} 内存缓存 */
|
|
this.cache = new Map();
|
|
}
|
|
|
|
/**
|
|
* 从内存获取值
|
|
* @template T
|
|
* @param {string} key - 键名
|
|
* @param {T} initValue - 默认值
|
|
* @returns {Promise<T>} 缓存的值或默认值
|
|
*/
|
|
get(key, initValue) {
|
|
return Promise.resolve(
|
|
this.cache.has(key)
|
|
? this.cache.get(key)
|
|
: initValue !== undefined
|
|
? initValue
|
|
: null,
|
|
);
|
|
}
|
|
|
|
/**
|
|
* 写入内存
|
|
* @template T
|
|
* @param {string} key - 键名
|
|
* @param {T} value - 要存储的值
|
|
* @returns {Promise<T>} 返回写入的值
|
|
*/
|
|
set(key, value) {
|
|
this.cache.set(key, value);
|
|
return Promise.resolve(value);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Node.js SQLite 持久化存储实现
|
|
* 仅在 Node.js 环境下可用,浏览器/webpack 打包中为 null
|
|
*
|
|
* @class SqliteStorage
|
|
* @example
|
|
* const storage = new SqliteStorage('./data/storage.db');
|
|
* await storage.set('key', { data: 123 });
|
|
* await storage.get('key', null);
|
|
* @private
|
|
*/
|
|
var _SqliteStorageClass = function () {
|
|
var Database = require("better-sqlite3");
|
|
var pathModule = require("path");
|
|
var fs = require("fs");
|
|
|
|
return class SqliteStorage {
|
|
/**
|
|
* 创建 SQLite 存储实例
|
|
* @param {string} [dbPath='.windychen-storage/storage.db'] - 数据库文件路径
|
|
*/
|
|
constructor(dbPath) {
|
|
this.dbPath =
|
|
dbPath || pathModule.resolve(".windychen-storage", "storage.db");
|
|
var dir = pathModule.dirname(this.dbPath);
|
|
if (!fs.existsSync(dir)) {
|
|
fs.mkdirSync(dir, { recursive: true });
|
|
}
|
|
this.db = new Database(this.dbPath);
|
|
this.db.pragma("journal_mode = WAL");
|
|
this.db.exec(
|
|
"CREATE TABLE IF NOT EXISTS kv_store (key TEXT PRIMARY KEY, value TEXT)",
|
|
);
|
|
}
|
|
|
|
/**
|
|
* 获取存储值
|
|
* @template T
|
|
* @param {string} key - 键名
|
|
* @param {T} initValue - 默认值(未找到时返回)
|
|
* @returns {Promise<T>} 存储的值或默认值
|
|
*/
|
|
async get(key, initValue) {
|
|
var row = this.db
|
|
.prepare("SELECT value FROM kv_store WHERE key = ?")
|
|
.get(key);
|
|
if (row) {
|
|
try {
|
|
return JSON.parse(row.value);
|
|
} catch (e) {
|
|
log("[SqliteStorage][get]-parse-error", 2, e);
|
|
}
|
|
}
|
|
return initValue !== undefined ? initValue : null;
|
|
}
|
|
|
|
/**
|
|
* 设置存储值
|
|
* @template T
|
|
* @param {string} key - 键名
|
|
* @param {T} value - 要存储的值
|
|
* @returns {Promise<T>} 返回写入的值
|
|
*/
|
|
async set(key, value) {
|
|
var json = JSON.stringify(value);
|
|
this.db
|
|
.prepare("INSERT OR REPLACE INTO kv_store (key, value) VALUES (?, ?)")
|
|
.run(key, json);
|
|
return value;
|
|
}
|
|
};
|
|
};
|
|
|
|
var SqliteStorage = null;
|
|
try {
|
|
SqliteStorage = _SqliteStorageClass();
|
|
} catch (e) {
|
|
// better-sqlite3 不可用(浏览器 / webpack 打包环境),SqliteStorage 保持为 null
|
|
}
|
|
|
|
// ===== 环境检测与自动导出 =====
|
|
if (typeof window !== "undefined") {
|
|
// 浏览器环境:优先 IndexedDB → localStorage → 内存
|
|
window.IStorage = window.indexedDB
|
|
? IDBStorage
|
|
: window.localStorage
|
|
? ILocalStorage
|
|
: MemoryStorage;
|
|
if (typeof module !== "undefined") {
|
|
module.exports = new window.IStorage();
|
|
}
|
|
} else if (typeof module !== "undefined") {
|
|
// Node.js 环境:SQLite 存储 → 内存
|
|
module.exports = SqliteStorage ? new SqliteStorage() : new MemoryStorage();
|
|
}
|