nixabase
Version:
A lightweight and flexible database for JSON and TXT with memory caching and image compression
212 lines (196 loc) • 7.22 kB
JavaScript
const fs = require('fs').promises;
const path = require('path');
const sharp = require('sharp');
/**
* Nixabase - A lightweight database management system with file storage and caching capabilities.
*/
class Nixabase {
/**
* Creates a new Nixabase instance.
* @param {string} filePath - The path to the database file.
* @param {Object} [options={}] - Configuration options for the database.
* @param {string} [options.format='json'] - The format of the database file ('json' or 'txt').
* @param {Object} [options.performance={}] - Performance-related options.
*/
constructor(filePath, options = {}) {
this.filePath = filePath;
this.format = options.format || 'json';
this.performance = options.performance || {};
this.initialized = false;
this.memoryCache = new Map();
}
/**
* Initializes the database, creating the file if it doesn't exist.
* @returns {Promise<void>}
*/
async initialize() {
try {
await fs.access(this.filePath);
} catch (error) {
if (error.code === 'ENOENT') {
await this.writeData({});
} else {
throw error;
}
}
this.initialized = true;
}
/**
* Ensures the database is initialized before performing operations.
* @returns {Promise<void>}
*/
async ensureInitialized() {
if (!this.initialized) {
await this.initialize();
}
}
/**
* Reads the entire database content.
* @returns {Promise<Object>} The parsed database content.
*/
async read() {
await this.ensureInitialized();
const data = await fs.readFile(this.filePath, 'utf8');
return this.format === 'json' ? JSON.parse(data) : this.parseTxtData(data);
}
/**
* Saves a key-value pair to the database.
* @param {string} key - The key to save.
* @param {*} value - The value to save.
* @returns {Promise<void>}
*/
async save(key, value) {
await this.ensureInitialized();
const data = await this.read();
data[key] = value;
await this.writeData(data);
if (this.performance.memory && this.performance.memory.enabled) {
this.memoryCache.set(key, value);
if (this.memoryCache.size > this.performance.memory.maxItemCount) {
const oldestKey = this.memoryCache.keys().next().value;
this.memoryCache.delete(oldestKey);
}
}
}
/**
* Retrieves a value from the database by its key.
* @param {string} key - The key to retrieve.
* @returns {Promise<*>} The value associated with the key.
*/
async get(key) {
if (this.performance.memory && this.performance.memory.enabled && this.memoryCache.has(key)) {
return this.memoryCache.get(key);
}
const data = await this.read();
return data[key];
}
/**
* Updates an existing key-value pair in the database.
* @param {string} key - The key to update.
* @param {*} value - The new value.
* @returns {Promise<void>}
* @throws {Error} If the key doesn't exist in the database.
*/
async update(key, value) {
await this.ensureInitialized();
const data = await this.read();
if (key in data) {
data[key] = value;
await this.writeData(data);
if (this.performance.memory && this.performance.memory.enabled) {
this.memoryCache.set(key, value);
}
} else {
throw new Error(`Key "${key}" not found in the database.`);
}
}
/**
* Deletes a key-value pair from the database.
* @param {string} key - The key to delete.
* @returns {Promise<void>}
*/
async delete(key) {
await this.ensureInitialized();
const data = await this.read();
if (key in data) {
delete data[key];
await this.writeData(data);
if (this.performance.memory && this.performance.memory.enabled) {
this.memoryCache.delete(key);
}
}
}
/**
* Writes data to the database file.
* @param {Object} data - The data to write.
* @returns {Promise<void>}
*/
async writeData(data) {
if (this.format === 'json') {
await fs.writeFile(this.filePath, JSON.stringify(data, null, 2));
} else {
const txtData = Object.entries(data)
.map(([key, value]) => `${key}=${this.stringifyValue(value)}`)
.join('\n');
await fs.writeFile(this.filePath, txtData);
}
}
/**
* Converts a value to a string representation.
* @param {*} value - The value to stringify.
* @returns {string} The stringified value.
*/
stringifyValue(value) {
if (typeof value === 'object' && value !== null) {
return JSON.stringify(value);
}
return String(value);
}
/**
* Parses text data into an object.
* @param {string} data - The text data to parse.
* @returns {Object} The parsed object.
*/
parseTxtData(data) {
return data.split('\n').reduce((acc, line) => {
const [key, value] = line.split('=').map(item => item.trim());
if (key && value) {
try {
acc[key] = JSON.parse(value);
} catch (e) {
acc[key] = value;
}
}
return acc;
}, {});
}
/**
* Uploads a file to the database with optional image compression.
* @param {string} sourcePath - The path to the source file.
* @param {string} destinationKey - The key to store the file under in the database.
* @returns {Promise<void>}
*/
async uploadFile(sourcePath, destinationKey) {
await this.ensureInitialized();
let fileBuffer = await fs.readFile(sourcePath);
if (this.performance.imageCompression && this.performance.imageCompression.enabled) {
const { level, resize, quality } = this.performance.imageCompression;
fileBuffer = await sharp(fileBuffer)
.resize(resize)
.jpeg({ quality, progressive: true, force: false })
.png({ compressionLevel: level, progressive: true, force: false })
.toBuffer();
}
await this.save(destinationKey, fileBuffer.toString('base64'));
}
/**
* Retrieves a file from the database.
* @param {string} key - The key of the file in the database.
* @returns {Promise<Buffer|null>} The file buffer or null if not found.
*/
async getFile(key) {
const base64Data = await this.get(key);
return base64Data ? Buffer.from(base64Data, 'base64') : null;
}
}
module.exports = Nixabase;