UNPKG

@trainersky/light-db

Version:

Fully async JSON-based lightweight database

233 lines (232 loc) 8.51 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || (function () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); Object.defineProperty(exports, "__esModule", { value: true }); const fs = __importStar(require("fs/promises")); const path = __importStar(require("path")); class Storage { constructor(dirPath, dbName) { this.cache = null; this.dbName = dbName; this.dir = path.resolve(dirPath); this.file = path.join(this.dir, `${dbName}.json`); this.init(); } async init() { try { await fs.mkdir(this.dir, { recursive: true }); await fs.access(this.file); } catch { await fs.writeFile(this.file, "{}", "utf-8"); } await this.refreshCache(); } async refreshCache() { this.cache = await this.readFile(); } async readFile() { try { const data = await fs.readFile(this.file, "utf-8"); return JSON.parse(data); } catch { return {}; } } async writeFile() { if (this.cache) { await fs.writeFile(this.file, JSON.stringify(this.cache, null, 2), "utf-8"); } } deepGet(obj, path, defaultValue) { return path.split(".").reduce((o, k) => (o && o[k] !== undefined ? o[k] : defaultValue), obj); } deepSet(obj, path, value) { const keys = path.split("."); let current = obj; for (let i = 0; i < keys.length - 1; i++) { if (!current[keys[i]] || typeof current[keys[i]] !== "object") { current[keys[i]] = {}; } current = current[keys[i]]; } current[keys[keys.length - 1]] = value; } async get(key, defaultValue = null) { if (!this.cache) await this.refreshCache(); return this.deepGet(this.cache, key, defaultValue); } async has(key) { if (!this.cache) await this.refreshCache(); return this.deepGet(this.cache, key, undefined) !== undefined; } async set(key, value, options = {}) { if (!this.cache) await this.refreshCache(); const existingValue = this.deepGet(this.cache, key, undefined); if (typeof existingValue === "number" && options.operation) { switch (options.operation) { case "+": this.deepSet(this.cache, key, existingValue + value); break; case "-": this.deepSet(this.cache, key, existingValue - value); break; case "*": this.deepSet(this.cache, key, existingValue * value); break; case "/": if (value === 0) throw new Error("Division by zero"); this.deepSet(this.cache, key, existingValue / value); break; case "%": if (value === 0) throw new Error("Modulo by zero"); this.deepSet(this.cache, key, existingValue % value); break; case "**": this.deepSet(this.cache, key, existingValue ** value); break; default: throw new Error(`Invalid operation "${options.operation}"`); } } else if (options.append && Array.isArray(existingValue) && Array.isArray(value)) { this.deepSet(this.cache, key, [...existingValue, ...value]); } else if (options.merge && typeof existingValue === "object" && typeof value === "object") { this.deepSet(this.cache, key, { ...existingValue, ...value }); } else { this.deepSet(this.cache, key, value); } await this.writeFile(); } async update(key, updater) { if (!this.cache) await this.refreshCache(); const existingValue = this.deepGet(this.cache, key, undefined); if (existingValue === undefined) return false; this.deepSet(this.cache, key, updater(existingValue)); await this.writeFile(); return true; } async toggle(key) { if (!this.cache) await this.refreshCache(); const currentValue = this.deepGet(this.cache, key, undefined); if (typeof currentValue !== "boolean") { throw new Error("Toggle only works on boolean values."); } this.deepSet(this.cache, key, !currentValue); await this.writeFile(); } async delete(key) { if (!this.cache) await this.refreshCache(); const keys = key.split("."); let obj = this.cache; for (let i = 0; i < keys.length - 1; i++) { if (!(keys[i] in obj)) return false; obj = obj[keys[i]]; } if (!(keys[keys.length - 1] in obj)) return false; delete obj[keys[keys.length - 1]]; await this.writeFile(); return true; } async keys() { if (!this.cache) await this.refreshCache(); return Object.keys(this.cache); } async values() { if (!this.cache) await this.refreshCache(); return Object.values(this.cache); } async merge(key, newData) { if (!this.cache) await this.refreshCache(); const existingValue = this.deepGet(this.cache, key, {}); if (typeof existingValue !== "object") { throw new Error("Merge target must be an object."); } this.deepSet(this.cache, key, { ...existingValue, ...newData }); await this.writeFile(); } async plus(key, amount = 1) { if (!this.cache) await this.refreshCache(); let existingValue = this.deepGet(this.cache, key, 0); if (typeof existingValue !== "number") { throw new Error(`Cannot perform addition on non-numeric value at key "${key}".`); } this.deepSet(this.cache, key, existingValue + amount); await this.writeFile(); } async minus(key, amount = 1) { await this.plus(key, -amount); } async multiple(key, factor = 1) { if (!this.cache) await this.refreshCache(); let existingValue = this.deepGet(this.cache, key, 1); if (typeof existingValue !== "number") { throw new Error(`Cannot perform multiplication on non-numeric value at key "${key}".`); } this.deepSet(this.cache, key, existingValue * factor); await this.writeFile(); } async divide(key, divisor = 1) { if (!this.cache) await this.refreshCache(); let existingValue = this.deepGet(this.cache, key, 1); if (typeof existingValue !== "number") { throw new Error(`Cannot perform division on non-numeric value at key "${key}".`); } if (divisor === 0) { throw new Error("Division by zero is not allowed."); } this.deepSet(this.cache, key, existingValue / divisor); await this.writeFile(); } } exports.default = Storage;