axiodb
Version:
The Pure JavaScript Alternative to SQLite. Embedded NoSQL database for Node.js with MongoDB-style queries, zero native dependencies, built-in InMemoryCache, and web GUI. Perfect for desktop apps, CLI tools, and embedded systems. No compilation, no platfor
264 lines • 14.1 kB
JavaScript
"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
/* eslint-disable @typescript-eslint/no-explicit-any */
const collection_operation_1 = __importDefault(require("../Collection/collection.operation"));
const FileManager_1 = __importDefault(require("../../engine/Filesystem/FileManager"));
const FolderManager_1 = __importDefault(require("../../engine/Filesystem/FolderManager"));
const path_1 = __importDefault(require("path"));
// Crypto for hashing
const Crypto_helper_1 = require("../../Helper/Crypto.helper");
const response_helper_1 = __importDefault(require("../../Helper/response.helper"));
const PathSanitizer_helper_1 = __importDefault(require("../../Helper/PathSanitizer.helper"));
const outers_1 = require("outers");
const Index_service_1 = require("../Index/Index.service");
/**
* Represents a database instance.
* This class provides methods to create, delete, and manage collections within a database.
*/
class Database {
constructor(name, path) {
this.name = name;
this.path = path;
this.fileManager = new FileManager_1.default();
this.folderManager = new FolderManager_1.default();
this.ResponseHelper = new response_helper_1.default();
}
/**
* Creates a new collection inside the specified database.
* @param {string} collectionName - Name of the collection.
* @param {boolean} crypto - Enable crypto for the collection.
* @param {string} key - Key for crypto.
* @returns {Promise<AxioDB>} - Returns the instance of AxioDB.
*/
createCollection(collectionName_1) {
return __awaiter(this, arguments, void 0, function* (collectionName, crypto = false, key) {
// Sanitize collection name to prevent directory traversal attacks
const sanitizedCollectionName = PathSanitizer_helper_1.default.sanitizePathComponent(collectionName);
// Check if the collection already exists
const collectionExists = yield this.folderManager.DirectoryExists(PathSanitizer_helper_1.default.safePath(this.path, sanitizedCollectionName));
const collectionPath = PathSanitizer_helper_1.default.safePath(this.path, sanitizedCollectionName);
const CollectionMeta = yield this.getCollectionMetaDetails(collectionName);
if (CollectionMeta) {
crypto = CollectionMeta.isEncrypted
? Boolean(CollectionMeta.isEncrypted)
: false;
key = CollectionMeta.encryptionKey ? CollectionMeta.encryptionKey : key;
}
// If the collection does not exist, create it
if (collectionExists.statusCode !== outers_1.StatusCodes.OK) {
yield this.folderManager.CreateDirectory(collectionPath);
console.log(`Collection Created: ${collectionPath}`);
}
// Create AutoIndex meta for the collection
const Index = new Index_service_1.IndexManager(collectionPath);
yield Index.generateIndexMeta();
// if crypto is enabled, hash the collection name
if (crypto === true) {
const newCryptoInstance = new Crypto_helper_1.CryptoHelper(key);
const collection = new collection_operation_1.default(collectionName, collectionPath, crypto, newCryptoInstance, key);
// Store collection metadata in the collectionMap
yield this.AddCollectionMetadata({
name: collectionName,
path: collectionPath,
encryptionKey: key === undefined ? "" : key,
isEncrypted: crypto === undefined ? false : crypto,
});
return collection;
}
else {
const collection = new collection_operation_1.default(collectionName, collectionPath);
// Store collection metadata in the collectionMap
yield this.AddCollectionMetadata({
name: collectionName,
path: collectionPath,
encryptionKey: key === undefined ? "" : key,
isEncrypted: crypto === undefined ? false : crypto,
});
return collection;
}
});
}
/**
* Checks if a collection exists in the database.
* @param {string} collectionName - Name of the collection to check.
* @returns {Promise<boolean>} - Returns true if the collection exists, false otherwise.
**/
isCollectionExists(collectionName) {
return __awaiter(this, void 0, void 0, function* () {
const collectionPath = path_1.default.join(this.path, collectionName);
const exists = yield this.folderManager.DirectoryExists(collectionPath);
return exists.statusCode === outers_1.StatusCodes.OK;
});
}
/**
* Deletes a collection from the database.
* @param {string} collectionName - Name of the collection to delete.
* @returns {Promise<void>} - Returns a promise.
* @throws {Error} - Throws an error if the collection does not exist.
*/
deleteCollection(collectionName) {
return __awaiter(this, void 0, void 0, function* () {
const collectionPath = path_1.default.join(this.path, collectionName);
const exists = yield this.folderManager.DirectoryExists(collectionPath);
if (exists.statusCode === outers_1.StatusCodes.OK) {
// Remove collection metadata
const status = yield this.dropCollectionMetadata(collectionName);
if (status && "statusCode" in status && status.statusCode !== 200) {
return status;
}
yield this.folderManager.DeleteDirectory(collectionPath);
return this.ResponseHelper.Success(`Collection: ${collectionName} deleted successfully`);
}
else {
return this.ResponseHelper.Error(`Collection: ${collectionName} does not exist`);
}
});
}
/**
* Lists all collections in the database.
* @returns {Promise<FinalCollectionsInfo>} - Returns a promise with the list of collections data.
* @throws {Error} - Throws an error if the database does not exist.
*/
getCollectionInfo() {
return __awaiter(this, void 0, void 0, function* () {
const collections = yield this.folderManager.ListDirectory(this.path);
// Remove All .meta related things
collections.data = collections.data.filter((collection) => !collection.endsWith(".meta"));
const totalSize = yield this.folderManager.GetDirectorySize(path_1.default.resolve(this.path));
// Get collection Status
const CollectionStatus = yield Promise.all(collections.data.map((collection) => this.getCollectionMetaDetails(collection)));
if ("data" in collections && "data" in totalSize) {
const FinalCollections = {
CurrentPath: this.path,
RootName: this.name,
MatrixUnits: "MB",
TotalCollections: `${collections.data.length} Collections`,
TotalSize: parseInt((totalSize.data / 1024 / 1024).toFixed(4)),
ListOfCollections: collections.data,
collectionMetaStatus: CollectionStatus,
AllCollectionsPaths: collections.data.map((collection) => path_1.default.join(this.path, collection)),
};
return this.ResponseHelper.Success(FinalCollections);
}
});
}
/**
* Removes the metadata entry for a collection from the collection metadata file.
*
* Reads the JSON file located at `${this.path}/collection.meta`, validates that the
* file exists and contains an array of collection metadata objects, removes any entry
* whose `name` matches the provided `collectionName`, and writes the updated array
* back to the same file.
*
* The method returns a SuccessInterface on successful removal (even if no matching
* collection was found) or an ErrorInterface describing the failure.
*
* @param collectionName - The name of the collection whose metadata should be removed.
* @returns A promise that resolves to SuccessInterface on success or ErrorInterface on failure.
*
* @remarks
* - If the metadata file does not exist, an ErrorInterface is returned.
* - If the metadata file cannot be parsed as a JSON array, an ErrorInterface is returned.
* - This method performs I/O using a FileManager instance and uses this.ResponseHelper
* to construct success/error responses. It does not throw; failures are reported via
* the returned ErrorInterface.
*
* @example
* // Remove the "users" collection metadata
* await db.dropCollectionMetadata("users");
*/
dropCollectionMetadata(collectionName) {
return __awaiter(this, void 0, void 0, function* () {
const FileManagement = new FileManager_1.default();
const isFileExist = yield FileManagement.FileExists(`${this.path}/collection.meta`);
if (isFileExist.status == false) {
return this.ResponseHelper.Error("Collection metadata file does not exist");
}
else {
const FullData = JSON.parse((yield FileManagement.ReadFile(`${this.path}/collection.meta`)).data);
if (!Array.isArray(FullData)) {
return this.ResponseHelper.Error("Invalid collection metadata format");
}
const UpdatedData = FullData.filter((data) => data.name !== collectionName);
yield FileManagement.WriteFile(`${this.path}/collection.meta`, JSON.stringify(UpdatedData));
return this.ResponseHelper.Success(`Collection metadata for ${collectionName} dropped successfully`);
}
});
}
/**
* Adds metadata for a collection to the collection metadata file.
*
* @param collectionData - The metadata of the collection to add
* @returns A Promise that resolves when the operation is complete, or rejects with an error if the collection metadata format is invalid
* @private
*
* This method performs the following operations:
* 1. Checks if the collection metadata file exists
* 2. If the file doesn't exist, creates it with the provided collection metadata
* 3. If the file exists, reads the existing metadata, adds the new collection metadata (if not already present), and writes back to the file
*
* @throws {Error} If the collection metadata format is invalid
*/
AddCollectionMetadata(collectionData) {
return __awaiter(this, void 0, void 0, function* () {
const FileManagement = new FileManager_1.default();
const isFileExist = yield FileManagement.FileExists(`${this.path}/collection.meta`);
if (isFileExist.status == false) {
yield FileManagement.WriteFile(`${this.path}/collection.meta`, JSON.stringify([collectionData]));
}
else {
const FullData = JSON.parse((yield FileManagement.ReadFile(`${this.path}/collection.meta`)).data);
if (!Array.isArray(FullData)) {
return new response_helper_1.default().Error("Invalid collection metadata format");
}
const isSameExist = FullData.filter((data) => data.name === collectionData.name);
if (isSameExist.length == 0) {
FullData.push(collectionData);
yield FileManagement.WriteFile(`${this.path}/collection.meta`, JSON.stringify(FullData));
}
}
});
}
/**
* Retrieves metadata details for a specific collection.
*
* @param collectionName - The name of the collection to retrieve metadata for
* @returns A Promise that resolves to the collection's metadata if found, or undefined if not found
* @private
*
* This method:
* 1. Checks if the collection.meta file exists
* 2. Reads and parses the metadata file if it exists
* 3. Validates that the data is an array
* 4. Finds and returns the metadata for the specified collection
*/
getCollectionMetaDetails(collectionName) {
return __awaiter(this, void 0, void 0, function* () {
const FileManagement = new FileManager_1.default();
const isFileExist = yield FileManagement.FileExists(`${this.path}/collection.meta`);
if (isFileExist.status == false) {
return undefined;
}
const FullData = JSON.parse((yield FileManagement.ReadFile(`${this.path}/collection.meta`)).data);
if (!Array.isArray(FullData)) {
return undefined;
}
const collectionMeta = FullData.find((data) => data.name === collectionName);
return collectionMeta;
});
}
}
exports.default = Database;
//# sourceMappingURL=database.operation.js.map