@onurege3467/zerohelper
Version:
ZeroHelper is a versatile high-performance utility library and database framework for Node.js, fully written in TypeScript.
209 lines (208 loc) • 7.79 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.JsonDatabase = 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"));
class JsonDatabase 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('JsonDB: "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);
await promises_1.default.mkdir(dir, { recursive: true });
const fileContent = await promises_1.default.readFile(this.filePath, 'utf-8');
this.db = JSON.parse(fileContent);
}
catch (error) {
if (error.code === 'ENOENT') {
this.db = {};
await this._saveNow();
}
else {
this.db = {};
}
}
}
_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, JSON.stringify(this.db, null, 2));
this.isDirty = false;
}
catch (error) {
console.error("JsonDB save error:", error);
}
}
flushSync() {
if (this.isDirty) {
try {
(0, fs_1.writeFileSync)(this.filePath, JSON.stringify(this.db, null, 2));
this.isDirty = false;
}
catch (error) { }
}
}
async ensureTable(table) {
await this.initPromise;
if (!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 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);
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;
this.db[table].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 initial = this.db[table].length;
this.db[table] = this.db[table].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 results = where && Object.keys(where).length > 0
? (this.db[table] || []).filter(row => Object.keys(where).every(k => String(row[k]) === String(where[k])))
: (this.db[table] || []);
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) {
const ex = await this.selectOne(table, where);
return ex ? this.update(table, data, where) : 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(() => {
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 increment(table, incs, where = {}) {
return this._execute('increment', table, async () => {
await this.ensureTable(table);
return this._queueRequest(() => {
let affected = 0;
this.db[table].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.JsonDatabase = JsonDatabase;
exports.default = JsonDatabase;