bajo 2.0.1 → 2.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (101) hide show
  1. package/.github/FUNDING.yml +13 -0
  2. package/.github/workflows/repo-lockdown.yml +24 -0
  3. package/.jsdoc.conf.json +7 -6
  4. package/LICENSE +1 -1
  5. package/README.md +50 -18
  6. package/class/app.js +376 -54
  7. package/class/bajo.js +228 -149
  8. package/class/base.js +106 -0
  9. package/class/helper/bajo.js +135 -84
  10. package/class/helper/{plugin.js → base.js} +96 -22
  11. package/class/{base → misc}/err.js +44 -14
  12. package/class/misc/log.js +212 -0
  13. package/class/misc/print.js +264 -0
  14. package/class/plugin.js +120 -68
  15. package/docs/App.html +23 -1
  16. package/docs/Bajo.html +2 -9
  17. package/docs/Base.html +3 -0
  18. package/docs/Err.html +3 -1
  19. package/docs/Log.html +5 -1
  20. package/docs/Plugin.html +1 -1
  21. package/docs/Print.html +1 -1
  22. package/docs/class_app.js.html +378 -56
  23. package/docs/class_bajo.js.html +230 -151
  24. package/docs/class_base.js.html +109 -0
  25. package/docs/class_helper_bajo.js.html +138 -87
  26. package/docs/class_helper_base.js.html +246 -0
  27. package/docs/class_misc_err.js.html +129 -0
  28. package/docs/class_misc_log.js.html +210 -0
  29. package/docs/class_misc_print.js.html +267 -0
  30. package/docs/class_plugin.js.html +122 -70
  31. package/docs/data/search.json +1 -1
  32. package/docs/global.html +4 -1
  33. package/docs/index.html +2 -2
  34. package/docs/index.js.html +35 -0
  35. package/docs/lib_current-loc.js.html +36 -0
  36. package/docs/lib_formats.js.html +8 -8
  37. package/docs/lib_import-module.js.html +59 -0
  38. package/docs/lib_log-levels.js.html +17 -7
  39. package/docs/lib_parse-args-argv.js.html +83 -0
  40. package/docs/lib_parse-env.js.html +53 -0
  41. package/docs/lib_resolve-path.js.html +3 -6
  42. package/docs/lib_shim.js.html +8 -3
  43. package/docs/module-Helper_Bajo.html +3 -0
  44. package/docs/module-Helper_Base.html +3 -0
  45. package/docs/module-Lib.html +15 -0
  46. package/docs/static/home.md +32 -0
  47. package/docs/static/logo-ecosystem.png +0 -0
  48. package/docs/static/logo.png +0 -0
  49. package/extend/bajo/intl/en-US.json +8 -2
  50. package/extend/bajo/intl/id.json +8 -2
  51. package/index.js +22 -2
  52. package/lib/current-loc.js +24 -2
  53. package/lib/formats.js +6 -6
  54. package/lib/import-module.js +29 -0
  55. package/lib/log-levels.js +15 -5
  56. package/lib/parse-args-argv.js +20 -12
  57. package/lib/parse-env.js +18 -7
  58. package/lib/resolve-path.js +1 -4
  59. package/lib/shim.js +6 -1
  60. package/package.json +4 -7
  61. package/wiki/CONFIG.md +30 -0
  62. package/wiki/CONTRIBUTING.md +5 -0
  63. package/wiki/DEV_GUIDE.md +3 -0
  64. package/wiki/ECOSYSTEM.md +93 -0
  65. package/wiki/GETTING-STARTED.md +356 -0
  66. package/wiki/USER-GUIDE.md +256 -0
  67. package/class/base/log.js +0 -205
  68. package/class/base/plugin.js +0 -168
  69. package/class/base/print.js +0 -272
  70. package/docs/BasePlugin.html +0 -5
  71. package/docs/class_base_err.js.html +0 -99
  72. package/docs/class_base_log.js.html +0 -208
  73. package/docs/class_base_plugin.js.html +0 -180
  74. package/docs/class_base_print.js.html +0 -275
  75. package/docs/class_helper_plugin.js.html +0 -172
  76. package/docs/lib_create-method.js.html +0 -42
  77. package/docs/module-class_helper_bajo.html +0 -3
  78. package/docs/module-class_helper_plugin.html +0 -3
  79. package/docs/module-lib_create-method.html +0 -3
  80. package/docs/module-lib_formats.html +0 -3
  81. package/docs/module-lib_log-levels.html +0 -3
  82. package/docs/module-lib_resolve-path.html +0 -3
  83. package/docs/module-lib_shim.html +0 -3
  84. package/docs/tutorial-contribution.html +0 -3
  85. package/docs/tutorial-ecosystem.html +0 -3
  86. package/docs/tutorial-getting-started.html +0 -13
  87. package/docs/tutorial-plugin-dev.html +0 -3
  88. package/docs/tutorial-user-guide.html +0 -3
  89. package/lib/create-method.js +0 -39
  90. package/lib/dayjs.js +0 -8
  91. package/lib/omitted-plugin-keys.js +0 -3
  92. package/lib/read-all-configs.js +0 -19
  93. package/misc-docs/.hook.md +0 -11
  94. package/misc-docs/bitcoin.jpeg +0 -0
  95. package/misc-docs/contribution.md +0 -20
  96. package/misc-docs/ecosystem.md +0 -94
  97. package/misc-docs/getting-started.md +0 -142
  98. package/misc-docs/plugin-dev.md +0 -0
  99. package/misc-docs/toc.json +0 -17
  100. package/misc-docs/user-guide.md +0 -1
  101. /package/docs/{bitcoin.jpeg → static/bitcoin.jpeg} +0 -0
@@ -0,0 +1,13 @@
1
+ github: ardhi
2
+ patreon: bajoframework
3
+ #open_collective: # Replace with a single Open Collective username
4
+ #ko_fi: # Replace with a single Ko-fi username
5
+ #tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
6
+ #community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
7
+ #liberapay: # Replace with a single Liberapay username
8
+ #issuehunt: # Replace with a single IssueHunt username
9
+ #lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry
10
+ #polar: # Replace with a single Polar username
11
+ #buy_me_a_coffee: # Replace with a single Buy Me a Coffee username
12
+ #thanks_dev: # Replace with a single thanks.dev username
13
+ custom: ["https://www.paypal.com/ncp/payment/EWLERL7SCUU64"]
@@ -0,0 +1,24 @@
1
+ name: 'Repo Lockdown'
2
+
3
+ on:
4
+ #issues:
5
+ #types: opened
6
+ pull_request_target:
7
+ types: opened
8
+ #schedule:
9
+ #- cron: '* */6 * * *'
10
+
11
+ permissions:
12
+ #issues: write
13
+ pull-requests: write
14
+
15
+ jobs:
16
+ action:
17
+ runs-on: ubuntu-latest
18
+ steps:
19
+ - uses: dessant/repo-lockdown@v4
20
+ with:
21
+ pr-comment: >
22
+ This repository does not accept pull requests,
23
+ see the README.md for details.
24
+ skip-closed-pr-comment: true
package/.jsdoc.conf.json CHANGED
@@ -6,21 +6,22 @@
6
6
  "verbose": true,
7
7
  "destination": "./docs",
8
8
  "template": "node_modules/clean-jsdoc-theme",
9
- "tutorials": "./misc-docs",
9
+ "readme": "./docs/static/home.md",
10
10
  "theme_opts": {
11
11
  "default_theme": "light",
12
12
  "display-module-header": true,
13
- "title": "Bajo Framework",
14
- "homepageTitle": "Bajo Framework",
13
+ "title": "Bajo API",
14
+ "homepageTitle": "Bajo API",
15
+ "sections": ["Classes", "Events", "Modules", "Global"],
15
16
  "menu": [{
16
- "title": "Bajo.app",
17
- "link": "https://bajo.app"
18
- }, {
19
17
  "title": "NPM",
20
18
  "link": "https://www.npmjs.com/package/bajo"
21
19
  }, {
22
20
  "title": "Github",
23
21
  "link": "https://github.com/ardhi/bajo"
22
+ }, {
23
+ "title": "Bajo",
24
+ "link": "https://bajo.app"
24
25
  }]
25
26
  }
26
27
  },
package/LICENSE CHANGED
@@ -1,6 +1,6 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) 2023 Ardhi Lukianto
3
+ Copyright (c) 2023-2025 Ardhi Lukianto
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
package/README.md CHANGED
@@ -1,18 +1,50 @@
1
- # bajo
2
-
3
- ![GitHub package.json version](https://img.shields.io/github/package-json/v/ardhi/bajo) ![NPM Version](https://img.shields.io/npm/v/bajo)
4
-
5
- > <br />**Attention**: I do NOT accept any pull request at the moment, thanks! ([Why?](misc-docs/contribution.md))<br /><br />
6
-
7
- ## Documentations
8
-
9
- - [Getting Started](misc-docs/getting-started.md)
10
- - [User Guide](misc-docs/user-guide.md)
11
- - [Plugin Development](misc-docs/plugin-dev.md)
12
- - [Ecosystem](misc-docs/ecosystem.md)
13
- - [Contribution](misc-docs/contribution.md)
14
- - [API](docs)
15
-
16
- ## License
17
-
18
- [MIT](LICENSE)
1
+ # bajo
2
+
3
+ The ultimate framework for whipping up a massive app in no time!
4
+
5
+ ![GitHub package.json version](https://img.shields.io/github/package-json/v/ardhi/bajo) ![NPM Version](https://img.shields.io/npm/v/bajo)
6
+
7
+ > <br />**Attention**: I do NOT accept any pull request at the moment, thanks! ([Why?](wiki/CONTRIBUTING.md))<br /><br />
8
+
9
+ <a href="https://bajo.app"><img src="docs/static/logo.png" width="300" alt="bajo"></a>
10
+
11
+ [Bajo](https://bajo.app) is one of a kind framework. Think of it as Lego blocks; you create your very own customized structure by attaching together only the needed blocks. This same principle for app building is now possible with Bajo: pick the right plugins, glue them together, and fire it up!
12
+
13
+ From a simple command-line app to a monster-sized, multi-page web app, Bajo will deliver your app in no time just by adding more plugins.
14
+
15
+ ## Documentations
16
+
17
+ - [Homepage](https://bajo.app/)
18
+ - [Getting Started](wiki/GETTING-STARTED.md)
19
+ - [User Guide](wiki/USER-GUIDE.md)
20
+ - [Developer Guide](wiki/DEV-GUIDE.md)
21
+ - [Config Object](wiki/CONFIG.md)
22
+ - [Ecosystem](wiki/ECOSYSTEM.md)
23
+ - [API](https://ardhi.github.io/bajo)
24
+ - [Contributing](wiki/CONTRIBUTING.md)
25
+
26
+ ## Hire Me
27
+
28
+ If you have a Bajo Framework-based project and need a professional service or assistance, please <a href="https://github.com/ardhi#professional-service">click here</a>. I'd be happy to work on it at a competitive price and with fast turnaround!
29
+
30
+ ## Support Me
31
+
32
+ Please support me using the channels below. Your donation will motivate me to work faster and more diligently on future development.
33
+
34
+ <a href="https://github.com/sponsors/ardhi">
35
+ <img src="https://img.shields.io/badge/Github-slategrey?style=flat&logo=github" height="50">
36
+ </a>
37
+ <a href="https://www.patreon.com/bajoframework">
38
+ <img src="https://img.shields.io/badge/Patreon-f2c3b2?style=flat&logo=patreon" height="50">
39
+ </a>
40
+ <a href="https://www.paypal.com/ncp/payment/EWLERL7SCUU64">
41
+ <img src="https://img.shields.io/badge/Paypal-blue?style=flat&logo=paypal" height="50">
42
+ </a>
43
+
44
+ <p>
45
+ <div><img alt="bc1qwtv78cwp9ef8hnqaw84fxg5856l0pggqe32g6f" src="docs/static/bitcoin.jpeg" width="150" height="150" /><br>Bitcoin</div>
46
+ </p>
47
+
48
+ ## License
49
+
50
+ [MIT](LICENSE)
package/class/app.js CHANGED
@@ -1,49 +1,237 @@
1
1
  import util from 'util'
2
2
  import lodash from 'lodash'
3
3
  import Bajo from './bajo.js'
4
+ import fastGlob from 'fast-glob'
5
+ import { sprintf } from 'sprintf-js'
6
+ import outmatch from 'outmatch'
7
+ import fs from 'fs-extra'
8
+ import aneka from 'aneka/index.js'
9
+ import Base from './base.js'
4
10
  import resolvePath from '../lib/resolve-path.js'
5
11
  import parseArgsArgv from '../lib/parse-args-argv.js'
6
12
  import parseEnv from '../lib/parse-env.js'
7
- import {
8
- buildBaseConfig,
9
- buildExtConfig,
10
- buildPlugins,
11
- collectConfigHandlers,
12
- bootOrder,
13
- bootPlugins,
14
- exitHandler,
15
- runAsApplet
16
- } from './helper/bajo.js'
17
-
18
- const { last } = lodash
13
+ import { runAsApplet } from './helper/bajo.js'
14
+ import dayjs from 'dayjs'
15
+ import utc from 'dayjs/plugin/utc.js'
16
+ import customParseFormat from 'dayjs/plugin/customParseFormat.js'
17
+ import localizedFormat from 'dayjs/plugin/localizedFormat.js'
18
+
19
+ dayjs.extend(utc)
20
+ dayjs.extend(customParseFormat)
21
+ dayjs.extend(localizedFormat)
22
+
23
+ const { isPlainObject, get, reverse, map, isString, last, without, keys } = lodash
24
+ let unknownLangWarning = false
25
+
26
+ function outmatchNs (source, pattern) {
27
+ const { breakNsPath } = this.bajo
28
+ const [src, subSrc] = source.split(':')
29
+ if (!subSrc) return pattern === src
30
+ try {
31
+ const { fullNs, path } = breakNsPath(pattern)
32
+ const isMatch = outmatch(path)
33
+ return src === fullNs && isMatch(subSrc)
34
+ } catch (err) {
35
+ return false
36
+ }
37
+ }
19
38
 
20
39
  /**
21
- * App class. This is where everything starts, the boot process:
22
- *
23
- * 1. Parsing all arguments and environment values
24
- * 2. Create {@link Bajo|Bajo} instance
25
- * 3. Building {@link module:class/helper/bajo.buildBaseConfig|base config}
26
- * 4. {@link module:class/helper/bajo.buildPlugins|Building plugins}
27
- * 5. Collect all {@link module:class/helper/bajo.collectConfigHandlers|config handler}
28
- * 6. Building {@link module:class/helper/bajo.buildExtConfig|extra config}
29
- * 7. Setup {@link module:class/helper/bajo.bootOrder|boot order}
30
- * 8. {@link module:class/helper/bajo.bootPlugins|Boot loaded plugins}
31
- * 9. Attach {@link module:class/helper/bajo.exitHandler|exit handlers}
32
- * 10. Finish
33
- *
34
- * After boot process is completed, event ```bajo:afterBootComplete``` is emitted.
35
- *
36
- * If app mode is ```applet```, it runs your choosen applet instead.
40
+ * @typedef {Object} TAppEnv
41
+ * @property {string} dev=development
42
+ * @property {string} prod=production
43
+ * @see App
44
+ */
45
+
46
+ /**
47
+ * @typedef {Object} TAppLib
48
+ * @property {Object} _ - Access to {@link https://lodash.com|lodash}
49
+ * @property {Object} fs - Access to {@link https://github.com/jprichardson/node-fs-extra|fs-extra}
50
+ * @property {Object} fastGlob - Access to {@link https://github.com/mrmlnc/fast-glob|fast-glob}
51
+ * @property {Object} sprintf - Access to {@link https://github.com/alexei/sprintf.js|sprintf}
52
+ * @property {Object} aneka - Access to {@link https://github.com/ardhi/aneka|aneka}
53
+ * @property {Object} outmatch - Access to {@link https://github.com/axtgr/outmatch|outmatch}
54
+ * @property {Object} dayjs - Access to {@link https://day.js.org|dayjs} with utc & customParseFormat plugin already applied
55
+ * @see App
56
+ */
57
+ const lib = {
58
+ _: lodash,
59
+ fs,
60
+ fastGlob,
61
+ sprintf,
62
+ outmatch,
63
+ dayjs,
64
+ aneka
65
+ }
66
+
67
+ /**
68
+ * App class. This is the root. This is where all plugins call it home.
37
69
  *
38
70
  * @class
39
71
  */
40
72
  class App {
41
73
  /**
42
- * Class constructor
74
+ * Your main namespace. And yes, you suppose to NOT CHANGE this
43
75
  *
76
+ * @memberof App
77
+ * @constant {string}
78
+ * @default 'main'
79
+ */
80
+ static mainNs = 'main'
81
+
82
+ /**
83
+ * App environments
84
+ * @memberof App
85
+ * @constant {TAppEnv}
86
+ */
87
+ static envs = { dev: 'development', prod: 'production' }
88
+
89
+ /**
44
90
  * @param {string} cwd - Current working dirctory
45
91
  */
46
92
  constructor (cwd) {
93
+ /**
94
+ * Date/time when your app start
95
+ * @type {Date}
96
+ */
97
+ this.runAt = new Date()
98
+
99
+ /**
100
+ * Applets
101
+ *
102
+ * @type {Array}
103
+ */
104
+ this.applets = []
105
+
106
+ /**
107
+ * List of all loaded plugin's package names
108
+ *
109
+ * @type {Array}
110
+ */
111
+ this.pluginPkgs = []
112
+
113
+ /**
114
+ * @typedef {Object} TAppConfigHandler
115
+ * @property {string} ext - File extension
116
+ * @property {function} [readHandler] - Function to call for reading
117
+ * @property {function} [writeHandler] - Function to call for writing
118
+ * @see App#configHandlers
119
+ */
120
+
121
+ /**
122
+ * Config handlers.
123
+ *
124
+ * By default, there are two built-in handlers available: ```.js```
125
+ * and ```.json```. Use plugins to add more, e.g {@link https://github.com/ardhi/bajo-config|bajo-config}
126
+ * lets you to use ```.yaml/.yml``` and ```.toml```
127
+ *
128
+ * @type {TAppConfigHandler[]}
129
+ */
130
+ this.configHandlers = []
131
+
132
+ /**
133
+ * Gives you direct access to the most commonly used 3rd party library in a Bajo based app.
134
+ * No manual import necessary, always available, anywhere, anytime!
135
+ *
136
+ * Example:
137
+ * ```javascript
138
+ * const { camelCase, kebabCase } = this.app.lib._
139
+ * console.log(camelCase('Elit commodo sit et aliqua'))
140
+ * ```
141
+ *
142
+ * @type {TAppLib}
143
+ */
144
+ this.lib = lib
145
+ this.lib.outmatchNs = outmatchNs.bind(this)
146
+
147
+ /**
148
+ * Instance of system log
149
+ *
150
+ * @type {Log}
151
+ */
152
+ this.log = {}
153
+
154
+ /**
155
+ * All plugin's class definitions are saved here as key-value pairs with plugin name as its key.
156
+ * The special key ```base``` is for {@link Base}'s class so anytime you want to
157
+ * create your own plugin, just use something like this:
158
+ *
159
+ * ```javascript
160
+ * class MyPlugin extends this.app.pluginClass.base {
161
+ * ... your class
162
+ * }
163
+ */
164
+ this.pluginClass = { base: Base }
165
+
166
+ /**
167
+ * If app runs in applet mode, this will be the applet's name
168
+ *
169
+ * @type {string}
170
+ */
171
+ this.applet = undefined
172
+
173
+ /**
174
+ * Program arguments
175
+ *
176
+ * ```
177
+ * $ node index.js arg1 arg2
178
+ * ...
179
+ * console.log(this.args) // it should print: ['arg1', 'arg2']
180
+ * ```
181
+ *
182
+ * @type {string[]}
183
+ * @see module:Lib.parseArgsArgv
184
+ */
185
+ this.args = []
186
+
187
+ /**
188
+ * Program options.
189
+ *
190
+ * - Dash (```-```) breaks the string into object keys
191
+ * - While colon (```:```) is used as namespace separator. If no namespace found, it is saved under ```_``` key.
192
+ *
193
+ * Values are parsed automatically. See {@link https://github.com/ladjs/dotenv-parse-variables|dotenv-parse-variables}
194
+ * for details.
195
+ *
196
+ * ```
197
+ * $ node index.js --my-name-first=John --my-name-last=Doe --my-birthDay=secret --nameSpace:path-subPath=true
198
+ * ...
199
+ * // {
200
+ * // _: {
201
+ * // my: {
202
+ * // name: { first: 'John', last: 'Doe' },
203
+ * // birthDay: 'secret'
204
+ * // }
205
+ * // },
206
+ * // nameSpace: { path: { subPath: true } }
207
+ * // }
208
+ * ```
209
+ *
210
+ * @type {Object}
211
+ * @see module:Lib.parseArgsArgv
212
+ */
213
+ this.argv = {}
214
+
215
+ /**
216
+ * Environment variables. Support dotenv (```.env```) file too!
217
+ *
218
+ * - Underscore (```_```) translates key to camel-cased one
219
+ * - Double underscores (```__```) breaks the key into object keys
220
+ * - While dot (```.```) is used as namespace separator. If no namespace found, it is saved under ```_``` key.
221
+ *
222
+ * Values are also parsed automatically using {@link https://github.com/ladjs/dotenv-parse-variables|dotenv-parse-variables}.
223
+ *
224
+ * Example:
225
+ *
226
+ * - ```MY_KEY=secret``` → ```{ _: { myKey: 'secret' } }```
227
+ * - ```MY_KEY__SUB_KEY=supersecret``` → ```{ _: { myKey: { subKey: 'supersecret' } } }```
228
+ * - ```MY_NS.MY_NAME=John``` → ```{ myNs: { myName: 'John' } }```
229
+ *
230
+ * @type {Object}
231
+ * @see module:Lib.parseEnv
232
+ */
233
+ this.envVars = {}
234
+
47
235
  if (!cwd) cwd = process.cwd()
48
236
  const l = last(process.argv)
49
237
  if (l.startsWith('--cwd')) {
@@ -54,19 +242,35 @@ class App {
54
242
  process.env.APPDIR = this.dir
55
243
  }
56
244
 
245
+ get mainNs () {
246
+ return this.constructor.mainNs
247
+ }
248
+
249
+ /**
250
+ * Add and save plugin and it's class definition (if provided)
251
+ *
252
+ * @method
253
+ * @param {TPlugin} plugin - A valid bajo plugin
254
+ * @param {Object} [pluginClass] - Plugin's class definition
255
+ */
256
+ addPlugin = (plugin, pluginClass) => {
257
+ if (this[plugin.ns]) throw new Error(`Plugin '${plugin.ns}' added already`)
258
+ this[plugin.ns] = plugin
259
+ if (pluginClass) this.pluginClass[plugin.ns] = pluginClass
260
+ }
261
+
57
262
  /**
58
- * Add a plugin to the app
263
+ * Get all loaded plugin namesspaces
59
264
  *
60
265
  * @method
61
- * @param {Object} plugin - A valid bajo plugin
266
+ * @returns {string[]}
62
267
  */
63
- addPlugin = (plugin) => {
64
- if (this[plugin.name]) throw new Error(`Plugin '${plugin.name}' added already`)
65
- this[plugin.name] = plugin
268
+ getAllNs = () => {
269
+ return without(keys(this.pluginClass), 'base')
66
270
  }
67
271
 
68
272
  /**
69
- * Dumping variable on screen
273
+ * Dumping variable on screen. Like ```console.log``` but with max 10 depth.
70
274
  *
71
275
  * @method
72
276
  * @param {...any} args - any arguments passed will be displayed on screen. If the last argument is a boolean 'true', app will quit rightaway
@@ -75,38 +279,156 @@ class App {
75
279
  const terminate = last(args) === true
76
280
  if (terminate) args.pop()
77
281
  for (const arg of args) {
78
- const result = util.inspect(arg, false, null, true)
282
+ const result = util.inspect(arg, { depth: 10, colors: true })
79
283
  console.log(result)
80
284
  }
81
285
  if (terminate) process.kill(process.pid, 'SIGINT')
82
286
  }
83
287
 
84
288
  /**
85
- * Booting the app
289
+ * Boot process:
290
+ *
291
+ * - Parsing {@link module:Lib.parseArgsArgv|program arguments, options} and {@link module:Lib.parseEnv|environment values}
292
+ * - Create {@link Bajo|Bajo} instance & initialize it
293
+ * - {@link module:Helper/Bajo.runAsApplet|Run in applet mode} if ```-a``` or ```--applet``` is given
294
+ *
295
+ * After boot process is completed, event ```bajo:afterBootComplete``` is emitted.
296
+ *
297
+ * If app mode is ```applet```, it runs your choosen applet instead.
86
298
  *
87
299
  * @method
88
300
  * @async
301
+ * @returns {App}
302
+ * @fires bajo:afterBootComplete
89
303
  */
90
304
  boot = async () => {
91
- // argv/args
92
- const { args, argv } = await parseArgsArgv.call(this.app) ?? {}
93
- this.argv = argv
305
+ // argv/args/env
306
+ this.bajo = new Bajo(this)
307
+ const { argv, args } = await parseArgsArgv.call(this) ?? {}
94
308
  this.args = args
95
- this.env = parseEnv() ?? {}
96
-
97
- const bajo = new Bajo(this)
98
- await buildBaseConfig.call(bajo)
99
- await buildPlugins.call(bajo)
100
- await collectConfigHandlers.call(bajo)
101
- await buildExtConfig.call(bajo)
102
- await bootOrder.call(bajo)
103
- await bootPlugins.call(bajo)
104
- await exitHandler.call(bajo)
309
+ this.argv = argv
310
+ this.envVars = parseEnv.call(this)
311
+ this.applet = this.envVars._.applet ?? this.argv._.applet
312
+ await this.bajo.init()
105
313
  // boot complete
106
- const elapsed = new Date() - bajo.runAt
107
- bajo.log.info('bootCompleted%s', bajo.lib.aneka.secToHms(elapsed, true))
108
- await bajo.runHook('bajo:afterBootComplete')
109
- if (bajo.applet) await runAsApplet.call(bajo)
314
+ const elapsed = new Date() - this.runAt
315
+ this.bajo.log.debug('bootCompleted%s', this.lib.aneka.secToHms(elapsed, true))
316
+ /**
317
+ * Run after boot process is completed
318
+ *
319
+ * @global
320
+ * @event bajo:afterBootComplete
321
+ * @see {@tutorial hook}
322
+ * @see App#boot
323
+ */
324
+ await this.bajo.runHook('bajo:afterBootComplete')
325
+ if (this.applet) await runAsApplet.call(this.bajo)
326
+ return this
327
+ }
328
+
329
+ /**
330
+ * Terminate the app and back to console
331
+ *
332
+ * @method
333
+ * @param {string} [signal=SIGINT] - Signal to send
334
+ */
335
+ exit = (signal = 'SIGINT') => {
336
+ process.kill(process.pid, 'SIGINT')
337
+ }
338
+
339
+ /**
340
+ * Load internationalization & languages files for particular plugin
341
+ *
342
+ * @method
343
+ * @param {string} ns - Plugin name
344
+ */
345
+ loadIntl = (ns) => {
346
+ this[ns].intl = {}
347
+ for (const l of this.bajo.config.intl.supported) {
348
+ this[ns].intl[l] = {}
349
+ const path = `${this[ns].dir.pkg}/extend/bajo/intl/${l}.json`
350
+ if (!fs.existsSync(path)) continue
351
+ const trans = fs.readFileSync(path, 'utf8')
352
+ try {
353
+ this[ns].intl[l] = JSON.parse(trans)
354
+ } catch (err) {}
355
+ }
356
+ }
357
+
358
+ /**
359
+ * Translate text and interpolate with given ```args```.
360
+ *
361
+ * There is a shortcut to this method attached on all plugins. You'll normally call that shorcut
362
+ * instead of this method, because it is bound to plugin's name already
363
+ *
364
+ * ```javascript
365
+ * ... within your main plugin
366
+ * const translated = this.app.t('main', 'My cute cat is %s', 'purring')
367
+ * // or
368
+ * const translated = this.t('My cute cat is %s', 'purring')
369
+ * ```
370
+ * @method
371
+ * @param {string} ns - Namespace
372
+ * @param {string} text - Text to translate
373
+ * @param {...any} params - Arguments
374
+ * @returns {string}
375
+ */
376
+ t = (ns, text, ...params) => {
377
+ if (!text) {
378
+ text = ns
379
+ ns = 'bajo'
380
+ }
381
+ const opts = last(params)
382
+ let lang = this.bajo.config.lang
383
+ if (isPlainObject(opts)) {
384
+ params.pop()
385
+ if (opts.lang) lang = opts.lang
386
+ }
387
+ const { fallback, supported } = this.bajo.config.intl
388
+ if (!unknownLangWarning && !supported.includes(lang)) {
389
+ unknownLangWarning = true
390
+ this.bajo.log.warn(`Unsupported language, fallback to '${fallback}'`)
391
+ }
392
+ const plugins = reverse(without([...this.getAllNs()], ns))
393
+ plugins.unshift(ns)
394
+ plugins.push('bajo')
395
+
396
+ let trans
397
+ for (const p of plugins) {
398
+ const store = get(this, `${p}.intl.${lang}`, {})
399
+ trans = get(store, text)
400
+ if (trans) break
401
+ }
402
+ if (!trans) {
403
+ for (const p of plugins) {
404
+ const store = get(this, `${p}.intl.${fallback}`, {})
405
+ trans = get(store, text)
406
+ if (trans) break
407
+ }
408
+ }
409
+ if (!trans) trans = text
410
+ const values = map(params, a => {
411
+ if (!isString(a)) return a
412
+ return a
413
+ })
414
+ return sprintf(trans, ...values)
415
+ }
416
+
417
+ /**
418
+ * Helper method to list all supported config formats
419
+ *
420
+ * @returns {string[]}
421
+ */
422
+ getConfigFormats = () => {
423
+ return map(this.configHandlers, 'ext')
424
+ }
425
+
426
+ startPlugin = (ns, ...args) => {
427
+ this[ns].start(...args)
428
+ }
429
+
430
+ stopPlugin = (ns, ...args) => {
431
+ this[ns].stop(...args)
110
432
  }
111
433
  }
112
434