@ayanaware/bento
Version:
Modular runtime framework designed to solve complex tasks
200 lines • 8 kB
JavaScript
"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