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.
- package/.jsdoc.conf.json +2 -1
- package/class/app.js +220 -107
- package/class/bajo.js +246 -174
- package/class/base.js +85 -24
- package/class/cache.js +8 -16
- package/class/err.js +31 -10
- package/class/log.js +46 -38
- package/class/plugin.js +53 -30
- package/class/print.js +18 -15
- package/class/tools.js +6 -5
- package/docs/App.html +51 -1
- package/docs/Bajo.html +1 -1
- package/docs/Base.html +34 -1
- package/docs/Cache.html +1 -1
- package/docs/Err.html +2 -2
- package/docs/Log.html +2 -2
- package/docs/Main.html +3 -0
- package/docs/Plugin.html +1 -1
- package/docs/Print.html +1 -1
- package/docs/Tools.html +1 -1
- package/docs/class_app.js.html +222 -109
- package/docs/class_bajo.js.html +260 -179
- package/docs/class_base.js.html +87 -26
- package/docs/class_cache.js.html +10 -18
- package/docs/class_err.js.html +33 -12
- package/docs/class_log.js.html +48 -40
- package/docs/class_plugin.js.html +55 -32
- package/docs/class_print.js.html +20 -17
- package/docs/class_tools.js.html +8 -7
- package/docs/data/search.json +1 -1
- package/docs/extend_bajo_hook.js.html +289 -0
- package/docs/global.html +22 -1
- package/docs/index.html +1 -1
- package/docs/index.js.html +53 -16
- package/docs/lib_applet-hook.js.html +328 -0
- package/docs/lib_config.js.html +167 -0
- package/docs/lib_helper.js.html +186 -192
- package/docs/module-Applet.html +3 -0
- package/docs/module-Helper-Main.html +3 -0
- package/docs/module-Helper.html +1 -8
- package/docs/module-Hook.html +1 -1
- package/docs/tutorial-01_WELCOME.html +3 -0
- package/docs/tutorial-02_GETTING_STARTED.html +125 -0
- package/docs/tutorial-03_USER_GUIDE.html +54 -0
- package/docs/tutorial-04_DEV_GUIDE.html +178 -0
- package/docs/tutorial-05_ECOSYSTEM.html +3 -0
- package/extend/bajo/intl/en-US.json +2 -1
- package/extend/bajo/intl/id.json +2 -1
- package/index.js +51 -14
- package/lib/applet-hook.js +325 -0
- package/lib/config.js +164 -0
- package/lib/helper.js +184 -190
- package/package.json +6 -2
- package/test/00-global-hooks.test.js +44 -0
- package/test/e2e/_run.js +19 -0
- package/test/e2e/e2e-applet-process.test.js +66 -0
- package/test/e2e/e2e-boot-process.test.js +62 -0
- package/test/e2e/e2e-hooks-process.test.js +68 -0
- package/test/integration/_setup.js +55 -0
- package/test/integration/aspect-01-boot-instance.test.js +24 -0
- package/test/integration/aspect-02-plugin-loading.test.js +24 -0
- package/test/integration/aspect-03-config-merge.test.js +27 -0
- package/test/integration/aspect-04-intl-loading.test.js +29 -0
- package/test/integration/aspect-05-cache-roundtrip.test.js +26 -0
- package/test/integration/aspect-06-log-file-save.test.js +28 -0
- package/test/integration/aspect-07-plugin-data-dir.test.js +25 -0
- package/test/integration/aspect-08-plugin-lookup.test.js +25 -0
- package/test/integration/aspect-09-config-formats.test.js +26 -0
- package/test/integration/aspect-10-hooks.test.js +38 -0
- package/test/unit/app.test.js +115 -0
- package/test/unit/applet-hook.test.js +10 -0
- package/test/unit/bajo.test.js +151 -0
- package/test/unit/base.test.js +90 -0
- package/test/unit/cache.test.js +89 -0
- package/test/unit/config.test.js +14 -0
- package/test/unit/err.test.js +57 -0
- package/test/unit/helper.test.js +116 -0
- package/test/unit/index.test.js +40 -0
- package/test/unit/log.test.js +84 -0
- package/test/unit/plugin.test.js +95 -0
- package/test/unit/print.test.js +72 -0
- package/test/unit/tools.test.js +29 -0
- package/wiki/CHANGES.md +4 -0
- package/wiki/tutorials/01_WELCOME.md +18 -0
- package/wiki/tutorials/02_GETTING_STARTED.md +366 -0
- package/wiki/tutorials/03_USER_GUIDE.md +262 -0
- package/wiki/tutorials/04_DEV_GUIDE.md +386 -0
- package/wiki/tutorials/05_ECOSYSTEM.md +95 -0
- package/wiki/tutorials/tutorials.json +17 -0
- package/lib/hook.js +0 -228
- package/test/app.test.js +0 -183
- package/test/bajo.test.js +0 -124
- package/test/base.test.js +0 -75
- package/test/cache.test.js +0 -94
- package/test/e2e.test.js +0 -137
- package/test/err.test.js +0 -73
- package/test/helper.test.js +0 -39
- package/test/import-module.test.js +0 -138
- package/test/integration.test.js +0 -218
- package/test/log.test.js +0 -119
- package/test/plugin.test.js +0 -116
- package/test/print.test.js +0 -100
- package/test/tools.test.js +0 -38
- package/wiki/CONFIG.md +0 -36
- package/wiki/DEV_GUIDE.md +0 -3
- package/wiki/ECOSYSTEM.md +0 -93
- package/wiki/GETTING-STARTED.md +0 -356
- package/wiki/USER-GUIDE.md +0 -256
package/wiki/GETTING-STARTED.md
DELETED
|
@@ -1,356 +0,0 @@
|
|
|
1
|
-
# Getting Started
|
|
2
|
-
|
|
3
|
-
This is the quickstart guide for getting started with Bajo, a framework for building robust and scalable applications. This section covers essential steps for beginners, including installation, setting up your project structure, and understanding basic configurations. You will be introduced to the core concepts of Bajo's plugin system and lifecycle, learning how to build your first "Hello World!" application and utilize the powerful hook system for custom extensions. This foundation will prepare you to dive into Bajo's specialized sub-frameworks, such as Dobo and Waibu, to further develop your application's capabilities.
|
|
4
|
-
|
|
5
|
-
## Installation
|
|
6
|
-
|
|
7
|
-
Create a new empty directory named ```my-project```. This will be your app directory throughout this tutorial. Now, ```cd``` into your newly created directory and type:
|
|
8
|
-
|
|
9
|
-
```bash
|
|
10
|
-
$ npm init
|
|
11
|
-
```
|
|
12
|
-
|
|
13
|
-
You'll be asked to name your project, provide its description, author info, etc. Please continue until the *package.json* file is created.
|
|
14
|
-
|
|
15
|
-
Then, open it using your favorite editor, edit it, and insert the following lines:
|
|
16
|
-
|
|
17
|
-
```javascript
|
|
18
|
-
...
|
|
19
|
-
"type": "module",
|
|
20
|
-
"bajo": {
|
|
21
|
-
"type": "app"
|
|
22
|
-
},
|
|
23
|
-
...
|
|
24
|
-
```
|
|
25
|
-
|
|
26
|
-
After completing these steps, install Bajo by running:
|
|
27
|
-
|
|
28
|
-
```bash
|
|
29
|
-
$ npm install bajo
|
|
30
|
-
```
|
|
31
|
-
|
|
32
|
-
Now, create your app's bootstrap file, ```index.js```, and add these lines:
|
|
33
|
-
|
|
34
|
-
```javascript
|
|
35
|
-
// index.js file
|
|
36
|
-
import bajo from 'bajo'
|
|
37
|
-
await bajo()
|
|
38
|
-
```
|
|
39
|
-
|
|
40
|
-
A Bajo-based app **ALWAYS** needs a data directory for its config files, etc. This directory can be located inside or outside your app directory. If this directory doesn't exist yet, Bajo will create a new one for you, named ```data```, in the same location as your ```index.js``` file. By default, Bajo will set this as your data directory.
|
|
41
|
-
|
|
42
|
-
Bajo will also automatically create the ```main``` directory to serve as your main plugin if it doesn't already exist. A factory file named ```index.js``` will be added inside the ```main``` directory. More on this later in the next chapter.
|
|
43
|
-
|
|
44
|
-
Now, run your app:
|
|
45
|
-
|
|
46
|
-
```bash
|
|
47
|
-
$ node index.js
|
|
48
|
-
```
|
|
49
|
-
|
|
50
|
-
Congratulations! Your Bajo-based app is up and running!
|
|
51
|
-
|
|
52
|
-
## Playing Around
|
|
53
|
-
|
|
54
|
-
By now, your directory structure should look like this:
|
|
55
|
-
|
|
56
|
-
```
|
|
57
|
-
.
|
|
58
|
-
└── my-project
|
|
59
|
-
├── data
|
|
60
|
-
│ └── config
|
|
61
|
-
├── main
|
|
62
|
-
│ └── index.js
|
|
63
|
-
├── node_modules
|
|
64
|
-
│ └── ...
|
|
65
|
-
├── index.js
|
|
66
|
-
├── package.json
|
|
67
|
-
└── package-lock.json
|
|
68
|
-
```
|
|
69
|
-
Your app runs in the ```dev``` environment by default. In this environment, the log level is set to ```debug```, which can be overridden using program arguments:
|
|
70
|
-
|
|
71
|
-
```bash
|
|
72
|
-
$ node index.js --log-level=trace --log-timeTaken --log-pretty
|
|
73
|
-
```
|
|
74
|
-
|
|
75
|
-
Now, Bajo will show you a bunch of pretty, colorful logs, including the time each step took. This is very useful for debugging and for finding out which activity takes the most time.
|
|
76
|
-
|
|
77
|
-
But typing program arguments is tedious and boring. Let's use a config file to do some magic. Please create the JSON file ```data/config/bajo.json``` and add these lines to it:
|
|
78
|
-
|
|
79
|
-
```json
|
|
80
|
-
{
|
|
81
|
-
"env": "dev",
|
|
82
|
-
"log": {
|
|
83
|
-
"pretty": true,
|
|
84
|
-
"level": "trace",
|
|
85
|
-
"timeTaken": true
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
```
|
|
89
|
-
|
|
90
|
-
Now, try simply running your app without any arguments:
|
|
91
|
-
|
|
92
|
-
```bash
|
|
93
|
-
$ node index.js
|
|
94
|
-
```
|
|
95
|
-
|
|
96
|
-
Much better! And hassle-free!
|
|
97
|
-
|
|
98
|
-
You can mix and match config file and program arguments on any key-value pairs anytime you want. You can even utilize environment variables and a dotenv ```.env``` file if you really need to. Please read the *User Guide* for more in-depth information on this.
|
|
99
|
-
|
|
100
|
-
## Your First Project
|
|
101
|
-
|
|
102
|
-
Let's start with **Hello World!**, the Bajo way.
|
|
103
|
-
|
|
104
|
-
The objectives of this short course are:
|
|
105
|
-
1. reading values from the configuration
|
|
106
|
-
2. copying those values into properties in the main plugin during initialization
|
|
107
|
-
3. displaying values while the program is running
|
|
108
|
-
4. notifying if the program terminates
|
|
109
|
-
|
|
110
|
-
Let's go!
|
|
111
|
-
|
|
112
|
-
### Config Object
|
|
113
|
-
|
|
114
|
-
Please open the ```data/config/main.json``` file or create a new one if it doesn't exist. This is the main plugin configuration file.
|
|
115
|
-
|
|
116
|
-
Enter these lines:
|
|
117
|
-
|
|
118
|
-
```json
|
|
119
|
-
{
|
|
120
|
-
"firstName": "Tanjiro",
|
|
121
|
-
"lastName": "Kamado",
|
|
122
|
-
"age": 15
|
|
123
|
-
}
|
|
124
|
-
```
|
|
125
|
-
|
|
126
|
-
Each Bajo plugin can be configured through the configuration file located at ```{dataDir}/config/{ns}.json```, where ```{dataDir}``` is the data directory location and ```{ns}``` is the namespace or plugin name. Please visit [User Guide](USER-GUIDE.md) for more info.
|
|
127
|
-
|
|
128
|
-
As you may know now, in Bajo, you create everything through plugins. If your project is small or not very complicated, you can use the main plugin that's always ready and available. But over time, as your app gets bigger and bigger, you'll need to start thinking about breaking things into small pieces through independent plugins.
|
|
129
|
-
|
|
130
|
-
If you intend to create your own customized plugin, refer to the [Developer Guide](DEV-GUIDE.md).
|
|
131
|
-
|
|
132
|
-
### Plugin Factory
|
|
133
|
-
|
|
134
|
-
Now let's open ```main/index.js``` and update it according to the example below.
|
|
135
|
-
|
|
136
|
-
This file is the main plugin factory. It gets created automatically if it's not there.
|
|
137
|
-
|
|
138
|
-
```javascript
|
|
139
|
-
async function factory (pkgName) {
|
|
140
|
-
const me = this
|
|
141
|
-
|
|
142
|
-
return class Main extends this.app.baseClass.Base {
|
|
143
|
-
constructor () {
|
|
144
|
-
super(pkgName, me.app)
|
|
145
|
-
this.config = {}
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
init = async () => {
|
|
149
|
-
this.firstName = this.config.firstName
|
|
150
|
-
this.lastName = this.config.lastName
|
|
151
|
-
this.age = this.config.age
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
start = async () => {
|
|
155
|
-
this.log.info('First name: %s, Last name: %s, age: %d', this.firstName, this.lastName, this.age)
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
exit = async () => {
|
|
159
|
-
this.log.warn('Program aborted')
|
|
160
|
-
}
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
export default factory
|
|
165
|
-
```
|
|
166
|
-
|
|
167
|
-
A couple of things to note:
|
|
168
|
-
|
|
169
|
-
- At boot, the main plugin reads its configuration file main.json and merges it with all program arguments and environment variables it can find which belong to it and builds its config object.
|
|
170
|
-
- Then, plugin initialization happens. In this step, all code inside the asynchronous method ```init``` will be executed. In this example, all object config's key-value pairs will be assigned to the plugin's properties.
|
|
171
|
-
- Then, the plugin will start. All code inside the asynchronous method ```start``` will be invoked. In this example, it simply writes the first name, last name, and age to the logger.
|
|
172
|
-
- At program exit, the asynchronous method ```exit``` will be executed.
|
|
173
|
-
|
|
174
|
-
That's the lifecycle of all Bajo plugins, including the main plugin.
|
|
175
|
-
|
|
176
|
-
But unfortunately, if you run it now, you'll get an error. That's because the root keys of the config object from the configuration file (```firstName```, ```lastName```, and ```age```) aren't defined in ```this.config``` and are ignored during initialization.
|
|
177
|
-
|
|
178
|
-
That's why we need to fix it first:
|
|
179
|
-
|
|
180
|
-
```
|
|
181
|
-
...
|
|
182
|
-
constructor () {
|
|
183
|
-
super(pkgName, me.app)
|
|
184
|
-
this.config = {
|
|
185
|
-
firstName: 'John',
|
|
186
|
-
lastName: 'Doe',
|
|
187
|
-
age: 50
|
|
188
|
-
}
|
|
189
|
-
}
|
|
190
|
-
...
|
|
191
|
-
```
|
|
192
|
-
|
|
193
|
-
```this.config``` defined in the constructor serves as the default config object. During initialization, this will be merged with values from the configuration file. If one or more keys are missing from the configuration file, the related default values are used.
|
|
194
|
-
|
|
195
|
-
If you run this now, you'll get a bunch of logs. Let's filter it with:
|
|
196
|
-
|
|
197
|
-
```bash
|
|
198
|
-
$ node index.js --log-level=info
|
|
199
|
-
2025-09-12T00:09:38.946Z +97ms INFO: main First name: Tanjiro, Last name: Kamado, age: 15
|
|
200
|
-
2025-09-12T00:09:38.949Z +3ms WARN: main Program aborted
|
|
201
|
-
```
|
|
202
|
-
|
|
203
|
-
Sweet!
|
|
204
|
-
|
|
205
|
-
## The Hook System
|
|
206
|
-
|
|
207
|
-
### Tapping a Hook
|
|
208
|
-
|
|
209
|
-
Bajo offers you a hook system in which you can tap certain actions anywhere in the system with your own code. Let's go through the simplest one: running your code just after the boot process is completed.
|
|
210
|
-
|
|
211
|
-
First, create ```main/extend/bajo/hook/bajo@after-boot-complete.js```. If you're curious about the reason for the unusual file name, please refer to the [User Guide](USER-GUIDE.md) on Hook's naming rules.
|
|
212
|
-
|
|
213
|
-
```javascript
|
|
214
|
-
async function afterBootComplete () {
|
|
215
|
-
this.log.info('Hook after boot complete')
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
export default afterBootComplete
|
|
219
|
-
```
|
|
220
|
-
|
|
221
|
-
Pretty easy, right?
|
|
222
|
-
|
|
223
|
-
### Your Own Hook
|
|
224
|
-
|
|
225
|
-
Now let's create your own hook. Even though it's silly to use a hook for such a simple program, its purpose is to demonstrate how easy it is to do it in Bajo.
|
|
226
|
-
|
|
227
|
-
This time, we want to change the property ```lastName``` with hook.
|
|
228
|
-
|
|
229
|
-
Open ```index.js``` and update it accordingly:
|
|
230
|
-
|
|
231
|
-
```javascript
|
|
232
|
-
...
|
|
233
|
-
init = async () => {
|
|
234
|
-
const { runHook } = this.app.bajo // add this line
|
|
235
|
-
this.firstName = this.config.firstName
|
|
236
|
-
this.lastName = this.config.lastName
|
|
237
|
-
this.age = this.config.age
|
|
238
|
-
await runHook('main:myHook') // and this line
|
|
239
|
-
}
|
|
240
|
-
...
|
|
241
|
-
```
|
|
242
|
-
|
|
243
|
-
The above code should add a hook named ```main:myHook``` in the initialization step.
|
|
244
|
-
|
|
245
|
-
Now, create a new file ```main/extend/bajo/hook/main@my-hook.js```:
|
|
246
|
-
|
|
247
|
-
```javascript
|
|
248
|
-
async function myHook () {
|
|
249
|
-
this.lastName = 'THE Daemon Slayer'
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
export default myHook
|
|
253
|
-
```
|
|
254
|
-
|
|
255
|
-
As every class method, hook, and function handler in Bajo is called within its own plugin scope, it is sufficient to set ```this.lastName``` directly inside a hook.
|
|
256
|
-
|
|
257
|
-
Now run it. It should show you something like these:
|
|
258
|
-
|
|
259
|
-
```bash
|
|
260
|
-
2025-09-12T12:09:09.004Z +115ms INFO: main First name: Tanjiro, Last name: THE Daemon Slayer, age: 15
|
|
261
|
-
2025-09-12T12:09:09.008Z +4ms INFO: main Hook after boot complete
|
|
262
|
-
2025-09-12T12:09:09.009Z +1ms WARN: main Program aborted
|
|
263
|
-
```
|
|
264
|
-
|
|
265
|
-
## Using Plugins
|
|
266
|
-
|
|
267
|
-
Bajo is designed to be an ecosystem with many small plugins. Think of Lego blocks: you build a structure just by picking up the right ones and sticking them together to create your very own structure.
|
|
268
|
-
|
|
269
|
-
In this series, we'll learn how to use such plugins to extend our app.
|
|
270
|
-
|
|
271
|
-
### File Format
|
|
272
|
-
|
|
273
|
-
We love YAML format so much so let's use it for our configuration file:
|
|
274
|
-
|
|
275
|
-
1. YAML support is part of the ```bajo-config``` plugin, so we need to install it first
|
|
276
|
-
```bash
|
|
277
|
-
$ npm install bajo-config
|
|
278
|
-
```
|
|
279
|
-
2. Now, open the ```data/config/.plugins``` file and put ```bajo-config``` in it, line by line. If it doesn't exist yet, create it first. Don't worry about the order; Bajo will figure it out automatically if you have many plugins.
|
|
280
|
-
3. Delete your old configuration file ```data/config/main.json``` and create a new ```data/config/main.yml```. By the way, it doesn't matter whether you use ```.yml``` or ```.yaml```. Both are supported.
|
|
281
|
-
4. Enter the following lines, it should be the same object as before, just in YAML format:
|
|
282
|
-
```yaml
|
|
283
|
-
firstName: Tanjiro
|
|
284
|
-
lastName: Kamado
|
|
285
|
-
age: 15
|
|
286
|
-
```
|
|
287
|
-
5. Run and check the output. It should be the exact same output as before, except for the log's timestamps.
|
|
288
|
-
|
|
289
|
-
### Applet Mode
|
|
290
|
-
|
|
291
|
-
**Applets** are small tools embedded in plugins that can be invoked when Bajo is running in **applet mode**. Their lifecycle is totally independent of the main program, but they can reuse the same resources and config objects.
|
|
292
|
-
|
|
293
|
-
You can run Bajo in applet mode by using the ```--applet``` or ```-a``` switches:
|
|
294
|
-
|
|
295
|
-
```bash
|
|
296
|
-
$ node index.js -a
|
|
297
|
-
```
|
|
298
|
-
|
|
299
|
-
But applet mode requires you to install ```bajo-cli``` beforehand. So, please install it first:
|
|
300
|
-
|
|
301
|
-
```bash
|
|
302
|
-
$ npm install bajo-cli
|
|
303
|
-
```
|
|
304
|
-
|
|
305
|
-
Don't forget to add ```bajo-cli``` to the ```data/config/.plugins``` file. Again, the order doesn't really matter here.
|
|
306
|
-
|
|
307
|
-
If you run it now, you'll see something like this on your terminal:
|
|
308
|
-
|
|
309
|
-
```bash
|
|
310
|
-
ℹ App runs in applet mode
|
|
311
|
-
? Please select: (Use arrow keys)
|
|
312
|
-
❯ bajoConfig
|
|
313
|
-
bajoCli
|
|
314
|
-
```
|
|
315
|
-
|
|
316
|
-
The first thing to notice is the information that the app is running in applet mode. It's a normal command-line application with a pretty decent UI, and all the logs are gone!
|
|
317
|
-
|
|
318
|
-
By default, logs are turned off in applet mode to give you a clear and distraction-free console. However, during debugging, you might want to turn them on. How? Simply add the ```--log-applet``` switch to your invocation, and your logs will be printed everywhere again.
|
|
319
|
-
|
|
320
|
-
Applets are a way for a Bajo plugin developer to help you by providing small tools for everyday life. It's up to the plugin developer to provide such applets, so don't be surprised if you don't get any built-in applets with some plugins.
|
|
321
|
-
|
|
322
|
-
### System Info
|
|
323
|
-
|
|
324
|
-
Let's install one more plugin: ```bajo-sysinfo```. This plugin is a thin wrapper around [systeminformation](https://github.com/sebhildebrandt/systeminformation) with a few twists:
|
|
325
|
-
|
|
326
|
-
- It can be called directly as an applet.
|
|
327
|
-
- It is also exported as Waibu REST API endpoints. We'll cover this later in the tutorial.
|
|
328
|
-
|
|
329
|
-
If you try to run your app as shown below (yes, you can have a value for the applet switch; for more details, please [see here](https://github.com/ardhi/bajo-cli)), you'll see something like this after the loading spinner stops:
|
|
330
|
-
|
|
331
|
-
```bash
|
|
332
|
-
$ node index.js -a bajoSysinfo:battery
|
|
333
|
-
ℹ App runs in applet mode
|
|
334
|
-
ℹ Done!
|
|
335
|
-
┌──────────────────┬─────────────────────┐
|
|
336
|
-
│ hasBattery │ true │
|
|
337
|
-
├──────────────────┼─────────────────────┤
|
|
338
|
-
│ cycleCount │ 0 │
|
|
339
|
-
├──────────────────┼─────────────────────┤
|
|
340
|
-
│ isCharging │ true │
|
|
341
|
-
├──────────────────┼─────────────────────┤
|
|
342
|
-
│ designedCapacity │ 61998 │
|
|
343
|
-
├──────────────────┼─────────────────────┤
|
|
344
|
-
│ maxCapacity │ 51686 │
|
|
345
|
-
├──────────────────┼─────────────────────┤
|
|
346
|
-
...
|
|
347
|
-
```
|
|
348
|
-
|
|
349
|
-
## More
|
|
350
|
-
|
|
351
|
-
Bajo has a lot more to offer. Get ready to dive deeper by continuing your journey with Bajo's sub-frameworks:
|
|
352
|
-
|
|
353
|
-
- [Dobo Database Management System](https://github.com/ardhi/dobo/wiki/GETTING-STARTED.md)
|
|
354
|
-
- [Waibu Web Framework](https://github.com/ardhi/waibu/wiki/GETTING-STARTED.md)
|
|
355
|
-
- [Sumba Biz Suites](https://github.com/ardhi/sumba/wiki/GETTING-STARTED.md)
|
|
356
|
-
- [Masohi Messaging](https://github.com/ardhi/masohi/wiki/GETTING-STARTED.md)
|
package/wiki/USER-GUIDE.md
DELETED
|
@@ -1,256 +0,0 @@
|
|
|
1
|
-
# User Guide
|
|
2
|
-
|
|
3
|
-
## Overview
|
|
4
|
-
|
|
5
|
-
### Definitions
|
|
6
|
-
|
|
7
|
-
Before proceeding, familiarize yourself with these terminologies used throughout this documentation:
|
|
8
|
-
|
|
9
|
-
1. ```{appDir}```: The application's project directory where all your code residese.
|
|
10
|
-
2. ```{dataDir}```: The directory for all data; defaults to ```{appDir}/data``` and is created automatically if missing.
|
|
11
|
-
3. ```{tmpDir}```: The temporary directory, defaults to the OS temporary directory.
|
|
12
|
-
4. ```{pkgName}```: The plugin's package name, typically matching its npm listing.
|
|
13
|
-
5. ```{ns}```: The plugin name or namespace, which is the camel-cased version of the package name.
|
|
14
|
-
6. ```{mainNs}```: The special main namespace and directory named main within your ```{appDir}```; this is where all application code should be written.
|
|
15
|
-
|
|
16
|
-
### Directory Structure
|
|
17
|
-
|
|
18
|
-
Your typical Bajo app directory structure should look like this:
|
|
19
|
-
|
|
20
|
-
```
|
|
21
|
-
.
|
|
22
|
-
└── {appDir}
|
|
23
|
-
├── {dataDir}
|
|
24
|
-
│ ├── config
|
|
25
|
-
│ │ ├── .plugin
|
|
26
|
-
│ │ ├── bajo.json
|
|
27
|
-
│ │ ├── main.json
|
|
28
|
-
│ │ └── ...
|
|
29
|
-
│ └── plugins
|
|
30
|
-
│ └── ...
|
|
31
|
-
├── main
|
|
32
|
-
│ ├── extend
|
|
33
|
-
│ │ └── ...
|
|
34
|
-
│ ├── index.js
|
|
35
|
-
│ └── ...
|
|
36
|
-
├── package.json
|
|
37
|
-
├── index.js
|
|
38
|
-
└── ...
|
|
39
|
-
```
|
|
40
|
-
|
|
41
|
-
1. You can move ```{dataDir}``` out of ```{appDir}``` if you want, but you need to tell Bajo where to find it. For more on this, please follow along.
|
|
42
|
-
2. ```{dataDir}``` should be the only place Bajo **writes** anything. Bajo and its plugins should **never** be allowed to write anything outside ```{dataDir}``` on their own.
|
|
43
|
-
3. ```config``` is a special directory within ```{dataDir}``` where your configuration files should reside. Inside this directory, you should find:
|
|
44
|
-
- a special file named ```.plugins``` that tells Bajo which plugins should be loaded
|
|
45
|
-
- a file named ```bajo.json``` to configure global settings
|
|
46
|
-
- all plugin-specific config files, named after their namespace
|
|
47
|
-
4. The ```main``` directory, or ```{mainNs}``` namespace, is the special plugin where you put your application code. And yes, it is actually a normal Bajo plugin! This means everything in there will be handled just like a regular plugin—it has the ability to extend other plugins, has its own config file, and more — with a few differences:
|
|
48
|
-
- it's always available and can't be disabled
|
|
49
|
-
- it's always the last one to start
|
|
50
|
-
- if this directory is missing, it will be created automatically on startup
|
|
51
|
-
- if the plugin's factory function is missing (```index.js```), it will be created dynamically
|
|
52
|
-
5. ```index.js``` is the main entry point for your app.
|
|
53
|
-
|
|
54
|
-
To set your ```{dataDir}``` somewhere else, you need to tell Bajo where to find it by using an argument switch.
|
|
55
|
-
|
|
56
|
-
Assuming your data directory is ```my-data-dir``` at the same level as your app directory, run your app like this:
|
|
57
|
-
|
|
58
|
-
```bash
|
|
59
|
-
$ node index.js --dir-data=../my-data-dir
|
|
60
|
-
```
|
|
61
|
-
|
|
62
|
-
If using program arguments seems a bit like a hassle for you, just use Bajo's [dotenv](https://github.com/motdotla/dotenv) support. Create a ```.env``` file in your app directory and put this inside:
|
|
63
|
-
|
|
64
|
-
```text
|
|
65
|
-
DIR__DATA=../my-data-dir # double underscores!!!
|
|
66
|
-
```
|
|
67
|
-
|
|
68
|
-
From now on, you can start the app just by typing:
|
|
69
|
-
|
|
70
|
-
```bash
|
|
71
|
-
$ node index.js
|
|
72
|
-
```
|
|
73
|
-
|
|
74
|
-
### Runtime
|
|
75
|
-
|
|
76
|
-
Bajo should run perfectly fine on Node.js version 20 or higher. Using the latest stable runtime is recommended. Bajo-based apps are also known to run with **Bun** without any problems. But Bajo **cannot** run on Deno due to its heavy reliance on Node.js-specific libraries and environments.
|
|
77
|
-
|
|
78
|
-
Bajo is a pure ES6 framework that utilizes dynamic imports ```a lot```. Running on a system with a fast disk (e.g., SSD) and enough RAM is highly recommended, especially when you load a lot of plugins.
|
|
79
|
-
|
|
80
|
-
## Configuration
|
|
81
|
-
|
|
82
|
-
### General Rules
|
|
83
|
-
|
|
84
|
-
- All configuration files must be placed in the ```{dataDir}/config``` subfolder.
|
|
85
|
-
- Config files must be named after their plugin namespace.
|
|
86
|
-
- The file format should be either ```.json``` or ```.js```. If a ```.js``` file is used, it must be in ES6 format and should export either a plain JavaScript object or a function. Both synchronous and asynchronous functions are supported. If it returns a function, this function will be called within its plugin scope and should return a plain JS object.
|
|
87
|
-
- Other formats (```.yml```, ```.yaml```, and ```.toml```) can also be used by installing and loading the [bajoConfig](https://github.com/ardhi/bajo-config) plugin.
|
|
88
|
-
- Other formats (```.yml```, ```.yaml``` and ```.toml```) can also be used by installing & loading [bajoConfig](https://github.com/ardhi/bajo-config) plugin
|
|
89
|
-
- The order of precedence is ```.js``` > ```.json``` > ```.yml``` > ```.yaml``` > ```.toml```. This means that if a .js file exists, it will be used instead of a .json file or any other format.
|
|
90
|
-
|
|
91
|
-
Example: bajo.json
|
|
92
|
-
```json
|
|
93
|
-
{
|
|
94
|
-
"env": "prod",
|
|
95
|
-
"log": {
|
|
96
|
-
"pretty": true,
|
|
97
|
-
"timeTaken": true
|
|
98
|
-
},
|
|
99
|
-
"lang": "id"
|
|
100
|
-
}
|
|
101
|
-
```
|
|
102
|
-
|
|
103
|
-
### Using Plugins
|
|
104
|
-
|
|
105
|
-
Plugins are what make the Bajo Framework so great and flexible: they extend app features and functionalities!
|
|
106
|
-
|
|
107
|
-
To use plugins, follow these steps:
|
|
108
|
-
|
|
109
|
-
1. Install it with ```npm install {package}```, where ```{package}``` is the plugin's package name. You can install as many plugins as you want; for a complete list of plugins, please [click here](ecosystem.md).
|
|
110
|
-
2. Optionally, create ```{dataDir}/config/{ns}.json``` to customize the plugin's settings, where ```{ns}``` is the namespace or plugin name.
|
|
111
|
-
3. Open or create ```{dataDir}/config/.plugins``` and list the plugin's ```{package}``` name in it, one per line.
|
|
112
|
-
|
|
113
|
-
For example, the text below will load ```bajo-config```, ```bajo-extra```, and ```bajo-template```:
|
|
114
|
-
|
|
115
|
-
```text
|
|
116
|
-
# .plugin file
|
|
117
|
-
bajo-config
|
|
118
|
-
bajo-extra
|
|
119
|
-
bajo-template
|
|
120
|
-
```
|
|
121
|
-
|
|
122
|
-
If you later decide to disable one or more plugins, you just need to remove them from the ```.plugins``` file or place a ```#``` hash mark in front of the package name and restart your app.
|
|
123
|
-
|
|
124
|
-
> **Warning**: Please do not confuse ```{package}``` and ```{ns}```. The plugin package is the name of the JS package listed on npm, while ```{ns}``` is the namespace or plugin name, which is basically the camel-cased version of the plugin's package name.
|
|
125
|
-
|
|
126
|
-
### Environment Support
|
|
127
|
-
|
|
128
|
-
Configuration file support for different environments is also available. All you need to do is create a ```{ns}-{env}.json``` file in your ```{dataDir}/config```, where:
|
|
129
|
-
|
|
130
|
-
- ```{ns}```: the namespace/plugin name
|
|
131
|
-
- ```{env}```: your desired environment (```dev``` or ```prod```)
|
|
132
|
-
- App-wide settings with ```bajo-{env}.json``` are also possible.
|
|
133
|
-
|
|
134
|
-
Bajo is smart enough to select which config file will be used based on the following order of precedence:
|
|
135
|
-
|
|
136
|
-
1. Use ```{ns}-{env}.json``` if the file exists.
|
|
137
|
-
2. If not, use ```{ns}.json```.
|
|
138
|
-
3. If that also doesn't exist, then use the plugin's default config values.
|
|
139
|
-
|
|
140
|
-
### Runtime Override
|
|
141
|
-
|
|
142
|
-
You can easily override ANY key-value pair setting with environment variables and program argument switches. Bajo also supports [dotenv](https://github.com/motdotla/dotenv) with a ```.env``` file.
|
|
143
|
-
|
|
144
|
-
The order of precedence is: environment variable > argument switches > config files > default, built-in values.
|
|
145
|
-
|
|
146
|
-
All values (whether they come from environment variables, argument switches, or config files) will be parsed using [dotenv-parse-variables](https://github.com/ladjs/dotenv-parse-variables), so please make sure you visit the repository to fully understand how it works.
|
|
147
|
-
|
|
148
|
-
#### dotenv
|
|
149
|
-
|
|
150
|
-
- Create or open ```{appDir}/.env```
|
|
151
|
-
- Use ```__``` (double underscores) as replacement for dots in an object.
|
|
152
|
-
- ```DIR__DATA```: Sets the ```{dataDir}``` data directory.
|
|
153
|
-
- ```DIR__TMP```: Sets ```{tmpDir}``` temporary directory.
|
|
154
|
-
- For every key in ```{ns|bajo}.json```, use its snake-cased, upper-cased version. For example:
|
|
155
|
-
- ```env: 'prod'``` → ```ENV=prod```
|
|
156
|
-
- ```log.dateFormat: 'YYYY-MM-DD'``` → ```LOG__DATE_FORMAT=YYYY-MM-DD```
|
|
157
|
-
- ```exitHandler: true``` → ```EXIT_HANDLER=true```
|
|
158
|
-
- To override a plugin's config, prepend every key in the plugin's config with the snake-cased, upper-cased version of the namespace followed by a dot. For example:
|
|
159
|
-
- ```key``` in ```myPlugin``` → ```MY_PLUGIN.KEY=...```
|
|
160
|
-
- ```key.subKey.subSubKey``` in ```myPlugin``` → ```MY_PLUGIN.KEY__SUB_KEY__SUB_SUB_KEY=...```
|
|
161
|
-
|
|
162
|
-
Example:
|
|
163
|
-
```text
|
|
164
|
-
# .env file
|
|
165
|
-
ENV=prod
|
|
166
|
-
LOG__PRETTY=true
|
|
167
|
-
LOG__TIME_TAKEN=true
|
|
168
|
-
LANG=id
|
|
169
|
-
```
|
|
170
|
-
|
|
171
|
-
#### Argument Switches
|
|
172
|
-
|
|
173
|
-
- Use switches, for example: ```node index.js --xxx=one --yyy=two```
|
|
174
|
-
- Use ```-``` as the replacement for dots in an object.
|
|
175
|
-
- ```--dir-data```: Sets the ```{dataDir}``` data directory.
|
|
176
|
-
- ```--dir-tmp```: Sets the ```{tmpDir}``` temporary directory.
|
|
177
|
-
- For every key in ```{ns|bajo}.json```, add ```--``` prefix. For example:
|
|
178
|
-
- ```env: 'prod'``` → ```--env=prod```
|
|
179
|
-
- ```log.dateFormat: 'YYYY-MM-DD'``` → ```--log-dateFormat=YYYY-MM-DD```
|
|
180
|
-
- ```exitHandler: true``` → ```--exitHandler```
|
|
181
|
-
- To override a plugin's config, prepend every key in the plugin's config with the plugin name followed by a colon ```:```. For example:
|
|
182
|
-
- ```key``` in ```myPlugin``` → ```--myPlugin:key=...```
|
|
183
|
-
- ```key.subKey.subSubKey``` in ```myPlugin``` → ```--myPlugin:key-subKey-subSubKey=...```
|
|
184
|
-
|
|
185
|
-
Example:
|
|
186
|
-
```bash
|
|
187
|
-
$ node index.js --env=prod --log-pretty --log-timeTaken --lang=id
|
|
188
|
-
```
|
|
189
|
-
|
|
190
|
-
## System Hook
|
|
191
|
-
|
|
192
|
-
A **hook** refers to a mechanism that allows you to inject a custom function to extend Bajo's functionality at specific points. These points are typically predefined by the framework, providing opportunities to execute code before, during, or after a particular operation.
|
|
193
|
-
|
|
194
|
-
### Usage
|
|
195
|
-
|
|
196
|
-
In Bajo, hooks can be created anywhere very easily. Simply call the ```runHook``` method followed by the parameters you want to pass.
|
|
197
|
-
|
|
198
|
-
The hook name is always in the form of [TNsPairs](global.html#TNsPathPairs), while its parameters are a rest parameter. This means you can pass any number of parameters to the function, or none at all.
|
|
199
|
-
|
|
200
|
-
Example:
|
|
201
|
-
|
|
202
|
-
1. In your JavaScript file, add the following code snippet:
|
|
203
|
-
|
|
204
|
-
```javascript
|
|
205
|
-
const { runHook } = this.app.bajo
|
|
206
|
-
await runHook('main:sayHello', 'Don', 'Meri', { movie: 'Jumbo', year: 2025 })
|
|
207
|
-
````
|
|
208
|
-
2. Go to directory ```{appDir}/main/extend/bajo/hook```. Create one if it doesn't exist yet.
|
|
209
|
-
3. Create file ```main@say-hello.js``` in the directory above.
|
|
210
|
-
4. Enter these lines:
|
|
211
|
-
```javascript
|
|
212
|
-
async function sayHello (...params) {
|
|
213
|
-
const [mainChar, friend, payload] = params
|
|
214
|
-
console.log(mainChar, friend, payload) // output: Don, Meri, { movie: 'Jumbo', year: 2025 }
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
export default sayHello
|
|
218
|
-
```
|
|
219
|
-
|
|
220
|
-
Note the hook name and its associated file name:
|
|
221
|
-
|
|
222
|
-
```main:sayHello``` → ```main@say-hello.js```
|
|
223
|
-
|
|
224
|
-
Because a colon (```:```) is prohibited in a file name, Bajo replaces it with the ```@``` symbol.
|
|
225
|
-
|
|
226
|
-
During the boot process, Bajo will scan for hook files and load them into the hook list. When your ```runHook``` is executed, Bajo will find its related object from the list. If such a hook exists, its function handler will be called.
|
|
227
|
-
|
|
228
|
-
### Anatomy
|
|
229
|
-
|
|
230
|
-
Many times, there are more than one handler listening for a particular hook name. Especially in a framework that uses plugins extensively like Bajo, many plugins can listen to one hook at the same time. This creates a problem with call order.
|
|
231
|
-
|
|
232
|
-
To overcome this problem, Bajo gives you the opportunity to set a ```level```. Functions with a lower level will be called earlier. Functions with no level will be assigned level 999 by default.
|
|
233
|
-
|
|
234
|
-
Now, change your ```main@say-hello.js``` file above to export an object instead of a function:
|
|
235
|
-
|
|
236
|
-
```javascript
|
|
237
|
-
const sayHello = {
|
|
238
|
-
level: 10, // <-- will get called early
|
|
239
|
-
handler: async function (...params) {
|
|
240
|
-
const [mainChar, friend, payload] = params
|
|
241
|
-
console.log(mainChar, friend, payload) // output: Don, Meri, { movie: 'Jumbo', year: 2025 }
|
|
242
|
-
}
|
|
243
|
-
}
|
|
244
|
-
```
|
|
245
|
-
|
|
246
|
-
### Caveats
|
|
247
|
-
|
|
248
|
-
Hooks give you a lot of flexibility and freedom, but you need to be aware of the following caveats:
|
|
249
|
-
|
|
250
|
-
1. You need to use an **asynchronous** function. Even if your function is synchronous, it will be called as an asynchronous one—and as you know, there is a performance degradation when using asynchronous operations
|
|
251
|
-
2. **Stay away** from using ```runHook``` inside a hook! Even though it's possible, your code will become unreadable and messy pretty soon.
|
|
252
|
-
3. It's hard to trace errors in a hook. Because of its sequential nature, if a handler that's called earlier than yours throws an error, your hook won't be called at all.
|
|
253
|
-
4. If you use so many plugins that use the hook system so extensively with so many files, your app's boot time can take much longer than it's supposed to.
|
|
254
|
-
|
|
255
|
-
My advice is to **use it wisely**. Don't use hooks unless necessary; this will make your app or plugin clean and easy to understand.
|
|
256
|
-
|