UNPKG

@ayanaware/bento

Version:

Modular runtime framework designed to solve complex tasks

200 lines 8 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.Application = void 0; const fs_1 = require("fs"); const path = require("path"); const errors_1 = require("@ayanaware/errors"); const Bento_1 = require("../Bento"); const Globals_1 = require("../Globals"); const Entity_1 = require("../entities/interfaces/Entity"); const VariableFileLoader_1 = require("../plugins/cfg/VariableFileLoader"); const FSEntityLoader_1 = require("../plugins/loaders/FSEntityLoader"); /** @ignore */ const CALLER_LINE_REGEX = /(?:at (?:.+?\()|at )(?:file:\/\/\/)*(.+?):[0-9]+:[0-9]+/; /** * See constructor for more information * * Example usage: * * ```ts * const app = new Application(); * await app.start(); * await app.verify(); * ``` */ class Application { /** * Bento Application is a wrapper for common use case Bootstrap files * It abstracts away the use of built in plugins such as `FSEntityLoader` and `FSVariableLoader` * into an easy to digest Configuration Object. Get up and running even faster then before! * * You can override pretty much everything via the `ApplicationConfig` but here are the defaults: * * - Default Variables File: `../env.example.json` or `env.example.json` * - Variables File: `../env.json` or `./env.json` * - Plugins Directory: `./plugins` * - Components Directory: `./components` * * The above paths will only be used if they actually exist on the filesystem. * * Please note: The relative path is determined by the caller of `new Application();` * If you need a custom relative path / working directory you can pass it via * the 2nd argument. ex: `new Application({}, __dirname);` * * **The above is only relevant if you are using the defaults, if you specify paths in * config they must be absolute and won't be prefixed** * * @param cfg ApplicationConfig * @param directory Working directory * @param options Bento Options */ constructor(cfg, directory, options) { this.cfg = cfg || {}; this.directory = directory || this.getCallerDirectory(); // Register Application for `getApplication();` (0, Globals_1.useApplication)(this); this.bento = new Bento_1.Bento(options); // Force Register Bento for `getBento();`. Force to prevent any strangeness (0, Globals_1.useBento)(this.bento, true); // Name & Version if (this.cfg.name) this.bento.setProperty('APPLICATION_NAME', this.cfg.name); if (this.cfg.version) this.bento.setProperty('APPLICATION_VERSION', this.cfg.version); } getCallerDirectory() { let callerFile = null; try { const capture = {}; Error.captureStackTrace(capture); const splitStack = capture.stack.split('\n'); // Remove Error header splitStack.shift(); // 0: Call to getCallStack(), 1: Call to getCallerDirectory(), 2: Call to our caller const useLine = 2; let currentLine = 0; while (currentLine < useLine) { // If source mapping is enabled some lines with arrows will be added which need to be removed if (splitStack[1]?.trimLeft().startsWith('->')) { splitStack.shift(); } splitStack.shift(); currentLine++; } callerFile = CALLER_LINE_REGEX.exec(splitStack[0])?.[1]; } catch { // Ignore, maybe show a warning } /* This should never happen but it's handled just in case */ if (callerFile == null) return null; let lastIndex = callerFile.lastIndexOf(path.sep); if (lastIndex < 0) { // Stacktraces in ESM always use `/`, so this is a fix for Windows users: lastIndex = callerFile.lastIndexOf('/'); if (lastIndex < 0) return null; } return callerFile.slice(0, lastIndex); } async exists(location) { if (Array.isArray(location)) location = path.resolve(...location); try { await fs_1.promises.access(location, fs_1.constants.F_OK); return true; } catch (e) { return false; } } /** * See JSDoc documentation on constructor for more information. * * **Don't forget to call `Application.verify();` after this** */ async start() { // add required plugins if needed if (!this.bento.entities.hasEntity(VariableFileLoader_1.VariableFileLoader)) { const newVfl = new VariableFileLoader_1.VariableFileLoader(); await this.bento.addPlugin(newVfl); } if (!this.bento.entities.hasEntity(FSEntityLoader_1.FSEntityLoader)) { const newFsel = new FSEntityLoader_1.FSEntityLoader(); await this.bento.addPlugin(newFsel); } const throwDirectoryError = () => { throw new errors_1.IllegalStateError('Directory not defined and failed to infer via stack.'); }; // Default Variables let defaults = this.cfg.defaults; if (!Array.isArray(defaults)) { if (!this.directory) throwDirectoryError(); defaults = []; const defs = [[this.directory, '..', 'env.example.json'], [this.directory, 'env.example.json']]; for (const def of defs) { if (await this.exists(def)) defaults.push(def); } } // Variables let variables = this.cfg.variables; if (!Array.isArray(variables)) { if (!this.directory) throwDirectoryError(); variables = []; const defs = [[this.directory, '..', 'env.json'], [this.directory, 'env.json']]; for (const def of defs) { if (await this.exists(def)) variables.push(def); } } // Plugins let plugins = this.cfg.plugins; if (!Array.isArray(plugins)) { if (!this.directory) throwDirectoryError(); plugins = []; const defs = [[this.directory, 'plugins']]; for (const def of defs) { if (await this.exists(def)) plugins.push(def); } } // Components let components = this.cfg.components; if (!Array.isArray(components)) { if (!this.directory) throwDirectoryError(); components = []; const defs = [[this.directory, 'components']]; for (const def of defs) { if (await this.exists(def)) components.push(def); } } const vfl = this.bento.entities.getPlugin(VariableFileLoader_1.VariableFileLoader); await vfl.addFiles(defaults, true); await vfl.addFiles(variables, false); const fsel = this.bento.entities.getPlugin(FSEntityLoader_1.FSEntityLoader); await fsel.addDirectories(plugins, Entity_1.EntityType.PLUGIN); await fsel.addDirectories(components, Entity_1.EntityType.COMPONENT); } /** * **Always call this**. It does sanity checks and makes sure your applicaton is not in a broken state. * * @returns ApplicationState */ async verify() { const state = await this.bento.verify(); const vfl = this.bento.entities.getPlugin(VariableFileLoader_1.VariableFileLoader); const variableFiles = Array.from(vfl.files.keys()); const fsel = this.bento.entities.getPlugin(FSEntityLoader_1.FSEntityLoader); const entityFiles = Array.from(fsel.files); return { state, entityFiles, variableFiles }; } } exports.Application = Application; //# sourceMappingURL=Application.js.map