waibu 1.2.12 → 2.0.1

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 (75) hide show
  1. package/.github/FUNDING.yml +13 -0
  2. package/.github/workflows/repo-lockdown.yml +24 -0
  3. package/.jsdoc.conf.json +45 -0
  4. package/LICENSE +1 -1
  5. package/README.md +40 -7
  6. package/config-prod.json +1 -1
  7. package/docs/Waibu.html +3 -0
  8. package/docs/data/search.json +1 -0
  9. package/docs/fonts/Inconsolata-Regular.ttf +0 -0
  10. package/docs/fonts/OpenSans-Regular.ttf +0 -0
  11. package/docs/fonts/WorkSans-Bold.ttf +0 -0
  12. package/docs/global.html +3 -0
  13. package/docs/index.html +3 -0
  14. package/docs/index.js.html +566 -0
  15. package/docs/scripts/core.js +726 -0
  16. package/docs/scripts/core.min.js +23 -0
  17. package/docs/scripts/resize.js +90 -0
  18. package/docs/scripts/search.js +265 -0
  19. package/docs/scripts/search.min.js +6 -0
  20. package/docs/scripts/third-party/Apache-License-2.0.txt +202 -0
  21. package/docs/scripts/third-party/fuse.js +9 -0
  22. package/docs/scripts/third-party/hljs-line-num-original.js +369 -0
  23. package/docs/scripts/third-party/hljs-line-num.js +1 -0
  24. package/docs/scripts/third-party/hljs-original.js +5171 -0
  25. package/docs/scripts/third-party/hljs.js +1 -0
  26. package/docs/scripts/third-party/popper.js +5 -0
  27. package/docs/scripts/third-party/tippy.js +1 -0
  28. package/docs/scripts/third-party/tocbot.js +672 -0
  29. package/docs/scripts/third-party/tocbot.min.js +1 -0
  30. package/docs/static/bitcoin.jpeg +0 -0
  31. package/docs/static/home.md +31 -0
  32. package/docs/static/logo-ecosystem.png +0 -0
  33. package/docs/static/logo.png +0 -0
  34. package/docs/styles/clean-jsdoc-theme-base.css +1159 -0
  35. package/docs/styles/clean-jsdoc-theme-dark.css +412 -0
  36. package/docs/styles/clean-jsdoc-theme-light.css +482 -0
  37. package/docs/styles/clean-jsdoc-theme-scrollbar.css +30 -0
  38. package/docs/styles/clean-jsdoc-theme-without-scrollbar.min.css +1 -0
  39. package/docs/styles/clean-jsdoc-theme.min.css +1 -0
  40. package/extend/bajo/hook/waibu@on-close.js +5 -0
  41. package/extend/bajo/hook/waibu@on-ready.js +5 -0
  42. package/{bajo/hook/on-request.js → extend/bajo/hook/waibu@on-request.js} +6 -6
  43. package/{bajo/hook/on-response.js → extend/bajo/hook/waibu@on-response.js} +2 -2
  44. package/index.js +267 -51
  45. package/lib/app-hook.js +2 -2
  46. package/lib/app.js +9 -8
  47. package/lib/build-locals.js +8 -8
  48. package/lib/collect-route-path-handlers.js +3 -2
  49. package/lib/handle-download.js +2 -2
  50. package/lib/handle-forward.js +1 -1
  51. package/lib/home.js +2 -2
  52. package/lib/{log-routes.js → print-routes.js} +3 -3
  53. package/lib/webapp-scope/attach-intl.js +4 -4
  54. package/lib/webapp-scope/handle-compress.js +1 -1
  55. package/lib/webapp-scope/handle-cors.js +1 -1
  56. package/lib/webapp-scope/handle-helmet.js +1 -1
  57. package/lib/webapp-scope/handle-multipart-body.js +3 -3
  58. package/lib/webapp-scope/handle-rate-limit.js +2 -2
  59. package/lib/webapp-scope/is-route-disabled.js +9 -5
  60. package/lib/webapp-scope/merge-route-hooks.js +2 -2
  61. package/lib/webapp-scope/route-hook.js +1 -1
  62. package/package.json +7 -2
  63. package/wiki/CONFIG.md +45 -0
  64. package/wiki/CONTRIBUTING.md +5 -0
  65. package/wiki/DEV-GUIDE.md +1 -0
  66. package/wiki/ECOSYSTEM.md +19 -0
  67. package/wiki/GETTING-STARTED.md +76 -0
  68. package/wiki/USER-GUIDE.md +1 -0
  69. package/bajo/hook/on-close.js +0 -5
  70. package/bajo/hook/on-ready.js +0 -5
  71. /package/{bajo/hook/on-route.js → extend/bajo/hook/waibu@on-route.js} +0 -0
  72. /package/{bajo → extend/bajo}/intl/en-US.json +0 -0
  73. /package/{bajo → extend/bajo}/intl/id.json +0 -0
  74. /package/{bajoTemplate → extend/bajoTemplate}/layout/email.html +0 -0
  75. /package/{bajoTemplate → extend/bajoTemplate}/layout/email.id.html +0 -0
package/index.js CHANGED
@@ -2,7 +2,7 @@ import collectRoutePathHandlers from './lib/collect-route-path-handlers.js'
2
2
  import fastify from 'fastify'
3
3
  import appHook from './lib/app-hook.js'
4
4
  import routeHook from './lib/webapp-scope/route-hook.js'
5
- import logRoutes from './lib/log-routes.js'
5
+ import printRoutes from './lib/print-routes.js'
6
6
  import { boot } from './lib/app.js'
7
7
  import sensible from '@fastify/sensible'
8
8
  import noIcon from 'fastify-no-icon'
@@ -12,15 +12,83 @@ import handleRedirect from './lib/handle-redirect.js'
12
12
  import buildLocals from './lib/build-locals.js'
13
13
  import queryString from 'query-string'
14
14
 
15
+ /**
16
+ * @typedef TEscapeChars
17
+ * @type {Object}
18
+ * @memberof Waibu
19
+ * @property {string} <=<
20
+ * @property {string} >=>
21
+ * @property {string} "="
22
+ * @property {string} '='
23
+ */
24
+
25
+ /**
26
+ * Plugin factory
27
+ *
28
+ * @param {string} pkgName - NPM package name
29
+ * @returns {class}
30
+ */
15
31
  async function factory (pkgName) {
16
32
  const me = this
17
33
 
18
- return class Waibu extends this.lib.BajoPlugin {
34
+ /**
35
+ * Waibu Web Framework plugin for Bajo. This is the main foundation of all web apps attached to
36
+ * the system through a route prefix. Those web apps are then build as childrens with
37
+ * its own fastify's context.
38
+ *
39
+ * There are currently 3 web apps available:
40
+ * - {@link https://github.com/ardhi/waibu-static|waibu-static} for static content delivery
41
+ * - {@link https://github.com/ardhi/waibu-rest-api|waibu-rest-api} for rest api setup
42
+ * - and {@link https://github.com/ardhi/waibu-mpa|waibu-mpa} for normal multi-page application
43
+ *
44
+ * You should write your code as the extension of above web apps. Not to this main app.
45
+ * Unless, of course, if you want to write custom web apps with its own context.
46
+ *
47
+ * @class
48
+ */
49
+ class Waibu extends this.app.pluginClass.base {
50
+ /**
51
+ * @constant {string[]}
52
+ * @default ['onRequest', 'onResponse', 'preParsing', 'preValidation', 'preHandler', 'preSerialization', 'onSend', 'onTimeout', 'onError']
53
+ * @memberof Waibu
54
+ */
55
+ static hookTypes = ['onRequest', 'onResponse', 'preParsing', 'preValidation', 'preHandler',
56
+ 'preSerialization', 'onSend', 'onTimeout', 'onError']
57
+
58
+ /**
59
+ * @constant {string}
60
+ * @memberof Waibu
61
+ * @default 'w'
62
+ */
63
+ static alias = 'w'
64
+
65
+ /**
66
+ * @constant {string[]}
67
+ * @default ['bajo-extra']
68
+ * @memberof Waibu
69
+ */
70
+ static dependencies = ['bajo-extra']
71
+
72
+ /**
73
+ * @constant {TEscapeChars}
74
+ * @memberof Waibu
75
+ */
76
+ static escapeChars = {
77
+ '<': '&lt;',
78
+ '>': '&gt;',
79
+ '"': '&quot;',
80
+ "'": '&apos;'
81
+ }
82
+
19
83
  constructor () {
20
84
  super(pkgName, me.app)
21
- this.alias = 'w'
22
- this.dependencies = ['bajo-extra']
85
+
86
+ /**
87
+ * @see {@tutorial config}
88
+ * @type {Object}
89
+ */
23
90
  this.config = {
91
+ home: undefined,
24
92
  server: {
25
93
  host: '127.0.0.1',
26
94
  port: 7771
@@ -46,7 +114,7 @@ async function factory (pkgName) {
46
114
  lang: 'lang'
47
115
  },
48
116
  paramsCharMap: {},
49
- logRoutes: true,
117
+ printRoutes: true,
50
118
  pageTitleFormat: '%s : %s',
51
119
  siteInfo: {
52
120
  title: 'My Website',
@@ -77,12 +145,6 @@ async function factory (pkgName) {
77
145
  }
78
146
  }
79
147
  }
80
- this.escapeChars = {
81
- '<': '&lt;',
82
- '>': '&gt;',
83
- '"': '&quot;',
84
- "'": '&apos;'
85
- }
86
148
  this.qs = {
87
149
  parse: (item) => {
88
150
  return queryString.parse(item, {
@@ -94,15 +156,25 @@ async function factory (pkgName) {
94
156
  stringify: queryString.stringify,
95
157
  stringifyUrl: queryString.stringifyUrl
96
158
  }
97
- this.hookTypes = ['onRequest', 'onResponse', 'preParsing', 'preValidation', 'preHandler',
98
- 'preSerialization', 'onSend', 'onTimeout', 'onError']
99
159
  }
100
160
 
161
+ /**
162
+ * Initialize plugin
163
+ *
164
+ * @method
165
+ * @async
166
+ */
101
167
  init = async () => {
102
168
  if (this.config.home === '/') this.config.home = false
103
169
  await collectRoutePathHandlers.call(this)
104
170
  }
105
171
 
172
+ /**
173
+ * Start plugin
174
+ *
175
+ * @method
176
+ * @async
177
+ */
106
178
  start = async () => {
107
179
  const { generateId, runHook } = this.app.bajo
108
180
  const cfg = this.getConfig()
@@ -132,21 +204,33 @@ async function factory (pkgName) {
132
204
  await handleRedirect.call(this, instance)
133
205
  await handleForward.call(this, instance)
134
206
  await appHook.call(this)
135
- await routeHook.call(this, this.name)
207
+ await routeHook.call(this, this.ns)
136
208
  await boot.call(this)
137
209
  await instance.listen(cfg.server)
138
- if (cfg.logRoutes) logRoutes.call(this)
210
+ if (cfg.printRoutes) printRoutes.call(this)
139
211
  }
140
212
 
213
+ /**
214
+ * Exit handler
215
+ *
216
+ * @method
217
+ * @async
218
+ */
141
219
  exit = async () => {
142
220
  this.instance.close()
143
221
  }
144
222
 
145
- findRoute = (route) => {
146
- const { outmatch } = this.lib
147
- const { find } = this.lib._
223
+ /**
224
+ * Find route by route name
225
+ *
226
+ * @param {string} name - ns based route name
227
+ * @returns {Object} Route object
228
+ */
229
+ findRoute = (name) => {
230
+ const { outmatch } = this.app.lib
231
+ const { find } = this.app.lib._
148
232
  const { breakNsPath } = this.app.bajo
149
- let { ns, subNs = '', path } = breakNsPath(route)
233
+ let { ns, subNs = '', path } = breakNsPath(name)
150
234
  const params = path.split('|')
151
235
  if (params.length > 1) path = params[0]
152
236
  return find(this.routes, r => {
@@ -158,15 +242,37 @@ async function factory (pkgName) {
158
242
  })
159
243
  }
160
244
 
245
+ get escapeChars () {
246
+ return this.constructor.escapeChars
247
+ }
248
+
249
+ /**
250
+ * Escape text
251
+ *
252
+ * @method
253
+ * @param {string} text
254
+ * @returns {string}
255
+ */
161
256
  escape = (text = '') => {
162
257
  if (typeof text !== 'string') return text
163
- const { forOwn } = this.lib._
258
+ const { forOwn } = this.app.lib._
164
259
  forOwn(this.escapeChars, (v, k) => {
165
260
  text = text.replaceAll(k, v)
166
261
  })
167
262
  return text
168
263
  }
169
264
 
265
+ /**
266
+ * Fetch something from url. A wrapper of bajo-extra's fetchUrl which support
267
+ * bajo's ns based url.
268
+ *
269
+ * @method
270
+ * @async
271
+ * @param {string} url - Also support ns based url
272
+ * @param {Object} [opts={}] - node's fetch options
273
+ * @param {Object} [extra={}] - See {@link https://ardhi.github.io/bajo-extra|bajo-extra}
274
+ * @returns {Object}
275
+ */
170
276
  fetch = async (url, opts = {}, extra = {}) => {
171
277
  const { fetch } = this.app.bajoExtra
172
278
  extra.rawResponse = true
@@ -183,22 +289,43 @@ async function factory (pkgName) {
183
289
  return result
184
290
  }
185
291
 
292
+ /**
293
+ * Get visitor IP from fastify's request object
294
+ *
295
+ * @method
296
+ * @param {Object} req - request object
297
+ * @returns {string}
298
+ */
186
299
  getIp = (req) => {
187
- const { isEmpty } = this.lib._
300
+ const { isEmpty } = this.app.lib._
188
301
  let fwd = req.headers['x-forwarded-for'] ?? ''
189
302
  if (!Array.isArray(fwd)) fwd = fwd.split(',').map(ip => ip.trim())
190
303
  return isEmpty(fwd[0]) ? req.ip : fwd[0]
191
304
  }
192
305
 
306
+ /**
307
+ * Get origin of fastify's request object
308
+ *
309
+ * @method
310
+ * @param {Object} req
311
+ * @returns {string}
312
+ */
193
313
  getOrigin = (req) => {
194
- const { isEmpty } = this.lib._
314
+ const { isEmpty } = this.app.lib._
195
315
  let host = req.host
196
316
  if (isEmpty(host) || host === ':authority') host = `${this.config.server.host}:${this.config.server.port}`
197
317
  return `${req.protocol}://${host}`
198
318
  }
199
319
 
320
+ /**
321
+ * Get plugin by prefix
322
+ *
323
+ * @method
324
+ * @param {string} prefix
325
+ * @returns {Object}
326
+ */
200
327
  getPluginByPrefix = (prefix) => {
201
- const { get, find } = this.lib._
328
+ const { get, find } = this.app.lib._
202
329
  const item = find(this.app.waibu.routes, r => {
203
330
  return get(r, 'config.prefix') === prefix
204
331
  })
@@ -206,19 +333,34 @@ async function factory (pkgName) {
206
333
  if (ns) return this.app[ns]
207
334
  }
208
335
 
209
- getPluginPrefix = (base, webApp = 'waibuMpa') => {
210
- const { get, trim } = this.lib._
211
- let prefix = get(this, `app.${base}.config.waibu.prefix`, this.app[base].alias)
212
- if (base === 'main') {
336
+ /**
337
+ * Get plugin's prefix by name
338
+ *
339
+ * @method
340
+ * @param {string} name - Plugin's name
341
+ * @param {string} [webApp=waibuMpa] - Web app to use
342
+ * @returns {string}
343
+ */
344
+ getPluginPrefix = (name, webApp = 'waibuMpa') => {
345
+ const { get, trim } = this.app.lib._
346
+ let prefix = get(this, `app.${name}.config.${webApp}.prefix`, get(this, `app.${name}.config.waibu.prefix`, this.app[name].alias))
347
+ if (name === 'main') {
213
348
  const cfg = this.app[webApp].config
214
349
  if (cfg.mountMainAsRoot) prefix = ''
215
350
  }
216
-
217
351
  return trim(prefix, '/')
218
352
  }
219
353
 
220
- getRoutes = (grouped, lite) => {
221
- const { groupBy, orderBy, mapValues, map, pick } = this.lib._
354
+ /**
355
+ * Get all available routes
356
+ *
357
+ * @method
358
+ * @param {boolean} [grouped=false] - Returns as groups of urls and methods
359
+ * @param {*} [lite=false] - Retuns only urls and methods
360
+ * @returns {Array}
361
+ */
362
+ getRoutes = (grouped = false, lite = false) => {
363
+ const { groupBy, orderBy, mapValues, map, pick } = this.app.lib._
222
364
  const all = this.routes
223
365
  let routes
224
366
  if (grouped) {
@@ -229,18 +371,34 @@ async function factory (pkgName) {
229
371
  return routes
230
372
  }
231
373
 
232
- getUploadedFiles = async (reqId, fileUrl, returnDir) => {
374
+ /**
375
+ * Get uploaded files by request ID
376
+ *
377
+ * @method
378
+ * @param {string} reqId - Request ID
379
+ * @param {boolean} [fileUrl=false] - If ```true```, files returned as file url format (```file:///...```)
380
+ * @param {*} returnDir - If ```true```, also return its directory
381
+ * @returns {(Object|Array)} - Returns object if ```returnDir``` is ```true```, array of files otherwise
382
+ */
383
+ getUploadedFiles = async (reqId, fileUrl = false, returnDir = false) => {
233
384
  const { getPluginDataDir, resolvePath } = this.app.bajo
234
- const { fastGlob } = this.lib
235
- const dir = `${getPluginDataDir(this.name)}/upload/${reqId}`
385
+ const { fastGlob } = this.app.lib
386
+ const dir = `${getPluginDataDir(this.ns)}/upload/${reqId}`
236
387
  const result = await fastGlob(`${dir}/*`)
237
388
  if (!fileUrl) return returnDir ? { dir, files: result } : result
238
389
  const files = result.map(f => resolvePath(f, true))
239
390
  return returnDir ? { dir, files } : files
240
391
  }
241
392
 
393
+ /**
394
+ * Is namespace's path contains language detector token?
395
+ *
396
+ * @method
397
+ * @param {string} ns - Plugin name
398
+ * @returns {boolean}
399
+ */
242
400
  isIntlPath = (ns) => {
243
- const { get } = this.lib._
401
+ const { get } = this.app.lib._
244
402
  return get(this.app[ns], 'config.intl.detectors', []).includes('path')
245
403
  }
246
404
 
@@ -248,6 +406,13 @@ async function factory (pkgName) {
248
406
  throw this.error('_notFound', { path: name })
249
407
  }
250
408
 
409
+ /**
410
+ * Parse filter found from Fastify's request based on keys set in config object
411
+ *
412
+ * @method
413
+ * @param {Object} req - Request object
414
+ * @returns {Object}
415
+ */
251
416
  parseFilter = (req) => {
252
417
  const result = {}
253
418
  const items = Object.keys(this.config.qsKey)
@@ -257,26 +422,45 @@ async function factory (pkgName) {
257
422
  return result
258
423
  }
259
424
 
260
- routeDir = (ns, base) => {
261
- const { get } = this.lib._
262
- if (!base) base = ns
263
- const cfg = this.app[base].config
264
- const prefix = get(cfg, 'waibu.prefix', this.app[base].alias)
425
+ /**
426
+ * Get route directory by plugin's name
427
+ *
428
+ * @param {*} ns - Namespace
429
+ * @param {*} [baseNs] - Base namespace. If not provided, defaults to scope's ns
430
+ * @returns {string}
431
+ */
432
+ routeDir = (ns, baseNs) => {
433
+ const { get } = this.app.lib._
434
+ if (!baseNs) baseNs = ns
435
+ const cfg = this.app[baseNs].config
436
+ const prefix = get(cfg, 'waibu.prefix', this.app[baseNs].alias)
265
437
  const dir = prefix === '' ? '' : `/${prefix}`
266
- if (!ns) return dir
267
438
  const cfgMpa = get(this, 'app.waibuMpa.config')
268
- if (ns === this.app.bajo.mainNs && cfgMpa.mountMainAsRoot) return ''
269
- if (ns === base) return dir
439
+ if (ns === this.app.mainNs && cfgMpa.mountMainAsRoot) return ''
440
+ if (ns === baseNs) return dir
270
441
  return dir + `/${get(this.app[ns].config, 'waibu.prefix', this.app[ns].alias)}`
271
442
  }
272
443
 
273
- routePath = (name = '', options = {}) => {
444
+ /**
445
+ * Get route path by route's name:
446
+ * - If it is a ```mailto:``` or ```tel:``` url, it returns as is
447
+ * - If it is a ns based name, it will be parsed first
448
+ *
449
+ * @method
450
+ * @param {string} name
451
+ * @param {Object} [options={}] - Options object
452
+ * @param {string} [options.base=waibu] - Base namespace
453
+ * @param {boolean} [options.guessHost] - If true, guest host if host is not set
454
+ * @param {Object} [options.query={}] - Query string's object. If provided, it will be added to returned value
455
+ * @param {Object} [options.params={}] - Parameter object. If provided, it will be merged to returned value
456
+ * @returns {string}
457
+ */
458
+ routePath = (name, options = {}) => {
274
459
  const { getPlugin } = this.app.bajo
275
- const { defaultsDeep } = this.lib.aneka
276
- const { isEmpty, get, trimEnd, trimStart } = this.lib._
460
+ const { defaultsDeep } = this.app.lib.aneka
461
+ const { isEmpty, get, trimEnd, trimStart } = this.app.lib._
277
462
  const { breakNsPath } = this.app.bajo
278
- const { query = {}, base = 'waibu', params = {}, guessHost } = options
279
- if (isEmpty(name)) return name
463
+ const { query = {}, base = this.ns, params = {}, guessHost } = options
280
464
 
281
465
  const plugin = getPlugin(base)
282
466
  const cfg = plugin.config ?? {}
@@ -304,10 +488,22 @@ async function factory (pkgName) {
304
488
  return url
305
489
  }
306
490
 
491
+ /**
492
+ * Method to send mail through Masohi Messaging System. It is a thin wrapper
493
+ * for {@link https://github.com/ardhi/masohi-mail|masohi-mail} send method.
494
+ *
495
+ * If masohi is not loaded, nothing is delivered.
496
+ *
497
+ * @method
498
+ * @async
499
+ * @param {(string|Array)} tpl - Mail's template to use. If a string is given, the same template will be used for html & plaintext versions. Otherwise, the first template will be used for html mail, and the second one is for it's plaintext version
500
+ * @param {Object} [params={}] - {@link https://github.com/ardhi/masohi-mail|masohi-mail}'s params object.
501
+ * @returns
502
+ */
307
503
  sendMail = async (tpl, { to, cc, bcc, from, subject, data = {}, conn, source, options = {} }) => {
308
504
  conn = conn ?? 'masohiMail:default'
309
505
  if (!this.app.masohi || !this.app.masohiMail) return
310
- const { get, isString } = this.lib._
506
+ const { get, isString } = this.app.lib._
311
507
  const { generateId } = this.app.bajo
312
508
  const { render } = this.app.bajoTemplate
313
509
  if (isString(tpl)) tpl = [tpl]
@@ -319,11 +515,22 @@ async function factory (pkgName) {
319
515
  const message = await render(tpl[0], locals, opts)
320
516
  if (tpl[1]) opts.messageText = await render(tpl[1], locals, opts)
321
517
  const payload = { type: 'object', data: { to, cc, bcc, from, subject, message, options: opts } }
322
- await this.app.masohi.send({ payload, source: source ?? this.name, conn }) // mail sent through worker
518
+ await this.app.masohi.send({ payload, source: source ?? this.ns, conn }) // mail sent through worker
323
519
  }
324
520
 
521
+ /**
522
+ * Recursively unescape block of texts
523
+ *
524
+ * @method
525
+ * @param {string} content - Source content
526
+ * @param {string} start - Block's start
527
+ * @param {string} end - Block's end
528
+ * @param {string} startReplacer - Token to use as block's start replacer
529
+ * @param {string} endReplacer - Token to use as block's end replacer
530
+ * @returns {string}
531
+ */
325
532
  unescapeBlock = (content, start, end, startReplacer, endReplacer) => {
326
- const { extractText } = this.lib.aneka
533
+ const { extractText } = this.app.lib.aneka
327
534
  const { result } = extractText(content, start, end)
328
535
  if (result.length === 0) return content
329
536
  const unescaped = this.unescape(result)
@@ -333,8 +540,15 @@ async function factory (pkgName) {
333
540
  return this.unescapeBlock(block, start, end, startReplacer, endReplacer)
334
541
  }
335
542
 
543
+ /**
544
+ * Unescape text using {@link TEscapeChars} rules
545
+ *
546
+ * @method
547
+ * @param {string} text - Text to unescape
548
+ * @returns {string}
549
+ */
336
550
  unescape = (text) => {
337
- const { forOwn, invert } = this.lib._
551
+ const { forOwn, invert } = this.app.lib._
338
552
  const mapping = invert(this.escapeChars)
339
553
  forOwn(mapping, (v, k) => {
340
554
  text = text.replaceAll(k, v)
@@ -342,6 +556,8 @@ async function factory (pkgName) {
342
556
  return text
343
557
  }
344
558
  }
559
+
560
+ return Waibu
345
561
  }
346
562
 
347
563
  export default factory
package/lib/app-hook.js CHANGED
@@ -5,8 +5,8 @@ async function appHook () {
5
5
  for (const hook of hooks) {
6
6
  me.instance.addHook(hook, async function (...args) {
7
7
  args.push(this)
8
- if (['onClose', 'onReady'].includes(hook)) await runHook(`${me.name}:${hook}`, ...args)
9
- else await runHook(`${me.name}:${hook}`, ...args)
8
+ if (['onClose', 'onReady'].includes(hook)) await runHook(`${me.ns}:${hook}`, ...args)
9
+ else await runHook(`${me.ns}:${hook}`, ...args)
10
10
  })
11
11
  }
12
12
  }
package/lib/app.js CHANGED
@@ -2,11 +2,12 @@ import home from './home.js'
2
2
 
3
3
  export async function collect (glob = 'boot.js', baseNs) {
4
4
  const { eachPlugins, importModule } = this.app.bajo
5
- const { orderBy, get } = this.lib._
6
- if (!baseNs) baseNs = this.name
5
+ const { orderBy, get } = this.app.lib._
6
+ if (!baseNs) baseNs = this.ns
7
7
  const mods = []
8
8
 
9
- await eachPlugins(async function ({ config, file, ns, alias }) {
9
+ await eachPlugins(async function ({ file }) {
10
+ const { ns, alias, config } = this
10
11
  const mod = await importModule(file, { asHandler: true })
11
12
  mod.prefix = get(config, 'waibu.prefix', alias)
12
13
  if (get(config, 'intl.detectors', []).includes('path')) mod.prefix = `/:lang${mod.prefix}`
@@ -26,23 +27,23 @@ export async function collect (glob = 'boot.js', baseNs) {
26
27
  export async function boot () {
27
28
  const { runHook } = this.app.bajo
28
29
  const mods = await collect.call(this)
29
- await runHook(`${this.name}:beforeAppBoot`)
30
+ await runHook(`${this.ns}:beforeAppBoot`)
30
31
  for (const m of mods) {
31
32
  const disabled = this.app[m.ns].config.disabled
32
33
  if (Array.isArray(disabled) && disabled.length === 1 && ['*', 'all'].includes(disabled[0])) {
33
34
  this.log.warn('allRoutesConfigDisabled%s', m.ns)
34
35
  continue
35
36
  }
36
- await runHook(`${this.name}.${m.ns}:beforeAppBoot`)
37
+ await runHook(`${this.ns}.${m.ns}:beforeAppBoot`)
37
38
  this.log.debug('bootApp%s', m.ns)
38
39
  await this.instance.register(async (ctx) => {
39
40
  const plugin = this.app[m.ns]
40
41
  plugin.instance = ctx
41
- await runHook(`${plugin.name}:afterCreateContext`, ctx)
42
+ await runHook(`${plugin.ns}:afterCreateContext`, ctx)
42
43
  await m.handler.call(plugin, ctx, m.prefix)
43
44
  }, { prefix: m.prefix })
44
- await runHook(`${this.name}.${m.ns}:afterAppBoot`)
45
+ await runHook(`${this.ns}.${m.ns}:afterAppBoot`)
45
46
  }
46
- await runHook(`${this.name}:afterAppBoot`)
47
+ await runHook(`${this.ns}:afterAppBoot`)
47
48
  await home.call(this)
48
49
  }
@@ -1,8 +1,8 @@
1
1
  async function buildHomesMenu (req) {
2
2
  const { callHandler } = this.app.bajo
3
- const { get, find, orderBy } = this.lib._
3
+ const { get, find, orderBy } = this.app.lib._
4
4
  const routes = []
5
- for (const ns of this.app.bajo.pluginNames) {
5
+ for (const ns of this.app.getAllNs()) {
6
6
  let home = get(this, `app.${ns}.config.waibuMpa.home`)
7
7
  const homeHandler = get(this, `app.${ns}.config.waibuMpa.homeHandler`)
8
8
  if (homeHandler) home = await callHandler(this.app[ns], homeHandler)
@@ -14,15 +14,15 @@ async function buildHomesMenu (req) {
14
14
  }
15
15
 
16
16
  async function buildPagesMenu (req) {
17
- const { find, orderBy, merge, isString } = this.lib._
17
+ const { find, orderBy, merge, isString } = this.app.lib._
18
18
  const { callHandler, runHook } = this.app.bajo
19
19
  const all = [{ icon: 'house', href: '/', level: 1 }]
20
- for (const ns of this.app.bajo.pluginNames) {
20
+ for (const ns of this.app.getAllNs()) {
21
21
  const items = []
22
22
  let pages = this.app[ns].getConfig('waibuMpa.menuHandler', { defValue: [] })
23
23
  if (isString(pages)) pages = await callHandler(this.app[ns], pages, req)
24
24
  if (pages.length === 0) continue
25
- await runHook(`${this.name}.${ns}:afterBuildPagesMenu`, pages, req)
25
+ await runHook(`${this.ns}.${ns}:afterBuildPagesMenu`, pages, req)
26
26
  for (const page of pages) {
27
27
  const existing = find(all, { title: page.title })
28
28
  page.level = page.level ?? 1000
@@ -51,7 +51,7 @@ async function buildPagesMenu (req) {
51
51
  }
52
52
  async function buildLocals ({ tpl, params = {}, opts = {} } = {}) {
53
53
  const { runHook } = this.app.bajo
54
- const { set, merge, pick, get, isEmpty, find } = this.lib._
54
+ const { set, merge, pick, get, isEmpty, find } = this.app.lib._
55
55
  const { req, reply } = opts
56
56
 
57
57
  const appTitle = this.app.waibuMpa ? req.t(this.app.waibuMpa.getAppTitle(req.ns)) : ''
@@ -86,8 +86,8 @@ async function buildLocals ({ tpl, params = {}, opts = {} } = {}) {
86
86
  set(params, 'menu.homes', await buildHomesMenu.call(this, req))
87
87
  set(params, 'menu.pages', await buildPagesMenu.call(this, req))
88
88
  const merged = merge({}, params, { _meta })
89
- await runHook(`${this.name}:afterBuildLocals`, merged, req)
90
- if (!isEmpty(routeOpts.ns)) await runHook(`${this.name}.${routeOpts.ns}:afterBuildLocals`, merged, req)
89
+ await runHook(`${this.ns}:afterBuildLocals`, merged, req)
90
+ if (!isEmpty(routeOpts.ns)) await runHook(`${this.ns}.${routeOpts.ns}:afterBuildLocals`, merged, req)
91
91
  return merged
92
92
  }
93
93
 
@@ -1,10 +1,11 @@
1
1
  async function collectRoutePathHandlers () {
2
2
  const { eachPlugins } = this.app.bajo
3
- const { isEmpty } = this.lib._
3
+ const { isEmpty } = this.app.lib._
4
4
  this.routePathHandlers = this.routePathHandlers ?? {}
5
5
  const me = this
6
6
 
7
- await eachPlugins(async function ({ ns }) {
7
+ await eachPlugins(async function () {
8
+ const { ns } = this
8
9
  if (isEmpty(this.routePathHandlers) || !this.routePath) return undefined
9
10
  for (const key of this.routePathHandlers) {
10
11
  me.routePathHandlers[key] = { handler: this.routePath, ns }
@@ -2,9 +2,9 @@ import path from 'path'
2
2
 
3
3
  async function handleDownload (input, req, reply) {
4
4
  const { importPkg } = this.app.bajo
5
- const { isFunction } = this.lib._
5
+ const { isFunction } = this.app.lib._
6
6
  const mime = await importPkg('waibu:mime')
7
- const { fs } = this.lib
7
+ const { fs } = this.app.lib
8
8
  const file = isFunction(input) ? (await input.call(this, req)) : input
9
9
  if (!fs.existsSync(file)) throw this.error('_notFound')
10
10
  const mimeType = mime.getType(path.extname(file))
@@ -1,7 +1,7 @@
1
1
  import replyFrom from '@fastify/reply-from'
2
2
 
3
3
  async function handleForward (ctx) {
4
- const { defaultsDeep } = this.lib.aneka
4
+ const { defaultsDeep } = this.app.lib.aneka
5
5
  const me = this
6
6
 
7
7
  function rewriteHeaders (headers, req) {
package/lib/home.js CHANGED
@@ -1,7 +1,7 @@
1
1
  async function home () {
2
2
  const { callHandler } = this.app.bajo
3
- const { defaultsDeep } = this.lib.aneka
4
- const { isString, pick } = this.lib._
3
+ const { defaultsDeep } = this.app.lib.aneka
4
+ const { isString, pick } = this.app.lib._
5
5
  const config = this.getConfig()
6
6
  if (config.home) {
7
7
  if (isString(config.home)) config.home = { path: config.home }
@@ -1,5 +1,5 @@
1
1
  function printRoutes () {
2
- const { findIndex, orderBy, isArray } = this.lib._
2
+ const { findIndex, orderBy, isArray } = this.app.lib._
3
3
  let items = []
4
4
  this.routes.forEach(r => {
5
5
  const idx = findIndex(items, { url: r.url })
@@ -10,9 +10,9 @@ function printRoutes () {
10
10
  }
11
11
  })
12
12
  items = orderBy(items, ['url'])
13
- this.log.debug('loaded%s', this.print.write('routesL'))
13
+ this.log.trace('loaded%s', this.t('routesL'))
14
14
  items.forEach(item => {
15
- this.log.debug('- %s (%s)', item.url, item.methods.join('|'))
15
+ this.log.trace('- %s (%s)', item.url, item.methods.join('|'))
16
16
  })
17
17
  }
18
18