Comparing version 1.3.2 to 1.3.3
262
index.js
/*! | ||
* | ||
* Copyright(c) 2023 Mark Maher Ewida | ||
* Copyright(c) 2023 Mark Maher Ewida(Marco5dev) | ||
* MIT Licensed | ||
@@ -9,2 +9,3 @@ */ | ||
// ! NPM Packages | ||
const fs = require("fs").promises; | ||
@@ -16,5 +17,7 @@ const path = require("path"); | ||
const Fuse = require("fuse.js"); | ||
const colors = require("./lib/colors"); | ||
const ExcelJS = require("exceljs"); | ||
const json2xls = require("json2xls"); | ||
// ! Lib files | ||
const colors = require("./lib/colors"); | ||
const Schema = require("./lib/schema"); | ||
@@ -32,7 +35,7 @@ function formatDateTime(date) { | ||
const currentDate = formatDateTime(new Date()); | ||
const currentTime = currentDate.replace(/\D/g, ""); // Remove non-numeric characters | ||
const currentTime = currentDate.replace(/\D/g, ""); // *Remove non-numeric characters | ||
class jsonverse { | ||
constructor(options) { | ||
// Default configuration options | ||
// * Default configuration options | ||
const defaultOptions = { | ||
@@ -44,3 +47,3 @@ dataFolderPath: "./Data", | ||
// Merge the default options with the provided options | ||
// * Merge the default options with the provided options | ||
this.options = { ...defaultOptions, ...options }; | ||
@@ -55,2 +58,3 @@ | ||
this.cache = {}; | ||
this.data = {}; | ||
@@ -61,3 +65,3 @@ this.init(); | ||
async logToConsoleAndFile(message) { | ||
// Function to remove ANSI escape codes from a string | ||
// *Function to remove ANSI escape codes from a string | ||
function removeAnsiEscapeCodes(input) { | ||
@@ -67,3 +71,3 @@ return input.replace(/\x1b\[\d+m/g, ""); | ||
// Log to the file | ||
// *TODO: Log to the file | ||
const logFilePath = path.join(this.logFolderPath, "app.log"); | ||
@@ -125,3 +129,33 @@ try { | ||
// Encrypt sensitive data | ||
logWarning(message) { | ||
if (this.enableLogToConsoleAndFile) { | ||
this.logToConsoleAndFile( | ||
`${colors.bright}${colors.fg.yellow}[Warning]:${colors.reset} ${message}` | ||
); | ||
console.log( | ||
`${colors.bright}${colors.fg.yellow}[Warning]:${colors.reset} ${message}` | ||
); | ||
} else { | ||
console.log( | ||
`${colors.bright}${colors.fg.yellow}[Warning]:${colors.reset} ${message}` | ||
); | ||
} | ||
} | ||
logInfo(message) { | ||
if (this.enableLogToConsoleAndFile) { | ||
this.logToConsoleAndFile( | ||
`${colors.bright}${colors.fg.blue}[Info]:${colors.reset} ${message}` | ||
); | ||
console.log( | ||
`${colors.bright}${colors.fg.blue}[Info]:${colors.reset} ${message}` | ||
); | ||
} else { | ||
console.log( | ||
`${colors.bright}${colors.fg.blue}[Info]:${colors.reset} ${message}` | ||
); | ||
} | ||
} | ||
// TODO: Encrypt sensitive data | ||
encrypt(data, secretKey) { | ||
@@ -135,3 +169,3 @@ const encryptedData = crypto.AES.encrypt( | ||
// Decrypt sensitive data | ||
// TODO: Decrypt sensitive data | ||
decrypt(encryptedData, secretKey) { | ||
@@ -144,3 +178,3 @@ const decryptedData = crypto.AES.decrypt(encryptedData, secretKey).toString( | ||
// Create Data Backup | ||
// TODO: Create Data Backup | ||
async backupCreate(dataName) { | ||
@@ -186,3 +220,3 @@ const currentData = await this.readData(dataName); | ||
// Restore Data from Backup | ||
// TODO: Restore Data from Backup | ||
async backupRestore(dataName, backupFileName) { | ||
@@ -195,3 +229,3 @@ const backupFilePath = path.join( | ||
const backupData = await fs.readFile(backupFilePath, "utf8"); | ||
await this.writeDataByFileName(dataName, JSON.parse(backupData)); // Replace data in the file | ||
await this.writeDataByFileName(dataName, JSON.parse(backupData)); // * Replace data in the file | ||
this.logSuccess(`Data restored from backup: ${backupFileName}`); | ||
@@ -205,3 +239,3 @@ } catch (error) { | ||
// Cleanup Old Backups | ||
// TODO: Cleanup Old Backups | ||
async backupDelete(dataName, retentionDays) { | ||
@@ -216,3 +250,3 @@ const backupFiles = await this.getBackupFiles(dataName); | ||
const year = parseInt(backupDateStr.slice(0, 4)); | ||
const month = parseInt(backupDateStr.slice(4, 6)) - 1; // Months are 0-indexed | ||
const month = parseInt(backupDateStr.slice(4, 6)) - 1; // * Months are 0-indexed | ||
const day = parseInt(backupDateStr.slice(6, 8)); | ||
@@ -242,3 +276,3 @@ const hour = parseInt(backupDateStr.slice(8, 10)); | ||
// Get Backup Files | ||
// TODO: Get Backup Files | ||
async getBackupFiles(dataName) { | ||
@@ -250,3 +284,3 @@ const files = await fs.readdir(this.dataFolderPath + "/Backup"); | ||
// Initialize search index for a dataName | ||
// TODO: Initialize search index for a dataName | ||
initSearchIndex(dataName, options) { | ||
@@ -260,3 +294,3 @@ const data = this.readData(dataName); | ||
// Search data by query | ||
// TODO: Search data by query | ||
searchData(dataName, query) { | ||
@@ -275,16 +309,9 @@ if (this.searchIndex[dataName]) { | ||
} catch (error) { | ||
this.logError(`The path "${this.dataFolderPath}" doesn't exist.`); | ||
const answer = await this.askForConfirmation( | ||
`Do you want to create the path folder? (Y/N): ` | ||
); | ||
this.logInfo(`The path "${this.dataFolderPath}" doesn't exist.`); | ||
if (answer.toLowerCase() === "y" || answer.toLowerCase() === "yes") { | ||
try { | ||
await fs.mkdir(this.dataFolderPath, { recursive: true }); | ||
this.logSuccess(`Path folder created successfully.`); | ||
} catch (error) { | ||
this.logError(`Creating path folder: ${error}`); | ||
} | ||
} else { | ||
this.logError(`Path folder not created.`); | ||
try { | ||
await fs.mkdir(this.dataFolderPath, { recursive: true }); | ||
this.logSuccess(`Path folder created successfully.`); | ||
} catch (error) { | ||
this.logError(`Creating path folder: ${error}`); | ||
} | ||
@@ -298,16 +325,9 @@ } | ||
} catch (error) { | ||
this.logError(`The file "${filePath}" doesn't exist.`); | ||
const answer = await this.askForConfirmation( | ||
`${colors.bright}${colors.fg.yellow}[Question]:${colors.reset} Do you want to create the file? (Y/N): ` | ||
); | ||
this.logInfo(`The file "${filePath}" doesn't exist.`); | ||
if (answer.toLowerCase() === "y" || answer.toLowerCase() === "yes") { | ||
try { | ||
await fs.writeFile(filePath, "[]"); | ||
this.logSuccess(`File created successfully.`); | ||
} catch (error) { | ||
this.logError(`Creating file: ${error}`); | ||
} | ||
} else { | ||
this.logError(`File not created.`); | ||
try { | ||
await fs.writeFile(filePath, "[]"); | ||
this.logSuccess(`File created successfully.`); | ||
} catch (error) { | ||
this.logError(`Creating file: ${error}`); | ||
} | ||
@@ -317,16 +337,2 @@ } | ||
async askForConfirmation(question) { | ||
return new Promise((resolve) => { | ||
const readline = require("readline").createInterface({ | ||
input: process.stdin, | ||
output: process.stdout, | ||
}); | ||
readline.question(question, (answer) => { | ||
readline.close(); | ||
resolve(answer); | ||
}); | ||
}); | ||
} | ||
async randomID() { | ||
@@ -338,3 +344,3 @@ const uuid = randomUUID(); | ||
// Export Data | ||
// TODO: Export Data | ||
async exportData(dataName, format = "json") { | ||
@@ -344,3 +350,3 @@ const data = await this.readData(dataName); | ||
if (format === "json") { | ||
// Export as JSON | ||
// *Export as JSON | ||
const filePath = this.getFilePath(dataName + ".json"); | ||
@@ -350,3 +356,3 @@ await fs.writeFile(filePath, JSON.stringify(data, null, 2), "utf8"); | ||
} else if (format === "csv") { | ||
// Export as CSV | ||
// *Export as CSV | ||
const filePath = this.getFilePath(dataName + ".csv"); | ||
@@ -356,3 +362,3 @@ const csvData = data.map((item) => { | ||
id: item.id, | ||
...item, // Include other properties in CSV | ||
...item, // *Include other properties in CSV | ||
}; | ||
@@ -364,3 +370,3 @@ }); | ||
} else if (format === "xlsx") { | ||
// Export as XLSX using exceljs | ||
// *Export as XLSX using exceljs | ||
const filePath = this.getFilePath(dataName + ".xlsx"); | ||
@@ -371,6 +377,6 @@ await this.writeDataToXLSX(filePath, data); | ||
// Import Data | ||
// TODO: Import Data | ||
async importData(dataName, format = "json", filePath) { | ||
if (format === "json") { | ||
// Import JSON | ||
// * Import JSON | ||
const rawData = await fs.readFile(filePath, "utf8"); | ||
@@ -381,3 +387,3 @@ const newData = JSON.parse(rawData); | ||
} else if (format === "csv") { | ||
// Import CSV | ||
// * Import CSV | ||
const csvData = await this.readCSV(filePath); | ||
@@ -387,3 +393,3 @@ await this.writeDataByFileName(dataName, csvData); | ||
} else if (format === "xlsx") { | ||
// Import XLSX data using exceljs | ||
// * Import XLSX data using exceljs | ||
const xlsxData = await this.readXLSX(filePath); | ||
@@ -393,3 +399,3 @@ await this.writeDataByFileName(dataName, xlsxData); | ||
} else { | ||
// Handle unsupported file format | ||
// * Handle unsupported file format | ||
this.logError(`Unsupported file format: ${format}`); | ||
@@ -399,3 +405,3 @@ } | ||
// Data Transformation (if needed) | ||
// TODO: Data Transformation (if needed) | ||
async transformData(dataName, transformFunction) { | ||
@@ -408,3 +414,3 @@ const data = await this.readData(dataName); | ||
// Helper method to convert data to CSV | ||
// TODO: Helper method to convert data to CSV | ||
async convertToCSV(data) { | ||
@@ -416,3 +422,3 @@ const { Parser } = require("json2csv"); | ||
// Helper method to read CSV | ||
// TODO: Helper method to read CSV | ||
async readCSV(filePath) { | ||
@@ -429,3 +435,3 @@ const results = []; | ||
// Function to read data from an XLSX file using exceljs | ||
// TODO: Function to read data from an XLSX file using exceljs | ||
async readXLSX(filePath) { | ||
@@ -435,3 +441,3 @@ try { | ||
await workbook.xlsx.readFile(filePath); | ||
const worksheet = workbook.getWorksheet(1); // Assuming there's only one sheet | ||
const worksheet = workbook.getWorksheet(1); // * Assuming there's only one sheet | ||
@@ -453,3 +459,3 @@ const data = []; | ||
// Helper method to write data to an XLSX file using exceljs | ||
// TODO: Helper method to write data to an XLSX file using exceljs | ||
async writeDataToXLSX(filePath, data) { | ||
@@ -459,3 +465,3 @@ const workbook = new ExcelJS.Workbook(); | ||
// Add headers to the worksheet | ||
// * Add headers to the worksheet | ||
if (data.length > 0) { | ||
@@ -466,3 +472,3 @@ const headers = Object.keys(data[0]); | ||
// Add data rows to the worksheet | ||
// * Add data rows to the worksheet | ||
data.forEach((row) => { | ||
@@ -476,7 +482,7 @@ worksheet.addRow(Object.values(row)); | ||
// Export Data to XLSX | ||
// TODO: Export Data to XLSX | ||
async exportDataToXLSX(dataName) { | ||
const data = await this.readData(dataName); | ||
// Check if the data is empty | ||
// * Check if the data is empty | ||
if (data.length === 0) { | ||
@@ -487,10 +493,10 @@ this.logError(`No data to export in ${dataName}.`); | ||
// Create an XLSX object from the JSON data | ||
// * Create an XLSX object from the JSON data | ||
const xls = json2xls(data); | ||
// Specify the file path where the XLSX file will be saved | ||
// * Specify the file path where the XLSX file will be saved | ||
const filePath = this.getFilePath(dataName + ".xlsx"); | ||
try { | ||
// Save the XLSX data to the file | ||
// * Save the XLSX data to the file | ||
fs.writeFileSync(filePath, xls, "binary"); | ||
@@ -503,6 +509,6 @@ this.logSuccess(`Data exported to XLSX: ${filePath}`); | ||
// Function to convert XLS data to JSON | ||
// TODO: Function to convert XLS data to JSON | ||
async xlsToJSON(xlsData) { | ||
const wb = XLSX.read(xlsData, { type: "buffer" }); | ||
const ws = wb.Sheets[wb.SheetNames[0]]; // Assuming there's only one sheet | ||
const ws = wb.Sheets[wb.SheetNames[0]]; // * Assuming there's only one sheet | ||
return XLSX.utils.sheet_to_json(ws); | ||
@@ -512,3 +518,3 @@ } | ||
isCacheExpired(dataName) { | ||
const CACHE_EXPIRATION_TIME = 10 * 60 * 1000; // 10 minutes in milliseconds | ||
const CACHE_EXPIRATION_TIME = 10 * 60 * 1000; // * 10 minutes in milliseconds | ||
@@ -519,3 +525,3 @@ if ( | ||
) { | ||
// Cache has expired | ||
// * Cache has expired | ||
return true; | ||
@@ -534,3 +540,3 @@ } | ||
// Check if data is already in the cache and not expired | ||
// * Check if data is already in the cache and not expired | ||
if (this.cache[dataName] && !this.isCacheExpired(dataName)) { | ||
@@ -546,3 +552,3 @@ return this.cache[dataName].data; | ||
// Parse and cache the data with a timestamp | ||
// * Parse and cache the data with a timestamp | ||
const parsedData = JSON.parse(data); | ||
@@ -560,3 +566,3 @@ this.cache[dataName] = { | ||
}); | ||
// Retry reading the file | ||
// * Retry reading the file | ||
try { | ||
@@ -598,3 +604,3 @@ const newData = await fs.readFile(filePath, "utf8"); | ||
} | ||
// Invalidate the cache for this data name | ||
// * Invalidate the cache for this data name | ||
delete this.cache[dataName]; | ||
@@ -613,11 +619,11 @@ } | ||
async editByID(id, updatedData) { | ||
// Read existing data from the file | ||
// * Read existing data from the file | ||
const existingData = await this.findByID(id); | ||
if (existingData) { | ||
// Find the index of the item with the specified ID | ||
// * Find the index of the item with the specified ID | ||
const itemIndex = existingData.findIndex((item) => item.id === id); | ||
if (itemIndex !== -1) { | ||
// Update the existing data with the new data | ||
// * Update the existing data with the new data | ||
existingData[itemIndex] = { | ||
@@ -627,3 +633,3 @@ ...existingData[itemIndex], | ||
}; | ||
// Write the updated data back to the file | ||
// * Write the updated data back to the file | ||
await this.writeDataById(id, existingData); | ||
@@ -637,3 +643,3 @@ this.logSuccess(`Item with ID ${id} has been edited.`); | ||
// Add Data | ||
// TODO: Add Data | ||
async saveData(dataName, newData) { | ||
@@ -648,9 +654,9 @@ try { | ||
// Remove any existing data with the same ID | ||
// * Remove any existing data with the same ID | ||
const updatedData = existingData.filter((item) => item.id !== newId); | ||
// Add the new data with the same ID | ||
// * Add the new data with the same ID | ||
updatedData.push(newDataWithId); | ||
// Write the updated data to the file | ||
// * Write the updated data to the file | ||
await this.writeDataByFileName(dataName, updatedData); | ||
@@ -678,6 +684,3 @@ | ||
this.logSuccess(`Item has been deleted.`); | ||
this.logToConsoleAndFile( | ||
`${colors.bright}${colors.fg.blue}[Info]:${colors.reset} Deleted item:`, | ||
dataItemToDelete | ||
); | ||
this.logInfo(`Deleted item:`, dataItemToDelete); | ||
@@ -688,5 +691,3 @@ if (typeof window == "undefined") { | ||
} else { | ||
this.logToConsoleAndFile( | ||
`${colors.bright}${colors.fg.blue}[Info]:${colors.reset} Item is already deleted.` | ||
); | ||
this.logInfo(`Item is already deleted.`); | ||
} | ||
@@ -700,14 +701,14 @@ } catch (error) { | ||
try { | ||
// Read the data from the specified JSON file | ||
// * Read the data from the specified JSON file | ||
const data = await this.readData(dataName); | ||
if (data) { | ||
// Find the item with the specified ID | ||
// * Find the item with the specified ID | ||
const itemToDelete = data.find((item) => item.id === id); | ||
if (itemToDelete) { | ||
// Filter out the item with the matching ID | ||
// * Filter out the item with the matching ID | ||
const newData = data.filter((item) => item.id !== id); | ||
// Write the updated data back to the JSON file | ||
// * Write the updated data back to the JSON file | ||
await this.writeDataByFileName(dataName, newData); | ||
@@ -719,10 +720,6 @@ | ||
} else { | ||
this.logToConsoleAndFile( | ||
`${colors.bright}${colors.fg.blue}[Info]:${colors.reset} Item with ID ${id} not found in ${dataName}.json.` | ||
); | ||
this.logInfo(`Item with ID ${id} not found in ${dataName}.json.`); | ||
} | ||
} else { | ||
this.logToConsoleAndFile( | ||
`${colors.bright}${colors.fg.blue}[Info]:${colors.reset} No data found in ${dataName}.json.` | ||
); | ||
this.logInfo(`No data found in ${dataName}.json.`); | ||
} | ||
@@ -751,4 +748,41 @@ } catch (error) { | ||
} | ||
// TODO: model | ||
model(dataName, schema) { | ||
const filePath = this.getFilePath(dataName); | ||
return { | ||
save: async function (newData) { | ||
const validationErrors = schema.validate(newData); | ||
if (validationErrors) { | ||
return Promise.reject(validationErrors); | ||
} | ||
return this.saveData(dataName, newData); | ||
}.bind(this), // Bind the function to the current context | ||
delete: async function (id) { | ||
return this.delByDataName(dataName, id); | ||
}.bind(this), | ||
update: async function (id, updatedData) { | ||
return this.editByID(id, updatedData); | ||
}.bind(this), | ||
find: async function (id) { | ||
return this.findByID(id); | ||
}.bind(this), | ||
findAll: async function () { | ||
return this.readData(dataName); | ||
}.bind(this), | ||
// Implement other methods as needed | ||
}; | ||
} | ||
} | ||
module.exports = jsonverse; | ||
module.exports = { | ||
Schema, | ||
jsonverse, | ||
}; |
{ | ||
"name": "jsonverse", | ||
"version": "1.3.2", | ||
"version": "1.3.3", | ||
"description": "jsonVerse is a lightweight JSON-based database package for Node.js. It provides a simple interface to store, retrieve, and manage data using JSON files.", | ||
"main": "index.js", | ||
"scripts": { | ||
"test": "mocha ./test/jsonverse.test.js" | ||
"test:jsonverse": "mocha ./test/jsonverse.test.js", | ||
"test:schema": "mocha ./test/schema.test.js" | ||
}, | ||
@@ -9,0 +10,0 @@ "repository": { |
@@ -234,2 +234,2 @@ # jsonVerse | ||
The jsonVerse package simplifies the management of JSON data files within a specified folder. With the provided examples and usage instructions, you'll be able to efficiently integrate the jsonVerse package into your projects to streamline data operations. | ||
The jsonVerse package simplifies the management of JSON data files within a specified folder. With the provided examples and usage instructions, you'll be able to efficiently integrate the jsonVerse package into your projects to streamline data operations. |
33980
7
699
234