bajo 2.18.0 → 2.20.0

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 (74) hide show
  1. package/.github/workflows/test.yml +37 -0
  2. package/class/_helper.js +23 -7
  3. package/class/app.js +64 -47
  4. package/class/bajo.js +182 -138
  5. package/class/base.js +3 -3
  6. package/class/cache.js +60 -0
  7. package/class/err.js +14 -11
  8. package/class/log.js +41 -40
  9. package/class/plugin.js +35 -36
  10. package/class/print.js +54 -51
  11. package/class/tools.js +3 -4
  12. package/docs/App.html +7 -7
  13. package/docs/Bajo.html +2 -2
  14. package/docs/Base.html +1 -1
  15. package/docs/Cache.html +3 -0
  16. package/docs/Err.html +2 -2
  17. package/docs/Log.html +2 -2
  18. package/docs/Plugin.html +1 -1
  19. package/docs/Print.html +1 -1
  20. package/docs/Tools.html +3 -0
  21. package/docs/class__helper.js.html +694 -0
  22. package/docs/class_app.js.html +307 -149
  23. package/docs/class_bajo.js.html +316 -464
  24. package/docs/class_base.js.html +35 -32
  25. package/docs/class_cache.js.html +150 -0
  26. package/docs/class_err.js.html +144 -0
  27. package/docs/class_log.js.html +270 -0
  28. package/docs/class_plugin.js.html +98 -71
  29. package/docs/class_print.js.html +261 -0
  30. package/docs/class_tools.js.html +44 -0
  31. package/docs/data/search.json +1 -1
  32. package/docs/global.html +1 -4
  33. package/docs/index.html +1 -1
  34. package/docs/index.js.html +21 -14
  35. package/docs/lib_find-deep.js.html +27 -0
  36. package/docs/lib_formats.js.html +19 -19
  37. package/docs/lib_freeze.js.html +19 -0
  38. package/docs/lib_import-module.js.html +16 -14
  39. package/docs/lib_index.js.html +9 -0
  40. package/docs/lib_log-levels.js.html +2 -2
  41. package/docs/module-Helper.html +3 -0
  42. package/docs/module-Lib.html +3 -8
  43. package/docs/scripts/core.js +477 -476
  44. package/docs/scripts/resize.js +36 -36
  45. package/docs/scripts/search.js +105 -105
  46. package/docs/scripts/third-party/fuse.js +1 -1
  47. package/docs/scripts/third-party/hljs-line-num-original.js +285 -282
  48. package/docs/scripts/third-party/hljs-line-num.js +1 -1
  49. package/docs/scripts/third-party/hljs-original.js +1202 -1195
  50. package/docs/scripts/third-party/hljs.js +1 -1
  51. package/docs/scripts/third-party/popper.js +1 -1
  52. package/docs/scripts/third-party/tippy.js +1 -1
  53. package/docs/scripts/third-party/tocbot.js +509 -508
  54. package/index.js +8 -11
  55. package/lib/find-deep.js +3 -3
  56. package/lib/formats.js +17 -17
  57. package/lib/freeze.js +3 -3
  58. package/lib/import-module.js +8 -8
  59. package/package.json +3 -2
  60. package/test/app.test.js +183 -0
  61. package/test/bajo.test.js +125 -0
  62. package/test/base.test.js +74 -107
  63. package/test/cache.test.js +94 -0
  64. package/test/e2e.test.js +137 -0
  65. package/test/err.test.js +73 -0
  66. package/test/helper.test.js +39 -0
  67. package/test/import-module.test.js +138 -0
  68. package/test/integration.test.js +218 -0
  69. package/test/log.test.js +119 -0
  70. package/test/plugin.test.js +116 -0
  71. package/test/print.test.js +100 -0
  72. package/test/tools.test.js +38 -0
  73. package/wiki/CHANGES.md +12 -0
  74. package/.mocharc.json +0 -4
@@ -0,0 +1,37 @@
1
+ name: Bajo Tests
2
+
3
+ # Trigger the workflow on pushes or pull requests to the main branch
4
+ on:
5
+ push:
6
+ branches: [ main ]
7
+ pull_request:
8
+ branches: [ main ]
9
+
10
+ jobs:
11
+ test-package:
12
+ runs-on: ubuntu-latest
13
+
14
+ strategy:
15
+ # Test your package across multiple Node.js versions
16
+ matrix:
17
+ node-version: [22.x, 24.x]
18
+
19
+ steps:
20
+ # Step 1: Download the repository code onto the runner machine
21
+ - name: Checkout repository
22
+ uses: actions/checkout@v6
23
+
24
+ # Step 2: Set up the specific Node.js version from the matrix
25
+ - name: Use Node.js ${{ matrix.node-version }}
26
+ uses: actions/setup-node@v6
27
+ with:
28
+ node-version: ${{ matrix.node-version }}
29
+ cache: 'npm' # Speeds up subsequent runs by caching node_modules
30
+
31
+ # Step 3: Perform a clean install of your exact dependencies
32
+ - name: Install dependencies
33
+ run: npm ci
34
+
35
+ # Step 4: Execute the test command defined in package.json
36
+ - name: Run package tests
37
+ run: npm test
package/class/_helper.js CHANGED
@@ -140,13 +140,13 @@ dayjs.extend(weekOfYear)
140
140
 
141
141
  /**
142
142
  * @typedef {Object} TAppLib
143
- * @property {Object} _ - Access to {@link https://lodash.com|lodash}
144
- * @property {Object} fs - Access to {@link https://github.com/jprichardson/node-fs-extra|fs-extra}
145
- * @property {Object} fastGlob - Access to {@link https://github.com/mrmlnc/fast-glob|fast-glob}
146
- * @property {Object} sprintf - Access to {@link https://github.com/alexei/sprintf.js|sprintf}
147
- * @property {Object} aneka - Access to {@link https://github.com/ardhi/aneka|aneka}
148
- * @property {Object} outmatch - Access to {@link https://github.com/axtgr/outmatch|outmatch}
149
- * @property {Object} dayjs - Access to {@link https://day.js.org|dayjs} with utc & customParseFormat plugin already applied
143
+ * @property {Object} _ Access to {@link https://lodash.com|lodash}.
144
+ * @property {Object} fs Access to {@link https://github.com/jprichardson/node-fs-extra|fs-extra}.
145
+ * @property {Object} fastGlob Access to {@link https://github.com/mrmlnc/fast-glob|fast-glob}.
146
+ * @property {Object} sprintf Access to {@link https://github.com/alexei/sprintf.js|sprintf}.
147
+ * @property {Object} aneka Access to {@link https://github.com/ardhi/aneka|aneka}.
148
+ * @property {Object} outmatch Access to {@link https://github.com/axtgr/outmatch|outmatch}.
149
+ * @property {Object} dayjs Access to {@link https://day.js.org|dayjs} with utc & customParseFormat plugin already applied.
150
150
  * @property {Object} freeze
151
151
  * @property {Object} findDeep
152
152
  * @see App
@@ -173,6 +173,8 @@ export const lib = {
173
173
  * - read the list of plugins from ```.plugins``` file
174
174
  *
175
175
  * @async
176
+ * @method
177
+ * @memberof module:Helper
176
178
  */
177
179
  export async function buildBaseConfig () {
178
180
  // dirs
@@ -214,6 +216,7 @@ export async function buildBaseConfig () {
214
216
  * - attach these plugins to the app instance
215
217
  *
216
218
  * @async
219
+ * @memberof module:Helper
217
220
  */
218
221
  export async function buildPlugins () {
219
222
  const { resolvePath } = this.app.lib.aneka
@@ -247,6 +250,7 @@ export async function buildPlugins () {
247
250
  * Collect all config handlers, including the one provided by plugins
248
251
  *
249
252
  * @async
253
+ * @memberof module:Helper
250
254
  */
251
255
  export async function collectConfigHandlers () {
252
256
  for (const pkg of this.app.pluginPkgs) {
@@ -260,6 +264,7 @@ export async function collectConfigHandlers () {
260
264
  if (!mod) continue
261
265
  if (isFunction(mod)) mod = await mod.call(this.app[camelCase(pkg)])
262
266
  if (isPlainObject(mod)) mod = [mod]
267
+ mod.forEach(m => set(m, 'ns', camelCase(pkg)))
263
268
  this.app.configHandlers = this.app.configHandlers.concat(mod)
264
269
  }
265
270
  }
@@ -271,6 +276,7 @@ export async function collectConfigHandlers () {
271
276
  * - Set environment (```dev``` or ```prod```)
272
277
  *
273
278
  * @async
279
+ * @memberof module:Helper
274
280
  */
275
281
  export async function buildExtConfig () {
276
282
  // config merging
@@ -321,6 +327,7 @@ export async function buildExtConfig () {
321
327
  * Setup plugins boot orders by reading plugin's ```.bootorder``` file if provided.
322
328
  *
323
329
  * @async
330
+ * @memberof module:Helper
324
331
  */
325
332
  export async function bootOrder () {
326
333
  const { freeze } = this.app.lib
@@ -349,6 +356,7 @@ export async function bootOrder () {
349
356
  * Build configurations
350
357
  *
351
358
  * @async
359
+ * @memberof module:Helper
352
360
  */
353
361
  export async function buildConfigs () {
354
362
  this.bajo.log.debug('readConfigs')
@@ -363,6 +371,7 @@ export async function buildConfigs () {
363
371
  * Ensure for names and aliases to be unique and no clashes with other plugins
364
372
  *
365
373
  * @async
374
+ * @memberof module:Helper
366
375
  */
367
376
  export async function checkNameAliases () {
368
377
  this.bajo.log.debug('checkAliasNameClash')
@@ -382,6 +391,7 @@ export async function checkNameAliases () {
382
391
  * Ensure dependencies are met
383
392
  *
384
393
  * @async
394
+ * @memberof module:Helper
385
395
  */
386
396
  export async function checkDependencies () {
387
397
  const { join } = this.bajo
@@ -417,6 +427,7 @@ export async function checkDependencies () {
417
427
  * Collect and build hooks and push them to the bajo's hook system
418
428
  *
419
429
  * @async
430
+ * @memberof module:Helper
420
431
  * @fires bajo:afterCollectHooks
421
432
  */
422
433
  export async function collectHooks () {
@@ -482,6 +493,7 @@ export async function collectHooks () {
482
493
  * @fires {ns}:before{method}
483
494
  * @fires {ns}:after{method}
484
495
  * @fires bajo:afterAll{method}
496
+ * @memberof module:Helper
485
497
  */
486
498
  export async function run () {
487
499
  const me = this
@@ -552,6 +564,7 @@ export async function run () {
552
564
  * 5. {@link module:Helper/Base.run|run plugins}
553
565
  *
554
566
  * @async
567
+ * @memberof module:Helper
555
568
  */
556
569
  export async function bootPlugins () {
557
570
  await buildConfigs.call(this.app)
@@ -565,6 +578,7 @@ export async function bootPlugins () {
565
578
  * Attach plugins exit handlers and make sure the app shutdowns gracefully
566
579
  *
567
580
  * @async
581
+ * @memberof module:Helper
568
582
  */
569
583
  export async function exitHandler () {
570
584
  if (!this.config.exitHandler) return
@@ -629,6 +643,7 @@ export async function exitHandler () {
629
643
  * @async
630
644
  * @fires {ns}:beforeAppletRun
631
645
  * @fires {ns}:afterAppletRun
646
+ * @memberof module:Helper
632
647
  */
633
648
  export async function runAsApplet () {
634
649
  const { isString, map, find } = this.app.lib._
@@ -671,6 +686,7 @@ export async function runAsApplet () {
671
686
  * @param {...any} params
672
687
  * @see {@tutorial hook}
673
688
  * @see module:Helper/Bajo.runAsApplet
689
+ * @memberof module:Helper
674
690
  */
675
691
  await this.runHook(`${applet.ns}:afterAppletRun`, ...this.app.args)
676
692
  }
package/class/app.js CHANGED
@@ -44,21 +44,21 @@ function getCallerFilename () {
44
44
  */
45
45
  class App {
46
46
  /**
47
- * @param {Object} [options] - App options
48
- * @param {string} [options.cwd] - Set current working directory. Defaults to the script directory
49
- * @param {string[]} [options.plugins] - Array of plugins to load. If provided, it override the list in ```package.json``` and ```.plugins``` file
50
- * @param {Object} [options.config] - Plugin's config object. If provided, plugin configs will no longer be read from its config files
47
+ * @param {Object} [options] - App options.
48
+ * @param {string} [options.cwd] - Set current working directory. Defaults to the script directory.
49
+ * @param {string[]} [options.plugins] - Array of plugins to load. If provided, it override the list in ```package.json``` and ```.plugins``` file.
50
+ * @param {Object} [options.config] - Plugin's config object. If provided, plugin configs will no longer be read from its config files.
51
51
  */
52
52
  constructor (options = {}) {
53
53
  /**
54
- * Copy of provided options
54
+ * Copy of provided options.
55
55
  *
56
56
  * @type {Object}
57
57
  */
58
58
  this.options = options
59
59
 
60
60
  /**
61
- * Your main namespace. And yes, you suppose to NOT CHANGE this
61
+ * Your main namespace. And yes, you suppose to NOT CHANGE this.
62
62
  *
63
63
  * @memberof App
64
64
  * @constant {string}
@@ -67,27 +67,29 @@ class App {
67
67
  this.mainNs = 'main'
68
68
 
69
69
  /**
70
- * App environments
70
+ * App environments.
71
+ *
71
72
  * @memberof App
72
73
  * @constant {TAppEnv}
73
74
  */
74
75
  this.envs = { dev: 'development', prod: 'production' }
75
76
 
76
77
  /**
77
- * Date/time when your app start
78
+ * Date/time when your app start.
79
+ *
78
80
  * @type {Date}
79
81
  */
80
82
  this.runAt = new Date()
81
83
 
82
84
  /**
83
- * Applets
85
+ * Applets container.
84
86
  *
85
87
  * @type {Array}
86
88
  */
87
89
  this.applets = []
88
90
 
89
91
  /**
90
- * List of all loaded plugin's package names
92
+ * Plugin's package names container. This is the list of plugins to load. It is read from ```package.json``` and ```.plugins``` file by default, but you can override it by providing ```options.plugins``` at constructor.
91
93
  *
92
94
  * @type {Array}
93
95
  */
@@ -95,9 +97,9 @@ class App {
95
97
 
96
98
  /**
97
99
  * @typedef {Object} TAppConfigHandler
98
- * @property {string} ext - File extension
99
- * @property {function} [readHandler] - Function to call for reading
100
- * @property {function} [writeHandler] - Function to call for writing
100
+ * @property {string} ext - File extension.
101
+ * @property {function} [readHandler] - Function to call for reading.
102
+ * @property {function} [writeHandler] - Function to call for writing.
101
103
  * @see App#configHandlers
102
104
  */
103
105
 
@@ -106,7 +108,7 @@ class App {
106
108
  *
107
109
  * By default, there are two built-in handlers available: ```.js```
108
110
  * and ```.json```. Use plugins to add more, e.g {@link https://github.com/ardhi/bajo-config|bajo-config}
109
- * lets you to use ```.yaml/.yml``` and ```.toml```
111
+ * lets you to use ```.yaml/.yml``` and ```.toml```.
110
112
  *
111
113
  * @type {TAppConfigHandler[]}
112
114
  */
@@ -129,7 +131,7 @@ class App {
129
131
  this.lib.parseObject = parseObject.bind(this)
130
132
 
131
133
  /**
132
- * Instance of system log
134
+ * Instance of system log.
133
135
  *
134
136
  * @type {Log}
135
137
  */
@@ -148,14 +150,14 @@ class App {
148
150
  this.baseClass = { Base, Tools }
149
151
 
150
152
  /**
151
- * If app runs in applet mode, this will be the applet's name
153
+ * If app runs in applet mode, this will be the applet's name.
152
154
  *
153
155
  * @type {string}
154
156
  */
155
157
  this.applet = undefined
156
158
 
157
159
  /**
158
- * Program arguments
160
+ * Program arguments.
159
161
  *
160
162
  * ```
161
163
  * $ node index.js arg1 arg2
@@ -234,11 +236,11 @@ class App {
234
236
  }
235
237
 
236
238
  /**
237
- * Add and save plugin and it's base class definition (if provided)
239
+ * Add and save plugin and it's base class definition (if provided).
238
240
  *
239
241
  * @method
240
- * @param {TPlugin} plugin - A valid bajo plugin
241
- * @param {Object} [baseClass] - Base class definition
242
+ * @param {TPlugin} plugin - A valid bajo plugin.
243
+ * @param {Object} [baseClass] - Base class definition.
242
244
  */
243
245
  addPlugin = (plugin, baseClass) => {
244
246
  if (this[plugin.ns]) throw new Error(`Plugin '${plugin.ns}' added already`)
@@ -247,7 +249,7 @@ class App {
247
249
  }
248
250
 
249
251
  /**
250
- * Get all loaded plugin namespaces
252
+ * Get all loaded plugin namespaces.
251
253
  *
252
254
  * @method
253
255
  * @returns {string[]}
@@ -257,10 +259,10 @@ class App {
257
259
  }
258
260
 
259
261
  /**
260
- * Get loaded plugins
262
+ * Get loaded plugins.
261
263
  *
262
264
  * @method
263
- * @param {string[]} [nss] - Array of namespaces. If empty, it returns all loaded plugins
265
+ * @param {string[]} [nss] - Array of namespaces. If empty, it returns all loaded plugins.
264
266
  * @returns {TPlugin[]}
265
267
  */
266
268
  getPlugins = (nss) => {
@@ -269,7 +271,7 @@ class App {
269
271
  }
270
272
 
271
273
  /**
272
- * Get all plugins loaded plugins
274
+ * Get all plugins loaded plugins.
273
275
  *
274
276
  * @method
275
277
  * @returns {TPlugin[]}
@@ -279,12 +281,12 @@ class App {
279
281
  }
280
282
 
281
283
  /**
282
- * Get plugin by name
284
+ * Get plugin by its namespace.
283
285
  *
284
286
  * @method
285
- * @param {string} name - Plugin name/namespace or alias
286
- * @param {boolean} [silent] - If ```true```, silently return undefined even on error
287
- * @returns {Object} Plugin object
287
+ * @param {string} name - Plugin name/namespace or alias.
288
+ * @param {boolean} [silent] - If ```true```, silently return undefined even on error.
289
+ * @returns {Object} Plugin object.
288
290
  */
289
291
  getPlugin = (name, silent) => {
290
292
  if (!this[name]) {
@@ -310,8 +312,8 @@ class App {
310
312
  * Get plugin data directory
311
313
  *
312
314
  * @method
313
- * @param {string} name - Plugin name (namespace) or alias
314
- * @param {boolean} [ensureDir=true] - Set ```true``` (default) to ensure directory is existed
315
+ * @param {string} name - Plugin name (namespace) or alias.
316
+ * @param {boolean} [ensureDir=true] - Set ```true``` (default) to ensure directory is existed.
315
317
  * @returns {string}
316
318
  */
317
319
  getPluginDataDir = (name, ensureDir = true) => {
@@ -330,8 +332,8 @@ class App {
330
332
  * - file under node_modules, e.g. ```myPlugin:node_modules/some-package/file.txt```
331
333
  *
332
334
  * @method
333
- * @param {string} file - File path, see above for supported types
334
- * @returns {string} Resolved file path
335
+ * @param {string} file - File path, see above for supported types.
336
+ * @returns {string} Resolved file path.
335
337
  */
336
338
  getPluginFile = (file) => {
337
339
  const { currentLoc } = this.lib.aneka
@@ -366,7 +368,7 @@ class App {
366
368
  * See {@link Bajo#config} for details.
367
369
  *
368
370
  * @method
369
- * @param {...any} args - Variables to dump
371
+ * @param {...any} args - Variables to dump.
370
372
  */
371
373
  dump = (...args) => {
372
374
  let caller = getCallerFilename()
@@ -445,10 +447,10 @@ class App {
445
447
  }
446
448
 
447
449
  /**
448
- * Terminate the app and back to console
450
+ * Terminate the app and back to console.
449
451
  *
450
452
  * @method
451
- * @param {string} [signal=SIGINT] - Signal to send
453
+ * @param {string} [signal=SIGINT] - Signal to send.
452
454
  */
453
455
  exit = (signal = 'SIGINT') => {
454
456
  if (signal === true) process.exit('1')
@@ -456,10 +458,10 @@ class App {
456
458
  }
457
459
 
458
460
  /**
459
- * Load internationalization & languages files for particular plugin
461
+ * Load internationalization & languages files for particular plugin.
460
462
  *
461
463
  * @method
462
- * @param {string} ns - Plugin name
464
+ * @param {string} ns - Plugin name.
463
465
  */
464
466
  loadIntl = (ns) => {
465
467
  const { fs } = this.lib
@@ -525,9 +527,9 @@ class App {
525
527
  * const translated = this.t('My cute cat is %s', 'purring')
526
528
  * ```
527
529
  * @method
528
- * @param {string} ns - Namespace
529
- * @param {string} text - Text to translate
530
- * @param {...any} params - Arguments
530
+ * @param {string} ns - Namespace.
531
+ * @param {string} text - Text to translate.
532
+ * @param {...any} params - Arguments.
531
533
  * @returns {string}
532
534
  */
533
535
  t = (ns, text, ...params) => {
@@ -548,31 +550,46 @@ class App {
548
550
  * Check whether translation text/key exists
549
551
  *
550
552
  * @method
551
- * @param {string} ns - Namespace
552
- * @param {string} text - Text to translate
553
+ * @param {string} ns - Namespace.
554
+ * @param {string} text - Text to translate.
553
555
  * @returns {boolean}
554
556
  */
555
-
556
557
  te = (ns, text, ...params) => {
557
558
  const { trans } = this._prepTrans(ns, text, params)
558
559
  return !!trans
559
560
  }
560
561
 
561
562
  /**
562
- * Helper method to list all supported config formats
563
+ * Helper method to list all supported config formats.
563
564
  *
565
+ * @method
566
+ * @param {boolean} [noDot] - If ```true```, it will return the list without dot prefix.
564
567
  * @returns {string[]}
565
568
  */
566
- getConfigFormats = () => {
567
- return map(this.configHandlers, 'ext')
569
+ getConfigFormats = (noDot) => {
570
+ const formats = map(this.configHandlers, 'ext')
571
+ return noDot ? formats.map(f => f.slice(1)) : formats
568
572
  }
569
573
 
574
+ /**
575
+ * Start a plugin.
576
+ *
577
+ * @param {string} ns - Plugin namespace.
578
+ * @param {...any} args - Arguments to pass to the plugin's start method.
579
+ */
570
580
  startPlugin = (ns, ...args) => {
571
581
  this[ns].start(...args)
572
582
  }
573
583
 
584
+ /**
585
+ * Stop a plugin.
586
+ *
587
+ * @param {string} ns - Plugin namespace.
588
+ * @param {...any} args - Arguments to pass to the plugin's stop method.
589
+ */
574
590
  stopPlugin = (ns, ...args) => {
575
- this[ns].stop(...args)
591
+ // Disabled for now, reserved for future use. It is not a good idea to stop a plugin because other plugins might be dependent on it.
592
+ // this[ns].stop(...args)
576
593
  }
577
594
  }
578
595