bajo 2.17.0 → 2.19.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 (73) hide show
  1. package/class/_helper.js +22 -7
  2. package/class/app.js +148 -35
  3. package/class/bajo.js +156 -206
  4. package/class/base.js +3 -3
  5. package/class/cache.js +61 -2
  6. package/class/err.js +14 -11
  7. package/class/log.js +41 -40
  8. package/class/plugin.js +35 -36
  9. package/class/print.js +54 -51
  10. package/class/tools.js +3 -4
  11. package/docs/App.html +7 -7
  12. package/docs/Bajo.html +2 -2
  13. package/docs/Base.html +1 -1
  14. package/docs/Cache.html +3 -0
  15. package/docs/Err.html +2 -2
  16. package/docs/Log.html +2 -2
  17. package/docs/Plugin.html +1 -1
  18. package/docs/Print.html +1 -1
  19. package/docs/Tools.html +3 -0
  20. package/docs/class__helper.js.html +694 -0
  21. package/docs/class_app.js.html +307 -149
  22. package/docs/class_bajo.js.html +316 -464
  23. package/docs/class_base.js.html +35 -32
  24. package/docs/class_cache.js.html +150 -0
  25. package/docs/class_err.js.html +144 -0
  26. package/docs/class_log.js.html +270 -0
  27. package/docs/class_plugin.js.html +98 -71
  28. package/docs/class_print.js.html +261 -0
  29. package/docs/class_tools.js.html +44 -0
  30. package/docs/data/search.json +1 -1
  31. package/docs/global.html +1 -4
  32. package/docs/index.html +1 -1
  33. package/docs/index.js.html +21 -14
  34. package/docs/lib_find-deep.js.html +27 -0
  35. package/docs/lib_formats.js.html +19 -19
  36. package/docs/lib_freeze.js.html +19 -0
  37. package/docs/lib_import-module.js.html +16 -14
  38. package/docs/lib_index.js.html +9 -0
  39. package/docs/lib_log-levels.js.html +2 -2
  40. package/docs/module-Helper.html +3 -0
  41. package/docs/module-Lib.html +3 -8
  42. package/docs/scripts/core.js +477 -476
  43. package/docs/scripts/resize.js +36 -36
  44. package/docs/scripts/search.js +105 -105
  45. package/docs/scripts/third-party/fuse.js +1 -1
  46. package/docs/scripts/third-party/hljs-line-num-original.js +285 -282
  47. package/docs/scripts/third-party/hljs-line-num.js +1 -1
  48. package/docs/scripts/third-party/hljs-original.js +1202 -1195
  49. package/docs/scripts/third-party/hljs.js +1 -1
  50. package/docs/scripts/third-party/popper.js +1 -1
  51. package/docs/scripts/third-party/tippy.js +1 -1
  52. package/docs/scripts/third-party/tocbot.js +509 -508
  53. package/index.js +8 -11
  54. package/lib/find-deep.js +3 -3
  55. package/lib/formats.js +17 -17
  56. package/lib/freeze.js +3 -3
  57. package/lib/import-module.js +9 -9
  58. package/package.json +1 -1
  59. package/test/app.test.js +183 -0
  60. package/test/bajo.test.js +125 -0
  61. package/test/base.test.js +74 -107
  62. package/test/cache.test.js +94 -0
  63. package/test/e2e.test.js +137 -0
  64. package/test/err.test.js +73 -0
  65. package/test/helper.test.js +39 -0
  66. package/test/import-module.test.js +138 -0
  67. package/test/integration.test.js +218 -0
  68. package/test/log.test.js +119 -0
  69. package/test/plugin.test.js +116 -0
  70. package/test/print.test.js +100 -0
  71. package/test/tools.test.js +38 -0
  72. package/wiki/CHANGES.md +10 -0
  73. package/.mocharc.json +0 -4
@@ -1,25 +1,21 @@
1
1
  <!DOCTYPE html><html lang="en" style="font-size:16px"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>Source: class/bajo.js</title><!--[if lt IE 9]>
2
2
  <script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script>
3
- <![endif]--><script src="scripts/third-party/hljs.js" defer="defer"></script><script src="scripts/third-party/hljs-line-num.js" defer="defer"></script><script src="scripts/third-party/popper.js" defer="defer"></script><script src="scripts/third-party/tippy.js" defer="defer"></script><script src="scripts/third-party/tocbot.min.js"></script><script>var baseURL="/",locationPathname="";baseURL=(locationPathname=document.location.pathname).substr(0,locationPathname.lastIndexOf("/")+1)</script><link rel="stylesheet" href="styles/clean-jsdoc-theme.min.css"><svg aria-hidden="true" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" style="display:none"><defs><symbol id="copy-icon" viewbox="0 0 488.3 488.3"><g><path d="M314.25,85.4h-227c-21.3,0-38.6,17.3-38.6,38.6v325.7c0,21.3,17.3,38.6,38.6,38.6h227c21.3,0,38.6-17.3,38.6-38.6V124 C352.75,102.7,335.45,85.4,314.25,85.4z M325.75,449.6c0,6.4-5.2,11.6-11.6,11.6h-227c-6.4,0-11.6-5.2-11.6-11.6V124 c0-6.4,5.2-11.6,11.6-11.6h227c6.4,0,11.6,5.2,11.6,11.6V449.6z"/><path d="M401.05,0h-227c-21.3,0-38.6,17.3-38.6,38.6c0,7.5,6,13.5,13.5,13.5s13.5-6,13.5-13.5c0-6.4,5.2-11.6,11.6-11.6h227 c6.4,0,11.6,5.2,11.6,11.6v325.7c0,6.4-5.2,11.6-11.6,11.6c-7.5,0-13.5,6-13.5,13.5s6,13.5,13.5,13.5c21.3,0,38.6-17.3,38.6-38.6 V38.6C439.65,17.3,422.35,0,401.05,0z"/></g></symbol><symbol id="search-icon" viewBox="0 0 512 512"><g><g><path d="M225.474,0C101.151,0,0,101.151,0,225.474c0,124.33,101.151,225.474,225.474,225.474 c124.33,0,225.474-101.144,225.474-225.474C450.948,101.151,349.804,0,225.474,0z M225.474,409.323 c-101.373,0-183.848-82.475-183.848-183.848S124.101,41.626,225.474,41.626s183.848,82.475,183.848,183.848 S326.847,409.323,225.474,409.323z"/></g></g><g><g><path d="M505.902,476.472L386.574,357.144c-8.131-8.131-21.299-8.131-29.43,0c-8.131,8.124-8.131,21.306,0,29.43l119.328,119.328 c4.065,4.065,9.387,6.098,14.715,6.098c5.321,0,10.649-2.033,14.715-6.098C514.033,497.778,514.033,484.596,505.902,476.472z"/></g></g></symbol><symbol id="font-size-icon" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0z"/><path d="M11.246 15H4.754l-2 5H.6L7 4h2l6.4 16h-2.154l-2-5zm-.8-2L8 6.885 5.554 13h4.892zM21 12.535V12h2v8h-2v-.535a4 4 0 1 1 0-6.93zM19 18a2 2 0 1 0 0-4 2 2 0 0 0 0 4z"/></symbol><symbol id="add-icon" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0z"/><path d="M11 11V5h2v6h6v2h-6v6h-2v-6H5v-2z"/></symbol><symbol id="minus-icon" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0z"/><path d="M5 11h14v2H5z"/></symbol><symbol id="dark-theme-icon" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0z"/><path d="M10 7a7 7 0 0 0 12 4.9v.1c0 5.523-4.477 10-10 10S2 17.523 2 12 6.477 2 12 2h.1A6.979 6.979 0 0 0 10 7zm-6 5a8 8 0 0 0 15.062 3.762A9 9 0 0 1 8.238 4.938 7.999 7.999 0 0 0 4 12z"/></symbol><symbol id="light-theme-icon" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0z"/><path d="M12 18a6 6 0 1 1 0-12 6 6 0 0 1 0 12zm0-2a4 4 0 1 0 0-8 4 4 0 0 0 0 8zM11 1h2v3h-2V1zm0 19h2v3h-2v-3zM3.515 4.929l1.414-1.414L7.05 5.636 5.636 7.05 3.515 4.93zM16.95 18.364l1.414-1.414 2.121 2.121-1.414 1.414-2.121-2.121zm2.121-14.85l1.414 1.415-2.121 2.121-1.414-1.414 2.121-2.121zM5.636 16.95l1.414 1.414-2.121 2.121-1.414-1.414 2.121-2.121zM23 11v2h-3v-2h3zM4 11v2H1v-2h3z"/></symbol><symbol id="reset-icon" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0z"/><path d="M18.537 19.567A9.961 9.961 0 0 1 12 22C6.477 22 2 17.523 2 12S6.477 2 12 2s10 4.477 10 10c0 2.136-.67 4.116-1.81 5.74L17 12h3a8 8 0 1 0-2.46 5.772l.997 1.795z"/></symbol><symbol id="down-icon" viewBox="0 0 16 16"><path fill-rule="evenodd" clip-rule="evenodd" d="M12.7803 6.21967C13.0732 6.51256 13.0732 6.98744 12.7803 7.28033L8.53033 11.5303C8.23744 11.8232 7.76256 11.8232 7.46967 11.5303L3.21967 7.28033C2.92678 6.98744 2.92678 6.51256 3.21967 6.21967C3.51256 5.92678 3.98744 5.92678 4.28033 6.21967L8 9.93934L11.7197 6.21967C12.0126 5.92678 12.4874 5.92678 12.7803 6.21967Z"></path></symbol><symbol id="codepen-icon" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0z"/><path d="M16.5 13.202L13 15.535v3.596L19.197 15 16.5 13.202zM14.697 12L12 10.202 9.303 12 12 13.798 14.697 12zM20 10.869L18.303 12 20 13.131V10.87zM19.197 9L13 4.869v3.596l3.5 2.333L19.197 9zM7.5 10.798L11 8.465V4.869L4.803 9 7.5 10.798zM4.803 15L11 19.131v-3.596l-3.5-2.333L4.803 15zM4 13.131L5.697 12 4 10.869v2.262zM2 9a1 1 0 0 1 .445-.832l9-6a1 1 0 0 1 1.11 0l9 6A1 1 0 0 1 22 9v6a1 1 0 0 1-.445.832l-9 6a1 1 0 0 1-1.11 0l-9-6A1 1 0 0 1 2 15V9z"/></symbol><symbol id="close-icon" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0z"/><path d="M12 10.586l4.95-4.95 1.414 1.414-4.95 4.95 4.95 4.95-1.414 1.414-4.95-4.95-4.95 4.95-1.414-1.414 4.95-4.95-4.95-4.95L7.05 5.636z"/></symbol><symbol id="menu-icon" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0z"/><path d="M3 4h18v2H3V4zm0 7h18v2H3v-2zm0 7h18v2H3v-2z"/></symbol></defs></svg></head><body data-theme="light"><div class="sidebar-container"><div class="sidebar" id="sidebar"><a href="/" class="sidebar-title sidebar-title-anchor">Bajo API</a><div class="sidebar-items-container"><div class="sidebar-section-title with-arrow" data-isopen="false" id="sidebar-classes"><div>Classes</div><svg><use xlink:href="#down-icon"></use></svg></div><div class="sidebar-section-children-container"><div class="sidebar-section-children"><a href="App.html">App</a></div><div class="sidebar-section-children"><a href="Bajo.html">Bajo</a></div><div class="sidebar-section-children"><a href="Base.html">Base</a></div><div class="sidebar-section-children"><a href="Err.html">Err</a></div><div class="sidebar-section-children"><a href="Log.html">Log</a></div><div class="sidebar-section-children"><a href="Plugin.html">Plugin</a></div><div class="sidebar-section-children"><a href="Print.html">Print</a></div></div><div class="sidebar-section-title with-arrow" data-isopen="false" id="sidebar-events"><div>Events</div><svg><use xlink:href="#down-icon"></use></svg></div><div class="sidebar-section-children-container"><div class="sidebar-section-children"><a href="global.html#event:bajo:afterAll%257Bmethod%257D">bajo:afterAll{method}</a></div><div class="sidebar-section-children"><a href="global.html#event:bajo:afterBootComplete">bajo:afterBootComplete</a></div><div class="sidebar-section-children"><a href="global.html#event:bajo:afterBuildCollection">bajo:afterBuildCollection</a></div><div class="sidebar-section-children"><a href="global.html#event:bajo:afterCollectHooks">bajo:afterCollectHooks</a></div><div class="sidebar-section-children"><a href="global.html#event:bajo:beforeAll%257Bmethod%257D">bajo:beforeAll{method}</a></div><div class="sidebar-section-children"><a href="global.html#event:bajo:beforeBuildCollection">bajo:beforeBuildCollection</a></div><div class="sidebar-section-children"><a href="global.html#event:%257Bns%257D:afterAppletRun">{ns}:afterAppletRun</a></div><div class="sidebar-section-children"><a href="global.html#event:%257Bns%257D:after%257Bmethod%257D">{ns}:after{method}</a></div><div class="sidebar-section-children"><a href="global.html#event:%257Bns%257D:beforeAppletRun">{ns}:beforeAppletRun</a></div><div class="sidebar-section-children"><a href="global.html#event:%257Bns%257D:before%257Bmethod%257D">{ns}:before{method}</a></div></div><div class="sidebar-section-title with-arrow" data-isopen="false" id="sidebar-modules"><div>Modules</div><svg><use xlink:href="#down-icon"></use></svg></div><div class="sidebar-section-children-container"><div class="sidebar-section-children"><a href="module-Helper_Bajo.html">Helper/Bajo</a></div><div class="sidebar-section-children"><a href="module-Helper_Base.html">Helper/Base</a></div><div class="sidebar-section-children"><a href="module-Lib.html">Lib</a></div></div><div class="sidebar-section-title with-arrow" data-isopen="false" id="sidebar-global"><div>Global</div><svg><use xlink:href="#down-icon"></use></svg></div><div class="sidebar-section-children-container"><div class="sidebar-section-children"><a href="global.html#TAppConfigHandler">TAppConfigHandler</a></div><div class="sidebar-section-children"><a href="global.html#TAppEnv">TAppEnv</a></div><div class="sidebar-section-children"><a href="global.html#TAppLib">TAppLib</a></div><div class="sidebar-section-children"><a href="global.html#TBajoDataType">TBajoDataType</a></div><div class="sidebar-section-children"><a href="global.html#TBajoFormatResult">TBajoFormatResult</a></div><div class="sidebar-section-children"><a href="global.html#TBajoFormatType">TBajoFormatType</a></div><div class="sidebar-section-children"><a href="global.html#TLogJson">TLogJson</a></div><div class="sidebar-section-children"><a href="global.html#TLogLevels">TLogLevels</a></div><div class="sidebar-section-children"><a href="global.html#TNsPathPairs">TNsPathPairs</a></div><div class="sidebar-section-children"><a href="global.html#TNsPathResult">TNsPathResult</a></div><div class="sidebar-section-children"><a href="global.html#TPrintOptions">TPrintOptions</a></div><div class="sidebar-section-children"><a href="global.html#boot">boot</a></div></div></div></div></div><div class="navbar-container" id="VuAckcnZhf"><nav class="navbar"><div class="navbar-left-items"><div class="navbar-item"><a id="" href="https://www.npmjs.com/package/bajo" target="">NPM</a></div><div class="navbar-item"><a id="" href="https://github.com/ardhi/bajo" target="">Github</a></div><div class="navbar-item"><a id="" href="https://bajo.app" target="">Bajo</a></div></div><div class="navbar-right-items"><div class="navbar-right-item"><button class="icon-button search-button" aria-label="open-search"><svg><use xlink:href="#search-icon"></use></svg></button></div><div class="navbar-right-item"><button class="icon-button theme-toggle" aria-label="toggle-theme"><svg><use class="theme-svg-use" xlink:href="#dark-theme-icon"></use></svg></button></div><div class="navbar-right-item"><button class="icon-button font-size" aria-label="change-font-size"><svg><use xlink:href="#font-size-icon"></use></svg></button></div></div><nav></nav></nav></div><div class="toc-container"><div class="toc-content"><span class="bold">On this page</span><div id="eed4d2a0bfd64539bb9df78095dec881"></div></div></div><div class="body-wrapper"><div class="main-content"><div class="main-wrapper"><section id="source-page" class="source-page"><header><h1 id="title" class="has-anchor">class_bajo.js</h1></header><article><pre class="prettyprint source lang-js"><code>import Plugin from './plugin.js'
3
+ <![endif]--><script src="scripts/third-party/hljs.js" defer="defer"></script><script src="scripts/third-party/hljs-line-num.js" defer="defer"></script><script src="scripts/third-party/popper.js" defer="defer"></script><script src="scripts/third-party/tippy.js" defer="defer"></script><script src="scripts/third-party/tocbot.min.js"></script><script>var baseURL="/",locationPathname="";baseURL=(locationPathname=document.location.pathname).substr(0,locationPathname.lastIndexOf("/")+1)</script><link rel="stylesheet" href="styles/clean-jsdoc-theme.min.css"><svg aria-hidden="true" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" style="display:none"><defs><symbol id="copy-icon" viewbox="0 0 488.3 488.3"><g><path d="M314.25,85.4h-227c-21.3,0-38.6,17.3-38.6,38.6v325.7c0,21.3,17.3,38.6,38.6,38.6h227c21.3,0,38.6-17.3,38.6-38.6V124 C352.75,102.7,335.45,85.4,314.25,85.4z M325.75,449.6c0,6.4-5.2,11.6-11.6,11.6h-227c-6.4,0-11.6-5.2-11.6-11.6V124 c0-6.4,5.2-11.6,11.6-11.6h227c6.4,0,11.6,5.2,11.6,11.6V449.6z"/><path d="M401.05,0h-227c-21.3,0-38.6,17.3-38.6,38.6c0,7.5,6,13.5,13.5,13.5s13.5-6,13.5-13.5c0-6.4,5.2-11.6,11.6-11.6h227 c6.4,0,11.6,5.2,11.6,11.6v325.7c0,6.4-5.2,11.6-11.6,11.6c-7.5,0-13.5,6-13.5,13.5s6,13.5,13.5,13.5c21.3,0,38.6-17.3,38.6-38.6 V38.6C439.65,17.3,422.35,0,401.05,0z"/></g></symbol><symbol id="search-icon" viewBox="0 0 512 512"><g><g><path d="M225.474,0C101.151,0,0,101.151,0,225.474c0,124.33,101.151,225.474,225.474,225.474 c124.33,0,225.474-101.144,225.474-225.474C450.948,101.151,349.804,0,225.474,0z M225.474,409.323 c-101.373,0-183.848-82.475-183.848-183.848S124.101,41.626,225.474,41.626s183.848,82.475,183.848,183.848 S326.847,409.323,225.474,409.323z"/></g></g><g><g><path d="M505.902,476.472L386.574,357.144c-8.131-8.131-21.299-8.131-29.43,0c-8.131,8.124-8.131,21.306,0,29.43l119.328,119.328 c4.065,4.065,9.387,6.098,14.715,6.098c5.321,0,10.649-2.033,14.715-6.098C514.033,497.778,514.033,484.596,505.902,476.472z"/></g></g></symbol><symbol id="font-size-icon" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0z"/><path d="M11.246 15H4.754l-2 5H.6L7 4h2l6.4 16h-2.154l-2-5zm-.8-2L8 6.885 5.554 13h4.892zM21 12.535V12h2v8h-2v-.535a4 4 0 1 1 0-6.93zM19 18a2 2 0 1 0 0-4 2 2 0 0 0 0 4z"/></symbol><symbol id="add-icon" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0z"/><path d="M11 11V5h2v6h6v2h-6v6h-2v-6H5v-2z"/></symbol><symbol id="minus-icon" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0z"/><path d="M5 11h14v2H5z"/></symbol><symbol id="dark-theme-icon" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0z"/><path d="M10 7a7 7 0 0 0 12 4.9v.1c0 5.523-4.477 10-10 10S2 17.523 2 12 6.477 2 12 2h.1A6.979 6.979 0 0 0 10 7zm-6 5a8 8 0 0 0 15.062 3.762A9 9 0 0 1 8.238 4.938 7.999 7.999 0 0 0 4 12z"/></symbol><symbol id="light-theme-icon" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0z"/><path d="M12 18a6 6 0 1 1 0-12 6 6 0 0 1 0 12zm0-2a4 4 0 1 0 0-8 4 4 0 0 0 0 8zM11 1h2v3h-2V1zm0 19h2v3h-2v-3zM3.515 4.929l1.414-1.414L7.05 5.636 5.636 7.05 3.515 4.93zM16.95 18.364l1.414-1.414 2.121 2.121-1.414 1.414-2.121-2.121zm2.121-14.85l1.414 1.415-2.121 2.121-1.414-1.414 2.121-2.121zM5.636 16.95l1.414 1.414-2.121 2.121-1.414-1.414 2.121-2.121zM23 11v2h-3v-2h3zM4 11v2H1v-2h3z"/></symbol><symbol id="reset-icon" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0z"/><path d="M18.537 19.567A9.961 9.961 0 0 1 12 22C6.477 22 2 17.523 2 12S6.477 2 12 2s10 4.477 10 10c0 2.136-.67 4.116-1.81 5.74L17 12h3a8 8 0 1 0-2.46 5.772l.997 1.795z"/></symbol><symbol id="down-icon" viewBox="0 0 16 16"><path fill-rule="evenodd" clip-rule="evenodd" d="M12.7803 6.21967C13.0732 6.51256 13.0732 6.98744 12.7803 7.28033L8.53033 11.5303C8.23744 11.8232 7.76256 11.8232 7.46967 11.5303L3.21967 7.28033C2.92678 6.98744 2.92678 6.51256 3.21967 6.21967C3.51256 5.92678 3.98744 5.92678 4.28033 6.21967L8 9.93934L11.7197 6.21967C12.0126 5.92678 12.4874 5.92678 12.7803 6.21967Z"></path></symbol><symbol id="codepen-icon" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0z"/><path d="M16.5 13.202L13 15.535v3.596L19.197 15 16.5 13.202zM14.697 12L12 10.202 9.303 12 12 13.798 14.697 12zM20 10.869L18.303 12 20 13.131V10.87zM19.197 9L13 4.869v3.596l3.5 2.333L19.197 9zM7.5 10.798L11 8.465V4.869L4.803 9 7.5 10.798zM4.803 15L11 19.131v-3.596l-3.5-2.333L4.803 15zM4 13.131L5.697 12 4 10.869v2.262zM2 9a1 1 0 0 1 .445-.832l9-6a1 1 0 0 1 1.11 0l9 6A1 1 0 0 1 22 9v6a1 1 0 0 1-.445.832l-9 6a1 1 0 0 1-1.11 0l-9-6A1 1 0 0 1 2 15V9z"/></symbol><symbol id="close-icon" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0z"/><path d="M12 10.586l4.95-4.95 1.414 1.414-4.95 4.95 4.95 4.95-1.414 1.414-4.95-4.95-4.95 4.95-1.414-1.414 4.95-4.95-4.95-4.95L7.05 5.636z"/></symbol><symbol id="menu-icon" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0z"/><path d="M3 4h18v2H3V4zm0 7h18v2H3v-2zm0 7h18v2H3v-2z"/></symbol></defs></svg></head><body data-theme="light"><div class="sidebar-container"><div class="sidebar" id="sidebar"><a href="/" class="sidebar-title sidebar-title-anchor">Bajo API</a><div class="sidebar-items-container"><div class="sidebar-section-title with-arrow" data-isopen="false" id="sidebar-classes"><div>Classes</div><svg><use xlink:href="#down-icon"></use></svg></div><div class="sidebar-section-children-container"><div class="sidebar-section-children"><a href="App.html">App</a></div><div class="sidebar-section-children"><a href="Bajo.html">Bajo</a></div><div class="sidebar-section-children"><a href="Base.html">Base</a></div><div class="sidebar-section-children"><a href="Cache.html">Cache</a></div><div class="sidebar-section-children"><a href="Err.html">Err</a></div><div class="sidebar-section-children"><a href="Log.html">Log</a></div><div class="sidebar-section-children"><a href="Plugin.html">Plugin</a></div><div class="sidebar-section-children"><a href="Print.html">Print</a></div><div class="sidebar-section-children"><a href="Tools.html">Tools</a></div></div><div class="sidebar-section-title with-arrow" data-isopen="false" id="sidebar-events"><div>Events</div><svg><use xlink:href="#down-icon"></use></svg></div><div class="sidebar-section-children-container"><div class="sidebar-section-children"><a href="global.html#event:bajo:afterAll%257Bmethod%257D">bajo:afterAll{method}</a></div><div class="sidebar-section-children"><a href="global.html#event:bajo:afterBootComplete">bajo:afterBootComplete</a></div><div class="sidebar-section-children"><a href="global.html#event:bajo:afterBuildCollection">bajo:afterBuildCollection</a></div><div class="sidebar-section-children"><a href="global.html#event:bajo:afterCollectHooks">bajo:afterCollectHooks</a></div><div class="sidebar-section-children"><a href="global.html#event:bajo:beforeAll%257Bmethod%257D">bajo:beforeAll{method}</a></div><div class="sidebar-section-children"><a href="global.html#event:bajo:beforeBuildCollection">bajo:beforeBuildCollection</a></div><div class="sidebar-section-children"><a href="global.html#event:%257Bns%257D:after%257Bmethod%257D">{ns}:after{method}</a></div><div class="sidebar-section-children"><a href="global.html#event:%257Bns%257D:beforeAppletRun">{ns}:beforeAppletRun</a></div><div class="sidebar-section-children"><a href="global.html#event:%257Bns%257D:before%257Bmethod%257D">{ns}:before{method}</a></div><div class="sidebar-section-children"><a href="module-Helper%257Bns%257D_afterAppletRun.html">Helper{ns}:afterAppletRun</a></div></div><div class="sidebar-section-title with-arrow" data-isopen="false" id="sidebar-modules"><div>Modules</div><svg><use xlink:href="#down-icon"></use></svg></div><div class="sidebar-section-children-container"><div class="sidebar-section-children"><a href="module-Helper.html">Helper</a></div><div class="sidebar-section-children"><a href="module-Lib.html">Lib</a></div></div><div class="sidebar-section-title with-arrow" data-isopen="false" id="sidebar-global"><div>Global</div><svg><use xlink:href="#down-icon"></use></svg></div><div class="sidebar-section-children-container"><div class="sidebar-section-children"><a href="global.html#TAppConfigHandler">TAppConfigHandler</a></div><div class="sidebar-section-children"><a href="global.html#TAppEnv">TAppEnv</a></div><div class="sidebar-section-children"><a href="global.html#TBajoDataType">TBajoDataType</a></div><div class="sidebar-section-children"><a href="global.html#TBajoFormatResult">TBajoFormatResult</a></div><div class="sidebar-section-children"><a href="global.html#TBajoFormatType">TBajoFormatType</a></div><div class="sidebar-section-children"><a href="global.html#TLogJson">TLogJson</a></div><div class="sidebar-section-children"><a href="global.html#TLogLevels">TLogLevels</a></div><div class="sidebar-section-children"><a href="global.html#TNsPathPairs">TNsPathPairs</a></div><div class="sidebar-section-children"><a href="global.html#TNsPathResult">TNsPathResult</a></div><div class="sidebar-section-children"><a href="global.html#TPrintOptions">TPrintOptions</a></div><div class="sidebar-section-children"><a href="global.html#boot">boot</a></div></div></div></div></div><div class="navbar-container" id="VuAckcnZhf"><nav class="navbar"><div class="navbar-left-items"><div class="navbar-item"><a id="" href="https://www.npmjs.com/package/bajo" target="">NPM</a></div><div class="navbar-item"><a id="" href="https://github.com/ardhi/bajo" target="">Github</a></div><div class="navbar-item"><a id="" href="https://bajo.app" target="">Bajo</a></div></div><div class="navbar-right-items"><div class="navbar-right-item"><button class="icon-button search-button" aria-label="open-search"><svg><use xlink:href="#search-icon"></use></svg></button></div><div class="navbar-right-item"><button class="icon-button theme-toggle" aria-label="toggle-theme"><svg><use class="theme-svg-use" xlink:href="#dark-theme-icon"></use></svg></button></div><div class="navbar-right-item"><button class="icon-button font-size" aria-label="change-font-size"><svg><use xlink:href="#font-size-icon"></use></svg></button></div></div><nav></nav></nav></div><div class="toc-container"><div class="toc-content"><span class="bold">On this page</span><div id="eed4d2a0bfd64539bb9df78095dec881"></div></div></div><div class="body-wrapper"><div class="main-content"><div class="main-wrapper"><section id="source-page" class="source-page"><header><h1 id="title" class="has-anchor">class_bajo.js</h1></header><article><pre class="prettyprint source lang-js"><code>import Tools from './tools.js'
4
+ import Plugin from './plugin.js'
4
5
  import increment from 'add-filename-increment'
5
6
  import fs from 'fs-extra'
6
7
  import path from 'path'
7
8
  import os from 'os'
8
- import ms from 'ms'
9
- import dotenvParseVariables from 'dotenv-parse-variables'
10
9
  import emptyDir from 'empty-dir'
11
10
  import lodash from 'lodash'
12
- import currentLoc from '../lib/current-loc.js'
13
11
  import { createRequire } from 'module'
14
12
  import getGlobalPath from 'get-global-path'
15
- import { customAlphabet } from 'nanoid'
16
13
  import fastGlob from 'fast-glob'
17
14
  import querystring from 'querystring'
18
- import deepFreeze from 'deep-freeze-strict'
19
- import resolvePath from '../lib/resolve-path.js'
20
15
  import importModule from '../lib/import-module.js'
21
16
  import logLevels from '../lib/log-levels.js'
22
17
  import { types as formatTypes, formats } from '../lib/formats.js'
18
+ import aneka from 'aneka'
23
19
  import {
24
20
  buildBaseConfig,
25
21
  buildExtConfig,
@@ -28,17 +24,37 @@ import {
28
24
  bootOrder,
29
25
  bootPlugins,
30
26
  exitHandler
31
- } from './helper/bajo.js'
27
+ } from './_helper.js'
32
28
 
33
29
  const require = createRequire(import.meta.url)
34
30
 
35
31
  const {
36
32
  isFunction, map, isObject,
37
- trim, filter, isEmpty, orderBy, pullAt, find, camelCase, isNumber,
38
- cloneDeep, isPlainObject, isArray, isString, set, omit, keys, indexOf,
33
+ trim, filter, isEmpty, orderBy, pullAt, find, camelCase,
34
+ cloneDeep, isPlainObject, isArray, isString, omit, keys, indexOf,
39
35
  last, get, has, values, dropRight, pick
40
36
  } = lodash
41
37
 
38
+ const { resolvePath } = aneka
39
+
40
+ /**
41
+ * Name based ```{ns}:{path}``` format.
42
+ *
43
+ * @typedef {string} TNsPathPairs
44
+ * @see TNsPathResult
45
+ * @see Bajo#buildNsPath
46
+ * @see Bajo#breakNsPath
47
+ */
48
+
49
+ /**
50
+ * Object returned by {@link Bajo#getUnitFormat|bajo:getUnitFormat}.
51
+ *
52
+ * @typedef {Object} TBajoFormatResult
53
+ * @property {string} unitSys - Unit system.
54
+ * @property {Object} format - Format object.
55
+ * @see Bajo#getUnitFormat
56
+ */
57
+
42
58
  /**
43
59
  * The Core. The main engine. The one and only plugin that control app's boot process and
44
60
  * making sure all other plugins working nicely.
@@ -46,31 +62,28 @@ const {
46
62
  * @class
47
63
  */
48
64
  class Bajo extends Plugin {
49
- static alias = 'bajo'
50
65
  /**
51
- * @param {App} app - App instance. Usefull to call app method inside a plugin
66
+ * @param {App} app - App instance. Usefull to call app method inside a plugin.
52
67
  */
53
68
  constructor (app) {
54
69
  super('bajo', app)
70
+ this.alias = 'bajo'
55
71
  this.whiteSpace = [' ', '\t', '\n', '\r']
56
72
  /**
57
- * Config object
73
+ * Config object.
58
74
  *
59
75
  * @type {Object}
60
76
  * @see {@tutorial config}
61
77
  */
62
78
  this.config = {}
63
79
 
80
+ // by defaualt, only these config formats below are supported.
64
81
  app.configHandlers = [
65
- { ext: '.js', readHandler: this._defConfigHandler },
82
+ { ext: '.js', readHandler: this.fromJs },
66
83
  { ext: '.json', readHandler: this.fromJson, writeHandler: this.toJson }
67
84
  ]
68
- }
69
85
 
70
- async _defConfigHandler (file, opts = {}) {
71
- let mod = await importModule(file)
72
- if (isFunction(mod)) mod = await mod.call(this, opts)
73
- return mod
86
+ this.hooks = []
74
87
  }
75
88
 
76
89
  /**
@@ -95,40 +108,12 @@ class Bajo extends Plugin {
95
108
  await bootOrder.call(this)
96
109
  await bootPlugins.call(this)
97
110
  await exitHandler.call(this)
111
+ if (this.app.bajoSpatial) {
112
+ this.anekaSpatial = await this.importPkg('bajoSpatial:aneka-spatial')
113
+ }
98
114
  }
99
115
 
100
- /**
101
- * Resolve file name to filesystem's path. Windows path separator ```\```
102
- * is normalized to Unix's ```/```
103
- *
104
- * @method
105
- * @param {string} file - File to resolve
106
- * @param {boolean} [asFileUrl=false] - Return as file URL format ```file:///&lt;name>```
107
- * @returns {string}
108
- */
109
- resolvePath = (file, asFileUrl) => {
110
- return resolvePath(file, asFileUrl)
111
- }
112
-
113
- /**
114
- * Freeze object
115
- *
116
- * @method
117
- * @param {Object} obj - Object to freeze
118
- * @param {boolean} [shallow=false] - If ```false``` (default), deep freeze object
119
- */
120
- freeze = (obj, shallow = false) => {
121
- if (shallow) Object.freeze(obj)
122
- else deepFreeze(obj)
123
- }
124
-
125
- setImmediate = async () => {
126
- return new Promise((resolve) => {
127
- setImmediate(() => resolve())
128
- })
129
- }
130
-
131
- breakNsPathFromFile = ({ file, dir, baseNs, suffix = '', getType } = {}) => {
116
+ breakNsPathFromFile = ({ file = '', dir = '', ns, suffix = '', getType } = {}) => {
132
117
  let item = file.replace(dir + suffix, '')
133
118
  let type
134
119
  if (getType) {
@@ -140,32 +125,24 @@ class Bajo extends Plugin {
140
125
  let [name, _path] = item.split('@')
141
126
  if (!_path) {
142
127
  _path = name
143
- name = baseNs
128
+ name = ns
144
129
  }
145
130
  _path = camelCase(_path)
146
131
  const names = map(name.split('.'), n => camelCase(n))
147
- const [ns, subNs] = names
148
- return { ns, subNs, path: _path, fullNs: names.join('.'), type }
132
+ const [_ns, subNs] = names
133
+ return { ns: _ns, subNs, path: _path, fullNs: names.join('.'), type }
149
134
  }
150
135
 
151
136
  /**
152
- * Name based ```{ns}:{path}``` format
153
- * @typedef {string} TNsPathPairs
154
- * @see TNsPathResult
155
- * @see Bajo#buildNsPath
156
- * @see Bajo#breakNsPath
157
- */
158
-
159
- /**
160
- * Build ns/path pairs
137
+ * Build ns/path pairs.
161
138
  *
162
139
  * @method
163
- * @param {object} [options={}] - Options object
164
- * @param {string} [options.ns=''] - Namespace
165
- * @param {string} [options.subNs] - Sub namespace
166
- * @param {string} [options.subSubNs] - Sub sub namespace
167
- * @param {string} [options.path] - Path
168
- * @returns {TNsPathPairs} - Ns/path pairs
140
+ * @param {object} [options={}] - Options object.
141
+ * @param {string} [options.ns=''] - Namespace.
142
+ * @param {string} [options.subNs] - Sub namespace.
143
+ * @param {string} [options.subSubNs] - Sub sub namespace.
144
+ * @param {string} [options.path] - Path.
145
+ * @returns {TNsPathPairs} Ns/path pairs.
169
146
  */
170
147
  buildNsPath = ({ ns = '', subNs, subSubNs, path } = {}) => {
171
148
  if (subNs) ns += '.' + subNs
@@ -174,21 +151,21 @@ class Bajo extends Plugin {
174
151
  }
175
152
 
176
153
  /**
177
- * Object returned by {@link Bajo#breakNsPath|bajo:breakNsPath}
154
+ * Object returned by {@link Bajo#breakNsPath|bajo:breakNsPath}.
178
155
  *
179
156
  * @typedef {Object} TNsPathResult
180
- * @property {string} ns - Namespace
181
- * @property {string} [subNs] - Sub namespace
182
- * @property {string} [subSubNs] - Sub of sub namespace
183
- * @property {string} path - Path without query string or hash
184
- * @property {string} fullPath - Full path, including query string and hash
157
+ * @property {string} ns - Namespace.
158
+ * @property {string} [subNs] - Sub namespace.
159
+ * @property {string} [subSubNs] - Sub of sub namespace.
160
+ * @property {string} path - Path without query string or hash.
161
+ * @property {string} fullPath - Full path, including query string and hash.
185
162
  * @see TNsPathPairs
186
163
  * @see Bajo#buildNsPath
187
164
  * @see Bajo#breakNsPath
188
165
  */
189
166
 
190
167
  /**
191
- * Break name to its namespace &amp; path infos
168
+ * Break name to its namespace &amp; path infos.
192
169
  *
193
170
  * @method
194
171
  * @param {(TNsPathPairs|string)} name - Name to break
@@ -208,7 +185,7 @@ class Bajo extends Plugin {
208
185
  [ns, subNs, subSubNs] = ns.split('.')
209
186
  if (checkNs) {
210
187
  if (!this.app[ns]) {
211
- const plugin = this.getPlugin(ns)
188
+ const plugin = this.app.getPlugin(ns)
212
189
  if (plugin) ns = plugin.ns
213
190
  }
214
191
  if (!this.app[ns]) throw this.error('unknownPluginOrNotLoaded%s')
@@ -245,19 +222,19 @@ class Bajo extends Plugin {
245
222
  *
246
223
  * @method
247
224
  * @async
248
- * @param {Object} options - Options
225
+ * @param {Object} options - Options.
249
226
  * @param {string} [options.ns] - Namespace. If not provided, defaults to ```bajo```
250
- * @param {function} [options.handler] - Handler to call while building the collection item
251
- * @param {string[]} [options.dupChecks=[]] - Array of keys to check for duplicates
252
- * @param {string} options.container - Key used as container name
253
- * @param {boolean} [options.useDefaultName=true] - If true (default) and ```name``` key is not provided, returned collection will be named ```default```
227
+ * @param {function} [options.handler] - Handler to call while building the collection item.
228
+ * @param {string[]} [options.dupChecks=[]] - Array of keys to check for duplicates.
229
+ * @param {string} options.container - Key used as container name.
230
+ * @param {boolean} [options.useDefaultName=true] - If true (default) and ```name``` key is not provided, returned collection will be named ```default```.
254
231
  * @fires bajo:beforeBuildCollection
255
232
  * @fires bajo:afterBuildCollection
256
233
  * @returns {Object[]} The collection
257
234
  */
258
235
  buildCollections = async (options = {}) => {
259
- let { ns, handler, dupChecks = [], container, useDefaultName } = options
260
- useDefaultName = useDefaultName ?? true
236
+ const { parseObject } = this.app.lib
237
+ let { ns, handler, dupChecks = [], container, useDefaultName = true, noDefault = true } = options
261
238
  if (!ns) ns = this.ns
262
239
  const cfg = this.app[ns].getConfig()
263
240
  let items = get(cfg, container, [])
@@ -265,7 +242,7 @@ class Bajo extends Plugin {
265
242
  this.app[ns].log.trace('collecting%s', this.t(container))
266
243
 
267
244
  /**
268
- * Run before collection is built
245
+ * Run before collection is built.
269
246
  *
270
247
  * @global
271
248
  * @event bajo:beforeBuildCollection
@@ -276,7 +253,7 @@ class Bajo extends Plugin {
276
253
  await this.runHook(`${ns}:beforeBuildCollection`, container)
277
254
  const deleted = []
278
255
  for (const index in items) {
279
- const item = items[index]
256
+ const item = parseObject(items[index])
280
257
  if (useDefaultName) {
281
258
  if (!has(item, 'name')) {
282
259
  if (find(items, { name: 'default' })) throw this.app[ns].error('collExists%s', 'default')
@@ -300,6 +277,8 @@ class Bajo extends Plugin {
300
277
  }
301
278
  }
302
279
 
280
+ if (!noDefault &amp;&amp; !items.find(item => item.name === 'default')) this.app[ns].fatal('missing%s%s', 'default', container)
281
+
303
282
  /**
304
283
  * Run after collection is built
305
284
  *
@@ -325,26 +304,25 @@ class Bajo extends Plugin {
325
304
  *
326
305
  * @method
327
306
  * @async
328
- * @param {(TNsPathPairs|Object|function)} name - Method's name, function handler, plain object or plugin instance
329
- * @param {...any} [args] - One or more arguments passed as parameter to the handler
330
- * @returns {any} Returned value
307
+ * @param {(TNsPathPairs|Object|function)} name - Method's name, function handler, plain object or plugin instance.
308
+ * @param {...any} [args] - One or more arguments passed as parameter to the handler.
309
+ * @returns {any} Returned value.
331
310
  */
332
311
  callHandler = async (item, ...args) => {
333
312
  let result
334
313
  let scope = this
335
- if (item instanceof Plugin) {
314
+ if (item instanceof Tools || item instanceof Plugin) {
336
315
  scope = item
337
316
  item = args.shift()
338
317
  }
339
- const bajo = scope.app.bajo
340
318
  if (isString(item)) {
341
- if (item.startsWith('applet:') &amp;&amp; bajo.app.applets.length > 0) {
319
+ if (item.startsWith('applet:') &amp;&amp; this.app.applets.length > 0) {
342
320
  const [, ns, path] = item.split(':')
343
- const applet = find(bajo.app.applets, a => (a.ns === ns || a.alias === ns))
344
- if (applet &amp;&amp; scope.app.bajoCli) result = await scope.app.bajoCli.runApplet(applet, path, ...args)
321
+ const applet = find(this.app.applets, a => (a.ns === ns || a.alias === ns))
322
+ if (applet &amp;&amp; this.app.bajoCli) result = await this.app.bajoCli.runApplet(applet, path, ...args)
345
323
  } else {
346
324
  const [ns, method, ...params] = item.split(':')
347
- const fn = bajo.getMethod(`${ns}:${method}`)
325
+ const fn = this.getMethod(`${ns}:${method}`)
348
326
  if (fn) {
349
327
  if (params.length > 0) args.unshift(...params)
350
328
  result = await fn(...args)
@@ -367,34 +345,30 @@ class Bajo extends Plugin {
367
345
  *
368
346
  * @method
369
347
  * @async
370
- * @param {function} handler - Function handler. Can be an async function. Scoped to the running plugin
348
+ * @param {function} handler - Function handler. Can be an async function. Scoped to the running plugin.
371
349
  * @param {(string|Object)} [options={}] - Options. If a string is provided, it serves as the glob pattern, otherwise:
372
350
  * @param {(string|string[])} [options.glob] - Glob pattern. If provided,
373
- * @param {boolean} [options.useBajo=false] - If true, add ```bajo``` to the running plugins too
374
- * @param {string} [options.prefix=''] - Prepend glob pattern with prefix
375
- * @param {boolean} [options.noUnderscore=true] - If true (default), matched file with name starts with underscore is ignored
376
- * @param {any} [options.returnItems] - If true, each value of returned handler call will be saved as an object with running plugin name as its keys
351
+ * @param {boolean} [options.useBajo=false] - If true, add ```bajo``` to the running plugins too.
352
+ * @param {string} [options.prefix=''] - Prepend glob pattern with prefix.
353
+ * @param {boolean} [options.noUnderscore=true] - If true (default), matched file with name starts with underscore is ignored.
354
+ * @param {any} [options.returnItems] - If true, each value of returned handler call will be saved as an object with running plugin name as its keys.
377
355
  * @returns {any}
378
356
  */
379
357
  eachPlugins = async (handler, options = {}) => {
380
- if (typeof options === 'string') options = { glob: options }
358
+ if (isString(options)) options = { glob: options }
359
+ const { glob = [], useBajo, prefix = '', noUnderscore = true, returnItems, opts = {} } = options
360
+ const globs = isString(glob) ? [glob] : [...glob]
361
+ const pluginPkgs = useBajo ? [...cloneDeep(this.app.pluginPkgs), 'bajo'] : this.app.pluginPkgs
381
362
  const result = {}
382
- const pluginPkgs = cloneDeep(this.app.pluginPkgs) ?? []
383
- const { glob, useBajo, prefix = '', noUnderscore = true, returnItems } = options
384
- if (useBajo) pluginPkgs.unshift('bajo')
385
363
  for (const pkgName of pluginPkgs) {
386
364
  const ns = camelCase(pkgName)
387
365
  let r
388
- if (glob) {
366
+ if (globs.length > 0) {
389
367
  const base = prefix === '' ? `${this.app[ns].dir.pkg}/extend` : `${this.app[ns].dir.pkg}/extend/${prefix}`
390
- let opts = isString(glob) ? { pattern: [glob] } : glob
391
- let pattern = opts.pattern ?? []
392
- if (isString(pattern)) pattern = [pattern]
393
- opts = omit(opts, ['pattern'])
394
- for (const i in pattern) {
395
- if (!path.isAbsolute(pattern[i])) pattern[i] = `${base}/${pattern[i]}`
396
- }
397
- const files = await fastGlob(pattern, opts)
368
+ const patterns = globs.map(glob => {
369
+ return !path.isAbsolute(glob) ? `${base}/${glob}` : glob
370
+ })
371
+ const files = await fastGlob.glob(patterns, opts)
398
372
  for (const f of files) {
399
373
  if (path.basename(f)[0] === '_' &amp;&amp; noUnderscore) continue
400
374
  const resp = await handler.call(this.app[ns], { file: f, dir: base })
@@ -425,22 +399,13 @@ class Bajo extends Plugin {
425
399
  }
426
400
 
427
401
  /**
428
- * Object returned by {@link Bajo#getUnitFormat|bajo:getUnitFormat}
429
- *
430
- * @typedef {Object} TBajoFormatResult
431
- * @property {string} unitSys - Unit system
432
- * @property {Object} format - Format object
433
- * @see Bajo#getUnitFormat
434
- */
435
-
436
- /**
437
- * Get unit format
402
+ * Get unit format.
438
403
  *
439
404
  * @method
440
- * @param {Object} [options={}] - Options
441
- * @param {string} [options.lang] - Language to use. Defaults to the one you set in config
442
- * @param {string} [options.unitSys] - Unit system to use. Defaults to language's unit system or ```metric``` if unspecified
443
- * @returns {TBajoFormatResult} - Returned value
405
+ * @param {Object} [options={}] - Options.
406
+ * @param {string} [options.lang] - Language to use. Defaults to the one you set in config.
407
+ * @param {string} [options.unitSys] - Unit system to use. Defaults to language's unit system or ```metric``` if unspecified.
408
+ * @returns {TBajoFormatResult} Returned value.
444
409
  */
445
410
  getUnitFormat = (options = {}) => {
446
411
  const lang = options.lang ?? this.config.lang
@@ -450,16 +415,16 @@ class Bajo extends Plugin {
450
415
  }
451
416
 
452
417
  /**
453
- * Format value by type
418
+ * Format value by type.
454
419
  *
455
420
  * @method
456
- * @param {string} type - Format type. See {@link TBajoFormatType} for acceptable values
457
- * @param {any} value - Value to format
458
- * @param {string} [dataType] - Value's data type. See {@link TBajoDataType} for acceptable values
459
- * @param {Object} [options={}] - Options
460
- * @param {boolean} [options.withUnit=true] - Return with its unit appended
461
- * @param {string} [options.lang] - Format value according to this language. Defaults to the one you set in config
462
- * @returns {(Array|string)} Return string if ```withUnit``` is true. Otherwise is an array of ```[value, unit, separator]```
421
+ * @param {string} type - Format type. See {@link TBajoFormatType} for acceptable values.
422
+ * @param {any} value - Value to format.
423
+ * @param {string} [dataType] - Value's data type. See {@link TBajoDataType} for acceptable values.
424
+ * @param {Object} [options={}] - Options.
425
+ * @param {boolean} [options.withUnit=true] - Return with its unit appended.
426
+ * @param {string} [options.lang] - Format value according to this language. Defaults to the one you set in config.
427
+ * @returns {(Array|string)} Return string if ```withUnit``` is true. Otherwise is an array of ```[value, unit, separator]```.
463
428
  */
464
429
  formatByType = (type, value, dataType, options = {}) => {
465
430
  const { defaultsDeep } = this.app.lib.aneka
@@ -476,21 +441,21 @@ class Bajo extends Plugin {
476
441
  }
477
442
 
478
443
  /**
479
- * Format value
444
+ * Format value.
480
445
  *
481
446
  * @method
482
- * @param {any} value - Value to format
483
- * @param {string} [type] - Data type to use. See {@link TBajoDataType} for acceptable values. If not provided, return the untouched value
484
- * @param {Object} [options={}] - Options
485
- * @param {string} [options.emptyValue=''] - Empty value to use if function resulted empty. Defaults to the one from your config
486
- * @param {boolean} [options.withUnit=true] - Return with its unit appended
487
- * @param {string} [options.lang] - Format value according to this language. Defaults to the one you set in config
488
- * @param {string} [options.latitude] - If Bajo Spatial is loaded and data type is a double or float, then format it as latitude in degree, minute, second
489
- * @param {string} [options.longitude] - If Bajo Spatial is loaded and data type is a double or float, then format it as longitude in degree, minute, second
490
- * @returns {string} Formatted value
447
+ * @param {any} value - Value to format.
448
+ * @param {string} [type] - Data type to use. See {@link TBajoDataType} for acceptable values. If not provided, return the untouched value.
449
+ * @param {Object} [options={}] - Options.
450
+ * @param {string} [options.emptyValue=''] - Empty value to use if function resulted empty. Defaults to the one from your config.
451
+ * @param {boolean} [options.withUnit=true] - Return with its unit appended.
452
+ * @param {string} [options.lang] - Format value according to this language. Defaults to the one you set in config.
453
+ * @param {string} [options.latitude] - If Bajo Spatial is loaded and data type is a double or float, then format it as latitude in degree, minute, second.
454
+ * @param {string} [options.longitude] - If Bajo Spatial is loaded and data type is a double or float, then format it as longitude in degree, minute, second.
455
+ * @returns {string} Formatted value.
491
456
  */
492
457
  format = (value, type, options = {}) => {
493
- const { defaultsDeep } = this.app.lib.aneka
458
+ const { defaultsDeep, isSet } = this.app.lib.aneka
494
459
  const { format } = this.config.intl
495
460
  const { emptyValue = format.emptyValue } = options
496
461
  const lang = options.lang ?? this.config.lang
@@ -500,8 +465,8 @@ class Bajo extends Plugin {
500
465
  if (type === 'auto') {
501
466
  if (value instanceof Date) type = 'datetime'
502
467
  }
503
- if (['float', 'double'].includes(type) &amp;&amp; this.app.bajoSpatial) {
504
- const { latToDms, lngToDms } = this.app.lib.anekaSpatial
468
+ if (['float', 'double'].includes(type) &amp;&amp; this.anekaSpatial) {
469
+ const { latToDms, lngToDms } = this.anekaSpatial
505
470
  if (options.latitude) return latToDms(value)
506
471
  if (options.longitude) return lngToDms(value)
507
472
  }
@@ -532,46 +497,16 @@ class Bajo extends Plugin {
532
497
  }
533
498
  if (['array'].includes(type)) return value.join(', ')
534
499
  if (['object'].includes(type)) return JSON.stringify(value)
500
+ if (['boolean'].includes(type) &amp;&amp; isSet(value)) return value ? this.t('true', { lang }) : this.t('false', { lang })
535
501
  return value
536
502
  }
537
503
 
538
504
  /**
539
- * Generate unique random characters that can be used as ID. Use {@link https://github.com/ai/nanoid|nanoid} under the hood
540
- *
541
- * @method
542
- * @param {(boolean|string|Object)} [options={}] - Options. If set to ```true``` or ```alpha```, it will generate alphaphet only characters. If set to ```int```, it will generate integer only characters. Otherwise:
543
- * @param {string} [options.pattern] - Character pattern to use. Defaults to all available alphanumeric characters
544
- * @param {number} [options.length=13] - Length of resulted characters
545
- * @param {string} [options.case] - If set to ```lower``` to use lower cased pattern only. For upper cased pattern, set it to ```upper```
546
- * @param {boolean} [options.returnInstance] - Set to ```true``` to return {@link https://github.com/ai/nanoid|nanoid} instance instead of string
547
- * @returns {(string|Object)} Return string or instance of {@link https://github.com/ai/nanoid|nanoid}
548
- */
549
- generateId = (options = {}) => {
550
- let type
551
- if (options === true) options = 'alpha'
552
- if (options === 'int') {
553
- type = options
554
- options = { pattern: '0123456789', length: 15 }
555
- } else if (options === 'alpha') {
556
- type = options
557
- options = { pattern: 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ', length: 15 }
558
- }
559
- let { pattern, length = 13, returnInstance } = options
560
- pattern = pattern ?? 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'
561
- if (options.case === 'lower') pattern = pattern.toLowerCase()
562
- else if (options.case === 'upper') pattern = pattern.toUpperCase()
563
- const nid = customAlphabet(pattern, length)
564
- if (returnInstance) return nid
565
- const value = nid()
566
- return type === 'int' ? parseInt(value) : value
567
- }
568
-
569
- /**
570
- * Get NPM global module directory
505
+ * Get NPM global module directory.
571
506
  *
572
507
  * @method
573
- * @param {string} [pkgName] - If provided, return this package global directory. Otherwise the npm global module directory
574
- * @param {boolean} [silent=true] - Set to ```false``` to throw exception in case of error. Otherwise silently returns undefined
508
+ * @param {string} [pkgName] - If provided, return this package global directory. Otherwise the npm global module directory.
509
+ * @param {boolean} [silent=true] - Set to ```false``` to throw exception in case of error. Otherwise silently returns undefined.
575
510
  * @returns {string}
576
511
  */
577
512
  getGlobalModuleDir = (pkgName, silent = true) => {
@@ -595,49 +530,30 @@ class Bajo extends Plugin {
595
530
  }
596
531
 
597
532
  /**
598
- * Get class method by name
533
+ * Get class method by name.
599
534
  *
600
535
  * @method
601
- * @param {string} name - Name in format ```ns:methodName```
602
- * @param {boolean} [thrown=true] - If ```true``` (default), throw exceptiom in case of error
603
- * @returns {function} Class method
536
+ * @param {string} name - Name in format ```ns:methodName```.
537
+ * @param {boolean} [thrown=true] - If ```true``` (default), throw exception in case of error.
538
+ * @returns {function} Class method.
604
539
  */
605
540
  getMethod = (name = '', thrown = true) => {
606
- const { ns, path } = this.breakNsPath(name)
541
+ const { ns, path } = this.breakNsPath(name, thrown)
607
542
  const method = get(this.app, `${ns}.${path}`)
608
543
  if (method &amp;&amp; isFunction(method)) return method
609
544
  if (thrown) throw this.error('cantFindMethod%s', name)
610
545
  }
611
546
 
612
547
  /**
613
- * Find item deep in paths
614
- *
615
- * @method
616
- * @param {string} item - Item to find
617
- * @param {Array} paths - Array of path to look for
618
- * @returns {string}
619
- */
620
- findDeep = (item, paths) => {
621
- let dir
622
- for (const p of paths) {
623
- const d = `${p}/${item}`
624
- if (fs.existsSync(d)) {
625
- dir = d
626
- break
627
- }
628
- }
629
- return dir
630
- }
631
-
632
- /**
633
- * Get module directory, locally and globally
548
+ * Get module directory, locally and globally.
634
549
  *
635
550
  * @method
636
- * @param {string} pkgName - Package name to find
637
- * @param {string} base - Provide base name if ```pkgName``` is a module under ```base```'s package name
638
- * @returns {string} Return absolute package directory
551
+ * @param {string} pkgName - Package name to find.
552
+ * @param {string} base - Provide base name if ```pkgName``` is a module under ```base```'s package name.
553
+ * @returns {string} Return absolute package directory.
639
554
  */
640
555
  getModuleDir = (pkgName, base) => {
556
+ const { findDeep } = this.app.lib
641
557
  if (pkgName === 'main') return resolvePath(this.app.dir)
642
558
  if (base === 'main') base = this.app.dir
643
559
  else if (this &amp;&amp; this.app &amp;&amp; this.app[base]) base = this.app[base].pkgName
@@ -646,78 +562,12 @@ class Bajo extends Plugin {
646
562
  const gdir = this.getGlobalModuleDir()
647
563
  paths.unshift(gdir)
648
564
  paths.unshift(resolvePath(path.join(this.app.dir, 'node_modules')))
649
- let dir = this.findDeep(pkgPath, paths)
650
- if (base &amp;&amp; !dir) dir = this.findDeep(`${base}/node_modules/${pkgPath}`, paths)
565
+ let dir = findDeep(pkgPath, paths)
566
+ if (base &amp;&amp; !dir) dir = findDeep(`${base}/node_modules/${pkgPath}`, paths)
651
567
  if (!dir) return null
652
568
  return resolvePath(path.dirname(dir))
653
569
  }
654
570
 
655
- /**
656
- * Get plugin data directory
657
- *
658
- * @method
659
- * @param {string} name - Plugin name (namespace) or alias
660
- * @param {boolean} [ensureDir=true] - Set ```true``` (default) to ensure directory is existed
661
- * @returns {string}
662
- */
663
- getPluginDataDir = (name, ensureDir = true) => {
664
- const plugin = this.getPlugin(name)
665
- const dir = `${this.app.bajo.dir.data}/plugins/${plugin.ns}`
666
- if (ensureDir) fs.ensureDirSync(dir)
667
- return dir
668
- }
669
-
670
- /**
671
- * Resolve file path from:
672
- *
673
- * - local/absolute file
674
- * - TNsPath (```myPlugin:/path/to/file.txt```)
675
- *
676
- * @method
677
- * @param {string} file - File path, see above for supported types
678
- * @returns {string} Resolved file path
679
- */
680
- getPluginFile = (file) => {
681
- if (!this) return file
682
- if (file[0] === '.') file = `${currentLoc(import.meta).dir}/${trim(file.slice(1), '/')}`
683
- if (file.includes(':')) {
684
- if (file.slice(1, 2) === ':') return file // windows fs
685
- const { ns, path } = this.breakNsPath(file)
686
- if (ns !== 'file' &amp;&amp; this &amp;&amp; this.app &amp;&amp; this.app[ns] &amp;&amp; ns.length > 1) {
687
- file = `${this.app[ns].dir.pkg}${path}`
688
- }
689
- }
690
- return file
691
- }
692
-
693
- /**
694
- * Get plugin by name
695
- *
696
- * @method
697
- * @param {string} name - Plugin name/namespace or alias
698
- * @param {boolean} [silent] - If ```true```, silently return undefined even on error
699
- * @returns {Object} Plugin object
700
- */
701
- getPlugin = (name, silent) => {
702
- if (!this.app[name]) {
703
- // alias?
704
- let plugin
705
- for (const key in this.app) {
706
- const item = this.app[key]
707
- if (item instanceof Plugin &amp;&amp; (item.alias === name || item.pkgName === name)) {
708
- plugin = item
709
- break
710
- }
711
- }
712
- if (!plugin) {
713
- if (silent) return false
714
- throw this.error('pluginWithNameAliasNotLoaded%s', name)
715
- }
716
- name = plugin.ns
717
- }
718
- return this.app[name]
719
- }
720
-
721
571
  /**
722
572
  * Import file/module from any loaded plugins.
723
573
  *
@@ -755,8 +605,8 @@ class Bajo extends Plugin {
755
605
  *
756
606
  * @method
757
607
  * @async
758
- * @param {...TNsPathPairs} pkgs - One or more packages in format ```{ns}:{packageName}```
759
- * @returns {(Object|Array)} See above
608
+ * @param {...TNsPathPairs} pkgs - One or more packages in format ```{ns}:{packageName}```.
609
+ * @returns {(Object|Array)} See above.
760
610
  */
761
611
  importPkg = async (...pkgs) => {
762
612
  const { defaultsDeep } = this.app.lib.aneka
@@ -789,7 +639,7 @@ class Bajo extends Plugin {
789
639
  }
790
640
  result[name] = mod
791
641
  }
792
- if (notFound.length > 0) throw this.error('cantFind%s', this.join(notFound))
642
+ if (notFound.length > 0 &amp;&amp; opts.throwNotFound) throw this.error('cantFind%s', this.join(notFound))
793
643
  if (opts.asObject) return result
794
644
  if (pkgs.length === 1) return result[keys(result)[0]]
795
645
  return values(result)
@@ -800,21 +650,21 @@ class Bajo extends Plugin {
800
650
  *
801
651
  * @method
802
652
  * @async
803
- * @param {(string|TNsPathPairs)} dir - Directory to check
653
+ * @param {(string|TNsPathPairs)} dir - Directory to check.
804
654
  * @param {function} filterFn - Filter function to filter out files that cause false positives.
805
655
  * @returns {boolean}
806
656
  */
807
657
  isEmptyDir = async (dir, filterFn) => {
808
- dir = resolvePath(this.getPluginFile(dir))
658
+ dir = resolvePath(this.app.getPluginFile(dir))
809
659
  await fs.exists(dir)
810
660
  return await emptyDir(dir, filterFn)
811
661
  }
812
662
 
813
663
  /**
814
- * Check whether log level is within log's app current level
664
+ * Check whether log level is within log's app current level.
815
665
  *
816
666
  * @method
817
- * @param {string} level - Level to check. See {@link TLogLevels} for more
667
+ * @param {string} level - Level to check. See {@link TLogLevels} for more.
818
668
  * @returns {boolean}
819
669
  */
820
670
  isLogInRange = (level) => {
@@ -838,11 +688,11 @@ class Bajo extends Plugin {
838
688
  }
839
689
 
840
690
  /**
841
- * Check whether directory is a valid Bajo app
691
+ * Check whether directory is a valid Bajo app.
842
692
  *
843
693
  * @method
844
- * @param {string} dir - Directory to check
845
- * @param {boolean} [returnPkg] - Set ```true``` to return its package.json content
694
+ * @param {string} dir - Directory to check.
695
+ * @param {boolean} [returnPkg] - Set ```true``` to return its package.json content.
846
696
  * @returns {(boolean|Object)}
847
697
  */
848
698
  isValidApp = (dir, returnPkg) => {
@@ -851,11 +701,11 @@ class Bajo extends Plugin {
851
701
  }
852
702
 
853
703
  /**
854
- * Check whether directory is a valid Bajo plugin
704
+ * Check whether directory is a valid Bajo plugin.
855
705
  *
856
706
  * @method
857
- * @param {string} dir - Directory to check
858
- * @param {boolean} [returnPkg] - Set ```true``` to return its package.json content
707
+ * @param {string} dir - Directory to check.
708
+ * @param {boolean} [returnPkg] - Set ```true``` to return its package.json content.
859
709
  * @returns {(boolean|Object)}
860
710
  */
861
711
  isValidPlugin = (dir, returnPkg) => {
@@ -868,31 +718,31 @@ class Bajo extends Plugin {
868
718
  *
869
719
  * @method
870
720
  * @param {any[]} array - Array to join
871
- * @param {(string|Object)} options - If provided and is a string, it will be used as separator
872
- * @param {string} [options.separator=', '] - Separator to use
873
- * @param {string} [options.lastSeparator=and] - Text to use as the last separator
721
+ * @param {(string|Object)} options - If provided and is a string, it will be used as separator.
722
+ * @param {string} [options.separator=', '] - Separator to use.
723
+ * @param {string} [options.lastSeparator=and] - Text to use as the last separator.
874
724
  * @returns {string}
875
725
  */
876
- join = (array, options) => {
877
- const { isSet } = this.app.lib.aneka
878
- const translate = val => {
879
- return this.t(val).toLowerCase()
726
+ join = (input = [], options = {}) => {
727
+ const array = [...input]
728
+ if (isString(options)) options = { separator: options }
729
+ let { separator = ', ', lastSeparator = 'and', lang } = options
730
+ const translate = (val) => {
731
+ return this.t(val, { lang }).toLowerCase()
880
732
  }
881
733
  if (array.length === 0) return translate('none')
882
734
  if (array.length === 1) return array[0]
883
- if (isSet(options) &amp;&amp; !isPlainObject(options)) return array.join(options)
884
- let { separator = ', ', lastSeparator = 'and' } = options ?? {}
885
735
  lastSeparator = translate(lastSeparator)
886
736
  const last = (array.pop() ?? '').trim()
887
737
  return array.map(a => (a + '').trim()).join(separator) + ` ${lastSeparator} ${last}`
888
738
  }
889
739
 
890
740
  /**
891
- * Return its numeric portion of a value
741
+ * Return its numeric portion of a value.
892
742
  *
893
743
  * @method
894
- * @param {string} [value=''] - Value to get its numeric portion
895
- * @param {string} [defUnit=''] - Default unit if value doesn't have one
744
+ * @param {string} [value=''] - Value to get its numeric portion.
745
+ * @param {string} [defUnit=''] - Default unit if value doesn't have one.
896
746
  * @returns {string}
897
747
  */
898
748
  numUnit = (value = '', defUnit = '') => {
@@ -902,155 +752,117 @@ class Bajo extends Plugin {
902
752
  }
903
753
 
904
754
  /**
905
- * Parse duration to its millisecond value. Use {@link https://github.com/vercel/ms|ms} under the hood
906
- *
907
- * @method
908
- * @param {(number|string)} dur - If string is given, parse this to its millisecond value. Otherwise returns as is
909
- * @returns {number}
910
- * @see {@link https://github.com/vercel/ms|ms}
911
- */
912
- parseDur = (dur) => {
913
- return isNumber(dur) ? dur : ms(dur)
914
- }
915
-
916
- /**
917
- * Parse datetime string as Javascript date object. Please visit {@link https://day.js.org|dayjs} for valid formats and more infos
918
- *
919
- * @method
920
- * @param {string} dt - Datetime string
921
- * @returns {Object} Javascript date object
922
- * @see {@link https://day.js.org|dayjs}
923
- */
924
- parseDt = (dt) => {
925
- const value = this.app.lib.dayjs(dt)
926
- if (!value.isValid()) throw this.error('dtUnparsable%s', dt)
927
- return value.toDate()
928
- }
929
-
930
- /**
931
- * Parse an object and optionally normalize its values recursively. Use {@link https://github.com/ladjs/dotenv-parse-variables}
932
- * to parse values, so please have a visit to know how it works
755
+ * Read and parse file as config object. Supported types: ```.js``` and ```.json```.
756
+ * More supports can be added using plugin. {@link https://github.com/ardhi/bajo-config|bajo-config} gives you additional supports for ```.yml```, ```.yaml``` and ```.toml``` file.
933
757
  *
934
- * If ```options.parseValue``` is ```true```, any key ends with ```Dur``` and ```Dt``` will
935
- * also be parsed as millisecond and Javascript date time accordingly.
758
+ * If file extension is ```.*```, it will be auto detected and parsed accordingly
936
759
  *
937
760
  * @method
938
- * @param {(Object|string)} input - If string is given, parse it first using JSON.parse
939
- * @param {Object} [options={}] - Options
940
- * @param {boolean} [options.silent=true] - If ```true``` (default), exception are not thrown and silently ignored
941
- * @param {boolean} [options.parseValue=false] - If ```true```, values will be parsed &amp; normalized
942
- * @param {string} [options.lang] - If provided, use this language instead of the one in config
761
+ * @async
762
+ * @param {string} file - File to read and parse.
763
+ * @param {Object} [options={}] - Options.
764
+ * @param {boolean} [options.ignoreError] - Any exception will be silently discarded.
765
+ * @param {string} [options.ns] - If given, use this as the scope.
766
+ * @param {string} [options.pattern] - If given and auto detection is on (extension is ```.*```), it will be used for instead the default one.
767
+ * @param {Object} [options.defValue={}] - Default value to use if value returned empty.
768
+ * @param {Object} [options.parserOpts={}] - Parser setting.
769
+ * @param {Object} [options.globOpts={}] - {@link https://github.com/mrmlnc/fast-glob|fast-glob} options.
943
770
  * @returns {Object}
944
- * @see {@link https://github.com/ladjs/dotenv-parse-variables}
945
771
  */
946
- parseObject = (input, options = {}) => {
947
- const { silent = true, parseValue = false, lang, ns } = options
948
- const { isSet } = this.app.lib.aneka
949
- const translate = (item) => {
950
- const scope = ns ? this.app[ns] : this
951
- const [text, ...args] = item.split('|')
952
- return scope.t(text, ...args, { lang })
772
+ readConfig = async (file, options = {}) => {
773
+ const { parseObject } = this.app.lib
774
+ const { defaultsDeep } = this.app.lib.aneka
775
+ const { uniq, isString, isArray, findIndex, isPlainObject, merge } = this.app.lib._
776
+ let { ns, baseNs, extend, checkOverride, merge: merged, pattern, ignoreError = true, defValue = {}, parserOpts = {}, globOpts = {}, handler, cache = {} } = options
777
+
778
+ const getParseOptsArgs = (opts, orig) => {
779
+ opts.parserOpts = opts.parserOpts ?? {}
780
+ opts.parserOpts.args = opts.parserOpts.args ?? []
781
+ const idx = findIndex(opts.parserOpts.args, item => {
782
+ return isPlainObject(item) &amp;&amp; Object.keys(item)[0] === '_orig'
783
+ })
784
+ if (idx > -1) opts.parserOpts.args[idx] = { _orig: orig }
785
+ else opts.parserOpts.args.push({ _orig: orig })
953
786
  }
954
- const statics = ['*']
955
- if (isString(input)) {
956
- try {
957
- input = JSON.parse(input)
958
- } catch (err) {
959
- if (silent) input = {}
960
- else throw err
787
+
788
+ const output = async (obj) => {
789
+ let orig = parseObject(obj)
790
+ if (!baseNs || extend === false) {
791
+ await this.runHook('bajo:afterReadConfig', file, orig, options)
792
+ if (cache.name) await this.app.cache.save(cache.name, orig, cache.ttlDur)
793
+ return orig
961
794
  }
962
- }
963
- let obj = cloneDeep(input)
964
- const keys = Object.keys(obj)
965
- const mutated = []
966
- keys.forEach(k => {
967
- let v = obj[k]
968
- if (isPlainObject(v)) obj[k] = this.parseObject(v, options)
969
- else if (isArray(v)) {
970
- v.forEach((i, idx) => {
971
- if (isPlainObject(i)) obj[k][idx] = this.parseObject(i, options)
972
- else if (statics.includes(i)) obj[k][idx] = i
973
- else if (parseValue) obj[k][idx] = dotenvParseVariables(set({}, 'item', obj[k][idx]), { assignToProcessEnv: false }).item
974
- if (isArray(obj[k][idx])) obj[k][idx] = obj[k][idx].map(item => typeof item === 'string' ? item.trim() : item)
975
- })
976
- } else if (isSet(v)) {
977
- if (isString(v) &amp;&amp; v.startsWith('t:') &amp;&amp; lang) v = translate(v.slice(2))
978
- try {
979
- if (statics.includes(v)) obj[k] = v
980
- else if (k.startsWith('t:') &amp;&amp; isString(v)) {
981
- const newK = k.slice(2)
982
- if (lang) obj[newK] = translate(v)
983
- else obj[newK] = v
984
- mutated.push(k)
985
- } else if (parseValue) {
986
- obj[k] = dotenvParseVariables(set({}, 'item', v), { assignToProcessEnv: false }).item
987
- if (isArray(obj[k])) obj[k] = obj[k].map(item => typeof item === 'string' ? item.trim() : item)
988
- }
989
- if (k.slice(-3) === 'Dur') obj[k] = this.parseDur(v)
990
- if (k.slice(-2) === 'Dt') obj[k] = this.parseDt(v)
991
- } catch (err) {
992
- obj[k] = undefined
993
- if (!silent) throw err
994
- }
795
+ const { suffix = '', keys = [] } = options
796
+ let bases = this.app.getAllNs()
797
+ if (isString(extend)) extend = extend.split(',').map(i => i.trim)
798
+ if (isArray(extend)) bases = [...extend, 'main']
799
+ bases = uniq(bases)
800
+ let ext = isArray(orig) ? [] : {}
801
+ const dir = this.app[ns].dir.pkg
802
+ let [names, _path] = file.split(':')
803
+ if (file.slice(0, names.length + 1) !== `${ns}:`) _path = file.slice(dir.length + 1)
804
+ if (_path.startsWith('extend/')) _path = _path.slice(7)
805
+ if (_path.startsWith(`${baseNs}/`)) _path = _path.slice(baseNs.length + 1)
806
+ _path = _path.slice(0, -(path.extname(_path).length)) + '.*'
807
+ // check for override? Override only exists in main plugin
808
+ const opts = omit(options, ['suffix', 'keys', 'extend'])
809
+ if (checkOverride) {
810
+ getParseOptsArgs(opts, orig)
811
+ const fileExt = `${this.app.main.dir.pkg}/extend/${baseNs}/override/${ns}${suffix}/${_path}`
812
+ await this.runHook('bajo.override:beforeReadConfig', fileExt, options)
813
+ const result = parseObject(await this.readConfig(fileExt, { ...opts, extend: false, checkOverride: false, merge: false }))
814
+ await this.runHook('bajo.override:afterReadConfig', fileExt, result, options)
815
+ if (!isEmpty(result)) orig = result
995
816
  }
996
- })
997
- if (mutated.length > 0) obj = omit(obj, mutated)
998
- return obj
999
- }
1000
-
1001
- pick = (obj, items, excludeUnset) => {
1002
- const { isSet } = this.app.lib.aneka
1003
- const result = {}
1004
- for (const item of items) {
1005
- const [k, nk] = item.split(':')
1006
- if (excludeUnset &amp;&amp; !isSet(obj[k])) continue
1007
- result[nk ?? k] = obj[k]
817
+ getParseOptsArgs(opts, orig)
818
+ const binder = merged ? merge : defaultsDeep
819
+ for (const base of bases) {
820
+ if (!this.app[base]) continue
821
+ options.sourceNs = base
822
+ const fileExt = `${this.app[base].dir.pkg}/extend/${baseNs}/extend/${ns}${suffix}/${_path}`
823
+ await this.runHook('bajo.extend:beforeReadConfig', fileExt, options)
824
+ const result = parseObject(await this.readConfig(fileExt, { ...opts, extend: false, merge: false }))
825
+ await this.runHook('bajo.extend:afterReadConfig', fileExt, result, options)
826
+ if (isEmpty(result)) continue
827
+ if (isArray(result)) ext = [...result, ...ext]
828
+ else ext = binder({}, result, ext)
829
+ }
830
+ delete options.sourceNs
831
+ let result = isArray(orig) ? [...orig, ...ext] : binder({}, keys.length > 0 ? pick(ext, keys) : ext, orig)
832
+ if (handler) result = await this.callHandler(this.app[ns], handler, result)
833
+ await this.runHook('bajo:afterReadConfig', file, result, options)
834
+ if (cache.name) await this.app.cache.save(cache.name, result, cache.ttlDur)
835
+ return result
1008
836
  }
1009
- return result
1010
- }
1011
837
 
1012
- /**
1013
- * Read and parse file as config object. Supported types: ```.js``` and ```.json```.
1014
- * More supports can be added using plugin. {@link https://github.com/ardhi/bajo-config|bajo-config} gives you additional supports for ```.yml```, ```.yaml``` and ```.toml``` file
1015
- *
1016
- * If file extension is ```.*```, it will be auto detected and parsed accordingly
1017
- *
1018
- * @method
1019
- * @async
1020
- * @param {string} file - File to read and parse
1021
- * @param {Object} [options={}] - Options
1022
- * @param {boolean} [options.ignoreError] - Any exception will be silently discarded
1023
- * @param {string} [options.ns] - If given, use this as the scope
1024
- * @param {string} [options.pattern] - If given and auto detection is on (extension is ```.*```), it will be used for instead the default one
1025
- * @param {Object} [options.globOptions={}] - {@link https://github.com/mrmlnc/fast-glob|fast-glob} options
1026
- * @param {Object} [options.defValue={}] - Default value to use if value returned empty
1027
- * @param {Object} [options.opts={}] - Parser setting
1028
- * @returns {Object}
1029
- */
1030
- readConfig = async (file, { ns, pattern, globOptions = {}, ignoreError, defValue = {}, opts = {} } = {}) => {
838
+ let result
839
+ if (cache.name) result = await this.app.cache.load(cache.name, cache.ttlDur)
840
+ if (result) return result
841
+ await this.runHook('bajo:beforeReadConfig', file, options)
842
+ parserOpts.readFromFile = true
1031
843
  if (!ns) ns = this.ns
1032
- file = resolvePath(this.getPluginFile(file))
844
+ file = resolvePath(this.app.getPluginFile(file))
1033
845
  let ext = path.extname(file)
1034
846
  const fname = path.dirname(file) + '/' + path.basename(file, ext)
1035
847
  ext = ext.toLowerCase()
1036
848
  if (ext === '.js') {
1037
849
  const { readHandler } = find(this.app.configHandlers, { ext })
1038
- return this.parseObject(await readHandler.call(this.app[ns], file, opts))
850
+ return await output(await readHandler.call(this.app[ns], file, parserOpts))
1039
851
  }
1040
- if (ext === '.json') return await this.fromJson(file, null)
852
+ if (ext === '.json') return await output(await this.fromJson(file, parserOpts))
1041
853
  if (!['', '.*'].includes(ext)) {
1042
854
  const item = find(this.app.configHandlers, { ext })
1043
855
  if (!item) {
1044
856
  if (!ignoreError) throw this.error('cantParse%s', file, { code: 'BAJO_CONFIG_NO_PARSER' })
1045
- return this.parseObject(defValue)
857
+ return await output(defValue)
1046
858
  }
1047
- return this.parseObject(await item.readHandler.call(this.app[ns], file, opts))
859
+ return await output(await item.readHandler.call(this.app[ns], file, parserOpts))
1048
860
  }
1049
861
  const item = pattern ?? `${fname}.{${map(map(this.app.configHandlers, 'ext'), k => k.slice(1)).join(',')}}`
1050
- const files = await fastGlob(item, globOptions)
862
+ const files = await fastGlob(item, globOpts ?? {})
1051
863
  if (files.length === 0) {
1052
864
  if (!ignoreError) throw this.error('noConfigFileFound', { code: 'BAJO_CONFIG_FILE_NOT_FOUND' })
1053
- return this.parseObject(defValue)
865
+ return await output(defValue)
1054
866
  }
1055
867
  let config = defValue
1056
868
  for (const f of files) {
@@ -1060,21 +872,22 @@ class Bajo extends Plugin {
1060
872
  if (!ignoreError) throw this.error('cantParse%s', f, { code: 'BAJO_CONFIG_NO_PARSER' })
1061
873
  continue
1062
874
  }
1063
- config = await item.readHandler.call(this.app[ns], f, null, opts)
875
+ config = await item.readHandler.call(this.app[ns], f, parserOpts)
1064
876
  if (!isEmpty(config)) break
1065
877
  }
1066
- return this.parseObject(config)
878
+ return await output(config)
1067
879
  }
1068
880
 
1069
881
  /**
1070
- * Read and parse json file
882
+ * Read and parse json file.
1071
883
  *
1072
884
  * @method
1073
- * @param {string} file - File to read
1074
- * @param {boolean} [thrownNotFound=false] - If ```true```, silently ignore if file is not found
885
+ * @param {string} file - File to read.
886
+ * @param {boolean} [thrownNotFound=false] - If ```true```, silently ignore if file is not found.
1075
887
  * @returns {Object}
1076
888
  */
1077
889
  readJson = (file, thrownNotFound = false) => {
890
+ const { parseObject } = this.app.lib
1078
891
  if (isPlainObject(thrownNotFound)) thrownNotFound = false
1079
892
  if (!fs.existsSync(file) &amp;&amp; thrownNotFound) throw this.error('notFound%s%s', this.t('file'), file)
1080
893
  let resp
@@ -1082,40 +895,78 @@ class Bajo extends Plugin {
1082
895
  resp = fs.readFileSync(file, 'utf8')
1083
896
  } catch (err) {}
1084
897
  if (isEmpty(resp)) return resp
1085
- return this.parseObject(JSON.parse(resp))
898
+ return parseObject(JSON.parse(resp))
899
+ }
900
+
901
+ /**
902
+ * Read and parse JavaScript file.
903
+ *
904
+ * @async
905
+ * @method
906
+ * @param {string} file - File to read and parse.
907
+ * @param {Object} [options={}] - Options.
908
+ * @returns {Object} Parsed JavaScript object.
909
+ */
910
+ async fromJs (file, options = {}) {
911
+ const args = options.args ?? []
912
+ let mod = await importModule(file)
913
+ if (isFunction(mod)) mod = await mod.call(this, ...args)
914
+ return mod
1086
915
  }
1087
916
 
1088
- fromJson (file, isContent) {
1089
- const content = isContent ? file : fs.readFileSync(file, 'utf8')
917
+ /**
918
+ * Read and parse JSON string or object.
919
+ *
920
+ * @param {string} data - Filename to load from or JSON string to parse.
921
+ * @param {Object} [options={}] - Options.
922
+ * @returns {Object} Parsed JSON object.
923
+ */
924
+ fromJson (data, options = {}) {
925
+ const content = options.readFromFile ? fs.readFileSync(data, 'utf8') : data
1090
926
  return JSON.parse(content)
1091
927
  }
1092
928
 
1093
- toJson = (file, isContent, opts = 2) => {
1094
- const content = isContent ? file : JSON.parse(fs.readFileSync(file, 'utf8'))
1095
- return JSON.stringify(content, null, opts)
929
+ /**
930
+ * Convert data to JSON string.
931
+ *
932
+ * @method
933
+ * @param {Object} data - Data to convert to JSON string.
934
+ * @param {Object} [options={}] - Options.
935
+ * @param {boolean} [options.writeToFile=false] - If true, write the JSON string to a file.
936
+ * @param {string} [options.saveAsFile] - The file path to save the JSON string if writeToFile is true.
937
+ * @returns {string} JSON string
938
+ */
939
+ toJson = (data, options = {}) => {
940
+ const content = JSON.stringify(data, null, omit(options, ['writeToFile']))
941
+ if (options.writeToFile) {
942
+ fs.writeFileSync(options.saveAsFile, content, 'utf8')
943
+ return
944
+ }
945
+ return content
1096
946
  }
1097
947
 
1098
948
  /**
1099
- * Read all config files by path
949
+ * Read all config files from path.
1100
950
  *
1101
951
  * @method
1102
952
  * @async
1103
- * @param {string} path - Base path to start looking config files
1104
- * @returns {Object}
953
+ * @param {string} path - Base path to start looking config files.
954
+ * @param {Object} [options={}] - Options.
955
+ * @returns {Object} Merged configuration object.
1105
956
  */
1106
- readAllConfigs = async (path) => {
957
+ readAllConfigs = async (path, options) => {
1107
958
  const { defaultsDeep } = this.app.lib.aneka
1108
959
  let cfg = {}
1109
960
  let ext = {}
1110
961
  // default config file
1111
962
  try {
1112
- cfg = await this.readConfig(`${path}.*`, { ignoreError: true })
963
+ cfg = await this.readConfig(`${path}.*`, options)
1113
964
  } catch (err) {
1114
965
  if (['BAJO_CONFIG_NO_PARSER'].includes(err.code)) throw err
1115
966
  }
1116
967
  // env based config file
1117
968
  try {
1118
- ext = await this.readConfig(`${path}-${this.config.env}.*`, { ignoreError: true })
969
+ ext = await this.readConfig(`${path}-${this.config.env}.*`, options)
1119
970
  } catch (err) {
1120
971
  if (!['BAJO_CONFIG_FILE_NOT_FOUND'].includes(err.code)) throw err
1121
972
  }
@@ -1123,29 +974,30 @@ class Bajo extends Plugin {
1123
974
  }
1124
975
 
1125
976
  /**
1126
- * Run named hook/event
977
+ * Run named hook/event.
1127
978
  *
1128
979
  * @method
1129
980
  * @async
1130
- * @param {TNsPathPairs} hookName
1131
- * @param {...any} [args] - Argument passed to the hook function
1132
- * @returns {Array} Array of hook execution results
981
+ * @param {TNsPathPairs} hookName - Name of the hook to run.
982
+ * @param {...any} [args] - Argument passed to the hook function.
983
+ * @returns {Array} Array of hook execution results.
1133
984
  */
1134
985
  runHook = async (hookName, ...args) => {
1135
- const [ns, path] = (hookName ?? '').split(':')
1136
- let fns = filter(this.app.bajo.hooks, { ns, path })
986
+ let fns = filter(this.hooks, { name: hookName })
1137
987
  if (isEmpty(fns)) return []
1138
988
  fns = orderBy(fns, ['level'])
1139
989
  const results = []
1140
990
  for (const i in fns) {
1141
991
  const fn = fns[i]
1142
- const scope = this.app[fn.src]
1143
- const res = await fn.handler.call(scope, ...args)
1144
- results.push({
1145
- hook: hookName,
1146
- resp: res
1147
- })
1148
- if (this.config.log.traceHook) scope.log.trace('hookExecuted%s', hookName)
992
+ const scope = this.app[fn.src ?? 'main'] ?? this
993
+ if (fn.noWait) fn.handler.call(scope, ...args)
994
+ else {
995
+ const res = await fn.handler.call(scope, ...args)
996
+ results.push({
997
+ hook: hookName,
998
+ resp: res
999
+ })
1000
+ }
1149
1001
  }
1150
1002
  return results
1151
1003
  }
@@ -1159,14 +1011,14 @@ class Bajo extends Plugin {
1159
1011
  *
1160
1012
  * @method
1161
1013
  * @async
1162
- * @param {string} file - File name
1163
- * @param {Object} item - Item to save
1164
- * @param {boolean} [printSaved=true] - Print info on screen
1165
- * @returns {string} Full file path
1014
+ * @param {string} file - File name.
1015
+ * @param {Object} item - Item to save.
1016
+ * @param {boolean} [printSaved=true] - Print info on screen.
1017
+ * @returns {string} Full file path.
1166
1018
  */
1167
1019
  saveAsDownload = async (file, item, printSaved = true) => {
1168
- const { print, getPluginDataDir } = this.app.bajo
1169
- const fname = increment(`${getPluginDataDir(this.ns)}/download/${trim(file, '/')}`, { fs: true })
1020
+ const { print } = this.app.bajo
1021
+ const fname = increment(`${this.app.getPluginDataDir(this.ns)}/download/${trim(file, '/')}`, { fs: true })
1170
1022
  const dir = path.dirname(fname)
1171
1023
  if (!fs.existsSync(dir)) fs.ensureDirSync(dir)
1172
1024
  await fs.writeFile(fname, item, 'utf8')
@@ -1176,4 +1028,4 @@ class Bajo extends Plugin {
1176
1028
  }
1177
1029
 
1178
1030
  export default Bajo
1179
- </code></pre></article></section></div></div></div><div class="search-container" id="PkfLWpAbet" style="display:none"><div class="wrapper" id="iCxFxjkHbP"><button class="icon-button search-close-button" id="VjLlGakifb" aria-label="close search"><svg><use xlink:href="#close-icon"></use></svg></button><div class="search-box-c"><svg><use xlink:href="#search-icon"></use></svg> <input type="text" id="vpcKVYIppa" class="search-input" placeholder="Search..." autofocus></div><div class="search-result-c" id="fWwVHRuDuN"><span class="search-result-c-text">Type anything to view search result</span></div></div></div><div class="mobile-menu-icon-container"><button class="icon-button" id="mobile-menu" data-isopen="false" aria-label="menu"><svg><use xlink:href="#menu-icon"></use></svg></button></div><div id="mobile-sidebar" class="mobile-sidebar-container"><div class="mobile-sidebar-wrapper"><a href="/" class="sidebar-title sidebar-title-anchor">Bajo API</a><div class="mobile-nav-links"><div class="navbar-item"><a id="" href="https://www.npmjs.com/package/bajo" target="">NPM</a></div><div class="navbar-item"><a id="" href="https://github.com/ardhi/bajo" target="">Github</a></div><div class="navbar-item"><a id="" href="https://bajo.app" target="">Bajo</a></div></div><div class="mobile-sidebar-items-c"><div class="sidebar-section-title with-arrow" data-isopen="false" id="sidebar-classes"><div>Classes</div><svg><use xlink:href="#down-icon"></use></svg></div><div class="sidebar-section-children-container"><div class="sidebar-section-children"><a href="App.html">App</a></div><div class="sidebar-section-children"><a href="Bajo.html">Bajo</a></div><div class="sidebar-section-children"><a href="Base.html">Base</a></div><div class="sidebar-section-children"><a href="Err.html">Err</a></div><div class="sidebar-section-children"><a href="Log.html">Log</a></div><div class="sidebar-section-children"><a href="Plugin.html">Plugin</a></div><div class="sidebar-section-children"><a href="Print.html">Print</a></div></div><div class="sidebar-section-title with-arrow" data-isopen="false" id="sidebar-events"><div>Events</div><svg><use xlink:href="#down-icon"></use></svg></div><div class="sidebar-section-children-container"><div class="sidebar-section-children"><a href="global.html#event:bajo:afterAll%257Bmethod%257D">bajo:afterAll{method}</a></div><div class="sidebar-section-children"><a href="global.html#event:bajo:afterBootComplete">bajo:afterBootComplete</a></div><div class="sidebar-section-children"><a href="global.html#event:bajo:afterBuildCollection">bajo:afterBuildCollection</a></div><div class="sidebar-section-children"><a href="global.html#event:bajo:afterCollectHooks">bajo:afterCollectHooks</a></div><div class="sidebar-section-children"><a href="global.html#event:bajo:beforeAll%257Bmethod%257D">bajo:beforeAll{method}</a></div><div class="sidebar-section-children"><a href="global.html#event:bajo:beforeBuildCollection">bajo:beforeBuildCollection</a></div><div class="sidebar-section-children"><a href="global.html#event:%257Bns%257D:afterAppletRun">{ns}:afterAppletRun</a></div><div class="sidebar-section-children"><a href="global.html#event:%257Bns%257D:after%257Bmethod%257D">{ns}:after{method}</a></div><div class="sidebar-section-children"><a href="global.html#event:%257Bns%257D:beforeAppletRun">{ns}:beforeAppletRun</a></div><div class="sidebar-section-children"><a href="global.html#event:%257Bns%257D:before%257Bmethod%257D">{ns}:before{method}</a></div></div><div class="sidebar-section-title with-arrow" data-isopen="false" id="sidebar-modules"><div>Modules</div><svg><use xlink:href="#down-icon"></use></svg></div><div class="sidebar-section-children-container"><div class="sidebar-section-children"><a href="module-Helper_Bajo.html">Helper/Bajo</a></div><div class="sidebar-section-children"><a href="module-Helper_Base.html">Helper/Base</a></div><div class="sidebar-section-children"><a href="module-Lib.html">Lib</a></div></div><div class="sidebar-section-title with-arrow" data-isopen="false" id="sidebar-global"><div>Global</div><svg><use xlink:href="#down-icon"></use></svg></div><div class="sidebar-section-children-container"><div class="sidebar-section-children"><a href="global.html#TAppConfigHandler">TAppConfigHandler</a></div><div class="sidebar-section-children"><a href="global.html#TAppEnv">TAppEnv</a></div><div class="sidebar-section-children"><a href="global.html#TAppLib">TAppLib</a></div><div class="sidebar-section-children"><a href="global.html#TBajoDataType">TBajoDataType</a></div><div class="sidebar-section-children"><a href="global.html#TBajoFormatResult">TBajoFormatResult</a></div><div class="sidebar-section-children"><a href="global.html#TBajoFormatType">TBajoFormatType</a></div><div class="sidebar-section-children"><a href="global.html#TLogJson">TLogJson</a></div><div class="sidebar-section-children"><a href="global.html#TLogLevels">TLogLevels</a></div><div class="sidebar-section-children"><a href="global.html#TNsPathPairs">TNsPathPairs</a></div><div class="sidebar-section-children"><a href="global.html#TNsPathResult">TNsPathResult</a></div><div class="sidebar-section-children"><a href="global.html#TPrintOptions">TPrintOptions</a></div><div class="sidebar-section-children"><a href="global.html#boot">boot</a></div></div></div><div class="mobile-navbar-actions"><div class="navbar-right-item"><button class="icon-button search-button" aria-label="open-search"><svg><use xlink:href="#search-icon"></use></svg></button></div><div class="navbar-right-item"><button class="icon-button theme-toggle" aria-label="toggle-theme"><svg><use class="theme-svg-use" xlink:href="#dark-theme-icon"></use></svg></button></div><div class="navbar-right-item"><button class="icon-button font-size" aria-label="change-font-size"><svg><use xlink:href="#font-size-icon"></use></svg></button></div></div></div></div><script type="text/javascript" src="scripts/core.min.js"></script><script src="scripts/search.min.js" defer="defer"></script><script src="scripts/third-party/fuse.js" defer="defer"></script><script type="text/javascript">var tocbotInstance=tocbot.init({tocSelector:"#eed4d2a0bfd64539bb9df78095dec881",contentSelector:".main-content",headingSelector:"h1, h2, h3",hasInnerContainers:!0,scrollContainer:".main-content",headingsOffset:130,onClick:bringLinkToView})</script></body></html>
1031
+ </code></pre></article></section></div></div></div><div class="search-container" id="PkfLWpAbet" style="display:none"><div class="wrapper" id="iCxFxjkHbP"><button class="icon-button search-close-button" id="VjLlGakifb" aria-label="close search"><svg><use xlink:href="#close-icon"></use></svg></button><div class="search-box-c"><svg><use xlink:href="#search-icon"></use></svg> <input type="text" id="vpcKVYIppa" class="search-input" placeholder="Search..." autofocus></div><div class="search-result-c" id="fWwVHRuDuN"><span class="search-result-c-text">Type anything to view search result</span></div></div></div><div class="mobile-menu-icon-container"><button class="icon-button" id="mobile-menu" data-isopen="false" aria-label="menu"><svg><use xlink:href="#menu-icon"></use></svg></button></div><div id="mobile-sidebar" class="mobile-sidebar-container"><div class="mobile-sidebar-wrapper"><a href="/" class="sidebar-title sidebar-title-anchor">Bajo API</a><div class="mobile-nav-links"><div class="navbar-item"><a id="" href="https://www.npmjs.com/package/bajo" target="">NPM</a></div><div class="navbar-item"><a id="" href="https://github.com/ardhi/bajo" target="">Github</a></div><div class="navbar-item"><a id="" href="https://bajo.app" target="">Bajo</a></div></div><div class="mobile-sidebar-items-c"><div class="sidebar-section-title with-arrow" data-isopen="false" id="sidebar-classes"><div>Classes</div><svg><use xlink:href="#down-icon"></use></svg></div><div class="sidebar-section-children-container"><div class="sidebar-section-children"><a href="App.html">App</a></div><div class="sidebar-section-children"><a href="Bajo.html">Bajo</a></div><div class="sidebar-section-children"><a href="Base.html">Base</a></div><div class="sidebar-section-children"><a href="Cache.html">Cache</a></div><div class="sidebar-section-children"><a href="Err.html">Err</a></div><div class="sidebar-section-children"><a href="Log.html">Log</a></div><div class="sidebar-section-children"><a href="Plugin.html">Plugin</a></div><div class="sidebar-section-children"><a href="Print.html">Print</a></div><div class="sidebar-section-children"><a href="Tools.html">Tools</a></div></div><div class="sidebar-section-title with-arrow" data-isopen="false" id="sidebar-events"><div>Events</div><svg><use xlink:href="#down-icon"></use></svg></div><div class="sidebar-section-children-container"><div class="sidebar-section-children"><a href="global.html#event:bajo:afterAll%257Bmethod%257D">bajo:afterAll{method}</a></div><div class="sidebar-section-children"><a href="global.html#event:bajo:afterBootComplete">bajo:afterBootComplete</a></div><div class="sidebar-section-children"><a href="global.html#event:bajo:afterBuildCollection">bajo:afterBuildCollection</a></div><div class="sidebar-section-children"><a href="global.html#event:bajo:afterCollectHooks">bajo:afterCollectHooks</a></div><div class="sidebar-section-children"><a href="global.html#event:bajo:beforeAll%257Bmethod%257D">bajo:beforeAll{method}</a></div><div class="sidebar-section-children"><a href="global.html#event:bajo:beforeBuildCollection">bajo:beforeBuildCollection</a></div><div class="sidebar-section-children"><a href="global.html#event:%257Bns%257D:after%257Bmethod%257D">{ns}:after{method}</a></div><div class="sidebar-section-children"><a href="global.html#event:%257Bns%257D:beforeAppletRun">{ns}:beforeAppletRun</a></div><div class="sidebar-section-children"><a href="global.html#event:%257Bns%257D:before%257Bmethod%257D">{ns}:before{method}</a></div><div class="sidebar-section-children"><a href="module-Helper%257Bns%257D_afterAppletRun.html">Helper{ns}:afterAppletRun</a></div></div><div class="sidebar-section-title with-arrow" data-isopen="false" id="sidebar-modules"><div>Modules</div><svg><use xlink:href="#down-icon"></use></svg></div><div class="sidebar-section-children-container"><div class="sidebar-section-children"><a href="module-Helper.html">Helper</a></div><div class="sidebar-section-children"><a href="module-Lib.html">Lib</a></div></div><div class="sidebar-section-title with-arrow" data-isopen="false" id="sidebar-global"><div>Global</div><svg><use xlink:href="#down-icon"></use></svg></div><div class="sidebar-section-children-container"><div class="sidebar-section-children"><a href="global.html#TAppConfigHandler">TAppConfigHandler</a></div><div class="sidebar-section-children"><a href="global.html#TAppEnv">TAppEnv</a></div><div class="sidebar-section-children"><a href="global.html#TBajoDataType">TBajoDataType</a></div><div class="sidebar-section-children"><a href="global.html#TBajoFormatResult">TBajoFormatResult</a></div><div class="sidebar-section-children"><a href="global.html#TBajoFormatType">TBajoFormatType</a></div><div class="sidebar-section-children"><a href="global.html#TLogJson">TLogJson</a></div><div class="sidebar-section-children"><a href="global.html#TLogLevels">TLogLevels</a></div><div class="sidebar-section-children"><a href="global.html#TNsPathPairs">TNsPathPairs</a></div><div class="sidebar-section-children"><a href="global.html#TNsPathResult">TNsPathResult</a></div><div class="sidebar-section-children"><a href="global.html#TPrintOptions">TPrintOptions</a></div><div class="sidebar-section-children"><a href="global.html#boot">boot</a></div></div></div><div class="mobile-navbar-actions"><div class="navbar-right-item"><button class="icon-button search-button" aria-label="open-search"><svg><use xlink:href="#search-icon"></use></svg></button></div><div class="navbar-right-item"><button class="icon-button theme-toggle" aria-label="toggle-theme"><svg><use class="theme-svg-use" xlink:href="#dark-theme-icon"></use></svg></button></div><div class="navbar-right-item"><button class="icon-button font-size" aria-label="change-font-size"><svg><use xlink:href="#font-size-icon"></use></svg></button></div></div></div></div><script type="text/javascript" src="scripts/core.min.js"></script><script src="scripts/search.min.js" defer="defer"></script><script src="scripts/third-party/fuse.js" defer="defer"></script><script type="text/javascript">var tocbotInstance=tocbot.init({tocSelector:"#eed4d2a0bfd64539bb9df78095dec881",contentSelector:".main-content",headingSelector:"h1, h2, h3",hasInnerContainers:!0,scrollContainer:".main-content",headingsOffset:130,onClick:bringLinkToView})</script></body></html>