UNPKG

asta.db

Version:

Enhanced database solution with QuickDB-like features

404 lines (357 loc) 10 kB
const fs = require("fs"); const path = require("path"); class AstaDB { constructor(options = {}) { this.dataPath = options.dataPath || "./astadb.json"; this.data = this.loadData(); this.tables = new Map(); } loadData() { try { if (fs.existsSync(this.dataPath)) { const rawData = fs.readFileSync(this.dataPath, "utf8"); return JSON.parse(rawData) || {}; } } catch (error) { console.error("Error loading data:", error); } return {}; } saveData() { try { fs.writeFileSync( this.dataPath, JSON.stringify(this.data, null, 2), "utf8" ); } catch (error) { console.error("Error saving data:", error); } } setDataPath(newPath) { this.dataPath = newPath; this.data = this.loadData(); return this; } all() { const entries = []; const flatten = (obj, prefix = "") => { for (const key in obj) { const fullKey = prefix ? `${prefix}.${key}` : key; if ( typeof obj[key] === "object" && obj[key] !== null && !Array.isArray(obj[key]) ) { flatten(obj[key], fullKey); } else { entries.push({ id: fullKey, value: obj[key] }); } } }; flatten(this.data); return entries; } set(key, value) { const keys = key.split("."); let current = this.data; for (let i = 0; i < keys.length - 1; i++) { const part = keys[i]; if (!current[part] || typeof current[part] !== "object") { current[part] = {}; } current = current[part]; } current[keys[keys.length - 1]] = value; this.saveData(); return this; } get(key, defaultValue = null) { if (!key) return this.data; const keys = key.split("."); let current = this.data; for (const part of keys) { if (current[part] === undefined) { return defaultValue; } current = current[part]; } return current; } fetch(key, defaultValue = null) { return this.get(key, defaultValue); } push(key, value) { const array = this.get(key, []); if (!Array.isArray(array)) { throw new Error(`Value at ${key} is not an array`); } array.push(value); this.set(key, array); return array.length; } delete(key) { const keys = key.split("."); let current = this.data; for (let i = 0; i < keys.length - 1; i++) { const part = keys[i]; if (current[part] === undefined) { return false; } current = current[part]; } const lastKey = keys[keys.length - 1]; const exists = current[lastKey] !== undefined; delete current[lastKey]; this.saveData(); return exists; } deleteAll() { this.data = {}; this.saveData(); return true; } has(key) { return this.get(key) !== null; } add(key, amount = 1) { const value = this.get(key, 0); if (typeof value !== "number") { throw new Error(`Value at ${key} is not a number`); } const newValue = value + amount; this.set(key, newValue); return newValue; } subtract(key, amount = 1) { return this.add(key, -amount); } startsWith(prefix) { const results = []; const entries = this.all(); for (const entry of entries) { if (entry.id.startsWith(prefix)) { results.push(entry); } } return results; } count(key) { const value = this.get(key); if (Array.isArray(value)) { return value.length; } else if (typeof value === "object" && value !== null) { return Object.keys(value).length; } return 0; } ensure(key, defaultValue) { if (!this.has(key)) { this.set(key, defaultValue); } return this.get(key); } includes(key, value) { const data = this.get(key); if (!Array.isArray(data)) { throw new Error(`Value at ${key} is not an array`); } return data.includes(value); } filter(key, condition) { const data = this.get(key); if (!Array.isArray(data)) { throw new Error(`Value at ${key} is not an array`); } if (typeof condition === "function") { return data.filter(condition); } else if (typeof condition === "object") { return data.filter((item) => { for (const prop in condition) { if (item[prop] !== condition[prop]) { return false; } } return true; }); } throw new Error("Condition must be a function or object"); } find(key, condition) { const data = this.get(key); if (!Array.isArray(data)) { throw new Error(`Value at ${key} is not an array`); } if (typeof condition === "function") { return data.find(condition); } else if (typeof condition === "object") { return data.find((item) => { for (const prop in condition) { if (item[prop] !== condition[prop]) { return false; } } return true; }); } throw new Error("Condition must be a function or object"); } isArray(key) { return Array.isArray(this.get(key)); } ensureArray(key) { const value = this.get(key); if (!Array.isArray(value)) { this.set(key, []); return []; } return value; } remove(key, callback) { const data = this.get(key); if (!Array.isArray(data)) { throw new Error(`Value at ${key} is not an array`); } const removed = data.filter(callback); this.set( key, data.filter((item) => !callback(item)) ); return removed; } table(name) { if (this.tables.has(name)) { return this.tables.get(name); } const table = { set: (key, value) => this.set(`${name}.${key}`, value), get: (key, defaultValue) => this.get(`${name}.${key}`, defaultValue), fetch: (key, defaultValue) => this.get(`${name}.${key}`, defaultValue), delete: (key) => this.delete(`${name}.${key}`), all: () => { const allData = this.all(); return allData.filter((entry) => entry.id.startsWith(`${name}.`)) .map((entry) => ({ ...entry, id: entry.id.replace(`${name}.`, '') })); }, push: (key, value) => this.push(`${name}.${key}`, value), add: (key, amount) => this.add(`${name}.${key}`, amount), subtract: (key, amount) => this.subtract(`${name}.${key}`, amount), has: (key) => this.has(`${name}.${key}`), startsWith: (prefix) => { const results = this.startsWith(`${name}.${prefix}`); return results.map(entry => ({ ...entry, id: entry.id.replace(`${name}.`, '') })); }, deleteAll: () => { const entries = this.all().filter((entry) => entry.id.startsWith(`${name}.`) ); entries.forEach((entry) => this.delete(entry.id)); return entries.length; }, count: (key) => this.count(`${name}.${key}`), ensure: (key, defaultValue) => this.ensure(`${name}.${key}`, defaultValue), includes: (key, value) => this.includes(`${name}.${key}`, value), filter: (key, condition) => this.filter(`${name}.${key}`, condition), find: (key, condition) => this.find(`${name}.${key}`, condition), isArray: (key) => this.isArray(`${name}.${key}`), ensureArray: (key) => this.ensureArray(`${name}.${key}`), remove: (key, callback) => this.remove(`${name}.${key}`, callback), array: (key) => this.array(`${name}.${key}`) }; this.tables.set(name, table); return table; } array(key) { const array = this.get(key, []); if (!Array.isArray(array)) { throw new Error(`Value at ${key} is not an array`); } return { push: (...items) => { array.push(...items); this.set(key, array); return array.length; }, pop: () => { const item = array.pop(); this.set(key, array); return item; }, shift: () => { const item = array.shift(); this.set(key, array); return item; }, unshift: (...items) => { array.unshift(...items); this.set(key, array); return array.length; }, splice: (...args) => { const result = array.splice(...args); this.set(key, array); return result; }, slice: (...args) => array.slice(...args), find: (...args) => array.find(...args), filter: (...args) => array.filter(...args), map: (...args) => array.map(...args), reduce: (...args) => array.reduce(...args), forEach: (...args) => array.forEach(...args), includes: (...args) => array.includes(...args), indexOf: (...args) => array.indexOf(...args), join: (...args) => array.join(...args), length: array.length, get: () => array, set: (newArray) => { if (!Array.isArray(newArray)) { throw new Error("Value must be an array"); } this.set(key, newArray); return newArray; }, }; } // QuickDB compatibility aliases static create() { return new AstaDB(); } pull(key, callback) { return this.remove(key, callback); } math(key, operation, amount) { const value = this.get(key, 0); if (typeof value !== "number") { throw new Error(`Value at ${key} is not a number`); } let newValue; switch (operation) { case 'add': newValue = value + amount; break; case 'subtract': newValue = value - amount; break; case 'multiply': newValue = value * amount; break; case 'divide': newValue = value / amount; break; case 'mod': newValue = value % amount; break; default: throw new Error(`Invalid math operation: ${operation}`); } this.set(key, newValue); return newValue; } } module.exports = AstaDB;