jsonion
Version:
A lightweight JSON file-based database with nested data access and manipulation capabilities.
251 lines • 10.1 kB
JavaScript
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