bajo 2.23.0 → 2.24.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 (108) hide show
  1. package/.jsdoc.conf.json +2 -1
  2. package/class/app.js +220 -107
  3. package/class/bajo.js +246 -174
  4. package/class/base.js +85 -24
  5. package/class/cache.js +8 -16
  6. package/class/err.js +31 -10
  7. package/class/log.js +46 -38
  8. package/class/plugin.js +53 -30
  9. package/class/print.js +18 -15
  10. package/class/tools.js +6 -5
  11. package/docs/App.html +51 -1
  12. package/docs/Bajo.html +1 -1
  13. package/docs/Base.html +34 -1
  14. package/docs/Cache.html +1 -1
  15. package/docs/Err.html +2 -2
  16. package/docs/Log.html +2 -2
  17. package/docs/Main.html +3 -0
  18. package/docs/Plugin.html +1 -1
  19. package/docs/Print.html +1 -1
  20. package/docs/Tools.html +1 -1
  21. package/docs/class_app.js.html +222 -109
  22. package/docs/class_bajo.js.html +260 -179
  23. package/docs/class_base.js.html +87 -26
  24. package/docs/class_cache.js.html +10 -18
  25. package/docs/class_err.js.html +33 -12
  26. package/docs/class_log.js.html +48 -40
  27. package/docs/class_plugin.js.html +55 -32
  28. package/docs/class_print.js.html +20 -17
  29. package/docs/class_tools.js.html +8 -7
  30. package/docs/data/search.json +1 -1
  31. package/docs/extend_bajo_hook.js.html +289 -0
  32. package/docs/global.html +22 -1
  33. package/docs/index.html +1 -1
  34. package/docs/index.js.html +53 -16
  35. package/docs/lib_applet-hook.js.html +328 -0
  36. package/docs/lib_config.js.html +167 -0
  37. package/docs/lib_helper.js.html +186 -192
  38. package/docs/module-Applet.html +3 -0
  39. package/docs/module-Helper-Main.html +3 -0
  40. package/docs/module-Helper.html +1 -8
  41. package/docs/module-Hook.html +1 -1
  42. package/docs/tutorial-01_WELCOME.html +3 -0
  43. package/docs/tutorial-02_GETTING_STARTED.html +125 -0
  44. package/docs/tutorial-03_USER_GUIDE.html +54 -0
  45. package/docs/tutorial-04_DEV_GUIDE.html +178 -0
  46. package/docs/tutorial-05_ECOSYSTEM.html +3 -0
  47. package/extend/bajo/intl/en-US.json +2 -1
  48. package/extend/bajo/intl/id.json +2 -1
  49. package/index.js +51 -14
  50. package/lib/applet-hook.js +325 -0
  51. package/lib/config.js +164 -0
  52. package/lib/helper.js +184 -190
  53. package/package.json +6 -2
  54. package/test/00-global-hooks.test.js +44 -0
  55. package/test/e2e/_run.js +19 -0
  56. package/test/e2e/e2e-applet-process.test.js +66 -0
  57. package/test/e2e/e2e-boot-process.test.js +62 -0
  58. package/test/e2e/e2e-hooks-process.test.js +68 -0
  59. package/test/integration/_setup.js +55 -0
  60. package/test/integration/aspect-01-boot-instance.test.js +24 -0
  61. package/test/integration/aspect-02-plugin-loading.test.js +24 -0
  62. package/test/integration/aspect-03-config-merge.test.js +27 -0
  63. package/test/integration/aspect-04-intl-loading.test.js +29 -0
  64. package/test/integration/aspect-05-cache-roundtrip.test.js +26 -0
  65. package/test/integration/aspect-06-log-file-save.test.js +28 -0
  66. package/test/integration/aspect-07-plugin-data-dir.test.js +25 -0
  67. package/test/integration/aspect-08-plugin-lookup.test.js +25 -0
  68. package/test/integration/aspect-09-config-formats.test.js +26 -0
  69. package/test/integration/aspect-10-hooks.test.js +38 -0
  70. package/test/unit/app.test.js +115 -0
  71. package/test/unit/applet-hook.test.js +10 -0
  72. package/test/unit/bajo.test.js +151 -0
  73. package/test/unit/base.test.js +90 -0
  74. package/test/unit/cache.test.js +89 -0
  75. package/test/unit/config.test.js +14 -0
  76. package/test/unit/err.test.js +57 -0
  77. package/test/unit/helper.test.js +116 -0
  78. package/test/unit/index.test.js +40 -0
  79. package/test/unit/log.test.js +84 -0
  80. package/test/unit/plugin.test.js +95 -0
  81. package/test/unit/print.test.js +72 -0
  82. package/test/unit/tools.test.js +29 -0
  83. package/wiki/CHANGES.md +4 -0
  84. package/wiki/tutorials/01_WELCOME.md +18 -0
  85. package/wiki/tutorials/02_GETTING_STARTED.md +366 -0
  86. package/wiki/tutorials/03_USER_GUIDE.md +262 -0
  87. package/wiki/tutorials/04_DEV_GUIDE.md +386 -0
  88. package/wiki/tutorials/05_ECOSYSTEM.md +95 -0
  89. package/wiki/tutorials/tutorials.json +17 -0
  90. package/lib/hook.js +0 -228
  91. package/test/app.test.js +0 -183
  92. package/test/bajo.test.js +0 -124
  93. package/test/base.test.js +0 -75
  94. package/test/cache.test.js +0 -94
  95. package/test/e2e.test.js +0 -137
  96. package/test/err.test.js +0 -73
  97. package/test/helper.test.js +0 -39
  98. package/test/import-module.test.js +0 -138
  99. package/test/integration.test.js +0 -218
  100. package/test/log.test.js +0 -119
  101. package/test/plugin.test.js +0 -116
  102. package/test/print.test.js +0 -100
  103. package/test/tools.test.js +0 -38
  104. package/wiki/CONFIG.md +0 -36
  105. package/wiki/DEV_GUIDE.md +0 -3
  106. package/wiki/ECOSYSTEM.md +0 -93
  107. package/wiki/GETTING-STARTED.md +0 -356
  108. package/wiki/USER-GUIDE.md +0 -256
@@ -1,6 +1,6 @@
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="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-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-Hook.html">Hook</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#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'
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-tutorials"><div>Tutorials</div><svg><use xlink:href="#down-icon"></use></svg></div><div class="sidebar-section-children-container"><div class="sidebar-section-children"><a href="tutorial-01_WELCOME.html">Welcome</a></div><div class="sidebar-section-children"><a href="tutorial-02_GETTING_STARTED.html">Getting Started</a></div><div class="sidebar-section-children"><a href="tutorial-03_USER_GUIDE.html">User Guide</a></div><div class="sidebar-section-children"><a href="tutorial-04_DEV_GUIDE.html">Developer Guide</a></div><div class="sidebar-section-children"><a href="tutorial-05_ECOSYSTEM.html">Ecosystem</a></div></div><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="Main.html">Main</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-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-Applet.html">Applet</a></div><div class="sidebar-section-children"><a href="module-Helper.html">Helper</a></div><div class="sidebar-section-children"><a href="module-Hook.html">Hook</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#TConfig">TConfig</a></div><div class="sidebar-section-children"><a href="global.html#boot">boot</a></div><div class="sidebar-section-children"><a href="global.html#package%25C2%25B7json">package·json</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
4
  import Plugin from './plugin.js'
5
5
  import increment from 'add-filename-increment'
6
6
  import fs from 'fs-extra'
@@ -15,11 +15,14 @@ import * as yaml from 'js-yaml'
15
15
  import aneka from 'aneka'
16
16
  import {
17
17
  buildBaseConfig,
18
- buildExtConfig,
18
+ buildConfig,
19
19
  buildPlugins,
20
20
  collectConfigHandlers,
21
21
  bootOrder,
22
- bootPlugins,
22
+ checkNameAliases,
23
+ checkDependencies,
24
+ collectHooks,
25
+ runPlugins,
23
26
  exitHandler,
24
27
  importModule,
25
28
  types as formatTypes,
@@ -29,20 +32,20 @@ import {
29
32
  const require = createRequire(import.meta.url)
30
33
 
31
34
  const {
32
- isFunction, map, isObject,
35
+ isFunction, map, isObject, findIndex, uniq, merge,
33
36
  trim, filter, isEmpty, orderBy, pullAt, find, camelCase,
34
37
  cloneDeep, isPlainObject, isArray, isString, omit, keys, indexOf,
35
- last, get, has, values, pick
38
+ last, get, has, values, pick, isBoolean
36
39
  } = lodash
37
40
 
38
- const { resolvePath, getGlobalModuleDir } = aneka
41
+ const { resolvePath, getGlobalModuleDir, defaultsDeep, isSet, parseObject } = aneka
39
42
 
40
43
  /**
41
- * Name based `{ns}:{path}` format.
44
+ * Name in `{ns}:{path}` format.
42
45
  *
43
46
  * @typedef {string} TNsPathPairs
44
47
  * @memberof Bajo
45
- * @see TNsPathResult
48
+ * @see Bajo.TNsPathResult
46
49
  * @see Bajo#buildNsPath
47
50
  * @see Bajo#breakNsPath
48
51
  */
@@ -58,23 +61,30 @@ const { resolvePath, getGlobalModuleDir } = aneka
58
61
  */
59
62
 
60
63
  /**
61
- * Object returned by {@link Bajo#breakNsPath|bajo:breakNsPath}.
64
+ * Object returned by {@link Bajo#breakNsPath|bajo:breakNsPath()}.
62
65
  *
63
66
  * @typedef {Object} TNsPathResult
64
67
  * @memberof Bajo
65
- * @property {string} ns - Namespace
68
+ * @property {string} [ns] - Namespace
66
69
  * @property {string} [subNs] - Sub namespace
67
70
  * @property {string} [subSubNs] - Sub of sub namespace
71
+ * @property {string} [fullNs] - Full namespace, including sub namespaces
68
72
  * @property {string} path - Path without query string or hash
69
- * @property {string} fullPath - Full path, including query string and hash
70
- * @see TNsPathPairs
73
+ * @property {string} [fullPath] - Full path, including query string and hash
74
+ * @property {string} [realPath] - Path without query string or hash, but with parameters replaced with their values
75
+ * @property {string} [realFullPath] - Full path, including query string and hash, but with parameters replaced with their values
76
+ * @property {Object} [qs] - Query string object
77
+ * @property {Object} [params] - Parameters object
78
+ * @see Bajo.TNsPathPairs
71
79
  * @see Bajo#buildNsPath
72
80
  * @see Bajo#breakNsPath
73
81
  */
74
82
 
75
83
  /**
76
- * The Core. The main engine. The one and only plugin that control app's boot process and
77
- * making sure all other plugins working nicely.
84
+ * The core plugin. The main engine. The special plugin that controls the app's boot process and
85
+ * makes sure all other plugins work nicely.
86
+ *
87
+ * Don't create your own instance of this class or {@link App} class. Instead, use the {@link boot} process.
78
88
  *
79
89
  * @class
80
90
  */
@@ -88,57 +98,61 @@ class Bajo extends Plugin {
88
98
  super('bajo', app)
89
99
 
90
100
  /**
91
- * Alias
101
+ * Alias. Read-only
92
102
  * @type {string}
103
+ * @default 'bajo'
93
104
  */
94
105
  this.alias = 'bajo'
95
106
 
96
107
  /**
97
- * White space characters
108
+ * Array of white space characters. Used to trim strings
98
109
  * @type {string[]}
99
110
  */
100
111
  this.whiteSpace = [' ', '\t', '\n', '\r']
101
112
 
102
113
  /**
103
- * Config object.
104
- *
105
- * @type {Bajo.TObject}
114
+ * Configuration object
115
+ * @type {Bajo.TConfig}
106
116
  * @see {@tutorial config}
107
117
  */
108
118
  this.config = {}
109
119
 
110
120
  /**
111
- * Hooks container.
112
- *
121
+ * Hooks container. This is where all hooks definition are stored.
113
122
  * @type {Array&lt;Bajo.THook>}
114
123
  */
115
124
  this.hooks = []
116
125
  }
117
126
 
118
127
  /**
119
- * Initialization:
120
- *
121
- * 1. Building {@link module:Helper/Bajo.buildBaseConfig|base config}
122
- * 2. {@link module:Helper/Bajo.buildPlugins|Building plugins}
123
- * 3. Collect all {@link module:Helper/Bajo.collectConfigHandlers|config handler}
124
- * 4. Building {@link module:Helper/Bajo.buildExtConfig|extra config}
125
- * 5. Setup {@link module:Helper/Bajo.bootOrder|boot order}
126
- * 6. {@link module:Helper/Bajo.bootPlugins|Boot loaded plugins}
127
- * 7. Attach {@link module:Helper/Bajo.exitHandler|exit handlers}
128
+ * Bajo boot process. This method is called by the {@link App} class during the app's boot process:
129
+ * 1. Building Bajo's {@link module:Helper.buildBaseConfig|base configuration}
130
+ * 2. Collect all {@link module:Helper.collectConfigHandlers|config handlers} from all loaded plugins
131
+ * 3. Build {@link module:Helper.buildConfig|configuration} object
132
+ * 4. {@link module:Helper.buildPlugins|Building plugins} listed in `package.json` or `.plugins` file
133
+ * 5. Determining the {@link module:Helper.bootOrder|boot order}
134
+ * 6. Ensure the {@link module:Helper.checkNameAliases|uniqueness} of all plugins' name and alias
135
+ * 7. Ensure all plugins {@link module:Helper.checkDependencies|dependencies} are met
136
+ * 8. Collect all {@link module:Helper.collectHooks|hooks} from all loaded plugins
137
+ * 9. {@link module:Helper.runPlugins|Run all plugins} according to the boot order
138
+ * 10. And finally attaching all {@link module:Helper.exitHandler|exit handlers} it could find
128
139
  *
129
140
  * @method
130
141
  * @async
131
142
  */
132
- init = async () => {
143
+ bootApp = async () => {
133
144
  await buildBaseConfig.call(this)
134
145
  await collectConfigHandlers.call(this)
135
- await buildExtConfig.call(this)
146
+ await buildConfig.call(this)
136
147
  await buildPlugins.call(this)
137
148
  await bootOrder.call(this)
138
- await bootPlugins.call(this)
149
+ await checkNameAliases.call(this)
150
+ await checkDependencies.call(this)
151
+ await collectHooks.call(this)
152
+ await runPlugins.call(this)
139
153
  await exitHandler.call(this)
140
154
  if (this.app.bajoSpatial) {
141
- this.anekaSpatial = await this.importPkg('bajoSpatial:aneka-spatial')
155
+ this.app.lib.anekaSpatial = await this.importPkg('bajoSpatial:aneka-spatial')
142
156
  }
143
157
  }
144
158
 
@@ -193,12 +207,24 @@ class Bajo extends Plugin {
193
207
  }
194
208
 
195
209
  /**
196
- * Break name to its namespace &amp; path infos
210
+ * Break `name` to their components.
211
+ *
212
+ * If path starts with `//`, e.g. name is `https://example.com`, it will be treated as a URL and returned as an
213
+ * object with only `path` property filled with the original name.
214
+ *
215
+ * If query string is present in the path, it will be parsed and returned as an object with `qs`
216
+ * property containing the parsed query string.
217
+ *
218
+ * If path contains parameters in the format of `:key|value` or `{key|value}`, they will be extracted and returned
219
+ * as an object with `params` property containing the extracted parameters.
220
+ *
221
+ * This method is one of the most used method in Bajo. It is because every files, names, resources, and even commands
222
+ * are all identified by their namespace and path.
197
223
  *
198
224
  * @method
199
- * @param {(TNsPathPairs|string)} name - Name to break
200
- * @param {boolean} [checkNs=true] - If `true` (default), namespace will be checked for its validity
201
- * @returns {TNsPathResult}
225
+ * @param {Bajo.TNsPathPairs} name - Name in format `ns:path`
226
+ * @param {boolean} [checkNs=true] - If `true` (default), check if the namespace exists in the app. If not, throw an error
227
+ * @returns {Bajo.TNsPathResult}
202
228
  */
203
229
  breakNsPath = (name = '', checkNs = true) => {
204
230
  let [ns, ...path] = name.split(':')
@@ -227,14 +253,17 @@ class Bajo extends Plugin {
227
253
  const params = {}
228
254
  for (const idx in parts) {
229
255
  const part = parts[idx]
230
- if (part[0] !== ':' || part.indexOf('|') === -1) {
256
+ if (part.indexOf('|') === -1 &amp;&amp; ![':', '{'].includes(part[0])) {
231
257
  realParts.push(part)
232
258
  continue
233
259
  }
234
- const [key, val] = part.split('|')
235
- parts[idx] = key
236
- params[key.slice(1)] = val
237
- realParts.push(val)
260
+ let [key, val] = part.split('|')
261
+ if (!val) val = key[0] === '{' ? '}' : ''
262
+ key = key.slice(1)
263
+ val = val[val.length - 1] === '}' ? val.slice(0, val.length - 1) : val
264
+ // parts[idx] = key
265
+ params[key] = val
266
+ realParts.push(isEmpty(val) ? parts[idx] : val)
238
267
  }
239
268
  path = parts.join('/')
240
269
  const realPath = realParts.join('/')
@@ -242,33 +271,39 @@ class Bajo extends Plugin {
242
271
  if (!isEmpty(qs)) fullPath += ('?' + querystring.stringify(qs, null, null, { encodeURIComponent: (text) => (text) }))
243
272
  let realFullPath = realPath
244
273
  if (!isEmpty(qs)) realFullPath += ('?' + querystring.stringify(qs, null, null, { encodeURIComponent: (text) => (text) }))
245
- return { ns, path, subNs, subSubNs, qs, fullPath, fullNs, realPath, realFullPath }
274
+ return { ns, path, subNs, subSubNs, qs, params, fullPath, fullNs, realPath, realFullPath }
246
275
  }
247
276
 
248
277
  /**
249
- * Method to transform config's array or object into an array of collection.
278
+ * Method to transform config's array or object into a collection of objects with uniformed structure. This is useful to build a collection of items
279
+ * from config's array or object, e.g. a collection of commands, routes, etc.
280
+ *
281
+ * You typically also provide a `handler` function to transform each item in the collection. The handler function will be called with an object
282
+ * containing the following keys:
283
+ * - `item`: the current item in the collection
284
+ * - `index`: the index of the current item in the collection
285
+ * - `cfg`: the entire config object
250
286
  *
251
287
  * @method
252
288
  * @async
253
- * @param {Object} options - Options.
254
- * @param {string} [options.ns] - Namespace. If not provided, defaults to `bajo`
255
- * @param {function} [options.handler] - Handler to call while building the collection item.
256
- * @param {string[]} [options.dupChecks=[]] - Array of keys to check for duplicates.
257
- * @param {string} options.container - Key used as container name.
258
- * @param {boolean} [options.useDefaultName=true] - If true (default) and `name` key is not provided, returned collection will be named `default`.
259
- * @fires bajo:beforeBuildCollection
260
- * @fires bajo:afterBuildCollection
261
- * @returns {Object[]} The collection
289
+ * @param {Object} [options={}] - Options
290
+ * @param {string} [options.ns='bajo'] - Namespace. If not provided, defaults to `bajo`
291
+ * @param {function} [options.handler] - Function to transform each item while building
292
+ * @param {string[]} [options.dupChecks=[]] - Array of keys to check for duplicates
293
+ * @param {string} options.container - Key used as container name
294
+ * @param {boolean} [options.useDefaultName=true] - If true (default) and `name` key is not provided, returned item will be named `default`
295
+ * @returns {Array&lt;Object>} Returned collection of objects
296
+ * @see module:Hook.{ns}:beforeBuildCollection
297
+ * @see module:Hook.{ns}:afterBuildCollection
262
298
  */
263
299
  buildCollections = async (options = {}) => {
264
300
  const { parseObject } = this.app.lib
265
- let { ns, handler, dupChecks = [], container, useDefaultName = true, noDefault = true } = options
266
- if (!ns) ns = this.ns
301
+ const { ns = this.ns, handler, dupChecks = [], container, useDefaultName = true, noDefault = true } = options
267
302
  const cfg = this.app[ns].getConfig()
268
303
  let items = get(cfg, container, [])
269
304
  if (!isArray(items)) items = [items]
270
305
  this.app[ns].log.trace('collecting%s', this.t(container))
271
- await this.runHook(`${ns}:beforeBuildCollection`, container)
306
+ await this.runHook(`${ns}:beforeBuildCollection`, container, items)
272
307
  const deleted = []
273
308
  for (const index in items) {
274
309
  const item = parseObject(items[index])
@@ -302,8 +337,7 @@ class Bajo extends Plugin {
302
337
  }
303
338
 
304
339
  /**
305
- * Calling any plugin's method by its name:
306
- *
340
+ * Calling a function handler in any plugins:
307
341
  * - If name is a string, the corresponding plugin's method will be called with passed args as its parameters
308
342
  * - If name is a plugin instance, this will be used as the scope instead. The first args is now the handler name and the rest are its parameters
309
343
  * - If name is a function, this function will be run under scope with the remaining args
@@ -311,9 +345,9 @@ class Bajo extends Plugin {
311
345
  *
312
346
  * @method
313
347
  * @async
314
- * @param {(TNsPathPairs|Object|function)} name - Method's name, function handler, plain object or plugin instance.
315
- * @param {...any} [args] - One or more arguments passed as parameter to the handler.
316
- * @returns {any} Returned value.
348
+ * @param {(TNsPathPairs|Plugin|function|Object)} name - Method's name, plugin instance, function handler or plain object. See above for details
349
+ * @param {...*} [args] - One or more arguments passed as parameter to the handler
350
+ * @returns {Promise&lt;*>} Returned value
317
351
  */
318
352
  callHandler = async (item, ...args) => {
319
353
  let result
@@ -358,8 +392,8 @@ class Bajo extends Plugin {
358
392
  * @param {boolean} [options.useBajo=false] - If true, add `bajo` to the running plugins too.
359
393
  * @param {string} [options.prefix=''] - Prepend glob pattern with prefix.
360
394
  * @param {boolean} [options.noUnderscore=true] - If true (default), matched file with name starts with underscore is ignored.
361
- * @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.
362
- * @returns {any}
395
+ * @param {*} [options.returnItems] - If true, each value of returned handler call will be saved as an object with running plugin name as its keys.
396
+ * @returns {*}
363
397
  */
364
398
  eachPlugins = async (handler, options = {}) => {
365
399
  if (isString(options)) options = { glob: options }
@@ -426,7 +460,7 @@ class Bajo extends Plugin {
426
460
  *
427
461
  * @method
428
462
  * @param {string} type - Format type. See {@link TBajoFormatType} for acceptable values.
429
- * @param {any} value - Value to format.
463
+ * @param {*} value - Value to format.
430
464
  * @param {string} [dataType] - Value's data type. See {@link TBajoDataType} for acceptable values.
431
465
  * @param {Object} [options={}] - Options.
432
466
  * @param {boolean} [options.withUnit=true] - Return with its unit appended.
@@ -434,7 +468,6 @@ class Bajo extends Plugin {
434
468
  * @returns {(Array|string)} Return string if `withUnit` is true. Otherwise is an array of `[value, unit, separator]`.
435
469
  */
436
470
  formatByType = (type, value, dataType, options = {}) => {
437
- const { defaultsDeep } = this.app.lib.aneka
438
471
  const { format } = this.getUnitFormat(options)
439
472
  const { withUnit = true } = options
440
473
  const lang = options.lang ?? this.config.lang
@@ -451,7 +484,7 @@ class Bajo extends Plugin {
451
484
  * Format value.
452
485
  *
453
486
  * @method
454
- * @param {any} value - Value to format.
487
+ * @param {*} value - Value to format.
455
488
  * @param {string} [type] - Data type to use. See {@link TBajoDataType} for acceptable values. If not provided, return the untouched value.
456
489
  * @param {Object} [options={}] - Options.
457
490
  * @param {string} [options.emptyValue=''] - Empty value to use if function resulted empty. Defaults to the one from your config.
@@ -462,7 +495,6 @@ class Bajo extends Plugin {
462
495
  * @returns {string} Formatted value.
463
496
  */
464
497
  format = (value, type, options = {}) => {
465
- const { defaultsDeep, isSet } = this.app.lib.aneka
466
498
  const { format } = this.config.intl
467
499
  const { emptyValue = format.emptyValue } = options
468
500
  const lang = options.lang ?? this.config.lang
@@ -587,7 +619,6 @@ class Bajo extends Plugin {
587
619
  * @returns {(Object|Array)} See above.
588
620
  */
589
621
  importPkg = async (...pkgs) => {
590
- const { defaultsDeep } = this.app.lib.aneka
591
622
  const result = {}
592
623
  const notFound = []
593
624
  let opts = { returnDefault: true, throwNotFound: false }
@@ -602,7 +633,7 @@ class Bajo extends Plugin {
602
633
  notFound.push(pkg)
603
634
  continue
604
635
  }
605
- const p = this.readJson(`${dir}/package.json`, opts.throwNotFound)
636
+ const p = await this.fromJson(`${dir}/package.json`, { readFromFile: true, throwNotFound: opts.throwNotFound })
606
637
  const mainFileOrg = dir + '/' + (p.main ?? get(p, 'exports.default', 'index.js'))
607
638
  let mainFile = resolvePath(mainFileOrg, os.platform() === 'win32')
608
639
  if (isEmpty(path.extname(mainFile))) {
@@ -677,35 +708,34 @@ class Bajo extends Plugin {
677
708
  }
678
709
 
679
710
  /**
680
- * Human friendly join array of items.
711
+ * Return human friendly joined array of items.
681
712
  *
682
713
  * @method
683
- * @param {any[]} array - Array to join
684
- * @param {(string|Object)} options - If provided and is a string, it will be used as separator.
714
+ * @param {Array&lt;*>} input - Array to join
715
+ * @param {(string|Object)} [options={}] - If provided and is a string, it will be used as separator.
685
716
  * @param {string} [options.separator=', '] - Separator to use.
686
- * @param {string} [options.lastSeparator=and] - Text to use as the last separator.
717
+ * @param {string} [options.lastSeparator='and'] - Text to use as the last separator.
687
718
  * @returns {string}
688
719
  */
689
720
  join = (input = [], options = {}) => {
690
721
  const array = [...input]
691
722
  if (isString(options)) options = { separator: options }
692
- let { separator = ', ', lastSeparator = 'and', lang } = options
723
+ const { separator = ', ', lastSeparator = 'and', lang } = options
693
724
  const translate = (val) => {
694
725
  return this.t(val, { lang }).toLowerCase()
695
726
  }
696
727
  if (array.length === 0) return translate('none')
697
728
  if (array.length === 1) return array[0]
698
- lastSeparator = translate(lastSeparator)
699
729
  const last = (array.pop() ?? '').trim()
700
- return array.map(a => (a + '').trim()).join(separator) + ` ${lastSeparator} ${last}`
730
+ return array.map(a => (a + '').trim()).join(separator) + ` ${translate(lastSeparator)} ${last}`
701
731
  }
702
732
 
703
733
  /**
704
- * Return its numeric portion of a value.
734
+ * Get numeric portion and its unit from a string.
705
735
  *
706
736
  * @method
707
- * @param {string} [value=''] - Value to get its numeric portion.
708
- * @param {string} [defUnit=''] - Default unit if value doesn't have one.
737
+ * @param {string} [value=''] - Value to get its numeric portion
738
+ * @param {string} [defUnit=''] - Default unit if value doesn't have one
709
739
  * @returns {string}
710
740
  */
711
741
  numUnit = (value = '', defUnit = '') => {
@@ -716,27 +746,31 @@ class Bajo extends Plugin {
716
746
 
717
747
  /**
718
748
  * Read and parse file as config object. Supported types: `.js`, `.json` and `.yml/.yaml`.
719
- * 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.
749
+ * More supports can be added using plugin. {@link https://ardhi.github.io/bajo-config|bajo-config} gives you additional supports for `.yml`, `.yaml` and `.toml` file.
720
750
  *
721
751
  * If file extension is `.*`, it will be auto detected and parsed accordingly
722
752
  *
723
753
  * @method
724
754
  * @async
725
755
  * @param {string} file - File to read and parse.
726
- * @param {Object} [options={}] - Options.
727
- * @param {boolean} [options.ignoreError] - Any exception will be silently discarded.
728
- * @param {string} [options.ns] - If given, use this as the scope.
729
- * @param {string} [options.pattern] - If given and auto detection is on (extension is `.*`), it will be used for instead the default one.
730
- * @param {Object} [options.defValue={}] - Default value to use if value returned empty.
731
- * @param {Object} [options.parserOpts={}] - Parser setting.
732
- * @param {Object} [options.globOpts={}] - {@link https://github.com/mrmlnc/fast-glob|fast-glob} options.
756
+ * @param {Object} [options={}] - Options
757
+ * @param {boolean} [options.ignoreError] - Any exception will be silently discarded
758
+ * @param {string} [options.ns] - If provided, scoped to this namespace. Otherwise, the running plugin's namespace will be used
759
+ * @param {string} [options.baseNs] - If provided, it will be used as the base namespace for extending config from other plugins
760
+ * @param {string|string[]|boolean} [options.extend] - If provided, it will be used as the base namespace for extending config from other plugins. Set to `false` to disable extending
761
+ * @param {boolean} [options.checkOverride] - If `true` and `baseNs` is provided or `extend` is not `false`, check for override config in main plugin
762
+ * @param {boolean} [options.merge] - If `true`, config from other plugins will be merged into the original config. Otherwise, it will perform a deep defaults merge
763
+ * @param {string} [options.pattern] - If provided and auto detection is on (extension is `.*`), it will be used for instead the auto generated one
764
+ * @param {Object} [options.defValue={}] - Default value to use if value returned empty
765
+ * @param {Object} [options.parserOpts={}] - Parser options
766
+ * @param {Object} [options.globOpts={}] - {@link https://github.com/mrmlnc/fast-glob|fast-glob} options
733
767
  * @returns {Object}
734
768
  */
735
769
  readConfig = async (file, options = {}) => {
736
- const { parseObject } = this.app.lib
737
- const { defaultsDeep } = this.app.lib.aneka
738
- const { uniq, isString, isArray, findIndex, isPlainObject, merge } = this.app.lib._
739
- let { ns, baseNs, extend, checkOverride, merge: merged, pattern, ignoreError = true, defValue = {}, parserOpts = {}, globOpts = {}, handler, cache = {} } = options
770
+ let {
771
+ ns, baseNs, extend, checkOverride, merge: merged, pattern, ignoreError = true,
772
+ defValue = {}, parserOpts = {}, globOpts = {}, handler, cache = {}
773
+ } = options
740
774
 
741
775
  const getParseOptsArgs = (opts, orig) => {
742
776
  opts.parserOpts = opts.parserOpts ?? {}
@@ -757,7 +791,7 @@ class Bajo extends Plugin {
757
791
  }
758
792
  const { suffix = '', keys = [] } = options
759
793
  let bases = this.app.getAllNs()
760
- if (isString(extend)) extend = extend.split(',').map(i => i.trim)
794
+ if (isString(extend)) extend = extend.split(',').map(i => i.trim())
761
795
  if (isArray(extend)) bases = [...extend, 'main']
762
796
  bases = uniq(bases)
763
797
  let ext = isArray(orig) ? [] : {}
@@ -802,23 +836,24 @@ class Bajo extends Plugin {
802
836
  if (cache.name) result = await this.app.cache.load(cache.name, cache.ttlDur)
803
837
  if (result) return result
804
838
  await this.runHook('bajo:beforeReadConfig', file, options)
805
- parserOpts.readFromFile = true
839
+ const readOpts = {
840
+ readFromFile: true,
841
+ throwNotFound: !ignoreError,
842
+ defValue,
843
+ parserOpts
844
+ }
806
845
  if (!ns) ns = this.ns
807
846
  file = resolvePath(this.app.getPluginFile(file))
808
847
  let ext = path.extname(file)
809
848
  const fname = path.dirname(file) + '/' + path.basename(file, ext)
810
849
  ext = ext.toLowerCase()
811
- if (['.js', '.json'].includes(ext)) {
812
- const item = find(this.app.configHandlers, { ext })
813
- return await output(await item.readHandler.call(this.app[ns], file, parserOpts))
814
- }
815
850
  if (!['', '.*'].includes(ext)) {
816
851
  const item = find(this.app.configHandlers, { ext })
817
852
  if (!item) {
818
853
  if (!ignoreError) throw this.error('cantParse%s', file, { code: 'BAJO_CONFIG_NO_PARSER' })
819
854
  return await output(defValue)
820
855
  }
821
- return await output(await item.readHandler.call(this.app[item.ns], file, parserOpts))
856
+ return await output(await item.readHandler.call(this.app[item.ns], file, readOpts))
822
857
  }
823
858
  const formats = this.app.getConfigFormats(true)
824
859
  const item = pattern ?? `${fname}.{${formats.join(',')}}`
@@ -836,147 +871,174 @@ class Bajo extends Plugin {
836
871
  continue
837
872
  }
838
873
  const _ns = ['.js', '.json'].includes(ext) ? ns : item.ns
839
- config = await item.readHandler.call(this.app[_ns], f, parserOpts)
874
+ config = await item.readHandler.call(this.app[_ns], f, readOpts)
840
875
  if (!isEmpty(config)) break
841
876
  }
842
877
  return await output(config)
843
878
  }
844
879
 
845
- /**
846
- * Read and parse json file.
847
- *
848
- * @method
849
- * @param {string} file - File to read.
850
- * @param {boolean} [thrownNotFound=false] - If `true`, silently ignore if file is not found.
851
- * @returns {Object}
852
- */
853
- readJson = (file, thrownNotFound = false) => {
854
- const { parseObject } = this.app.lib
855
- if (isPlainObject(thrownNotFound)) thrownNotFound = false
856
- if (!fs.existsSync(file) &amp;&amp; thrownNotFound) throw this.error('notFound%s%s', this.t('file'), file)
857
- let resp
858
- try {
859
- resp = fs.readFileSync(file, 'utf8')
860
- } catch (err) {}
861
- if (isEmpty(resp)) return resp
862
- return parseObject(JSON.parse(resp))
863
- }
864
-
865
880
  /**
866
881
  * Read and parse JavaScript file.
867
882
  *
868
883
  * @async
869
884
  * @method
870
- * @param {string} file - File to read and parse.
871
- * @param {Object} [options={}] - Options.
872
- * @returns {Object} Parsed JavaScript object.
885
+ * @param {string} file - File to read and parse
886
+ * @param {boolean} [options.readFromFile=false] - Ignored for this method. Always read from file.
887
+ * @param {boolean} [options.throwNotFound=false] - If `true`, throw exception if file is not found
888
+ * @param {object} [options.defValue={}] - Default value to use if value returned empty
889
+ * @param {object} [options.parserOpts={}] - Options to be passed if file exports a function
890
+ * @returns {Object} Parsed JavaScript object
873
891
  */
874
892
  async fromJs (file, options = {}) {
875
- const args = options.args ?? []
893
+ if (isBoolean(options)) options = {}
894
+ options.defValue = options.defValue ?? {}
895
+ if (options.throwNotFound &amp;&amp; !fs.existsSync(file)) throw this.error('notFound%s%s', this.t('file'), file)
876
896
  let mod = await importModule(file)
877
- if (isFunction(mod)) mod = await mod.call(this, ...args)
878
- return mod
897
+ if (isFunction(mod)) mod = await mod.call(this, options.parserOpts)
898
+ return isEmpty(mod) ? options.defValue : mod
879
899
  }
880
900
 
881
901
  /**
882
- * Read and parse JSON string or object.
902
+ * Parse JSON string or read and parse JSON file.
883
903
  *
884
- * @param {string} data - Filename to load from or JSON string to parse.
885
- * @param {Object} [options={}] - Options.
886
- * @returns {Object} Parsed JSON object.
904
+ * @async
905
+ * @method
906
+ * @param {string} text - Text to be parsed or file path to be read if `options.readFromFile` is `true`
907
+ * @param {object|boolean} [options={}] - Options object. If a boolean is provided, it will be treated as `options.readFromFile`
908
+ * @param {boolean} [options.readFromFile=false] - If `true`, `text` is treated as a file path
909
+ * @param {boolean} [options.throwNotFound=false] - If `true`, throw exception if file is not found
910
+ * @param {object} [options.defValue={}] - Default value to use if value returned empty
911
+ * @param {object} [options.parserOpts={}] - Options to be passed to `JSON.parse()`
912
+ * @returns {Object} Parsed object.
913
+ * @see Bajo#toJson
887
914
  */
888
- fromJson (data, options = {}) {
889
- const content = options.readFromFile ? fs.readFileSync(data, 'utf8') : data
890
- return JSON.parse(content)
915
+ async fromJson (text, options = {}) {
916
+ if (isBoolean(options)) options = { readFromFile: options }
917
+ options.defValue = options.defValue ?? {}
918
+ if (options.readFromFile &amp;&amp; !fs.existsSync(text) &amp;&amp; options.throwNotFound) throw this.error('notFound%s%s', this.t('file'), text)
919
+ const content = options.readFromFile ? fs.readFileSync(text, 'utf8') : text
920
+ const result = JSON.parse(content)
921
+ return isEmpty(result) ? options.defValue : result
891
922
  }
892
923
 
893
924
  /**
894
- * Parse YAML text
925
+ * Parse YAML text or read and parse YAML file.
895
926
  *
927
+ * @async
896
928
  * @method
897
- * @param {string} text - Text to be parsed
898
- * @param {object} options - Options object
929
+ * @param {string} text - Text to be parsed or file path to be read if `options.readFromFile` is `true`
930
+ * @param {object|boolean} [options={}] - Options object. If a boolean is provided, it will be treated as `options.readFromFile`
931
+ * @param {boolean} [options.readFromFile=false] - If `true`, `text` is treated as a file path
932
+ * @param {boolean} [options.throwNotFound=false] - If `true`, throw exception if file is not found
933
+ * @param {object} [options.parserOpts={}] - Options to be passed to `YAML.load()`
934
+ * @param {object} [options.defValue={}] - Default value to use if value returned empty
899
935
  * @returns {object} Parsed object
936
+ * @see Bajo#toYaml
900
937
  */
901
- fromYaml = (text, options = {}) => {
902
- const { fs } = this.app.lib
938
+ async fromYaml (text, options = {}) {
939
+ if (isBoolean(options)) options = { readFromFile: options }
940
+ options.defValue = options.defValue ?? {}
941
+ if (options.readFromFile &amp;&amp; !fs.existsSync(text) &amp;&amp; options.throwNotFound) throw this.error('notFound%s%s', this.t('file'), text)
903
942
  const content = options.readFromFile ? fs.readFileSync(text, 'utf8') : text
904
- return yaml.load(content)
943
+ const result = yaml.load(content)
944
+ return isEmpty(result) ? options.defValue : result
905
945
  }
906
946
 
907
947
  /**
908
- * Parse YML text. Alias for fromYaml.
948
+ * Parse YML text or read and parse YML file. Alias for {@link Bajo#fromYaml}.
909
949
  *
950
+ * @async
910
951
  * @method
911
- * @param {string} text - Text to be parsed
912
- * @param {object} options - Options object
952
+ * @param {string} text - Text to be parsed or file path to be read if `options.readFromFile` is `true`
953
+ * @param {object|boolean} [options={}] - Options object. If a boolean is provided, it will be treated as `options.readFromFile`
954
+ * @param {boolean} [options.readFromFile=false] - If `true`, `text` is treated as a file path
955
+ * @param {boolean} [options.throwNotFound=false] - If `true`, throw exception if file is not found
956
+ * @param {object} [options.defValue={}] - Default value to use if value returned empty
957
+ * @param {object} [options.parserOpts={}] - Options to be passed to `YAML.load()`
913
958
  * @returns {object} Parsed object
959
+ * @see Bajo#toYml
914
960
  */
915
- fromYml = (text, options = {}) => {
961
+ async fromYml (text, options = {}) {
916
962
  return this.fromYaml(text, options)
917
963
  }
918
964
 
919
965
  /**
920
- * Convert data to JSON string.
966
+ * Convert data to JSON string, optionally write to file if `options.writeToFile` is provided.
921
967
  *
968
+ * @async
922
969
  * @method
923
- * @param {Object} data - Data to convert to JSON string.
924
- * @param {Object} [options={}] - Options.
925
- * @param {boolean} [options.writeToFile=false] - If true, write the JSON string to a file.
926
- * @param {string} [options.saveAsFile] - The file path to save the JSON string if writeToFile is true.
927
- * @returns {string} JSON string
970
+ * @param {Object} data - Data to convert to JSON string
971
+ * @param {Object|string} [options={}] - Options object. If a string is provided, it will be treated as `options.writeToFile`
972
+ * @param {string} [options.writeToFile] - If not empty, write result to this file path instead of returning it as string
973
+ * @param {Object} [options.parserOpts={}] - Options for `JSON.stringify()`
974
+ * @returns {Promise&lt;string|void>} JSON string or void if written to file
975
+ * @see Bajo#fromJson
928
976
  */
929
- toJson = (data, options = {}) => {
930
- const content = JSON.stringify(data, null, omit(options, ['writeToFile']))
931
- if (options.writeToFile) {
932
- fs.writeFileSync(options.saveAsFile, content, 'utf8')
977
+ async toJson (data, options = {}) {
978
+ if (isString(options)) options = { writeToFile: options }
979
+ options.parserOpts = options.parserOpts ?? {}
980
+ options.parserOpts.space = options.parserOpts.space ?? 2
981
+ const content = JSON.stringify(data, null, options.parserOpts)
982
+ if (isString(options.writeToFile)) {
983
+ fs.writeFileSync(options.writeToFile, content, 'utf8')
933
984
  return
934
985
  }
935
986
  return content
936
987
  }
937
988
 
938
989
  /**
939
- * Convert object to YAML string
990
+ * Convert object to YAML string, optionally write to file if `options.writeToFile` is provided.
940
991
  *
992
+ * @async
941
993
  * @method
942
994
  * @param {object} object - Object to be converted
943
- * @param {object} options - Options object
944
- * @returns {string} YAML string
995
+ * @param {object|string} [options] - Options object. If a string is provided, it will be treated as `options.writeToFile`
996
+ * @param {string} [options.writeToFile] - If not empty, write result to this file path instead of returning it as string
997
+ * @param {Object} [options.parserOpts={}] - Options for `YAML.dump()`
998
+ * @returns {Promise&lt;string|void>} YAML string or void if written to file
999
+ * @see Bajo#fromYaml
945
1000
  */
946
- toYaml = (object, options = {}) => {
947
- const { omit } = this.app.lib._
948
- const { fs } = this.app.lib
949
- const content = yaml.dump(object, omit(options, ['writeToFile']))
1001
+ async toYaml (object, options = {}) {
1002
+ if (isString(options)) options = { writeToFile: options }
1003
+ options.parserOpts = options.parserOpts ?? {}
1004
+ const content = yaml.dump(object, options.parserOpts)
950
1005
  if (options.writeToFile) {
951
- fs.writeFileSync(options.saveAsFile, content, 'utf8')
952
- return
1006
+ if (isString(options.writeToFile)) {
1007
+ fs.writeFileSync(options.writeToFile, content, 'utf8')
1008
+ return
1009
+ }
953
1010
  }
954
1011
  return content
955
1012
  }
956
1013
 
957
1014
  /**
958
- * Convert object to YML string. Alias for toYaml.
959
- *
1015
+ * Convert object to YML string, optionally write to file if `options.writeToFile` is provided. Alias for {@link Bajo#toYaml}.
1016
+ * @async
960
1017
  * @method
961
1018
  * @param {object} object - Object to be converted
962
- * @param {object} options - Options object
963
- * @returns {string} YML string
1019
+ * @param {object|string} [options] - Options object. If a string is provided, it will be treated as `options.writeToFile`
1020
+ * @param {string} [options.writeToFile] - If not empty, write result to this file path instead of returning it as string
1021
+ * @param {Object} [options.parserOpts={}] - Options for `YAML.dump()`
1022
+ * @returns {Promise&lt;string|void>} YAML string or void if written to file
1023
+ * @see Bajo#fromYml
964
1024
  */
965
- toYml = (object, options = {}) => {
1025
+ async toYml (object, options = {}) {
966
1026
  return this.toYaml(object, options)
967
1027
  }
968
1028
 
969
1029
  /**
970
- * Read all config files from path.
1030
+ * Read all forms of configuration files from file path.
1031
+ *
1032
+ * Internally, it will call {@link Bajo#readConfig} twice, first for the default config file and
1033
+ * second for the environment based config file. Then it will merge both results using `defaultsDeep`.
971
1034
  *
972
1035
  * @method
973
1036
  * @async
974
1037
  * @param {string} path - Base path to start looking config files.
975
- * @param {Object} [options={}] - Options.
1038
+ * @param {Object} [options={}] - Options. See {@link Bajo#readConfig} for more.
976
1039
  * @returns {Object} Merged configuration object.
977
1040
  */
978
1041
  readAllConfigs = async (path, options) => {
979
- const { defaultsDeep } = this.app.lib.aneka
980
1042
  let cfg = {}
981
1043
  let ext = {}
982
1044
  // default config file
@@ -995,7 +1057,14 @@ class Bajo extends Plugin {
995
1057
  }
996
1058
 
997
1059
  /**
998
- * Run named hook/event.
1060
+ * Run named {@link module:Hook}.
1061
+ *
1062
+ * If no hook found with the given name, it will return an empty array.
1063
+ * If a hook has `noWait` set to `true`, it will not wait for the hook to finish and will not return its result.
1064
+ *
1065
+ * > **Note**: Even though this method returns a result, it is not recommended to use the result for any purpose.
1066
+ * Use the result only for debugging or logging purposes. Hooks are designed to be fast, lightweight and mutate
1067
+ * arguments given to them so that the next hook can benefit from the changes.
999
1068
  *
1000
1069
  * @method
1001
1070
  * @async
@@ -1023,6 +1092,18 @@ class Bajo extends Plugin {
1023
1092
  return results
1024
1093
  }
1025
1094
 
1095
+ /**
1096
+ * Get download directory. If doesn't exist, it will be created automatically.
1097
+ *
1098
+ * @method
1099
+ * @returns {string} Absolute path to the download directory
1100
+ */
1101
+ getDownloadDir = () => {
1102
+ const dir = `${this.app.getPluginDataDir(this.ns)}/download`
1103
+ fs.ensureDirSync(dir)
1104
+ return dir
1105
+ }
1106
+
1026
1107
  /**
1027
1108
  * Save item as file in Bajo's download directory. That is a directory inside your
1028
1109
  * Bajo plugin's data directory.
@@ -1038,27 +1119,27 @@ class Bajo extends Plugin {
1038
1119
  * @returns {string} Full file path.
1039
1120
  */
1040
1121
  saveAsDownload = async (file, item, printSaved = true) => {
1041
- const { print } = this.app.bajo
1042
- const fname = increment(`${this.app.getPluginDataDir(this.ns)}/download/${trim(file, '/')}`, { fs: true })
1043
- const dir = path.dirname(fname)
1044
- if (!fs.existsSync(dir)) fs.ensureDirSync(dir)
1122
+ const fname = increment(`${this.getDownloadDir()}/${trim(file, '/')}`, { fs: true })
1045
1123
  await fs.writeFile(fname, item, 'utf8')
1046
- if (printSaved) print.succeed('savedAs%s', path.resolve(fname), { skipSilence: true })
1124
+ if (printSaved) this.print.succeed('savedAs%s', path.resolve(fname), { skipSilence: true })
1047
1125
  return fname
1048
1126
  }
1049
1127
 
1050
1128
  /**
1051
- * Read config using all registered config handlers. The first handler that returns a
1129
+ * Parse `input` using all registered config handlers. The first handler that returns a
1052
1130
  * valid object or array will be used.
1053
1131
  *
1132
+ * Use this method if you want to parse a text `input` that can be in any format supported by the registered config handlers.
1133
+ *
1054
1134
  * @method
1055
1135
  * @param {string} input - The input string to be processed by the config handlers.
1056
1136
  * @param {string[]} [exts] - Optional array of extensions to filter the config handlers. If provided, only handlers with matching extensions will be used.
1057
1137
  * @param {object} [options={}] - Options to be passed to the config handlers.
1058
1138
  * @returns {Object|Array|null} The result from the first successful config handler, or null if none succeed.
1059
1139
  */
1060
- readAsConfig = async (input, exts, options = {}) => {
1140
+ parseWithConfig = async (input, exts, options = {}) => {
1061
1141
  let result
1142
+ options.readFromFile = false
1062
1143
  const handlers = exts ? this.app.configHandlers.filter(h => exts.includes(h.ext)) : this.app.configHandlers
1063
1144
  for (const handler of handlers) {
1064
1145
  if (result) break
@@ -1073,4 +1154,4 @@ class Bajo extends Plugin {
1073
1154
  }
1074
1155
 
1075
1156
  export default Bajo
1076
- </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-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-Hook.html">Hook</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#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>
1157
+ </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-tutorials"><div>Tutorials</div><svg><use xlink:href="#down-icon"></use></svg></div><div class="sidebar-section-children-container"><div class="sidebar-section-children"><a href="tutorial-01_WELCOME.html">Welcome</a></div><div class="sidebar-section-children"><a href="tutorial-02_GETTING_STARTED.html">Getting Started</a></div><div class="sidebar-section-children"><a href="tutorial-03_USER_GUIDE.html">User Guide</a></div><div class="sidebar-section-children"><a href="tutorial-04_DEV_GUIDE.html">Developer Guide</a></div><div class="sidebar-section-children"><a href="tutorial-05_ECOSYSTEM.html">Ecosystem</a></div></div><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="Main.html">Main</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-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-Applet.html">Applet</a></div><div class="sidebar-section-children"><a href="module-Helper.html">Helper</a></div><div class="sidebar-section-children"><a href="module-Hook.html">Hook</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#TConfig">TConfig</a></div><div class="sidebar-section-children"><a href="global.html#boot">boot</a></div><div class="sidebar-section-children"><a href="global.html#package%25C2%25B7json">package·json</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>