UNPKG

@onurege3467/zerohelper

Version:

ZeroHelper is a versatile high-performance utility library and database framework for Node.js, fully written in TypeScript.

245 lines (244 loc) 9.02 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.ToonDatabase = void 0; const IDatabase_1 = require("./IDatabase"); const promises_1 = __importDefault(require("fs/promises")); const fs_1 = require("fs"); const path_1 = __importDefault(require("path")); const toon_1 = require("../functions/toon"); class ToonDatabase extends IDatabase_1.IDatabase { constructor(config) { super(); this.db = {}; this.isDirty = false; this.isWriting = false; this.writeQueue = []; this.saveDebounceTimeout = null; if (!config || !config.path) throw new Error('ToonDB: "path" gereklidir.'); this.filePath = config.path; this.saveInterval = config.saveInterval || 500; process.on('exit', () => this.flushSync()); this.initPromise = this._load(); } async _execute(op, table, fn) { const start = Date.now(); const res = await fn(); this.recordMetric(op, table, Date.now() - start); return res; } async _load() { try { const dir = path_1.default.dirname(this.filePath); if (!(0, fs_1.existsSync)(dir)) await promises_1.default.mkdir(dir, { recursive: true }); if (!(0, fs_1.existsSync)(this.filePath)) { this.db = {}; await this._saveNow(); return; } const content = await promises_1.default.readFile(this.filePath, 'utf-8'); const parsed = (0, toon_1.parse)(content); if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed)) { this.db = {}; } else { this.db = {}; for (const [key, value] of Object.entries(parsed)) { this.db[key] = Array.isArray(value) ? value : []; } } } catch (error) { console.error("ToonDB load error:", error); this.db = {}; } } _getTable(table) { const data = this.db[table]; return Array.isArray(data) ? data : []; } _queueRequest(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 item = this.writeQueue.shift(); if (item) { try { const result = item.operation(); this.isDirty = true; this._scheduleSave(); item.resolve(result); } catch (error) { item.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; if (this.saveDebounceTimeout) clearTimeout(this.saveDebounceTimeout); this.saveDebounceTimeout = null; try { await promises_1.default.writeFile(this.filePath, (0, toon_1.stringify)(this.db)); this.isDirty = false; } catch (error) { console.error("ToonDB save error:", error); } } flushSync() { if (this.isDirty) { try { (0, fs_1.writeFileSync)(this.filePath, (0, toon_1.stringify)(this.db)); this.isDirty = false; } catch (error) { } } } async ensureTable(table) { await this.initPromise; if (!Array.isArray(this.db[table])) { return this._queueRequest(() => { this.db[table] = []; }); } } async insert(table, data) { await this.runHooks('beforeInsert', table, data); return this._execute('insert', table, async () => { await this.ensureTable(table); return this._queueRequest(() => { const tableData = this._getTable(table); const maxId = tableData.reduce((max, row) => (row._id > max ? row._id : max), 0); const newId = maxId + 1; const newRow = { _id: newId, ...data }; this.db[table].push(newRow); this.runHooks('afterInsert', table, newRow); return newId; }); }); } async update(table, data, where) { await this.runHooks('beforeUpdate', table, { data, where }); return this._execute('update', table, async () => { await this.ensureTable(table); return this._queueRequest(() => { let affected = 0; const tableData = this._getTable(table); tableData.forEach(row => { if (Object.keys(where).every(k => String(row[k]) === String(where[k]))) { Object.assign(row, data); affected++; } }); this.runHooks('afterUpdate', table, { affected }); return affected; }); }); } async delete(table, where) { await this.runHooks('beforeDelete', table, where); return this._execute('delete', table, async () => { await this.ensureTable(table); return this._queueRequest(() => { const tableData = this._getTable(table); const initial = tableData.length; this.db[table] = tableData.filter(row => !Object.keys(where).every(k => String(row[k]) === String(where[k]))); const affected = initial - this.db[table].length; this.runHooks('afterDelete', table, { affected }); return affected; }); }); } async select(table, where = null) { return this._execute('select', table, async () => { await this.initPromise; const tableData = this._getTable(table); const results = where && Object.keys(where).length > 0 ? tableData.filter(row => Object.keys(where).every(k => String(row[k]) === String(where[k]))) : tableData; return JSON.parse(JSON.stringify(results)); }); } async selectOne(table, where = null) { const res = await this.select(table, where); return res[0] || null; } async set(table, data, where) { await this.initPromise; await this.ensureTable(table); const ex = await this.selectOne(table, where); if (ex) { // Kayıt varsa güncelle await this.update(table, data, where); return ex._id; } else { // Kayıt yoksa ekle return this.insert(table, { ...where, ...data }); } } async bulkInsert(table, dataArray) { return this._execute('bulkInsert', table, async () => { if (!dataArray.length) return 0; await this.ensureTable(table); return this._queueRequest(() => { const tableData = this._getTable(table); let maxId = tableData.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 increment(table, incs, where = {}) { return this._execute('increment', table, async () => { await this.ensureTable(table); return this._queueRequest(() => { let affected = 0; const tableData = this._getTable(table); tableData.forEach(row => { if (Object.keys(where).every(k => String(row[k]) === String(where[k]))) { for (const [f, v] of Object.entries(incs)) row[f] = (Number(row[f]) || 0) + v; affected++; } }); return affected; }); }); } async decrement(table, decs, where = {}) { const incs = {}; for (const k in decs) incs[k] = -decs[k]; return this.increment(table, incs, where); } async close() { await this._saveNow(); } } exports.ToonDatabase = ToonDatabase; exports.default = ToonDatabase;