UNPKG

asta.db

Version:

Enhanced database solution with QuickDB-like features

339 lines (300 loc) 8.39 kB
const fs = require("fs"); const path = require("path"); class AstaDB { constructor(options = {}) { this.dataPath = options.dataPath || "./astadb.json"; this.data = this.loadData(); } 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() { // إرجاع مصفوفة من الإدخالات { id, value } لتكون متوافقة مع QuickDB 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.all(); 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 this; } 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 this; } current = current[part]; } delete current[keys[keys.length - 1]]; this.saveData(); return this; } deleteAll() { this.data = {}; this.saveData(); return this; } 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`); } this.set(key, value + amount); return this; } subtract(key, amount = 1) { return this.add(key, -amount); } startsWith(prefix) { const results = {}; const search = (data, currentPath = "") => { for (const key in data) { const fullPath = currentPath ? `${currentPath}.${key}` : key; if (fullPath.startsWith(prefix)) { results[fullPath] = data[key]; } if (typeof data[key] === "object") { search(data[key], fullPath); } } }; search(this.data); 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; } // وظائف إضافية لتكون متوافقة تمامًا مع QuickDB table(name) { // هذه الوظيفة تحاكي سلوك الجداول في QuickDB return { set: (key, value) => this.set(`${name}.${key}`, value), get: (key, defaultValue) => this.get(`${name}.${key}`, defaultValue), delete: (key) => this.delete(`${name}.${key}`), all: () => this.all().filter((entry) => entry.id.startsWith(`${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) => this.startsWith(`${name}.${prefix}`), deleteAll: () => { const entries = this.all().filter((entry) => entry.id.startsWith(`${name}.`) ); entries.forEach((entry) => this.delete(entry.id)); return entries.length; }, }; } // دعم لـ Array.prototype methods 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; }, }; } } module.exports = AstaDB;