Files
windyChenUtils/storage.js
T

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();
}