App

App class. This is the root. This is where all plugins call it home.

This class should not be instantiated directly (see example in App.TOptions if you feel adventurous). Instead, use the default boot script.

Plugins (including special plugins Main and Bajo) are loaded and attached as property members of this class. They can be accessed using their name/namespace.

In every plugin there is a reference to this class instance, so they can call each other easily, e.g. in your plugin method, you would call other plugin's method like this this.app.otherPlugin.method().

A typical Bajo app should follow the following structure:

my-app
├── package.json
├── index.js
├── main
│   ├── extend
│   │   ├── bajo
│   │   │   ├── hook
│   │   │   │   ├── my-plugin@before-action.js
│   │   │   │   └── my-other-plugin.domain@after-action.js
│   │   │   └── intl
│   │   │       ├── en-US.yml
│   │   │       └── id.yml
│   │   └── myPlugin
│   ├── index.js
├── data
│   ├── config
│   │   ├── .plugins
│   │   ├── bajo.yml
│   │   ├── myPlugin.yml
•   •   •   •

Constructor

new App(optionsopt)

Constructor. See App.TOptions if you want to write custom boot process for your app without using the default boot module.

Parameters:
NameTypeAttributesDefaultDescription
optionsApp.TOptions<optional>
{}

Options object.

Members

applet :string

If app runs in applet mode, this will be the current applet's name. Otherwise, it will be undefined. See applet for details.

Type:
  • string

applets :Array

Applets container. Usefull to get all available applets in your app. See applet for details.

Type:
  • Array

args :Array.<string>

Parsed program arguments.

Example:

$ node index.js arg1 arg2
...
console.log(this.args) // it should print: ['arg1', 'arg2']
Type:
  • Array.<string>

argv :Object

Parsed program options:

  • Dash (-) breaks the string into object keys
  • While colon (:) is used as namespace - object separator. If no such namespace found, it is saved under _.

Parsed object values are normalized to its primitives using dotenv-parse-variables

$ node index.js --my-name-first=John --my-name-last=Doe --my-birthDay=secret --nameSpace:path-subPath=true
...
// {
//   _: {
//    my: {
//       name: { first: 'John', last: 'Doe' },
//       birthDay: 'secret'
//     }
//   },
//   nameSpace: { path: { subPath: true } }
// }
Type:
  • Object

baseClass :Object

All plugin's base classes are saved here for reference so that you can extend them in your own plugins.

Two basic classes from Bajo package are also provided: Base and Tools. These are the most used classes in Bajo plugin development. You can extend them to create your own plugin's base class:

// factory function to create your own plugin's base class
async function () {
  const { Base } = this.app.baseClass
  class MyBase extends Base {
    constructor(pkgName, app) {
      super(pkgName, app)
      this.myProperty = 'myValue' // your class property
    }

    async myMethod() {
      // your class method
    }
  }
}
Type:
  • Object

cache :Cache

Internal cache instance. This is used to store temporary data in memory for faster access.

Type:

configHandlers :Array.<App.TConfigHandler>

Config handlers container. This is where all config handlers are stored. Each handler is responsible to read and write config file in a particular format. Bajo uses this heavily to read/write config files and/or anything that needs to be parsed/stringified from string to object and vice versa.

By default, there are three built-in handlers: .js, .json and .yml/.yaml. Use plugins to add more, e.g bajo-config lets you to use .toml.

Note: .js is for reading only, it cannot be used to write config file.

Type:

envVars :Object

Parsed environment variables. Support dotenv (.env) file too!

  • Underscore (_) between keys merge those keys into a single camel-cased key
  • Double underscores (__) between keys breaks the key into nested objects
  • Meanwhile a dot (.) between keys breaks the key into nested, name-spaced objects

Parsed object values are normalized to its primitives using dotenv-parse-variables

Example:

  • MY_KEY=secret{ _: { myKey: 'secret' } }
  • MY_KEY__SUB_KEY=supersecret{ _: { myKey: { subKey: 'supersecret' } } }
  • MY_NS.MY_NAME=John{ myNs: { myName: 'John' } }

Tips: During boot process, Bajo will add APPDIR environment variable which points to your app's root directory.

Type:
  • Object

envs :App.TEnv

Supported environments. Read-only

lib :App.TLib

Gives you direct access to the most commonly used 3rd party library in a Bajo based app. No manual import necessary, always available, anywhere, anytime!

Example
const { camelCase, kebabCase } = this.app.lib._
console.log(camelCase('Elit commodo sit et aliqua'))

log :Log

Instance of the system log. This will automatically be instantiated early during boot process. See Log for details.

Type:

mainNs :string

App's main plugin namespace (main). Read-only

Type:
  • string

options :App.TOptions

Copy of the provided options. See App.TOptions for details.

pluginPkgs :Array.<string>

Original plugin's package names (not to be confused with plugin namespaces) container. Ypu can use this information to get the real package name of a plugin.

Reminder: In Bajo, plugin name/namespace is the camel-cased version of its package name. E.g. my-pluginmyPlugin.

Type:
  • Array.<string>

runAt :Date

Date/time when your app start. You can use this e.g. to calculate how long your app has been running. Read-only

Type:
  • Date

Methods

addPlugin(plugin, baseClassopt)

Add and save plugin and its base class definition (if provided) to the app instance. You can then reference the plugin using its namespace, e.g. this.myPlugin or this['myPlugin'].

Parameters:
NameTypeAttributesDescription
pluginBase

A valid Bajo plugin.

baseClassclass<optional>

Base class definition.

dump(…args)

Dumping variable on screen. Like console.log with configurable options. Useful for quick debugging and testing. You can also use it to dump variables in production without worrying about performance because it is using Bajo's built-in cache to store the result of util's inspect, so it will only be processed once for each unique variable.

Any argument passed to this method will be displayed on screen. If the last argument is a string \q or .q, app will quit rightaway after dumping.

If you have bajoCli plugin installed, variables will be displayed in a nice box using boxen package. Otherwise, it will fallback to console.log with util's inspect result.

To have more control on how the variable is displayed, you can set options in Bajo's config under dump key. See Bajo.TConfig for details.

Parameters:
NameTypeAttributesDescription
argsany<repeatable>

Variables to dump.

exit(signalopt, reasonopt)

Terminate the app forcefully and back to console.

Parameters:
NameTypeAttributesDefaultDescription
signalstring | boolean<optional>
SIGINT

Signal to send. Set to true to terminate immediately without sending any signal

reasonstring<optional>

Reason to be printed on console before exiting

getAllNs() → {Array.<string>}

Get all loaded plugin namespaces.

Returns:
Type: 
Array.<string>

getAllPlugins() → {Array.<Base>}

Get all loaded plugins. Alias to getPlugins() with no namespace provided.

Returns:

Array of all loaded plugin instances

Type: 
Array.<Base>

getConfigFormats(noDotopt) → {Array.<string>}

Method to list all supported config formats, that is, all file extensions that are supported by the app's config handlers.

Parameters:
NameTypeAttributesDescription
noDotboolean<optional>

If true, it will return the list without dot prefix

Returns:
Type: 
Array.<string>

getPlugin(name, silentopt) → {Base}

Get plugin by its namespace or alias. If the plugin is not loaded, it will throw an error unless silent is set to true.

Parameters:
NameTypeAttributesDescription
namestring

Namespace or alias.

silentboolean<optional>

If true, silently return undefined even on error

Returns:

Plugin object.

Type: 
Base

getPluginDataDir(name, ensureDiropt) → {string}

Get plugin data directory. If the directory does not exist, it will be created automatically unless ensureDir is set to false.

Parameters:
NameTypeAttributesDefaultDescription
namestring

Namespace or alias.

ensureDirboolean<optional>
true

Set true (default) to ensure directory exists

Returns:
Type: 
string

getPluginFile(file) → {string}

Resolve file path from:

  • local/absolute file
  • Bajo.TNsPathPairs e.g. myPlugin:/path/to/file.txt
  • file under node_modules, e.g. myPlugin:node_modules/some-package/file.txt
Parameters:
NameTypeDescription
filestring

File path, see above for supported types

Returns:

Resolved file path

Type: 
string

getPlugins(nssopt, nameOnlyopt) → {Array.<Base>}

Get loaded plugins by their namespaces. If no namespace is provided, it will return all loaded plugins.

Parameters:
NameTypeAttributesDefaultDescription
nssArray.<string><optional>

Array of namespaces. If empty, it returns all loaded plugins

nameOnlyboolean<optional>
false

If true, it will return only the plugin namespaces instead of the plugin instances

Returns:

Array of plugin instances

Type: 
Array.<Base>

loadIntl(ns)

Load internationalization & languages files for particular plugin by its namespace. It will load all supported languages defined in config.intl.supported and save them in this[ns].intl object.

Parameters:
NameTypeDescription
nsstring

Plugin namespace

(async) run() → {Promise.<App>}

Run application and begin the boot process. This method is called automatically by boot script.

Boot process includes:

  • parsing environment values, program arguments, and options
  • create Bajo instance & initialize it
  • load all plugins, their hooks, their configs and any other necessary services & resources
  • run in applet mode if -a or --applet is given

After boot process is completed, hook bajo:afterBoot is triggered.

If app mode is applet, it runs your choosen applet instead.

Returns:

App instance.

Type: 
Promise.<App>

startPlugin(ns, …args)

Start a plugin by its namespace. It will call the plugin's start method with the provided arguments.

Parameters:
NameTypeAttributesDescription
nsstring

Plugin namespace.

argsany<repeatable>

Arguments to pass to the plugin's start method

stopPlugin(ns, …args)

Stop a plugin by its namespace. It will call the plugin's stop method with the provided arguments.

Reserved for future use.

Note: Basically it is not a good idea to stop a plugin because other plugins might be dependent on it. But if we could find a way to safely stop a plugin without breaking things, this will be a cool feature because it allows dynamic management of plugins without restarting the application.

Parameters:
NameTypeAttributesDescription
nsstring

Plugin namespace.

argsany<repeatable>

Arguments to pass to the plugin's stop method

t(ns, text, …params) → {string}

Translate text to the current language.

It will search for the translation in the plugin's intl object first, interpolating the parameters if found, then fallback to bajo plugin's intl object if not found.

If the last parameter is an object with lang key, it will use that language instead of the default one.

There is a shortcut to this method attached on all plugins. You'll normally call that shorcut instead of this method, because that shortcut is bound to plugin's namespace already

... within your main plugin
const translated = this.app.t('main', 'My cute cat is %s', 'purring')
// or within your plugin
const translated = this.t('My cute cat is %s', 'purring')
Parameters:
NameTypeAttributesDescription
nsstring

Namespace

textstring

Text to translate

params*<repeatable>

Parameters to interpolate into the translation text

Returns:
Type: 
string

te(ns, text) → {boolean}

Check whether translation text/key exists.

Parameters:
NameTypeDescription
nsstring

Namespace

textstring

Text to translate

Returns:
Type: 
boolean

Type Definitions

TConfigHandler

Config handler definition. Your own handler must follow this structure.

Type:
  • Object
Properties
NameTypeAttributesDescription
nsstring

Owner plugin namespace

extstring

Supported file extension

readHandlerApp.readHandler<optional>

Async function to call for reading

writeHandlerApp.writeHandler<optional>

Async function to call for writing

TEnv

Supported environments.

Environment is one of the most important aspect of a Bajo app. It is used to determine how your app should behave in different environments. It allows you to have different configurations and settings, and as a plugin developer, you can also use it to determine how your plugin should behave in different environments.

Type:
  • Object
Properties
NameTypeDefaultDescription
devstringdevelopment
stagstringstaging
prodstringproduction
teststringtesting

TLib

Most commonly used libraries by Bajo and its plugins. They are already imported and ready to use, so you don't have to import them again in your plugin.

Example:

const { fs, dayjs } = this.app.lib
fs.ensureDirSync('/path/to/dir')
const now = dayjs().format('YYYY-MM-DD HH:mm:ss')
Type:
  • Object
Properties
NameTypeAttributesDescription
_Object

Access to lodash.

fsObject

Access to fs-extra.

fastGlobObject

Access to fast-glob.

sprintfObject

Access to sprintf.

anekaObject

Access to aneka.

outmatchObject

Access to outmatch.

dayjsObject

Access to dayjs with utc & customParseFormat plugin already applied.

freezefunction

Freeze object. See freeze for more details.

findDeepfunction

Deep file file in an array of files. See findDeep for more details.

outmatchNsfunction

Like outmatch, but support scoped source & pattern. See outmatchNs for more details.

parseObjectfunction

Parse object and normalize their values. Also support translation. See parseObject for more details.

lockFilefunction

Lock a file. See lockFile for more details.

unlockFilefunction

Unlock a file. See unlockFile for more details.

setIntervalfunction

Like aneka.setInterval(), but with Bajo's lockFile support. See setInterval for more details.

dataTypesArray

Supported data types. See TDataType for more details.

anekaSpatialObject<optional>

Access to aneka-spatial helpers if bajoSpatial plugin is loaded.

See

TOptions

Options object passed to App constructor. By default, you don't need to pass any options, unless you want to manually override the default behavior.

Type:
  • Object
Properties
NameTypeAttributesDescription
cwdstring<optional>

Set current working directory. Defaults to the script directory

pluginsArray.<string><optional>

Array of plugins package names to load. If provided, it override the list in package.json and .plugins file

configObject<optional>

Plugin config objects, with plugin name as keys and their respective config objects as values. If provided, plugin configs will no longer be read from its config files

hooksArray.<module:Hook.THook><optional>

Array of hooks to be added to the app

Example
// If you feel adventurous and decide to manually boot your app without the default boot module, here we go,,,
import App from 'bajo/class/app.js'

const options = {
  plugins: ['my-plugin', 'my-other-plugin'],
  config: {
    myPlugin: {
      // plugin config here
    },
    myOtherPlugin: {
      // plugin config here
    }
   },
   hooks: [{
     name: 'myPlugin:myHook',
     handler: async function (arg1, arg2) {
       // do something with arg1 and arg2
     }
   }]
  }
}

const app = new App(options)

(async) readHandler(text, optionsopt) → {object}

Your read handler must follow this structure. It is used to read and parse config file in a particular format.

Parameters:
NameTypeAttributesDefaultDescription
textstring

Text to be parsed or file path to be read if options.readFromFile is true

optionsobject | boolean<optional>
{}

Options object. If a boolean is provided, it will be treated as options.readFromFile

Properties
NameTypeAttributesDefaultDescription
readFromFileboolean<optional>
false

If true, text is treated as a file path

throwNotFoundboolean<optional>
false

If true, throw exception if file is not found

parserOptsobject<optional>
{}

Options to be passed to the parser

Returns:

Parsed object

Type: 
object

(async) writeHandler(data, optionsopt) → {string}

Your write handler must follow this structure. It is used to write config file in a particular format.

Parameters:
NameTypeAttributesDefaultDescription
dataObject

Data to be stringified

optionsObject | string<optional>
{}

Options object. If a string is provided, it will be treated as options.writeToFile

Properties
NameTypeAttributesDefaultDescription
writeToFilestring<optional>

If not empty, write result to this file path instead of returning it as string

parserOptsobject<optional>
{}

Options to be passed to the parser

Returns:

Stringified result

Type: 
string