UNPKG

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
"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