UNPKG

flexi-db

Version:

A lightweight, flexible JSON-based database.

193 lines (171 loc) 6.07 kB
const fs = require('fs').promises; const path = require('path'); class FlexiDB { constructor(fileName = 'database.json', options = {}) { this.dataDir = path.resolve(options.dataDir || 'FlexiDB'); this.filePath = path.join(this.dataDir, fileName); this.autoBackupEnabled = false; if (this.autoBackupEnabled) { this.autoBackupInterval = setInterval(() => this.autoBackup(), 60000); } this.ready = this.init(); } async init() { await this.ensureDataDir(); await this.ensureFile(); } async ensureDataDir() { await fs.mkdir(this.dataDir, { recursive: true }); } async ensureFile() { const exists = await fs.access(this.filePath).then(() => true).catch(() => false); if (!exists) await fs.writeFile(this.filePath, '{}', 'utf8'); } async readData() { const data = await fs.readFile(this.filePath, 'utf8'); try { return JSON.parse(data); } catch { return {}; } } async writeData(data) { await fs.writeFile(this.filePath, JSON.stringify(data, null, 2), 'utf8'); } async set(key, value) { await this.ready; if (!key) throw new TypeError('Key is not defined!'); const data = await this.readData(); data[key] = value; await this.writeData(data); return value; } async get(key) { await this.ready; if (!key) throw new TypeError('Key is not defined!'); const data = await this.readData(); return data[key]; } async has(key) { await this.ready; if (!key) throw new TypeError('Key is not defined!'); const data = await this.readData(); return Object.prototype.hasOwnProperty.call(data, key); } async delete(key) { await this.ready; if (!key) throw new TypeError('Key is not defined!'); const data = await this.readData(); if (!Object.prototype.hasOwnProperty.call(data, key)) return false; delete data[key]; await this.writeData(data); return true; } async all(limit = 0) { await this.ready; const data = await this.readData(); const entries = Object.entries(data).map(([k, v]) => ({ data: k, value: v })); return limit > 0 ? entries.slice(0, limit) : entries; } async add(key, value) { await this.ready; if (!key) throw new TypeError('Key is not defined!'); if (isNaN(value)) throw new TypeError('Value must be a number!'); const data = await this.readData(); const current = Number(data[key] || 0); const newValue = current + value; data[key] = newValue; await this.writeData(data); return newValue; } async subtract(key, value) { await this.ready; if (!key) throw new TypeError('Key is not defined!'); if (isNaN(value)) throw new TypeError('Value must be a number!'); const data = await this.readData(); if (!Object.prototype.hasOwnProperty.call(data, key)) throw new TypeError('Key does not exist!'); const current = Number(data[key] || 0); const newValue = current - value; data[key] = newValue; await this.writeData(data); return newValue; } async math(key, operator, value) { await this.ready; if (!key) throw new TypeError('Key is not defined!'); if (!operator) throw new TypeError('Operator is not defined!'); if (isNaN(value)) throw new TypeError('Value must be a number!'); const data = await this.readData(); if (!Object.prototype.hasOwnProperty.call(data, key)) throw new TypeError('Key does not exist!'); const current = Number(data[key] || 0); let result; switch (operator) { case '+': result = current + value; break; case '-': result = current - value; break; case '*': result = current * value; break; case '/': if (value === 0) throw new TypeError('Cannot divide by zero!'); result = current / value; break; case '%': result = current % value; break; default: throw new TypeError('Invalid operator!'); } data[key] = result; await this.writeData(data); return result; } async push(key, value) { await this.ready; if (!key) throw new TypeError('Key is not defined!'); const data = await this.readData(); const arr = Array.isArray(data[key]) ? data[key] : []; arr.push(value); data[key] = arr; await this.writeData(data); return arr; } async backup(fileName) { await this.ready; if (!fileName) throw new TypeError('Filename is not defined!'); const backupPath = path.join(this.dataDir, `${fileName}.json`); const data = await this.readData(); await fs.writeFile(backupPath, JSON.stringify(data, null, 2), 'utf8'); return true; } async autoBackup() { if (!this.autoBackupEnabled) return; const timestamp = new Date().toISOString().replace(/[:.]/g, '-'); await this.backup(`backup-${timestamp}`); } async reset() { await this.ready; await this.writeData({}); } async destroy() { await this.ready; if (this.autoBackupEnabled) clearInterval(this.autoBackupInterval); } async transaction(operations) { await this.ready; const original = await this.readData(); const data = { ...original }; try { for (const op of operations) { if (op.type === 'set') data[op.key] = op.value; else if (op.type === 'delete') delete data[op.key]; else if (op.type === 'add') data[op.key] = Number(data[op.key] || 0) + op.value; else if (op.type === 'subtract') data[op.key] = Number(data[op.key] || 0) - op.value; else if (op.type === 'push') { const arr = Array.isArray(data[op.key]) ? data[op.key] : []; arr.push(op.value); data[op.key] = arr; } } await this.writeData(data); } catch (err) { await this.writeData(original); throw new Error(`Transaction failed: ${err.message}`); } } } module.exports = FlexiDB;