/* @alwatr/nitrobase-engine v7.5.0 */ "use strict"; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __hasOwnProp = Object.prototype.hasOwnProperty; var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); // src/main.ts var main_exports = {}; __export(main_exports, { AlwatrNitrobase: () => AlwatrNitrobase }); module.exports = __toCommonJS(main_exports); // src/alwatr-nitrobase.ts var import_nanolib2 = require("@alwatr/nanolib"); var import_exit_hook = require("@alwatr/nanolib/exit-hook"); var import_node_fs = require("@alwatr/nanolib/node-fs"); var import_nitrobase_helper = require("@alwatr/nitrobase-helper"); var import_nitrobase_reference = require("@alwatr/nitrobase-reference"); var import_nitrobase_types = require("@alwatr/nitrobase-types"); // src/logger.ts var import_nanolib = require("@alwatr/nanolib"); __dev_mode__: import_nanolib.packageTracer.add("@alwatr/nitrobase-engine", "7.5.0"); var logger = /* @__PURE__ */ (0, import_nanolib.createLogger)("@alwatr/nitrobase-engine"); // src/alwatr-nitrobase.ts __dev_mode__: logger.logFileModule?.("alwatr-nitrobase"); var _AlwatrNitrobase = class _AlwatrNitrobase { /** * Constructs an AlwatrNitrobase instance with the provided configuration. * * @param config The configuration of the AlwatrNitrobase engine. * @example * ```typescript * const alwatrStore = new AlwatrNitrobase({ * rootPath: './db', * saveDebounce: 40, * }); * ``` */ constructor(config) { this.config = config; /** * Keep all loaded nitrobase file context loaded in memory. */ this.cacheReferences__ = {}; var _a; logger.logMethodArgs?.("new", config); (_a = this.config).defaultChangeDebounce ?? (_a.defaultChangeDebounce = 40); this.rootDb__ = this.loadRootDb__(); (0, import_exit_hook.exitHook)(this.exitHook__.bind(this)); } /** * Checks if a nitrobase file with the given ID exists. * * @param storeId - The ID of the nitrobase file to check. * @returns `true` if the nitrobase file exists, `false` otherwise. * @example * ```typescript * if (!alwatrStore.hasStore('user1/profile')) { * alwatrStore.defineDocument(...) * } * ``` */ hasStore(storeId) { const id_ = (0, import_nitrobase_helper.getStoreId)(storeId); const exists = this.rootDb__.hasItem(id_); logger.logMethodFull?.("hasStore", id_, exists); return exists; } /** * Defines a new document with the given configuration and initial data. * If a document with the same ID already exists, an error is thrown. * * @param stat nitrobase file stat * @param initialData initial data for the document * @template TDoc document data type * @example * ```typescript * await alwatrStore.newDocument( * { * name: 'profile', * region: Region.PerUser, * ownerId: 'user1', * }, * { * name: 'Ali', * email: 'ali@alwatr.io', * } * ); * ``` */ newDocument(stat, initialData = null) { logger.logMethodArgs?.("newDocument", stat); return this.newStoreFile_( { ...stat, type: import_nitrobase_types.StoreFileType.Document }, initialData ); } /** * Defines a new collection with the given configuration and initial data. * If a collection with the same ID already exists, an error is thrown. * * @param stat nitrobase file stat * @param initialData initial data for the collection * @template TItem collection item data type * @example * ```typescript * await alwatrStore.newCollection( * { * name: 'orders', * region: Region.PerUser, * ownerId: 'user1', * } * ); * ``` */ newCollection(stat, initialData = null) { logger.logMethodArgs?.("newCollection", stat); return this.newStoreFile_( { ...stat, type: import_nitrobase_types.StoreFileType.Collection }, initialData ); } /** * Defines a AlwatrNitrobaseFile with the given configuration and initial data. * * @param stat nitrobase file stat * @param initialData initial data for the document * @template TDoc document data type */ newStoreFile_(stat, initialData = null) { logger.logMethodArgs?.("newStoreFile_", stat); stat.changeDebounce ?? (stat.changeDebounce = this.config.defaultChangeDebounce); let fileStoreRef; if (stat.type === import_nitrobase_types.StoreFileType.Document) { fileStoreRef = import_nitrobase_reference.DocumentReference.newRefFromData(stat, initialData, this.storeChanged_.bind(this)); } else if (stat.type === import_nitrobase_types.StoreFileType.Collection) { fileStoreRef = import_nitrobase_reference.CollectionReference.newRefFromData(stat, initialData, this.storeChanged_.bind(this)); } else { logger.accident("newStoreFile_", "store_file_type_not_supported", stat); throw new Error("store_file_type_not_supported", { cause: stat }); } if (this.rootDb__.hasItem(fileStoreRef.id)) { logger.accident("newStoreFile_", "store_file_already_defined", stat); throw new Error("store_file_already_defined", { cause: stat }); } this.rootDb__.addItem(fileStoreRef.id, stat); this.cacheReferences__[fileStoreRef.id] = fileStoreRef; this.storeChanged_(fileStoreRef); } /** * Open a document with the given id and create and return a DocumentReference. * If the document not exists or its not a document, an error is thrown. * * @template TDoc document data type * @param documentId document id {@link StoreFileId} * @returns document reference {@link DocumentReference} * @example * ```typescript * const userProfile = await alwatrStore.openDocument({ * name: 'user1/profile', * region: Region.PerUser, * ownerId: 'user1', * }); * userProfile.update({name: 'ali'}); * ``` */ async openDocument(documentId) { const id = (0, import_nitrobase_helper.getStoreId)(documentId); logger.logMethodArgs?.("openDocument", id); if (Object.hasOwn(this.cacheReferences__, id)) { const ref = this.cacheReferences__[id]; if (!(ref instanceof import_nitrobase_reference.DocumentReference)) { logger.accident("openDocument", "document_wrong_type", id); throw new Error("document_wrong_type", { cause: id }); } return this.cacheReferences__[id]; } if (!this.rootDb__.hasItem(id)) { logger.accident("openDocument", "document_not_found", id); throw new Error("document_not_found", { cause: id }); } const storeStat = this.rootDb__.getItemData(id); if (storeStat.type != import_nitrobase_types.StoreFileType.Document) { logger.accident("openDocument", "document_wrong_type", id); throw new Error("document_wrong_type", { cause: id }); } const context = await this.readContext__(storeStat); const docRef = import_nitrobase_reference.DocumentReference.newRefFromContext(context, this.storeChanged_.bind(this)); this.cacheReferences__[id] = docRef; return docRef; } /** * Open a collection with the given id and create and return a CollectionReference. * If the collection not exists or its not a collection, an error is thrown. * * @template TItem collection item data type * @param collectionId collection id {@link StoreFileId} * @returns collection reference {@link CollectionReference} * @example * ```typescript * const orders = await alwatrStore.openCollection({ * name: 'orders', * region: Region.PerUser, * ownerId: 'user1', * }); * orders.append({name: 'order 1'}); * ``` */ async openCollection(collectionId) { const id = (0, import_nitrobase_helper.getStoreId)(collectionId); logger.logMethodArgs?.("openCollection", id); if (Object.hasOwn(this.cacheReferences__, id)) { const ref = this.cacheReferences__[id]; if (!(ref instanceof import_nitrobase_reference.CollectionReference)) { logger.accident("openCollection", "collection_wrong_type", id); throw new Error("collection_wrong_type", { cause: id }); } return this.cacheReferences__[id]; } if (!this.rootDb__.hasItem(id)) { logger.accident("openCollection", "collection_not_found", id); throw new Error("collection_not_found", { cause: id }); } const storeStat = this.rootDb__.getItemData(id); if (storeStat.type != import_nitrobase_types.StoreFileType.Collection) { logger.accident("openCollection", "collection_wrong_type", id); throw new Error("collection_not_found", { cause: id }); } const context = await this.readContext__(storeStat); const colRef = import_nitrobase_reference.CollectionReference.newRefFromContext(context, this.storeChanged_.bind(this)); this.cacheReferences__[id] = colRef; return colRef; } /** * Unloads the nitrobase file with the given id from memory. * * @param storeId The unique identifier of the nitrobase file. {@link StoreFileId} * @example * ```typescript * alwatrStore.unloadStore({name: 'user-list', region: Region.Secret}); * alwatrStore.hasStore({name: 'user-list', region: Region.Secret}); // true * ``` */ unloadStore(storeId) { const id_ = (0, import_nitrobase_helper.getStoreId)(storeId); logger.logMethodArgs?.("unloadStore", id_); const ref = this.cacheReferences__[id_]; if (ref === void 0) return; if (ref.hasUnprocessedChanges_ === true) { ref.updateDelayed_ = false; this.storeChanged_(ref); } delete this.cacheReferences__[id_]; } /** * Remove document or collection from nitrobase and delete the file from disk. * If the file is not found, an error is thrown. * If the file is not unloaded, it will be unloaded first. * You don't need to await this method to complete unless you want to make sure the file is deleted on disk. * * @param storeId The ID of the file to delete. {@link StoreFileId} * @returns A Promise that resolves when the file is deleted. * @example * ```typescript * alwatrStore.removeStore({name: 'user-list', region: Region.Secret}); * alwatrStore.hasStore({name: 'user-list', region: Region.Secret}); // false * ``` */ async removeStore(storeId) { const id_ = (0, import_nitrobase_helper.getStoreId)(storeId); logger.logMethodArgs?.("removeStore", id_); if (!this.rootDb__.hasItem(id_)) { logger.accident("removeStore", "document_not_found", id_); throw new Error("document_not_found", { cause: id_ }); } const ref = this.cacheReferences__[id_]; if (ref !== void 0) { ref.freeze = true; ref.updateDelayed_ = false; ref.hasUnprocessedChanges_ = false; delete this.cacheReferences__[id_]; } const path = (0, import_nitrobase_helper.getStorePath)(this.rootDb__.getItemData(id_)); this.rootDb__.removeItem(id_); await import_nanolib2.delay.by(0); try { await (0, import_node_fs.unlink)((0, import_node_fs.resolve)(this.config.rootPath, path)); } catch (error) { logger.error("removeStore", "remove_file_failed", error, { id: storeId, path }); } } /** * Saves all changes in the nitrobase. * * @returns A Promise that resolves when all changes are saved. * @example * ```typescript * await alwatrStore.saveAll(); * ``` */ async saveAll() { logger.logMethod?.("saveAll"); for (const ref of Object.values(this.cacheReferences__)) { if (ref.hasUnprocessedChanges_ === true && ref.freeze !== true) { ref.updateDelayed_ = false; await this.storeChanged_(ref); } } } /** * Reads the context from a given path or StoreFileStat object. * * @param path The path or StoreFileStat object from which to read the context. * @returns A promise that resolves to the context object. */ async readContext__(path) { if (typeof path !== "string") path = (0, import_nitrobase_helper.getStorePath)(path); logger.logMethodArgs?.("readContext__", path); logger.time?.(`readContext__time(${path})`); const context = await (0, import_node_fs.readJson)((0, import_node_fs.resolve)(this.config.rootPath, path)); logger.timeEnd?.(`readContext__time(${path})`); return context; } /** * Writes the context to the specified path. * * @template T The type of the context. * @param path The path where the context will be written. * @param context The context to be written. * @param sync Indicates whether the write operation should be synchronous. * @returns A promise that resolves when the write operation is complete. */ writeContext__(path, context) { if (typeof path !== "string") path = (0, import_nitrobase_helper.getStorePath)(path); logger.logMethodArgs?.("writeContext__", path); return (0, import_node_fs.writeJson)((0, import_node_fs.resolve)(this.config.rootPath, path), context); } /** * Write nitrobase file context. * * @param from nitrobase file reference * @returns A promise that resolves when the write operation is complete. */ async storeChanged_(from) { logger.logMethodArgs?.("storeChanged__", from.id); const rev = from.getStoreMeta().rev; try { await this.writeContext__(from.path, from.getFullContext_()); if (rev === from.getStoreMeta().rev) { from.hasUnprocessedChanges_ = false; } } catch (error) { logger.error("storeChanged__", "write_context_failed", { id: from.id, error }); } } /** * Load storeFilesCollection or create new one. */ loadRootDb__() { logger.logMethod?.("loadRootDb__"); const fullPath = (0, import_node_fs.resolve)(this.config.rootPath, (0, import_nitrobase_helper.getStorePath)(_AlwatrNitrobase.rootDbStat__)); if (!(0, import_node_fs.existsSync)(fullPath)) { if (this.config.errorWhenNotInitialized === true) { throw new Error("store_not_found", { cause: "Nitrobase not initialized" }); } logger.banner("Initialize new alwatr-nitrobase"); return import_nitrobase_reference.CollectionReference.newRefFromData(_AlwatrNitrobase.rootDbStat__, null, this.storeChanged_.bind(this)); } const context = (0, import_node_fs.readJson)(fullPath, true); return import_nitrobase_reference.CollectionReference.newRefFromContext(context, this.storeChanged_.bind(this), "root-db"); } /** * Save all nitrobase files. */ exitHook__() { logger.logMethod?.("exitHook__"); for (const ref of Object.values(this.cacheReferences__)) { logger.logProperty?.(`StoreFile.${ref.id}.hasUnprocessedChanges`, ref.hasUnprocessedChanges_); if (ref.hasUnprocessedChanges_ === true && ref.freeze !== true) { logger.incident?.("exitHook__", "rescue_unsaved_context", { id: ref.id }); (0, import_node_fs.writeJson)((0, import_node_fs.resolve)(this.config.rootPath, ref.path), ref.getFullContext_(), true); ref.hasUnprocessedChanges_ = false; } } } /** * Get all nitrobase files. * * @returns all nitrobase files. * @example * ```typescript * const storeList = alwatrStore.getStoreList(); * for (const nitrobase of storeList) { * console.log(nitrobase.meta.id, nitrobase.data); * } */ getStoreList() { logger.logMethod?.("getStoreList"); return this.rootDb__.values(); } }; /** * The Alwatr Nitrobase version string. * * Use for nitrobase file format version for check compatibility. */ _AlwatrNitrobase.version = "7.5.0"; /** * The root nitrobase file stat. */ _AlwatrNitrobase.rootDbStat__ = { name: ".nitrobase", region: import_nitrobase_types.Region.Secret, type: import_nitrobase_types.StoreFileType.Collection, extension: import_nitrobase_types.StoreFileExtension.Json, changeDebounce: 40 }; var AlwatrNitrobase = _AlwatrNitrobase; // Annotate the CommonJS export names for ESM import in node: 0 && (module.exports = { AlwatrNitrobase }); /*! For license information please see main.cjs.LEGAL.txt */ //# sourceMappingURL=main.cjs.map