UNPKG

@onurege3467/zerohelper

Version:

ZeroHelper is a versatile JavaScript library offering helper functions and database utilities for developers. It supports MongoDB, MySQL, SQLite, Redis, and PostgreSQL.

222 lines (197 loc) 6.27 kB
const IDatabase = require('./IDatabase'); // Arayüzü import et /** * @implements {IDatabase} */ const fs = require('fs').promises; // Asenkron dosya işlemleri için const path = require('path'); class JsonDatabase extends IDatabase{ /** * @param {object} config - Yapılandırma nesnesi. * @param {string} config.filePath - JSON veritabanı dosyasının yolu. * @param {number} [config.saveInterval=500] - Değişiklikleri dosyaya yazma gecikmesi (ms). */ constructor(config) { super() if (!config || !config.filePath) { throw new Error('Yapılandırma içinde "filePath" belirtilmelidir.'); } this.filePath = config.filePath; this.db = {}; this.isDirty = false; this.isWriting = false; this.writeQueue = []; this.saveDebounceTimeout = null; this.saveInterval = config.saveInterval || 500; process.on('exit', () => this.flushSync()); this.initPromise = this._load(); } // --- ÖZEL (PRIVATE) METOTLAR --- async _load() { try { const dir = path.dirname(this.filePath); await fs.mkdir(dir, { recursive: true }); const fileContent = await fs.readFile(this.filePath, 'utf-8'); this.db = JSON.parse(fileContent); } catch (error) { if (error.code === 'ENOENT') { this.db = {}; await this._saveNow(); } else { console.error("Veritabanı dosyası okunurken hata:", error); this.db = {}; } } } _enqueueWrite(operation) { return new Promise((resolve, reject) => { this.writeQueue.push({ operation, resolve, reject }); this._processQueue(); }); } async _processQueue() { if (this.isWriting || this.writeQueue.length === 0) return; this.isWriting = true; const { operation, resolve, reject } = this.writeQueue.shift(); try { const result = operation(); this.isDirty = true; this._scheduleSave(); resolve(result); } catch (error) { reject(error); } finally { this.isWriting = false; this._processQueue(); } } _scheduleSave() { if (this.saveDebounceTimeout) clearTimeout(this.saveDebounceTimeout); this.saveDebounceTimeout = setTimeout(() => this._saveNow(), this.saveInterval); } async _saveNow() { if (!this.isDirty) return; clearTimeout(this.saveDebounceTimeout); this.saveDebounceTimeout = null; try { await fs.writeFile(this.filePath, JSON.stringify(this.db, null, 2)); this.isDirty = false; } catch (error) { console.error("Veritabanı dosyasına yazılırken hata:", error); } } flushSync() { if (this.isDirty) { console.log('Uygulama kapanıyor, bekleyen değişiklikler kaydediliyor...'); try { require('fs').writeFileSync(this.filePath, JSON.stringify(this.db, null, 2)); this.isDirty = false; } catch (error) { console.error('Senkron kaydetme sırasında hata:', error); } } } _matches(row, where) { return Object.keys(where).every(key => row[key] === where[key]); } // --- GENEL (PUBLIC) API --- async ensureTable(table) { await this.initPromise; if (!this.db[table]) { return this._enqueueWrite(() => { this.db[table] = []; }); } } async insert(table, data) { await this.ensureTable(table); return this._enqueueWrite(() => { const maxId = this.db[table].reduce((max, row) => (row.id > max ? row.id : max), 0); const newId = maxId + 1; const newRow = { id: newId, ...data }; this.db[table].push(newRow); return newId; }); } async update(table, data, where) { await this.ensureTable(table); return this._enqueueWrite(() => { let affectedRows = 0; this.db[table].forEach(row => { if (this._matches(row, where)) { Object.assign(row, data); affectedRows++; } }); return affectedRows; }); } async delete(table, where) { await this.ensureTable(table); return this._enqueueWrite(() => { const initialLength = this.db[table].length; this.db[table] = this.db[table].filter(row => !this._matches(row, where)); return initialLength - this.db[table].length; }); } async select(table, where = null) { await this.initPromise; const tableData = this.db[table] || []; let results = tableData; if (where && Object.keys(where).length > 0) { results = results.filter(row => this._matches(row, where)); } return JSON.parse(JSON.stringify(results)); } async set(table, data, where) { await this.ensureTable(table); const existing = await this.select(table, where); if (existing.length === 0) { return this.insert(table, { ...where, ...data }); } else { return this.update(table, data, where); } } async selectOne(table, where = null) { // Bu metot sadece okuma yaptığı için kuyruğa girmesine gerek yok. const results = await this.select(table, where); return results[0] || null; } async deleteOne(table, where) { await this.ensureTable(table); return this._enqueueWrite(() => { const index = this.db[table].findIndex(row => this._matches(row, where)); if (index > -1) { this.db[table].splice(index, 1); return 1; // 1 satır etkilendi } return 0; // Hiçbir satır etkilenmedi }); } async updateOne(table, data, where) { await this.ensureTable(table); return this._enqueueWrite(() => { const row = this.db[table].find(row => this._matches(row, where)); if (row) { Object.assign(row, data); return 1; // 1 satır etkilendi } return 0; // Hiçbir satır etkilenmedi }); } async bulkInsert(table, dataArray) { if (!Array.isArray(dataArray) || dataArray.length === 0) return 0; await this.ensureTable(table); return this._enqueueWrite(() => { let maxId = this.db[table].reduce((max, row) => (row.id > max ? row.id : max), 0); dataArray.forEach(data => { maxId++; this.db[table].push({ id: maxId, ...data }); }); return dataArray.length; }); } async close() { await this._saveNow(); } } module.exports = JsonDatabase;