@trainersky/light-db
Version:
Fully async JSON-based lightweight database
233 lines (232 loc) • 8.51 kB
JavaScript
"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;