UNPKG

jsonion

Version:

A lightweight JSON file-based database with nested data access and manipulation capabilities.

251 lines 10.1 kB
import * as fs from "fs"; import * as path from "path"; import { validateJsonExtension, sanitizePath, checkFileExists, ensureDirectoryExists, } from "./utils.js"; import { dbError } from "./types.js"; import { db } from "./db.js"; class manager { constructor(baseDir) { this.allowedOperations = [ "create", "open", "delete", "rename", "list", ]; this.baseDir = baseDir; if (baseDir) { try { const resolvedBase = path.resolve(baseDir); if (!fs.existsSync(resolvedBase)) { fs.mkdirSync(resolvedBase, { recursive: true }); } else if (!fs.statSync(resolvedBase).isDirectory()) { throw new dbError("validation", `Base directory path is not a directory: ${baseDir}`); } } catch (error) { if (error instanceof dbError) { throw error; } throw new dbError("validation", `Failed to initialize base directory: ${baseDir}`, error); } } } validateOperation(operation) { if (!this.allowedOperations.includes(operation)) { throw new dbError("validation", `Operation '${operation}' is not allowed for HarmManager`); } } create(filePath, initialData) { const operation = "create"; this.validateOperation(operation); try { validateJsonExtension(filePath, operation); const resolvedPath = sanitizePath(filePath, this.baseDir, operation); checkFileExists(resolvedPath, false, operation); ensureDirectoryExists(resolvedPath, operation); const dataToWrite = initialData || {}; fs.writeFileSync(resolvedPath, JSON.stringify(dataToWrite, null, 2), "utf8"); return new db(resolvedPath, this.baseDir); } catch (error) { if (error instanceof dbError) { throw error; } throw new dbError(operation, `Failed to create database: ${filePath}`, error); } } open(filePath) { const operation = "open"; this.validateOperation(operation); try { validateJsonExtension(filePath, operation); const resolvedPath = sanitizePath(filePath, this.baseDir, operation); checkFileExists(resolvedPath, true, operation); const data = fs.readFileSync(resolvedPath, "utf8"); JSON.parse(data); return new db(resolvedPath, this.baseDir); } catch (error) { if (error instanceof dbError) { throw error; } if (error instanceof SyntaxError) { throw new dbError(operation, `Invalid JSON in database file: ${filePath}`, error); } throw new dbError(operation, `Failed to open database: ${filePath}`, error); } } delete(filePath) { const operation = "delete"; this.validateOperation(operation); try { validateJsonExtension(filePath, operation); const resolvedPath = sanitizePath(filePath, this.baseDir, operation); checkFileExists(resolvedPath, true, operation); fs.unlinkSync(resolvedPath); } catch (error) { if (error instanceof dbError) { throw error; } throw new dbError(operation, `Failed to delete database: ${filePath}`, error); } } rename(oldPath, newPath) { const operation = "rename"; this.validateOperation(operation); try { validateJsonExtension(oldPath, operation); validateJsonExtension(newPath, operation); const resolvedOldPath = sanitizePath(oldPath, this.baseDir, operation); const resolvedNewPath = sanitizePath(newPath, this.baseDir, operation); checkFileExists(resolvedOldPath, true, operation); checkFileExists(resolvedNewPath, false, operation); ensureDirectoryExists(resolvedNewPath, operation); fs.renameSync(resolvedOldPath, resolvedNewPath); } catch (error) { if (error instanceof dbError) { throw error; } throw new dbError(operation, `Failed to rename database from ${oldPath} to ${newPath}`, error); } } exists(filePath) { try { validateJsonExtension(filePath, "validation"); const resolvedPath = sanitizePath(filePath, this.baseDir, "validation"); return fs.existsSync(resolvedPath); } catch (error) { return false; } } list(directory = ".") { const operation = "list"; this.validateOperation(operation); try { const resolvedDir = sanitizePath(directory, this.baseDir, operation); if (!fs.existsSync(resolvedDir)) { throw new dbError(operation, `Directory does not exist: ${directory}`); } if (!fs.statSync(resolvedDir).isDirectory()) { throw new dbError(operation, `Path is not a directory: ${directory}`); } const files = fs.readdirSync(resolvedDir); return files.filter((file) => file.endsWith(".json")); } catch (error) { if (error instanceof dbError) { throw error; } throw new dbError(operation, `Failed to list databases in: ${directory}`, error); } } copy(sourcePath, destPath) { const operation = "create"; this.validateOperation(operation); try { validateJsonExtension(sourcePath, operation); validateJsonExtension(destPath, operation); const resolvedSource = sanitizePath(sourcePath, this.baseDir, operation); const resolvedDest = sanitizePath(destPath, this.baseDir, operation); checkFileExists(resolvedSource, true, operation); checkFileExists(resolvedDest, false, operation); ensureDirectoryExists(resolvedDest, operation); const data = fs.readFileSync(resolvedSource, "utf8"); JSON.parse(data); fs.writeFileSync(resolvedDest, data, "utf8"); } catch (error) { if (error instanceof dbError) { throw error; } throw new dbError(operation, `Failed to copy database from ${sourcePath} to ${destPath}`, error); } } info(filePath) { try { validateJsonExtension(filePath, "validation"); const resolvedPath = sanitizePath(filePath, this.baseDir, "validation"); checkFileExists(resolvedPath, true, "validation"); const stats = fs.statSync(resolvedPath); const data = JSON.parse(fs.readFileSync(resolvedPath, "utf8")); const entries = Object.keys(data).length; return { path: resolvedPath, size: stats.size, entries: entries, modified: stats.mtime, }; } catch (error) { if (error instanceof dbError) { throw error; } throw new dbError("validation", `Failed to get info for database: ${filePath}`, error); } } merge(sourcePaths, destPath) { const operation = "create"; this.validateOperation(operation); try { validateJsonExtension(destPath, operation); const resolvedDest = sanitizePath(destPath, this.baseDir, operation); let mergedData = {}; for (const sourcePath of sourcePaths) { validateJsonExtension(sourcePath, operation); const resolvedSource = sanitizePath(sourcePath, this.baseDir, operation); checkFileExists(resolvedSource, true, operation); const data = JSON.parse(fs.readFileSync(resolvedSource, "utf8")); mergedData = { ...mergedData, ...data }; } ensureDirectoryExists(resolvedDest, operation); fs.writeFileSync(resolvedDest, JSON.stringify(mergedData, null, 2), "utf8"); } catch (error) { if (error instanceof dbError) { throw error; } throw new dbError(operation, `Failed to merge databases into: ${destPath}`, error); } } listRecursive(directory = ".") { const operation = "list"; this.validateOperation(operation); try { const resolvedDir = sanitizePath(directory, this.baseDir, operation); if (!fs.existsSync(resolvedDir)) { throw new dbError(operation, `Directory does not exist: ${directory}`); } if (!fs.statSync(resolvedDir).isDirectory()) { throw new dbError(operation, `Path is not a directory: ${directory}`); } const results = []; const scanDirectory = (dir) => { const items = fs.readdirSync(dir); for (const item of items) { const fullPath = path.join(dir, item); const stat = fs.statSync(fullPath); if (stat.isDirectory()) { scanDirectory(fullPath); } else if (item.endsWith(".json")) { results.push(path.relative(resolvedDir, fullPath)); } } }; scanDirectory(resolvedDir); return results; } catch (error) { if (error instanceof dbError) { throw error; } throw new dbError(operation, `Failed to recursively list databases in: ${directory}`, error); } } } export { manager }; //# sourceMappingURL=manager.js.map