@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
JavaScript
"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;